From baa0792b3cef826c96cf967d9d7722000b71f9c1 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Thu, 17 Nov 2022 17:10:06 -0500 Subject: [PATCH 001/340] set main back to dev mode (#1739) --- CHANGELOG.md | 2 ++ charts/consul/Chart.yaml | 6 +++--- charts/consul/values.yaml | 2 +- cli/version/version.go | 4 ++-- control-plane/version/version.go | 4 ++-- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d7e3d6198..5040bdabf8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +## UNRELEASED + ## 1.0.0 (November 17, 2022) BREAKING CHANGES: diff --git a/charts/consul/Chart.yaml b/charts/consul/Chart.yaml index 2eabc1fc9f..a8e1505280 100644 --- a/charts/consul/Chart.yaml +++ b/charts/consul/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 name: consul -version: 1.0.0 +version: 1.1.0-dev appVersion: 1.14.0 kubeVersion: ">=1.21.0-0" description: Official HashiCorp Consul Chart @@ -10,12 +10,12 @@ sources: - https://github.com/hashicorp/consul - https://github.com/hashicorp/consul-k8s annotations: - artifacthub.io/prerelease: false + artifacthub.io/prerelease: true artifacthub.io/images: | - name: consul image: hashicorp/consul:1.14.0 - name: consul-k8s-control-plane - image: hashicorp/consul-k8s-control-plane:1.0.0 + image: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.1.0-dev - name: consul-dataplane image: hashicorp/consul-dataplane:1.0.0 - name: envoy diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 1daec76b9f..bdf66a53a6 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -83,7 +83,7 @@ global: # image that is used for functionality such as catalog sync. # This can be overridden per component. # @default: hashicorp/consul-k8s-control-plane: - imageK8S: hashicorp/consul-k8s-control-plane:1.0.0 + imageK8S: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.1.0-dev # The name of the datacenter that the agents should # register as. This can't be changed once the Consul cluster is up and running diff --git a/cli/version/version.go b/cli/version/version.go index 591d8ccca0..933f072f35 100644 --- a/cli/version/version.go +++ b/cli/version/version.go @@ -14,12 +14,12 @@ var ( // // Version must conform to the format expected by // github.com/hashicorp/go-version for tests to work. - Version = "1.0.0" + Version = "1.1.0" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release // such as "dev" (in development), "beta", "rc1", etc. - VersionPrerelease = "" + VersionPrerelease = "dev" ) // GetHumanVersion composes the parts of the version in a way that's suitable diff --git a/control-plane/version/version.go b/control-plane/version/version.go index 591d8ccca0..933f072f35 100644 --- a/control-plane/version/version.go +++ b/control-plane/version/version.go @@ -14,12 +14,12 @@ var ( // // Version must conform to the format expected by // github.com/hashicorp/go-version for tests to work. - Version = "1.0.0" + Version = "1.1.0" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release // such as "dev" (in development), "beta", "rc1", etc. - VersionPrerelease = "" + VersionPrerelease = "dev" ) // GetHumanVersion composes the parts of the version in a way that's suitable From 831457765218143bad33d1a35542227fe65be4f6 Mon Sep 17 00:00:00 2001 From: Andrew Stucki Date: Fri, 18 Nov 2022 14:28:43 -0500 Subject: [PATCH 002/340] Add fix for api-gateway when using system-wide trusted CAs for external servers --- .../api-gateway-controller-deployment.yaml | 2 ++ .../api-gateway-controller-deployment.bats | 29 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/charts/consul/templates/api-gateway-controller-deployment.yaml b/charts/consul/templates/api-gateway-controller-deployment.yaml index 1e12df90a4..52884f725b 100644 --- a/charts/consul/templates/api-gateway-controller-deployment.yaml +++ b/charts/consul/templates/api-gateway-controller-deployment.yaml @@ -57,9 +57,11 @@ spec: protocol: TCP env: {{- if .Values.global.tls.enabled }} + {{- if or (not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots)) .Values.client.enabled }} - name: CONSUL_CACERT value: /consul/tls/ca/tls.crt {{- end }} + {{- end }} - name: HOST_IP valueFrom: fieldRef: diff --git a/charts/consul/test/unit/api-gateway-controller-deployment.bats b/charts/consul/test/unit/api-gateway-controller-deployment.bats index fe150fe158..b61486c2ed 100755 --- a/charts/consul/test/unit/api-gateway-controller-deployment.bats +++ b/charts/consul/test/unit/api-gateway-controller-deployment.bats @@ -1370,3 +1370,32 @@ load _helpers yq '.spec.template.spec.containers[0].env[3]' | tee /dev/stderr) [ "${actual}" = "null" ] } + +@test "apiGateway/Deployment: CONSUL_CACERT is set when using tls and clients even when useSystemRoots is true" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/api-gateway-controller-deployment.yaml \ + --set 'apiGateway.enabled=true' \ + --set 'apiGateway.image=bar' \ + --set 'server.enabled=false' \ + --set 'externalServers.hosts[0]=external-consul.host' \ + --set 'externalServers.enabled=true' \ + --set 'externalServers.useSystemRoots=true' \ + --set 'client.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].env[0].name == "CONSUL_CACERT"' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "apiGateway/Deployment: CONSUL_CACERT is not set when using tls and useSystemRoots" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/api-gateway-controller-deployment.yaml \ + --set 'apiGateway.enabled=true' \ + --set 'apiGateway.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'server.enabled=false' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].env[0].name == "CONSUL_CACERT"' | tee /dev/stderr) + [ "${actual}" = "true" ] +} From eb085204ee13ed4d633eeb45a8c7883c12864092 Mon Sep 17 00:00:00 2001 From: Andrew Stucki Date: Fri, 18 Nov 2022 14:30:56 -0500 Subject: [PATCH 003/340] Add changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5040bdabf8..8565dd7e95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## UNRELEASED +BUG FIXES: +* Helm: + * Don't pass in a CA file to the API Gateway controller when `externalServers.useSystemRoots` is `true`. [[GH-1743](https://github.com/hashicorp/consul-k8s/pull/1743)] + ## 1.0.0 (November 17, 2022) BREAKING CHANGES: From e0b6fa6e2d8d25fd54a6e78f7147bd59b6dbb1ee Mon Sep 17 00:00:00 2001 From: Andrew Stucki Date: Fri, 18 Nov 2022 14:50:34 -0500 Subject: [PATCH 004/340] Save forgotten changes --- .../api-gateway-controller-deployment.bats | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/charts/consul/test/unit/api-gateway-controller-deployment.bats b/charts/consul/test/unit/api-gateway-controller-deployment.bats index b61486c2ed..5f00cb65a0 100755 --- a/charts/consul/test/unit/api-gateway-controller-deployment.bats +++ b/charts/consul/test/unit/api-gateway-controller-deployment.bats @@ -1377,6 +1377,7 @@ load _helpers -s templates/api-gateway-controller-deployment.yaml \ --set 'apiGateway.enabled=true' \ --set 'apiGateway.image=bar' \ + --set 'global.tls.enabled=true' \ --set 'server.enabled=false' \ --set 'externalServers.hosts[0]=external-consul.host' \ --set 'externalServers.enabled=true' \ @@ -1384,7 +1385,20 @@ load _helpers --set 'client.enabled=true' \ . | tee /dev/stderr | yq '.spec.template.spec.containers[0].env[0].name == "CONSUL_CACERT"' | tee /dev/stderr) - [ "${actual}" = "false" ] + [ "${actual}" = "true" ] +} + +@test "apiGateway/Deployment: CONSUL_CACERT is set when using tls and internal servers" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/api-gateway-controller-deployment.yaml \ + --set 'apiGateway.enabled=true' \ + --set 'apiGateway.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'server.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].env[0].name == "CONSUL_CACERT"' | tee /dev/stderr) + [ "${actual}" = "true" ] } @test "apiGateway/Deployment: CONSUL_CACERT is not set when using tls and useSystemRoots" { @@ -1395,7 +1409,10 @@ load _helpers --set 'apiGateway.image=bar' \ --set 'global.tls.enabled=true' \ --set 'server.enabled=false' \ + --set 'externalServers.hosts[0]=external-consul.host' \ + --set 'externalServers.enabled=true' \ + --set 'externalServers.useSystemRoots=true' \ . | tee /dev/stderr | yq '.spec.template.spec.containers[0].env[0].name == "CONSUL_CACERT"' | tee /dev/stderr) - [ "${actual}" = "true" ] + [ "${actual}" = "false" ] } From 47173e77a3815bde0d9d5d7f0dbc9493ce448dbf Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Fri, 18 Nov 2022 11:52:56 -0800 Subject: [PATCH 005/340] added fix for backport assistant on OSS repos with forks (#1744) --- .github/workflows/backport.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index c1548332e0..9c241d9ada 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -2,7 +2,7 @@ name: Backport Assistant Runner on: - pull_request: + pull_request_target: types: - closed - labeled From a6c36a383ba537ac2e7c151d3b7bdc13b545de82 Mon Sep 17 00:00:00 2001 From: Kyle Schochenmaier Date: Fri, 18 Nov 2022 15:40:48 -0600 Subject: [PATCH 006/340] remove controller webhook cert roles from vault test and the values.yaml (#1740) * remove controller webhook cert roles from vault test and the values.yaml --- acceptance/tests/vault/vault_test.go | 17 ---------- charts/consul/templates/_helpers.tpl | 33 +++---------------- .../webhook-cert-manager-clusterrole.yaml | 2 +- ...bhook-cert-manager-clusterrolebinding.yaml | 2 +- .../webhook-cert-manager-configmap.yaml | 2 +- .../webhook-cert-manager-deployment.yaml | 2 +- ...ebhook-cert-manager-podsecuritypolicy.yaml | 2 +- .../webhook-cert-manager-serviceaccount.yaml | 2 +- .../test/unit/connect-inject-clusterrole.bats | 3 -- .../test/unit/connect-inject-deployment.bats | 24 ++------------ .../webhook-cert-manager-clusterrole.bats | 5 +-- ...bhook-cert-manager-clusterrolebinding.bats | 5 +-- .../unit/webhook-cert-manager-configmap.bats | 5 +-- .../unit/webhook-cert-manager-deployment.bats | 5 +-- ...ebhook-cert-manager-podsecuritypolicy.bats | 5 +-- .../webhook-cert-manager-serviceaccount.bats | 5 +-- charts/consul/values.yaml | 25 -------------- 17 files changed, 19 insertions(+), 125 deletions(-) diff --git a/acceptance/tests/vault/vault_test.go b/acceptance/tests/vault/vault_test.go index fdde364b5b..cf0c926b22 100644 --- a/acceptance/tests/vault/vault_test.go +++ b/acceptance/tests/vault/vault_test.go @@ -84,20 +84,6 @@ func TestVault(t *testing.T) { } serverPKIConfig.ConfigurePKIAndAuthRole(t, vaultClient) - // Configure controller webhook PKI - controllerWebhookPKIConfig := &vault.PKIAndAuthRoleConfiguration{ - BaseURL: "controller", - PolicyName: "controller-ca-policy", - RoleName: "controller-ca-role", - KubernetesNamespace: ns, - DataCenter: "dc1", - ServiceAccountName: fmt.Sprintf("%s-consul-%s", consulReleaseName, "controller"), - AllowedSubdomain: fmt.Sprintf("%s-consul-%s", consulReleaseName, "controller-webhook"), - MaxTTL: fmt.Sprintf("%ds", expirationInSeconds), - AuthMethodPath: KubernetesAuthMethodPath, - } - controllerWebhookPKIConfig.ConfigurePKIAndAuthRole(t, vaultClient) - // Configure connect injector webhook PKI connectInjectorWebhookPKIConfig := &vault.PKIAndAuthRoleConfiguration{ BaseURL: "connect", @@ -212,15 +198,12 @@ func TestVault(t *testing.T) { "connectInject.replicas": "1", "global.secretsBackend.vault.connectInject.tlsCert.secretName": connectInjectorWebhookPKIConfig.CertPath, "global.secretsBackend.vault.connectInject.caCert.secretName": connectInjectorWebhookPKIConfig.CAPath, - "global.secretsBackend.vault.controller.tlsCert.secretName": controllerWebhookPKIConfig.CertPath, - "global.secretsBackend.vault.controller.caCert.secretName": controllerWebhookPKIConfig.CAPath, "global.secretsBackend.vault.enabled": "true", "global.secretsBackend.vault.consulServerRole": consulServerRole, "global.secretsBackend.vault.consulClientRole": consulClientRole, "global.secretsBackend.vault.consulCARole": serverPKIConfig.RoleName, "global.secretsBackend.vault.connectInjectRole": connectInjectorWebhookPKIConfig.RoleName, - "global.secretsBackend.vault.controllerRole": controllerWebhookPKIConfig.RoleName, "global.secretsBackend.vault.manageSystemACLsRole": manageSystemACLsRole, "global.secretsBackend.vault.ca.secretName": vaultCASecret, diff --git a/charts/consul/templates/_helpers.tpl b/charts/consul/templates/_helpers.tpl index e2f735e690..3552c8c209 100644 --- a/charts/consul/templates/_helpers.tpl +++ b/charts/consul/templates/_helpers.tpl @@ -73,22 +73,6 @@ as well as the global.name setting. {{ "{{" }}- end -{{ "}}" }} {{- end -}} -{{- define "consul.controllerWebhookTLSCertTemplate" -}} - | - {{ "{{" }}- with secret "{{ .Values.global.secretsBackend.vault.controller.tlsCert.secretName }}" "{{- $name := include "consul.fullname" . -}}{{ printf "common_name=%s-controller-webhook" $name }}" - "alt_names={{ include "consul.controllerWebhookTLSAltNames" . }}" -{{ "}}" }} - {{ "{{" }}- .Data.certificate -{{ "}}" }} - {{ "{{" }}- end -{{ "}}" }} -{{- end -}} - -{{- define "consul.controllerWebhookTLSKeyTemplate" -}} - | - {{ "{{" }}- with secret "{{ .Values.global.secretsBackend.vault.controller.tlsCert.secretName }}" "{{- $name := include "consul.fullname" . -}}{{ printf "common_name=%s-controller-webhook" $name }}" - "alt_names={{ include "consul.controllerWebhookTLSAltNames" . }}" -{{ "}}" }} - {{ "{{" }}- .Data.private_key -{{ "}}" }} - {{ "{{" }}- end -{{ "}}" }} -{{- end -}} - {{- define "consul.serverTLSAltNames" -}} {{- $name := include "consul.fullname" . -}} {{- $ns := .Release.Namespace -}} @@ -109,12 +93,6 @@ as well as the global.name setting. {{ printf "%s-connect-injector,%s-connect-injector.%s,%s-connect-injector.%s.svc,%s-connect-injector.%s.svc.cluster.local" $name $name $ns $name $ns $name $ns}} {{- end -}} -{{- define "consul.controllerWebhookTLSAltNames" -}} -{{- $name := include "consul.fullname" . -}} -{{- $ns := .Release.Namespace -}} -{{ printf "%s-controller-webhook,%s-controller-webhook.%s,%s-controller-webhook.%s.svc,%s-controller-webhook.%s.svc.cluster.local" $name $name $ns $name $ns $name $ns}} -{{- end -}} - {{- define "consul.vaultReplicationTokenTemplate" -}} | {{ "{{" }}- with secret "{{ .Values.global.acls.replicationToken.secretName }}" -{{ "}}" }} @@ -285,20 +263,17 @@ Fails when at least one but not all of the following have been set: - global.secretsBackend.vault.connectInjectRole - global.secretsBackend.vault.connectInject.tlsCert.secretName - global.secretsBackend.vault.connectInject.caCert.secretName -- global.secretsBackend.vault.controllerRole -- global.secretsBackend.vault.controller.tlsCert.secretName -- global.secretsBackend.vault.controller.caCert.secretName The above values are needed in full to turn off web cert manager and allow -connect inject and controller to manage its own webhook certs. +connect inject to manage its own webhook certs. Usage: {{ template "consul.validateVaultWebhookCertConfiguration" . }} */}} {{- define "consul.validateVaultWebhookCertConfiguration" -}} -{{- if or .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName .Values.global.secretsBackend.vault.controllerRole .Values.global.secretsBackend.vault.controller.tlsCert.secretName .Values.global.secretsBackend.vault.controller.caCert.secretName}} -{{- if or (not .Values.global.secretsBackend.vault.connectInjectRole) (not .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName) (not .Values.global.secretsBackend.vault.connectInject.caCert.secretName) (not .Values.global.secretsBackend.vault.controllerRole) (not .Values.global.secretsBackend.vault.controller.tlsCert.secretName) (not .Values.global.secretsBackend.vault.controller.caCert.secretName) }} -{{fail "When one of the following has been set, all must be set: global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName, global.secretsBackend.vault.controllerRole, global.secretsBackend.vault.controller.tlsCert.secretName, and global.secretsBackend.vault.controller.caCert.secretName."}} +{{- if or .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName}} +{{- if or (not .Values.global.secretsBackend.vault.connectInjectRole) (not .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName) (not .Values.global.secretsBackend.vault.connectInject.caCert.secretName) }} +{{fail "When one of the following has been set, all must be set: global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName"}} {{ end }} {{ end }} {{- end -}} diff --git a/charts/consul/templates/webhook-cert-manager-clusterrole.yaml b/charts/consul/templates/webhook-cert-manager-clusterrole.yaml index c2a2422d02..e13e2dc741 100644 --- a/charts/consul/templates/webhook-cert-manager-clusterrole.yaml +++ b/charts/consul/templates/webhook-cert-manager-clusterrole.yaml @@ -1,4 +1,4 @@ -{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName .Values.global.secretsBackend.vault.controllerRole .Values.global.secretsBackend.vault.controller.tlsCert.secretName .Values.global.secretsBackend.vault.controller.caCert.secretName) -}} +{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} {{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/charts/consul/templates/webhook-cert-manager-clusterrolebinding.yaml b/charts/consul/templates/webhook-cert-manager-clusterrolebinding.yaml index ca2bb84bda..472ef4ee1d 100644 --- a/charts/consul/templates/webhook-cert-manager-clusterrolebinding.yaml +++ b/charts/consul/templates/webhook-cert-manager-clusterrolebinding.yaml @@ -1,4 +1,4 @@ -{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName .Values.global.secretsBackend.vault.controllerRole .Values.global.secretsBackend.vault.controller.tlsCert.secretName .Values.global.secretsBackend.vault.controller.caCert.secretName) -}} +{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} {{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/charts/consul/templates/webhook-cert-manager-configmap.yaml b/charts/consul/templates/webhook-cert-manager-configmap.yaml index 914d2b87dd..293dd32d9f 100644 --- a/charts/consul/templates/webhook-cert-manager-configmap.yaml +++ b/charts/consul/templates/webhook-cert-manager-configmap.yaml @@ -1,4 +1,4 @@ -{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName .Values.global.secretsBackend.vault.controllerRole .Values.global.secretsBackend.vault.controller.tlsCert.secretName .Values.global.secretsBackend.vault.controller.caCert.secretName) -}} +{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} {{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} apiVersion: v1 kind: ConfigMap diff --git a/charts/consul/templates/webhook-cert-manager-deployment.yaml b/charts/consul/templates/webhook-cert-manager-deployment.yaml index 557cc0219b..be22d4da7a 100644 --- a/charts/consul/templates/webhook-cert-manager-deployment.yaml +++ b/charts/consul/templates/webhook-cert-manager-deployment.yaml @@ -1,4 +1,4 @@ -{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName .Values.global.secretsBackend.vault.controllerRole .Values.global.secretsBackend.vault.controller.tlsCert.secretName .Values.global.secretsBackend.vault.controller.caCert.secretName) -}} +{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} {{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} apiVersion: apps/v1 kind: Deployment diff --git a/charts/consul/templates/webhook-cert-manager-podsecuritypolicy.yaml b/charts/consul/templates/webhook-cert-manager-podsecuritypolicy.yaml index b67dbda510..4d685edc39 100644 --- a/charts/consul/templates/webhook-cert-manager-podsecuritypolicy.yaml +++ b/charts/consul/templates/webhook-cert-manager-podsecuritypolicy.yaml @@ -1,4 +1,4 @@ -{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName .Values.global.secretsBackend.vault.controllerRole .Values.global.secretsBackend.vault.controller.tlsCert.secretName .Values.global.secretsBackend.vault.controller.caCert.secretName) -}} +{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} {{- if (and .Values.global.enablePodSecurityPolicies (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled))) }} {{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} apiVersion: policy/v1beta1 diff --git a/charts/consul/templates/webhook-cert-manager-serviceaccount.yaml b/charts/consul/templates/webhook-cert-manager-serviceaccount.yaml index fa4b24ef8b..68c54f3c27 100644 --- a/charts/consul/templates/webhook-cert-manager-serviceaccount.yaml +++ b/charts/consul/templates/webhook-cert-manager-serviceaccount.yaml @@ -1,4 +1,4 @@ -{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName .Values.global.secretsBackend.vault.controllerRole .Values.global.secretsBackend.vault.controller.tlsCert.secretName .Values.global.secretsBackend.vault.controller.caCert.secretName) -}} +{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} {{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} apiVersion: v1 kind: ServiceAccount diff --git a/charts/consul/test/unit/connect-inject-clusterrole.bats b/charts/consul/test/unit/connect-inject-clusterrole.bats index b1ad10a80c..4acdf211d2 100644 --- a/charts/consul/test/unit/connect-inject-clusterrole.bats +++ b/charts/consul/test/unit/connect-inject-clusterrole.bats @@ -193,9 +193,6 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=inject-ca-role' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=pki/issue/connect-webhook-cert-dc1' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=pki/issue/connect-webhook-cert-dc1' \ - --set 'global.secretsBackend.vault.controllerRole=test' \ - --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ - --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ --set 'global.secretsBackend.vault.consulClientRole=foo' \ --set 'global.secretsBackend.vault.consulServerRole=bar' \ --set 'global.secretsBackend.vault.consulCARole=test2' \ diff --git a/charts/consul/test/unit/connect-inject-deployment.bats b/charts/consul/test/unit/connect-inject-deployment.bats index b851d863ed..4879049c49 100755 --- a/charts/consul/test/unit/connect-inject-deployment.bats +++ b/charts/consul/test/unit/connect-inject-deployment.bats @@ -1633,9 +1633,6 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=test' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=foo/ca' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=foo/tls' \ - --set 'global.secretsBackend.vault.controllerRole=test' \ - --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ - --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ . | tee /dev/stderr | yq '.spec.template.spec.containers[0].command | any(contains("-enable-webhook-ca-update"))' | tee /dev/stderr) [ "${actual}" = "true" ] @@ -1742,7 +1739,7 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=connectinjectcarole' \ --set 'global.secretsBackend.vault.agentAnnotations=foo: bar' . [ "$status" -eq 1 ] - [[ "$output" =~ "When one of the following has been set, all must be set: global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName, global.secretsBackend.vault.controllerRole, global.secretsBackend.vault.controller.tlsCert.secretName, and global.secretsBackend.vault.controller.caCert.secretName." ]] + [[ "$output" =~ "When one of the following has been set, all must be set: global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName" ]] } @test "connectInject/Deployment: fails if vault is enabled and global.secretsBackend.vault.connectInject.tlsCert.secretName is set but global.secretsBackend.vault.connectInjectRole and global.secretsBackend.vault.connectInject.caCert.secretName are not" { @@ -1759,7 +1756,7 @@ load _helpers --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=foo/tls' \ --set 'global.secretsBackend.vault.agentAnnotations=foo: bar' . [ "$status" -eq 1 ] - [[ "$output" =~ "When one of the following has been set, all must be set: global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName, global.secretsBackend.vault.controllerRole, global.secretsBackend.vault.controller.tlsCert.secretName, and global.secretsBackend.vault.controller.caCert.secretName." ]] + [[ "$output" =~ "When one of the following has been set, all must be set: global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName" ]] } @test "connectInject/Deployment: fails if vault is enabled and global.secretsBackend.vault.connectInject.caCert.secretName is set but global.secretsBackend.vault.connectInjectRole and global.secretsBackend.vault.connectInject.tlsCert.secretName are not" { @@ -1776,7 +1773,7 @@ load _helpers --set 'global.secretsBackend.vault.connectInject.caCert.secretName=foo/ca' \ --set 'global.secretsBackend.vault.agentAnnotations=foo: bar' . [ "$status" -eq 1 ] - [[ "$output" =~ "When one of the following has been set, all must be set: global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName, global.secretsBackend.vault.controllerRole, global.secretsBackend.vault.controller.tlsCert.secretName, and global.secretsBackend.vault.controller.caCert.secretName." ]] + [[ "$output" =~ "When one of the following has been set, all must be set: global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName" ]] } @test "connectInject/Deployment: vault tls annotations are set when tls is enabled" { @@ -1794,9 +1791,6 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=test' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=foo/ca' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=pki/issue/connect-webhook-cert-dc1' \ - --set 'global.secretsBackend.vault.controllerRole=test' \ - --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ - --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ . | tee /dev/stderr | yq -r '.spec.template.metadata' | tee /dev/stderr) @@ -1870,9 +1864,6 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=inject-ca-role' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=pki/issue/connect-webhook-cert-dc1' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=pki/issue/connect-webhook-cert-dc1' \ - --set 'global.secretsBackend.vault.controllerRole=test' \ - --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ - --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ --set 'global.secretsBackend.vault.consulClientRole=foo' \ --set 'global.secretsBackend.vault.consulServerRole=bar' \ --set 'global.secretsBackend.vault.consulCARole=test2' \ @@ -1895,9 +1886,6 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=inject-ca-role' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=pki/issue/connect-webhook-cert-dc1' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=pki/issue/connect-webhook-cert-dc1' \ - --set 'global.secretsBackend.vault.controllerRole=test' \ - --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ - --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ --set 'server.serverCert.secretName=pki_int/issue/test' \ --set 'global.tls.caCert.secretName=pki_int/cert/ca' \ . | tee /dev/stderr | @@ -1927,9 +1915,6 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=inject-ca-role' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=pki/issue/connect-webhook-cert-dc1' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=pki/issue/connect-webhook-cert-dc1' \ - --set 'global.secretsBackend.vault.controllerRole=test' \ - --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ - --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ . | tee /dev/stderr | yq '.spec.template.spec.volumes[] | select(.name == "certs")' | tee /dev/stderr) [ "${actual}" == "" ] @@ -1949,9 +1934,6 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=inject-ca-role' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=pki/issue/connect-webhook-cert-dc1' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=pki/issue/connect-webhook-cert-dc1' \ - --set 'global.secretsBackend.vault.controllerRole=test' \ - --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ - --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ . | tee /dev/stderr | yq '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "certs")' | tee /dev/stderr) [ "${actual}" == "" ] diff --git a/charts/consul/test/unit/webhook-cert-manager-clusterrole.bats b/charts/consul/test/unit/webhook-cert-manager-clusterrole.bats index d58ce20928..4d1a4abdd2 100644 --- a/charts/consul/test/unit/webhook-cert-manager-clusterrole.bats +++ b/charts/consul/test/unit/webhook-cert-manager-clusterrole.bats @@ -130,7 +130,7 @@ load _helpers #-------------------------------------------------------------------- # Vault -@test "webhookCertManager/ClusterRole: disabled when the following are configured - global.secretsBackend.vault.enabled, global.secretsBackend.vault.enabled, global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName, global.secretsBackend.vault.controllerRole, global.secretsBackend.vault.controller.tlsCert.secretName, and .global.secretsBackend.vault.controller.caCert.secretName" { +@test "webhookCertManager/ClusterRole: disabled when the following are configured - global.secretsBackend.vault.enabled, global.secretsBackend.vault.enabled, global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, and global.secretsBackend.vault.connectInject.caCert.secretName" { cd `chart_dir` assert_empty helm template \ -s templates/webhook-cert-manager-clusterrole.yaml \ @@ -142,9 +142,6 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=inject-ca-role' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=pki/issue/connect-webhook-cert-dc1' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=pki/issue/connect-webhook-cert-dc1' \ - --set 'global.secretsBackend.vault.controllerRole=test' \ - --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ - --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ --set 'global.secretsBackend.vault.consulClientRole=foo' \ --set 'global.secretsBackend.vault.consulServerRole=bar' \ --set 'global.secretsBackend.vault.consulCARole=test2' \ diff --git a/charts/consul/test/unit/webhook-cert-manager-clusterrolebinding.bats b/charts/consul/test/unit/webhook-cert-manager-clusterrolebinding.bats index 89a60a0ef3..2e507d279d 100644 --- a/charts/consul/test/unit/webhook-cert-manager-clusterrolebinding.bats +++ b/charts/consul/test/unit/webhook-cert-manager-clusterrolebinding.bats @@ -24,7 +24,7 @@ load _helpers #-------------------------------------------------------------------- # Vault -@test "webhookCertManager/ClusterRoleBinding: disabled when the following are configured - global.secretsBackend.vault.enabled, global.secretsBackend.vault.enabled, global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName, global.secretsBackend.vault.controllerRole, global.secretsBackend.vault.controller.tlsCert.secretName, and .global.secretsBackend.vault.controller.caCert.secretName" { +@test "webhookCertManager/ClusterRoleBinding: disabled when the following are configured - global.secretsBackend.vault.enabled, global.secretsBackend.vault.enabled, global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, and global.secretsBackend.vault.connectInject.caCert.secretName" { cd `chart_dir` assert_empty helm template \ -s templates/webhook-cert-manager-clusterrolebinding.yaml \ @@ -36,9 +36,6 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=inject-ca-role' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=pki/issue/connect-webhook-cert-dc1' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=pki/issue/connect-webhook-cert-dc1' \ - --set 'global.secretsBackend.vault.controllerRole=test' \ - --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ - --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ --set 'global.secretsBackend.vault.consulClientRole=foo' \ --set 'global.secretsBackend.vault.consulServerRole=bar' \ --set 'global.secretsBackend.vault.consulCARole=test2' \ diff --git a/charts/consul/test/unit/webhook-cert-manager-configmap.bats b/charts/consul/test/unit/webhook-cert-manager-configmap.bats index 774ada21d1..196da220d4 100644 --- a/charts/consul/test/unit/webhook-cert-manager-configmap.bats +++ b/charts/consul/test/unit/webhook-cert-manager-configmap.bats @@ -24,7 +24,7 @@ load _helpers #-------------------------------------------------------------------- # Vault -@test "webhookCertManager/Configmap: disabled when the following are configured - global.secretsBackend.vault.enabled, global.secretsBackend.vault.enabled, global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName, global.secretsBackend.vault.controllerRole, global.secretsBackend.vault.controller.tlsCert.secretName, and .global.secretsBackend.vault.controller.caCert.secretName" { +@test "webhookCertManager/Configmap: disabled when the following are configured - global.secretsBackend.vault.enabled, global.secretsBackend.vault.enabled, global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, and global.secretsBackend.vault.connectInject.caCert.secretName" { cd `chart_dir` assert_empty helm template \ -s templates/webhook-cert-manager-configmap.yaml \ @@ -36,9 +36,6 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=inject-ca-role' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=pki/issue/connect-webhook-cert-dc1' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=pki/issue/connect-webhook-cert-dc1' \ - --set 'global.secretsBackend.vault.controllerRole=test' \ - --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ - --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ --set 'global.secretsBackend.vault.consulClientRole=foo' \ --set 'global.secretsBackend.vault.consulServerRole=bar' \ --set 'global.secretsBackend.vault.consulCARole=test2' \ diff --git a/charts/consul/test/unit/webhook-cert-manager-deployment.bats b/charts/consul/test/unit/webhook-cert-manager-deployment.bats index 28771038dc..e6549200cf 100644 --- a/charts/consul/test/unit/webhook-cert-manager-deployment.bats +++ b/charts/consul/test/unit/webhook-cert-manager-deployment.bats @@ -66,7 +66,7 @@ load _helpers #-------------------------------------------------------------------- # Vault -@test "webhookCertManager/Deployment: disabled when the following are configured - global.secretsBackend.vault.enabled, global.secretsBackend.vault.enabled, global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName, global.secretsBackend.vault.controllerRole, global.secretsBackend.vault.controller.tlsCert.secretName, and .global.secretsBackend.vault.controller.caCert.secretName" { +@test "webhookCertManager/Deployment: disabled when the following are configured - global.secretsBackend.vault.enabled, global.secretsBackend.vault.enabled, global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, and global.secretsBackend.vault.connectInject.caCert.secretName" { cd `chart_dir` assert_empty helm template \ -s templates/webhook-cert-manager-deployment.yaml \ @@ -78,9 +78,6 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=inject-ca-role' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=pki/issue/connect-webhook-cert-dc1' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=pki/issue/connect-webhook-cert-dc1' \ - --set 'global.secretsBackend.vault.controllerRole=test' \ - --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ - --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ --set 'global.secretsBackend.vault.consulClientRole=foo' \ --set 'global.secretsBackend.vault.consulServerRole=bar' \ --set 'global.secretsBackend.vault.consulCARole=test2' \ diff --git a/charts/consul/test/unit/webhook-cert-manager-podsecuritypolicy.bats b/charts/consul/test/unit/webhook-cert-manager-podsecuritypolicy.bats index 820be91404..d8e16f867c 100644 --- a/charts/consul/test/unit/webhook-cert-manager-podsecuritypolicy.bats +++ b/charts/consul/test/unit/webhook-cert-manager-podsecuritypolicy.bats @@ -35,7 +35,7 @@ load _helpers #-------------------------------------------------------------------- # Vault -@test "webhookCertManager/PodSecurityPolicy: disabled when the following are configured - global.secretsBackend.vault.enabled, global.secretsBackend.vault.enabled, global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName, global.secretsBackend.vault.controllerRole, global.secretsBackend.vault.controller.tlsCert.secretName, and .global.secretsBackend.vault.controller.caCert.secretName" { +@test "webhookCertManager/PodSecurityPolicy: disabled when the following are configured - global.secretsBackend.vault.enabled, global.secretsBackend.vault.enabled, global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, and global.secretsBackend.vault.connectInject.caCert.secretName" { cd `chart_dir` assert_empty helm template \ -s templates/webhook-cert-manager-podsecuritypolicy.yaml \ @@ -48,9 +48,6 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=inject-ca-role' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=pki/issue/connect-webhook-cert-dc1' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=pki/issue/connect-webhook-cert-dc1' \ - --set 'global.secretsBackend.vault.controllerRole=test' \ - --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ - --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ --set 'global.secretsBackend.vault.consulClientRole=foo' \ --set 'global.secretsBackend.vault.consulServerRole=bar' \ --set 'global.secretsBackend.vault.consulCARole=test2' \ diff --git a/charts/consul/test/unit/webhook-cert-manager-serviceaccount.bats b/charts/consul/test/unit/webhook-cert-manager-serviceaccount.bats index 766d20fb66..f420e7319c 100644 --- a/charts/consul/test/unit/webhook-cert-manager-serviceaccount.bats +++ b/charts/consul/test/unit/webhook-cert-manager-serviceaccount.bats @@ -45,7 +45,7 @@ load _helpers #-------------------------------------------------------------------- # Vault -@test "webhookCertManager/ServiceAccount: disabled when the following are configured - global.secretsBackend.vault.enabled, global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName, global.secretsBackend.vault.controllerRole, global.secretsBackend.vault.controller.tlsCert.secretName, and .global.secretsBackend.vault.controller.caCert.secretName" { +@test "webhookCertManager/ServiceAccount: disabled when the following are configured - global.secretsBackend.vault.enabled, global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, and global.secretsBackend.vault.connectInject.caCert.secretName" { cd `chart_dir` assert_empty helm template \ -s templates/webhook-cert-manager-serviceaccount.yaml \ @@ -57,9 +57,6 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=inject-ca-role' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=pki/issue/connect-webhook-cert-dc1' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=pki/issue/connect-webhook-cert-dc1' \ - --set 'global.secretsBackend.vault.controllerRole=test' \ - --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ - --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ --set 'global.secretsBackend.vault.consulClientRole=foo' \ --set 'global.secretsBackend.vault.consulServerRole=bar' \ --set 'global.secretsBackend.vault.consulCARole=test2' \ diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index bdf66a53a6..fde9ad9513 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -160,12 +160,6 @@ global: # and check the name of `metadata.name`. adminPartitionsRole: "" - # The Vault role to read Consul controller's webhook's - # CA and issue a certificate and private key. - # A Vault policy must be created which grants issue capabilities to - # `global.secretsBackend.vault.controller.tlsCert.secretName`. - controllerRole: "" - # The Vault role to read Consul connect-injector webhook's CA # and issue a certificate and private key. # A Vault policy must be created which grants issue capabilities to @@ -241,25 +235,6 @@ global: additionalConfig: | {} - controller: - # Configuration to the Vault Secret that Kubernetes will use on - # Kubernetes CRD creation, deletion, and update, to get TLS certificates - # used issued from vault to send webhooks to the controller. - tlsCert: - # The Vault secret path that issues TLS certificates for controller - # webhooks. - # @type: string - secretName: null - - # Configuration to the Vault Secret that Kubernetes will use on - # Kubernetes CRD creation, deletion, and update, to get CA certificates - # used issued from vault to send webhooks to the controller. - caCert: - # The Vault secret path that contains the CA certificate for controller - # webhooks. - # @type: string - secretName: null - connectInject: # Configuration to the Vault Secret that Kubernetes will use on # Kubernetes pod creation, deletion, and update, to get CA certificates From 94506bb87eb65b7bb6c95de9b05ff0f4312d6d6a Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Mon, 21 Nov 2022 12:49:07 -0500 Subject: [PATCH 007/340] Add discover binary to control-plane image (#1749) * add discover binary to control-plane image --- CHANGELOG.md | 2 ++ control-plane/Dockerfile | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8565dd7e95..4b10f3be33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ ## UNRELEASED BUG FIXES: +* Control Plane + * Add discover binary to control-plane image [[GH-1749](https://github.com/hashicorp/consul-k8s/pull/1749)] * Helm: * Don't pass in a CA file to the API Gateway controller when `externalServers.useSystemRoots` is `true`. [[GH-1743](https://github.com/hashicorp/consul-k8s/pull/1743)] diff --git a/control-plane/Dockerfile b/control-plane/Dockerfile index 9ac4188c22..3e31c92ef6 100644 --- a/control-plane/Dockerfile +++ b/control-plane/Dockerfile @@ -11,10 +11,14 @@ # # =================================== +# go-discover builds the discover binary (which we don't currently publish +# either). +FROM golang:1.19.2-alpine as go-discover +RUN CGO_ENABLED=0 go install github.com/hashicorp/go-discover/cmd/discover@49f60c093101c9c5f6b04d5b1c80164251a761a6 + # dev copies the binary from a local build # ----------------------------------- # BIN_NAME is a requirement in the hashicorp docker github action - FROM alpine:3.16 AS dev # NAME and VERSION are the name of the software in releases.hashicorp.com @@ -43,6 +47,7 @@ RUN apk add --no-cache ca-certificates gnupg libcap openssl su-exec iputils libc RUN addgroup ${BIN_NAME} && \ adduser -S -G ${BIN_NAME} 100 +COPY --from=go-discover /go/bin/discover /bin/ COPY pkg/bin/linux_${TARGETARCH}/${BIN_NAME} /bin COPY cni/pkg/bin/linux_${TARGETARCH}/${CNI_BIN_NAME} /bin @@ -94,6 +99,7 @@ ARG TARGETARCH RUN addgroup ${BIN_NAME} && \ adduser -S -G ${BIN_NAME} 100 +COPY --from=go-discover /go/bin/discover /bin/ COPY dist/${TARGETOS}/${TARGETARCH}/${BIN_NAME} /bin/ COPY dist/cni/${TARGETOS}/${TARGETARCH}/${CNI_BIN_NAME} /bin/ @@ -155,6 +161,7 @@ RUN groupadd --gid 1000 ${BIN_NAME} && \ adduser --uid 100 --system -g ${BIN_NAME} ${BIN_NAME} && \ usermod -a -G root ${BIN_NAME} +COPY --from=go-discover /go/bin/discover /bin/ COPY dist/${TARGETOS}/${TARGETARCH}/${BIN_NAME} /bin/ COPY dist/cni/${TARGETOS}/${TARGETARCH}/${CNI_BIN_NAME} /bin/ From ef421f968770e96ee2cbbb471423169b579aa1de Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Mon, 28 Nov 2022 15:38:55 -0800 Subject: [PATCH 008/340] ran prepare-dev script (#1754) - fixed changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b10f3be33..ab1b80bdd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## UNRELEASED +## 1.0.1 (November 21, 2022) + BUG FIXES: * Control Plane * Add discover binary to control-plane image [[GH-1749](https://github.com/hashicorp/consul-k8s/pull/1749)] From 0a64d26c3889fcb19a2e4758c812c3b69b3b7568 Mon Sep 17 00:00:00 2001 From: Kyle Schochenmaier Date: Wed, 30 Nov 2022 13:45:39 -0600 Subject: [PATCH 009/340] Add support for setting the namespace that the CNI plugin is installed (#1756) * Add support for setting the namespace that the CNI plugin is installed into --- CHANGELOG.md | 4 ++ charts/consul/templates/cni-clusterrole.yaml | 2 +- .../templates/cni-clusterrolebinding.yaml | 2 +- charts/consul/templates/cni-daemonset.yaml | 2 +- .../cni-networkattachmentdefinition.yaml | 2 +- .../templates/cni-podsecuritypolicy.yaml | 2 +- .../consul/templates/cni-resourcequota.yaml | 2 +- .../cni-securitycontextconstraints.yaml | 2 +- .../consul/templates/cni-serviceaccount.yaml | 2 +- charts/consul/test/unit/cni-clusterrole.bats | 23 ++++++++++ .../test/unit/cni-clusterrolebinding.bats | 22 +++++++++ charts/consul/test/unit/cni-daemonset.bats | 45 +++++++++++++++++++ .../unit/cni-networkattachmentdefinition.bats | 24 ++++++++++ .../test/unit/cni-podsecuritypolicy.bats | 24 ++++++++++ .../consul/test/unit/cni-resourcequota.bats | 23 ++++++++++ .../unit/cni-securitycontextcontstraints.bats | 24 ++++++++++ .../consul/test/unit/cni-serviceaccount.bats | 23 ++++++++++ charts/consul/values.yaml | 5 +++ 18 files changed, 225 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab1b80bdd2..5693dd1c28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## UNRELEASED +IMPROVEMENTS: +* Helm: + * CNI: Add `connectInject.cni.namespace` stanza which allows the CNI plugin resources to be deployed in a namespace other than the namespace that Consul is installed. [[GH-1756](https://github.com/hashicorp/consul-k8s/pull/1756)] + ## 1.0.1 (November 21, 2022) BUG FIXES: diff --git a/charts/consul/templates/cni-clusterrole.yaml b/charts/consul/templates/cni-clusterrole.yaml index 39dc5ead50..773942cca8 100644 --- a/charts/consul/templates/cni-clusterrole.yaml +++ b/charts/consul/templates/cni-clusterrole.yaml @@ -3,7 +3,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: {{ template "consul.fullname" . }}-cni - namespace: {{ .Release.Namespace }} + namespace: {{ default .Release.Namespace .Values.connectInject.cni.namespace }} labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} diff --git a/charts/consul/templates/cni-clusterrolebinding.yaml b/charts/consul/templates/cni-clusterrolebinding.yaml index 86c19d86aa..4b860388b6 100644 --- a/charts/consul/templates/cni-clusterrolebinding.yaml +++ b/charts/consul/templates/cni-clusterrolebinding.yaml @@ -16,5 +16,5 @@ roleRef: subjects: - kind: ServiceAccount name: {{ template "consul.fullname" . }}-cni - namespace: {{ .Release.Namespace }} + namespace: {{ default .Release.Namespace .Values.connectInject.cni.namespace }} {{- end }} diff --git a/charts/consul/templates/cni-daemonset.yaml b/charts/consul/templates/cni-daemonset.yaml index 7b9f90d939..e9a6807338 100644 --- a/charts/consul/templates/cni-daemonset.yaml +++ b/charts/consul/templates/cni-daemonset.yaml @@ -4,7 +4,7 @@ apiVersion: apps/v1 kind: DaemonSet metadata: name: {{ template "consul.fullname" . }}-cni - namespace: {{ .Release.Namespace }} + namespace: {{ default .Release.Namespace .Values.connectInject.cni.namespace }} labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} diff --git a/charts/consul/templates/cni-networkattachmentdefinition.yaml b/charts/consul/templates/cni-networkattachmentdefinition.yaml index d0feaf5cb1..80ef50bac6 100644 --- a/charts/consul/templates/cni-networkattachmentdefinition.yaml +++ b/charts/consul/templates/cni-networkattachmentdefinition.yaml @@ -3,7 +3,7 @@ apiVersion: "k8s.cni.cncf.io/v1" kind: NetworkAttachmentDefinition metadata: name: {{ template "consul.fullname" . }}-cni - namespace: {{ .Release.Namespace }} + namespace: {{ default .Release.Namespace .Values.connectInject.cni.namespace }} labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} diff --git a/charts/consul/templates/cni-podsecuritypolicy.yaml b/charts/consul/templates/cni-podsecuritypolicy.yaml index 15b96bc230..b600ed1b4b 100644 --- a/charts/consul/templates/cni-podsecuritypolicy.yaml +++ b/charts/consul/templates/cni-podsecuritypolicy.yaml @@ -3,7 +3,7 @@ apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: name: {{ template "consul.fullname" . }}-cni - namespace: {{ .Release.Namespace }} + namespace: {{ default .Release.Namespace .Values.connectInject.cni.namespace }} labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} diff --git a/charts/consul/templates/cni-resourcequota.yaml b/charts/consul/templates/cni-resourcequota.yaml index abfe5a8876..054c3061f5 100644 --- a/charts/consul/templates/cni-resourcequota.yaml +++ b/charts/consul/templates/cni-resourcequota.yaml @@ -3,7 +3,7 @@ apiVersion: v1 kind: ResourceQuota metadata: name: {{ template "consul.fullname" . }}-cni - namespace: {{ .Release.Namespace }} + namespace: {{ default .Release.Namespace .Values.connectInject.cni.namespace }} labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} diff --git a/charts/consul/templates/cni-securitycontextconstraints.yaml b/charts/consul/templates/cni-securitycontextconstraints.yaml index 95cfc555e1..2c09dba9b8 100644 --- a/charts/consul/templates/cni-securitycontextconstraints.yaml +++ b/charts/consul/templates/cni-securitycontextconstraints.yaml @@ -3,7 +3,7 @@ apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints metadata: name: {{ template "consul.fullname" . }}-cni - namespace: {{ .Release.Namespace }} + namespace: {{ default .Release.Namespace .Values.connectInject.cni.namespace }} labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} diff --git a/charts/consul/templates/cni-serviceaccount.yaml b/charts/consul/templates/cni-serviceaccount.yaml index 6b2a7627f7..cf4250b696 100644 --- a/charts/consul/templates/cni-serviceaccount.yaml +++ b/charts/consul/templates/cni-serviceaccount.yaml @@ -3,7 +3,7 @@ apiVersion: v1 kind: ServiceAccount metadata: name: {{ template "consul.fullname" . }}-cni - namespace: {{ .Release.Namespace }} + namespace: {{ default .Release.Namespace .Values.connectInject.cni.namespace }} labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} diff --git a/charts/consul/test/unit/cni-clusterrole.bats b/charts/consul/test/unit/cni-clusterrole.bats index 02675ed882..4556d48f0d 100644 --- a/charts/consul/test/unit/cni-clusterrole.bats +++ b/charts/consul/test/unit/cni-clusterrole.bats @@ -20,6 +20,29 @@ load _helpers [[ "${actual}" == "true" ]] } +@test "cni/ClusterRole: cni namespace has a default when not set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/cni-clusterrole.yaml \ + --set 'connectInject.cni.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r -c '.metadata.namespace' | tee /dev/stderr) + [[ "${actual}" == "default" ]] +} + +@test "cni/ClusterRole: able to set cni namespace" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/cni-clusterrole.yaml \ + --set 'connectInject.cni.enabled=true' \ + --set 'connectInject.cni.namespace=kube-system' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r -c '.metadata.namespace' | tee /dev/stderr) + [[ "${actual}" == "kube-system" ]] +} + @test "cni/ClusterRole: disabled with connectInject.cni.enabled=false and connectInject.enabled=true" { cd `chart_dir` assert_empty helm template \ diff --git a/charts/consul/test/unit/cni-clusterrolebinding.bats b/charts/consul/test/unit/cni-clusterrolebinding.bats index ba217e7706..98cdb283c4 100644 --- a/charts/consul/test/unit/cni-clusterrolebinding.bats +++ b/charts/consul/test/unit/cni-clusterrolebinding.bats @@ -55,3 +55,25 @@ load _helpers [ "${actual}" = "foo" ] } +@test "cni/ClusterRoleBinding: subject namespace is correct when not set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/cni-clusterrolebinding.yaml \ + --set 'connectInject.cni.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.subjects[0].namespace' | tee /dev/stderr) + [[ "${actual}" == "default" ]] +} + +@test "cni/ClusterRoleBinding: subject namespace can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/cni-clusterrolebinding.yaml \ + --set 'connectInject.cni.enabled=true' \ + --set 'connectInject.cni.namespace=kube-system' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.subjects[0].namespace' | tee /dev/stderr) + [[ "${actual}" == "kube-system" ]] +} diff --git a/charts/consul/test/unit/cni-daemonset.bats b/charts/consul/test/unit/cni-daemonset.bats index 17c80d2da0..3b5e046a67 100644 --- a/charts/consul/test/unit/cni-daemonset.bats +++ b/charts/consul/test/unit/cni-daemonset.bats @@ -295,3 +295,48 @@ rollingUpdate: [ "${actual}" = '{"mountPath":"bar","name":"cni-net-dir"}' ] } +@test "cni/DaemonSet: cni namespace has a default when not set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/cni-daemonset.yaml \ + --set 'connectInject.cni.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r -c '.metadata.namespace' | tee /dev/stderr) + [[ "${actual}" == "default" ]] +} + +@test "cni/DaemonSet: able to set cni namespace" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/cni-daemonset.yaml \ + --set 'connectInject.cni.enabled=true' \ + --set 'connectInject.cni.namespace=kube-system' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r -c '.metadata.namespace' | tee /dev/stderr) + [[ "${actual}" == "kube-system" ]] +} + +@test "cni/DaemonSet: still uses cni.namespace when helm -n is used" { + cd `chart_dir` + local actual=$(helm template -n foo \ + -s templates/cni-daemonset.yaml \ + --set 'connectInject.cni.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'connectInject.cni.namespace=kube-system' \ + . | tee /dev/stderr | + yq -r -c '.metadata.namespace' | tee /dev/stderr) + [[ "${actual}" == "kube-system" ]] +} + +@test "cni/DaemonSet: default namespace can be overridden by helm -n" { + cd `chart_dir` + local actual=$(helm template -n foo \ + -s templates/cni-daemonset.yaml \ + --set 'connectInject.cni.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r -c '.metadata.namespace' | tee /dev/stderr) + [[ "${actual}" == "foo" ]] +} diff --git a/charts/consul/test/unit/cni-networkattachmentdefinition.bats b/charts/consul/test/unit/cni-networkattachmentdefinition.bats index a7f0d1da03..65730079bb 100644 --- a/charts/consul/test/unit/cni-networkattachmentdefinition.bats +++ b/charts/consul/test/unit/cni-networkattachmentdefinition.bats @@ -59,3 +59,27 @@ load _helpers } +@test "cni/NetworkAttachmentDefinition: cni namespace has a default when not set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/cni-networkattachmentdefinition.yaml \ + --set 'connectInject.enabled=true' \ + --set 'connectInject.cni.enabled=true' \ + --set 'connectInject.cni.multus=true' \ + . | tee /dev/stderr | + yq -r -c '.metadata.namespace' | tee /dev/stderr) + [[ "${actual}" == "default" ]] +} + +@test "cni/NetworkAttachmentDefinition: able to set cni namespace" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/cni-networkattachmentdefinition.yaml \ + --set 'connectInject.enabled=true' \ + --set 'connectInject.cni.enabled=true' \ + --set 'connectInject.cni.multus=true' \ + --set 'connectInject.cni.namespace=kube-system' \ + . | tee /dev/stderr | + yq -r -c '.metadata.namespace' | tee /dev/stderr) + [[ "${actual}" == "kube-system" ]] +} diff --git a/charts/consul/test/unit/cni-podsecuritypolicy.bats b/charts/consul/test/unit/cni-podsecuritypolicy.bats index 37df761995..21af659cde 100644 --- a/charts/consul/test/unit/cni-podsecuritypolicy.bats +++ b/charts/consul/test/unit/cni-podsecuritypolicy.bats @@ -30,3 +30,27 @@ load _helpers [[ "${actual}" == "true" ]] } +@test "cni/PodSecurityPolicy: cni namespace has a default when not set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/cni-podsecuritypolicy.yaml \ + --set 'connectInject.cni.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'global.enablePodSecurityPolicies=true' \ + . | tee /dev/stderr | + yq -r -c '.metadata.namespace' | tee /dev/stderr) + [[ "${actual}" == "default" ]] +} + +@test "cni/PodSecurityPolicy: able to set cni namespace" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/cni-podsecuritypolicy.yaml \ + --set 'connectInject.cni.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'global.enablePodSecurityPolicies=true' \ + --set 'connectInject.cni.namespace=kube-system' \ + . | tee /dev/stderr | + yq -r -c '.metadata.namespace' | tee /dev/stderr) + [[ "${actual}" == "kube-system" ]] +} diff --git a/charts/consul/test/unit/cni-resourcequota.bats b/charts/consul/test/unit/cni-resourcequota.bats index 36c7a26b30..f7495d3565 100644 --- a/charts/consul/test/unit/cni-resourcequota.bats +++ b/charts/consul/test/unit/cni-resourcequota.bats @@ -29,6 +29,29 @@ load _helpers . } +@test "cni/ResourceQuota: cni namespace has a default when not set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/cni-resourcequota.yaml \ + --set 'connectInject.cni.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r -c '.metadata.namespace' | tee /dev/stderr) + [[ "${actual}" == "default" ]] +} + +@test "cni/ResourceQuota: able to set cni namespace" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/cni-resourcequota.yaml \ + --set 'connectInject.cni.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'connectInject.cni.namespace=kube-system' \ + . | tee /dev/stderr | + yq -r -c '.metadata.namespace' | tee /dev/stderr) + [[ "${actual}" == "kube-system" ]] +} + #-------------------------------------------------------------------- # pods diff --git a/charts/consul/test/unit/cni-securitycontextcontstraints.bats b/charts/consul/test/unit/cni-securitycontextcontstraints.bats index 759979aee2..933282f0dc 100644 --- a/charts/consul/test/unit/cni-securitycontextcontstraints.bats +++ b/charts/consul/test/unit/cni-securitycontextcontstraints.bats @@ -31,3 +31,27 @@ load _helpers [ "${actual}" = "true" ] } +@test "cni/SecurityContextConstraints: cni namespace has a default when not set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/cni-securitycontextconstraints.yaml \ + --set 'connectInject.cni.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'global.openshift.enabled=true' \ + . | tee /dev/stderr | + yq -r -c '.metadata.namespace' | tee /dev/stderr) + [[ "${actual}" == "default" ]] +} + +@test "cni/SecurityContextConstraints: able to set cni namespace" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/cni-securitycontextconstraints.yaml \ + --set 'connectInject.cni.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'global.openshift.enabled=true' \ + --set 'connectInject.cni.namespace=kube-system' \ + . | tee /dev/stderr | + yq -r -c '.metadata.namespace' | tee /dev/stderr) + [[ "${actual}" == "kube-system" ]] +} diff --git a/charts/consul/test/unit/cni-serviceaccount.bats b/charts/consul/test/unit/cni-serviceaccount.bats index 4f2071f823..73146bd0d9 100644 --- a/charts/consul/test/unit/cni-serviceaccount.bats +++ b/charts/consul/test/unit/cni-serviceaccount.bats @@ -29,6 +29,29 @@ load _helpers . } +@test "cni/ServiceAccount: cni namespace has a default when not set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/cni-serviceaccount.yaml \ + --set 'connectInject.cni.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r -c '.metadata.namespace' | tee /dev/stderr) + [[ "${actual}" == "default" ]] +} + +@test "cni/ServiceAccount: able to set cni namespace" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/cni-serviceaccount.yaml \ + --set 'connectInject.cni.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'connectInject.cni.namespace=kube-system' \ + . | tee /dev/stderr | + yq -r -c '.metadata.namespace' | tee /dev/stderr) + [[ "${actual}" == "kube-system" ]] +} + #-------------------------------------------------------------------- # global.imagePullSecrets diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index fde9ad9513..9122d62d53 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -1920,6 +1920,11 @@ connectInject: # @type: string logLevel: null + # Set the namespace to install the CNI plugin into. Overrides global namespace settings for CNI resources. + # Ex: "kube-system" + # @type: string + namespace: null + # Location on the kubernetes node where the CNI plugin is installed. Shoud be the absolute path and start with a '/' # Example on GKE: # From 1feaa9fef89f5203e442578c1c053d9a24b6b9f8 Mon Sep 17 00:00:00 2001 From: Thomas Eckert Date: Wed, 30 Nov 2022 17:09:27 -0500 Subject: [PATCH 010/340] Fix TLS Cert when using `enableAutoEncrypt` (#1753) * Fix name of autoencrypt cert * Update BATS * Update CHANGELOG * Remove HOST_IP * Don't mount consul-ca-cert when using system roots and external servers * Remove timeout and partition flags' * Don't mount consul-ca-cert when using system roots and external servers on main container --- CHANGELOG.md | 4 + .../api-gateway-controller-deployment.yaml | 20 +- .../api-gateway-controller-deployment.bats | 173 ++++++++++++++---- 3 files changed, 151 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5693dd1c28..7cc3a1b74f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ IMPROVEMENTS: * Helm: * CNI: Add `connectInject.cni.namespace` stanza which allows the CNI plugin resources to be deployed in a namespace other than the namespace that Consul is installed. [[GH-1756](https://github.com/hashicorp/consul-k8s/pull/1756)] +BUG FIXES: +* Helm: + * Use the correct autogenerated cert for the API Gateway Controller when connecting to servers versus clients. [[GH-1753](https://github.com/hashicorp/consul-k8s/pull/1753)] + * Don't mount the CA cert when `externalServers.useSystemRoots` is `true`. [[GH-1753](https://github.com/hashicorp/consul-k8s/pull/1753)] ## 1.0.1 (November 21, 2022) diff --git a/charts/consul/templates/api-gateway-controller-deployment.yaml b/charts/consul/templates/api-gateway-controller-deployment.yaml index 52884f725b..808f93b421 100644 --- a/charts/consul/templates/api-gateway-controller-deployment.yaml +++ b/charts/consul/templates/api-gateway-controller-deployment.yaml @@ -56,8 +56,8 @@ spec: name: sds protocol: TCP env: - {{- if .Values.global.tls.enabled }} {{- if or (not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots)) .Values.client.enabled }} + {{- if .Values.global.tls.enabled }} - name: CONSUL_CACERT value: /consul/tls/ca/tls.crt {{- end }} @@ -149,8 +149,9 @@ spec: - name: consul-bin mountPath: /consul-bin {{- end }} + {{- if not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) }} {{- if .Values.global.tls.enabled }} - {{- if .Values.global.tls.enableAutoEncrypt }} + {{- if and .Values.client.enabled .Values.global.tls.enableAutoEncrypt }} - name: consul-auto-encrypt-ca-cert {{- else }} - name: consul-ca-cert @@ -158,6 +159,7 @@ spec: mountPath: /consul/tls/ca readOnly: true {{- end }} + {{- end }} - mountPath: /consul/login name: consul-data readOnly: true @@ -222,10 +224,6 @@ spec: {{- if .Values.global.acls.manageSystemACLs }} - name: api-gateway-controller-acl-init env: - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - name: NAMESPACE valueFrom: fieldRef: @@ -242,15 +240,13 @@ spec: - mountPath: /consul/login name: consul-data readOnly: false + {{- if not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) }} {{- if .Values.global.tls.enabled }} - {{- if .Values.global.tls.enableAutoEncrypt }} - - name: consul-auto-encrypt-ca-cert - {{- else }} - name: consul-ca-cert - {{- end }} mountPath: /consul/tls/ca readOnly: true {{- end }} + {{- end }} command: - "/bin/sh" - "-ec" @@ -262,10 +258,6 @@ spec: {{- else }} -auth-method-name={{ template "consul.fullname" . }}-k8s-component-auth-method \ {{- end }} - {{- if .Values.global.adminPartitions.enabled }} - -partition={{ .Values.global.adminPartitions.name }} \ - {{- end }} - -api-timeout={{ .Values.global.consulAPITimeout }} \ -log-level={{ default .Values.global.logLevel .Values.apiGateway.logLevel }} \ -log-json={{ .Values.global.logJSON }} resources: diff --git a/charts/consul/test/unit/api-gateway-controller-deployment.bats b/charts/consul/test/unit/api-gateway-controller-deployment.bats index 5f00cb65a0..e0d646dc9a 100755 --- a/charts/consul/test/unit/api-gateway-controller-deployment.bats +++ b/charts/consul/test/unit/api-gateway-controller-deployment.bats @@ -189,7 +189,7 @@ load _helpers [ "${actual}" = "true" ] } -@test "apiGateway/Deployment: consul-auto-encrypt-ca-cert volumeMount is added when TLS with auto-encrypt is enabled" { +@test "apiGateway/Deployment: consul-auto-encrypt-ca-cert volumeMount is added when TLS with auto-encrypt is enabled with clients" { cd `chart_dir` local actual=$(helm template \ -s templates/api-gateway-controller-deployment.yaml \ @@ -197,11 +197,26 @@ load _helpers --set 'apiGateway.image=foo' \ --set 'global.tls.enabled=true' \ --set 'global.tls.enableAutoEncrypt=true' \ + --set 'client.enabled=true' \ . | tee /dev/stderr | yq '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "consul-auto-encrypt-ca-cert") | length > 0' | tee /dev/stderr) [ "${actual}" = "true" ] } +@test "apiGateway/Deployment: consul-ca-cert volumeMount is added when TLS with auto-encrypt is enabled without clients" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/api-gateway-controller-deployment.yaml \ + --set 'apiGateway.enabled=true' \ + --set 'apiGateway.image=foo' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'client.enabled=false' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "consul-ca-cert") | length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + @test "apiGateway/Deployment: get-auto-encrypt-client-ca init container is created when TLS with auto-encrypt is enabled" { cd `chart_dir` local actual=$(helm template \ @@ -315,27 +330,23 @@ load _helpers [ "${actual}" = "true" ] local actual=$(echo $object | - yq '[.env[0].name] | any(contains("HOST_IP"))' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(echo $object | - yq '[.env[1].name] | any(contains("NAMESPACE"))' | tee /dev/stderr) + yq '[.env[0].name] | any(contains("NAMESPACE"))' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | - yq '[.env[2].name] | any(contains("POD_NAME"))' | tee /dev/stderr) + yq '[.env[1].name] | any(contains("POD_NAME"))' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | - yq '[.env[3].name] | any(contains("CONSUL_LOGIN_META"))' | tee /dev/stderr) + yq '[.env[2].name] | any(contains("CONSUL_LOGIN_META"))' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | - yq '[.env[3].value] | any(contains("component=api-gateway-controller,pod=$(NAMESPACE)/$(POD_NAME)"))' | tee /dev/stderr) + yq '[.env[2].value] | any(contains("component=api-gateway-controller,pod=$(NAMESPACE)/$(POD_NAME)"))' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | - yq -r '.command | any(contains("-api-timeout=5s"))' | tee /dev/stderr) + yq -r '[.env[7].value] | any(contains("5s"))' | tee /dev/stderr) [ "${actual}" = "true" ] } @@ -356,31 +367,51 @@ load _helpers [ "${actual}" = "true" ] local actual=$(echo $object | - yq '[.env[0].name] | any(contains("HOST_IP"))' | tee /dev/stderr) + yq '.env[] | select(.name == "NAMESPACE") | [.valueFrom.fieldRef.fieldPath] | any(contains("metadata.namespace"))' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq '.env[] | select(.name == "POD_NAME") | [.valueFrom.fieldRef.fieldPath] | any(contains("metadata.name"))' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq '.env[] | select(.name == "CONSUL_LOGIN_META") | [.value] | any(contains("component=api-gateway-controller,pod=$(NAMESPACE)/$(POD_NAME)"))' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | - yq '[.env[1].name] | any(contains("NAMESPACE"))' | tee /dev/stderr) + yq '.env[] | select(.name == "CONSUL_ADDRESSES") | [.value] | any(contains("release-name-consul-server.default.svc"))' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | - yq '[.env[2].name] | any(contains("POD_NAME"))' | tee /dev/stderr) + yq '.env[] | select(.name == "CONSUL_GRPC_PORT") | [.value] | any(contains("8502"))' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | - yq '[.env[3].name] | any(contains("CONSUL_LOGIN_META"))' | tee /dev/stderr) + yq '.env[] | select(.name == "CONSUL_HTTP_PORT") | [.value] | any(contains("8501"))' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | - yq '[.env[3].value] | any(contains("component=api-gateway-controller,pod=$(NAMESPACE)/$(POD_NAME)"))' | tee /dev/stderr) + yq '.env[] | select(.name == "CONSUL_DATACENTER") | [.value] | any(contains("dc1"))' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | - yq '.volumeMounts[1] | any(contains("consul-ca-cert"))' | tee /dev/stderr) + yq '.env[] | select(.name == "CONSUL_API_TIMEOUT") | [.value] | any(contains("5s"))' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | - yq -r '.command | any(contains("-api-timeout=5s"))' | tee /dev/stderr) + yq '.env[] | select(.name == "CONSUL_USE_TLS") | [.value] | any(contains("true"))' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq '.env[] | select(.name == "CONSUL_CACERT_FILE") | [.value] | any(contains("/consul/tls/ca/tls.crt"))' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq '.volumeMounts[] | select(.name == "consul-ca-cert") | [.mountPath] | any(contains("/consul/tls/ca"))' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq '.volumeMounts[] | select(.name == "consul-data") | [.mountPath] | any(contains("/consul/login"))' | tee /dev/stderr) [ "${actual}" = "true" ] } @@ -407,35 +438,59 @@ load _helpers [ "${actual}" = "true" ] local actual=$(echo $object | - yq -r '.command | any(contains("-partition=default"))' | tee /dev/stderr) + yq '.env[] | select(.name == "NAMESPACE") | [.valueFrom.fieldRef.fieldPath] | any(contains("metadata.namespace"))' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq '.env[] | select(.name == "POD_NAME") | [.valueFrom.fieldRef.fieldPath] | any(contains("metadata.name"))' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq '.env[] | select(.name == "CONSUL_LOGIN_META") | [.value] | any(contains("component=api-gateway-controller,pod=$(NAMESPACE)/$(POD_NAME)"))' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq '.env[] | select(.name == "CONSUL_ADDRESSES") | [.value] | any(contains("release-name-consul-server.default.svc"))' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq '.env[] | select(.name == "CONSUL_GRPC_PORT") | [.value] | any(contains("8502"))' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq '.env[] | select(.name == "CONSUL_HTTP_PORT") | [.value] | any(contains("8501"))' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq '.env[] | select(.name == "CONSUL_DATACENTER") | [.value] | any(contains("dc1"))' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | - yq '[.env[0].name] | any(contains("HOST_IP"))' | tee /dev/stderr) + yq '.env[] | select(.name == "CONSUL_API_TIMEOUT") | [.value] | any(contains("5s"))' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | - yq '[.env[1].name] | any(contains("NAMESPACE"))' | tee /dev/stderr) + yq '.env[] | select(.name == "CONSUL_PARTITION") | [.value] | any(contains("default"))' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | - yq '[.env[2].name] | any(contains("POD_NAME"))' | tee /dev/stderr) + yq '.env[] | select(.name == "CONSUL_LOGIN_PARTITION") | [.value] | any(contains("default"))' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | - yq '[.env[3].name] | any(contains("CONSUL_LOGIN_META"))' | tee /dev/stderr) + yq '.env[] | select(.name == "CONSUL_USE_TLS") | [.value] | any(contains("true"))' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | - yq '[.env[3].value] | any(contains("component=api-gateway-controller,pod=$(NAMESPACE)/$(POD_NAME)"))' | tee /dev/stderr) + yq '.env[] | select(.name == "CONSUL_CACERT_FILE") | [.value] | any(contains("/consul/tls/ca/tls.crt"))' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | - yq '[.volumeMounts[1].name] | any(contains("consul-ca-cert"))' | tee /dev/stderr) + yq '.volumeMounts[] | select(.name == "consul-ca-cert") | [.mountPath] | any(contains("/consul/tls/ca"))' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | - yq -r '.command | any(contains("-api-timeout=5s"))' | tee /dev/stderr) + yq '.volumeMounts[] | select(.name == "consul-data") | [.mountPath] | any(contains("/consul/login"))' | tee /dev/stderr) [ "${actual}" = "true" ] } @@ -512,31 +567,51 @@ load _helpers [ "${actual}" = "true" ] local actual=$(echo $object | - yq '[.env[0].name] | any(contains("HOST_IP"))' | tee /dev/stderr) + yq '.env[] | select(.name == "NAMESPACE") | [.valueFrom.fieldRef.fieldPath] | any(contains("metadata.namespace"))' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq '.env[] | select(.name == "POD_NAME") | [.valueFrom.fieldRef.fieldPath] | any(contains("metadata.name"))' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq '.env[] | select(.name == "CONSUL_LOGIN_META") | [.value] | any(contains("component=api-gateway-controller,pod=$(NAMESPACE)/$(POD_NAME)"))' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq '.env[] | select(.name == "CONSUL_ADDRESSES") | [.value] | any(contains("release-name-consul-server.default.svc"))' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq '.env[] | select(.name == "CONSUL_GRPC_PORT") | [.value] | any(contains("8502"))' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq '.env[] | select(.name == "CONSUL_HTTP_PORT") | [.value] | any(contains("8501"))' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | - yq '[.env[1].name] | any(contains("NAMESPACE"))' | tee /dev/stderr) + yq '.env[] | select(.name == "CONSUL_DATACENTER") | [.value] | any(contains("dc1"))' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | - yq '[.env[2].name] | any(contains("POD_NAME"))' | tee /dev/stderr) + yq '.env[] | select(.name == "CONSUL_API_TIMEOUT") | [.value] | any(contains("5s"))' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | - yq '[.env[3].name] | any(contains("CONSUL_LOGIN_META"))' | tee /dev/stderr) + yq '.env[] | select(.name == "CONSUL_USE_TLS") | [.value] | any(contains("true"))' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | - yq '[.env[3].value] | any(contains("component=api-gateway-controller,pod=$(NAMESPACE)/$(POD_NAME)"))' | tee /dev/stderr) + yq '.env[] | select(.name == "CONSUL_CACERT_FILE") | [.value] | any(contains("/consul/tls/ca/tls.crt"))' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | - yq '[.volumeMounts[1].name] | any(contains("consul-auto-encrypt-ca-cert"))' | tee /dev/stderr) + yq '.volumeMounts[] | select(.name == "consul-ca-cert") | [.mountPath] | any(contains("/consul/tls/ca"))' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | - yq -r '.command | any(contains("-api-timeout=5s"))' | tee /dev/stderr) + yq '.volumeMounts[] | select(.name == "consul-data") | [.mountPath] | any(contains("/consul/login"))' | tee /dev/stderr) [ "${actual}" = "true" ] } @@ -1416,3 +1491,37 @@ load _helpers yq '.spec.template.spec.containers[0].env[0].name == "CONSUL_CACERT"' | tee /dev/stderr) [ "${actual}" = "false" ] } + +@test "apiGateway/Deployment: consul-ca-cert volume mount is not set when using externalServers and useSystemRoots" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/api-gateway-controller-deployment.yaml \ + --set 'apiGateway.enabled=true' \ + --set 'apiGateway.image=bar' \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.tls.enabled=true' \ + --set 'server.enabled=false' \ + --set 'externalServers.hosts[0]=external-consul.host' \ + --set 'externalServers.enabled=true' \ + --set 'externalServers.useSystemRoots=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].env[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) + [ "${actual}" = "" ] +} + +@test "apiGateway/Deployment: consul-ca-cert volume mount is not set on acl-init when using externalServers and useSystemRoots" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/api-gateway-controller-deployment.yaml \ + --set 'apiGateway.enabled=true' \ + --set 'apiGateway.image=bar' \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.tls.enabled=true' \ + --set 'server.enabled=false' \ + --set 'externalServers.hosts[0]=external-consul.host' \ + --set 'externalServers.enabled=true' \ + --set 'externalServers.useSystemRoots=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.initContainers[1].env[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) + [ "${actual}" = "" ] +} From daebf0b6b8cdec4e9ab1d0060d559f768bc04727 Mon Sep 17 00:00:00 2001 From: Thomas Eckert Date: Wed, 30 Nov 2022 18:11:35 -0500 Subject: [PATCH 011/340] Mount autoencrypt certs when using clients --- .../api-gateway-controller-deployment.yaml | 6 +-- .../api-gateway-controller-deployment.bats | 42 ++++++++++++++++++- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/charts/consul/templates/api-gateway-controller-deployment.yaml b/charts/consul/templates/api-gateway-controller-deployment.yaml index 808f93b421..eeecf05133 100644 --- a/charts/consul/templates/api-gateway-controller-deployment.yaml +++ b/charts/consul/templates/api-gateway-controller-deployment.yaml @@ -149,7 +149,7 @@ spec: - name: consul-bin mountPath: /consul-bin {{- end }} - {{- if not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) }} + {{- if or (not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots)) .Values.client.enabled }} {{- if .Values.global.tls.enabled }} {{- if and .Values.client.enabled .Values.global.tls.enableAutoEncrypt }} - name: consul-auto-encrypt-ca-cert @@ -179,7 +179,7 @@ spec: emptyDir: { } {{- end }} {{- if .Values.global.tls.enabled }} - {{- if not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) }} + {{- if or (not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots)) .Values.client.enabled }} - name: consul-ca-cert secret: {{- if .Values.global.tls.caCert.secretName }} @@ -240,7 +240,7 @@ spec: - mountPath: /consul/login name: consul-data readOnly: false - {{- if not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) }} + {{- if or (not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots)) .Values.client.enabled }} {{- if .Values.global.tls.enabled }} - name: consul-ca-cert mountPath: /consul/tls/ca diff --git a/charts/consul/test/unit/api-gateway-controller-deployment.bats b/charts/consul/test/unit/api-gateway-controller-deployment.bats index e0d646dc9a..8835844297 100755 --- a/charts/consul/test/unit/api-gateway-controller-deployment.bats +++ b/charts/consul/test/unit/api-gateway-controller-deployment.bats @@ -1505,7 +1505,7 @@ load _helpers --set 'externalServers.enabled=true' \ --set 'externalServers.useSystemRoots=true' \ . | tee /dev/stderr | - yq '.spec.template.spec.containers[0].env[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) + yq '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) [ "${actual}" = "" ] } @@ -1522,6 +1522,44 @@ load _helpers --set 'externalServers.enabled=true' \ --set 'externalServers.useSystemRoots=true' \ . | tee /dev/stderr | - yq '.spec.template.spec.initContainers[1].env[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) + yq '.spec.template.spec.initContainers[1].volumeMounts[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) [ "${actual}" = "" ] } + +@test "apiGateway/Deployment: consul-auto-encrypt-ca-cert volume mount is set when tls.enabled, client.enabled, externalServers, useSystemRoots, and autoencrypt" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/api-gateway-controller-deployment.yaml \ + --set 'apiGateway.enabled=true' \ + --set 'apiGateway.image=bar' \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.tls.enabled=true' \ + --set 'client.enabled=true' \ + --set 'server.enabled=false' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'externalServers.hosts[0]=external-consul.host' \ + --set 'externalServers.enabled=true' \ + --set 'externalServers.useSystemRoots=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "consul-auto-encrypt-ca-cert") | .mountPath' | tee /dev/stderr) + [ "${actual}" = '"/consul/tls/ca"' ] +} + +@test "apiGateway/Deployment: consul-ca-cert volume mount is set on acl-init container when tls.enabled, client.enabled, externalServers, useSystemRoots, and autoencrypt" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/api-gateway-controller-deployment.yaml \ + --set 'apiGateway.enabled=true' \ + --set 'apiGateway.image=bar' \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.tls.enabled=true' \ + --set 'client.enabled=true' \ + --set 'server.enabled=false' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'externalServers.hosts[0]=external-consul.host' \ + --set 'externalServers.enabled=true' \ + --set 'externalServers.useSystemRoots=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.initContainers[2].volumeMounts[] | select(.name == "consul-ca-cert") | .mountPath' | tee /dev/stderr) + [ "${actual}" = '"/consul/tls/ca"' ] +} From 4ad6d2a7df6731754ebb82ce093dc420871887ca Mon Sep 17 00:00:00 2001 From: Thomas Eckert Date: Wed, 30 Nov 2022 18:12:46 -0500 Subject: [PATCH 012/340] Fix changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cc3a1b74f..01a82e871c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ IMPROVEMENTS: * Helm: * CNI: Add `connectInject.cni.namespace` stanza which allows the CNI plugin resources to be deployed in a namespace other than the namespace that Consul is installed. [[GH-1756](https://github.com/hashicorp/consul-k8s/pull/1756)] + BUG FIXES: * Helm: * Use the correct autogenerated cert for the API Gateway Controller when connecting to servers versus clients. [[GH-1753](https://github.com/hashicorp/consul-k8s/pull/1753)] From ddcddd95f1501df46ecdef3463602910190aa03e Mon Sep 17 00:00:00 2001 From: Thomas Eckert Date: Wed, 30 Nov 2022 20:03:22 -0500 Subject: [PATCH 013/340] Remove clients check for acl-init container --- charts/consul/templates/api-gateway-controller-deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/consul/templates/api-gateway-controller-deployment.yaml b/charts/consul/templates/api-gateway-controller-deployment.yaml index eeecf05133..2c93395a3b 100644 --- a/charts/consul/templates/api-gateway-controller-deployment.yaml +++ b/charts/consul/templates/api-gateway-controller-deployment.yaml @@ -240,7 +240,7 @@ spec: - mountPath: /consul/login name: consul-data readOnly: false - {{- if or (not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots)) .Values.client.enabled }} + {{- if not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) }} {{- if .Values.global.tls.enabled }} - name: consul-ca-cert mountPath: /consul/tls/ca From fab3bcb55de61db70d039616cb39dcde6cb2bbeb Mon Sep 17 00:00:00 2001 From: Andrew Stucki Date: Wed, 30 Nov 2022 21:01:43 -0500 Subject: [PATCH 014/340] only create the cert volume when not using system roots --- .../api-gateway-controller-deployment.yaml | 2 +- .../api-gateway-controller-deployment.bats | 21 +------------------ 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/charts/consul/templates/api-gateway-controller-deployment.yaml b/charts/consul/templates/api-gateway-controller-deployment.yaml index 2c93395a3b..c548b63e4d 100644 --- a/charts/consul/templates/api-gateway-controller-deployment.yaml +++ b/charts/consul/templates/api-gateway-controller-deployment.yaml @@ -179,7 +179,7 @@ spec: emptyDir: { } {{- end }} {{- if .Values.global.tls.enabled }} - {{- if or (not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots)) .Values.client.enabled }} + {{- if not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) }} - name: consul-ca-cert secret: {{- if .Values.global.tls.caCert.secretName }} diff --git a/charts/consul/test/unit/api-gateway-controller-deployment.bats b/charts/consul/test/unit/api-gateway-controller-deployment.bats index 8835844297..3376dc63c7 100755 --- a/charts/consul/test/unit/api-gateway-controller-deployment.bats +++ b/charts/consul/test/unit/api-gateway-controller-deployment.bats @@ -1543,23 +1543,4 @@ load _helpers . | tee /dev/stderr | yq '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "consul-auto-encrypt-ca-cert") | .mountPath' | tee /dev/stderr) [ "${actual}" = '"/consul/tls/ca"' ] -} - -@test "apiGateway/Deployment: consul-ca-cert volume mount is set on acl-init container when tls.enabled, client.enabled, externalServers, useSystemRoots, and autoencrypt" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/api-gateway-controller-deployment.yaml \ - --set 'apiGateway.enabled=true' \ - --set 'apiGateway.image=bar' \ - --set 'global.acls.manageSystemACLs=true' \ - --set 'global.tls.enabled=true' \ - --set 'client.enabled=true' \ - --set 'server.enabled=false' \ - --set 'global.tls.enableAutoEncrypt=true' \ - --set 'externalServers.hosts[0]=external-consul.host' \ - --set 'externalServers.enabled=true' \ - --set 'externalServers.useSystemRoots=true' \ - . | tee /dev/stderr | - yq '.spec.template.spec.initContainers[2].volumeMounts[] | select(.name == "consul-ca-cert") | .mountPath' | tee /dev/stderr) - [ "${actual}" = '"/consul/tls/ca"' ] -} +} \ No newline at end of file From 7a79bfde848252395ea5411b7241693856d00680 Mon Sep 17 00:00:00 2001 From: Andrew Stucki Date: Wed, 30 Nov 2022 21:02:28 -0500 Subject: [PATCH 015/340] add newline --- charts/consul/test/unit/api-gateway-controller-deployment.bats | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/consul/test/unit/api-gateway-controller-deployment.bats b/charts/consul/test/unit/api-gateway-controller-deployment.bats index 3376dc63c7..83d296b15d 100755 --- a/charts/consul/test/unit/api-gateway-controller-deployment.bats +++ b/charts/consul/test/unit/api-gateway-controller-deployment.bats @@ -1543,4 +1543,4 @@ load _helpers . | tee /dev/stderr | yq '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "consul-auto-encrypt-ca-cert") | .mountPath' | tee /dev/stderr) [ "${actual}" = '"/consul/tls/ca"' ] -} \ No newline at end of file +} From 1788b54710dcb6388d190d7ebddaee376101ebb7 Mon Sep 17 00:00:00 2001 From: Ranjandas Date: Tue, 6 Dec 2022 12:24:26 +1100 Subject: [PATCH 016/340] Remove controller from preset values file (#1755) * Remove controller from preset values file in CLI --- cli/cmd/install/install_test.go | 4 ++-- cli/cmd/status/status_test.go | 4 ++-- cli/cmd/upgrade/upgrade_test.go | 4 ++-- cli/preset/demo.go | 2 -- cli/preset/quickstart.go | 2 -- cli/preset/secure.go | 2 -- 6 files changed, 6 insertions(+), 12 deletions(-) diff --git a/cli/cmd/install/install_test.go b/cli/cmd/install/install_test.go index 04c250b1e4..07c04defef 100644 --- a/cli/cmd/install/install_test.go +++ b/cli/cmd/install/install_test.go @@ -558,7 +558,7 @@ func TestInstall(t *testing.T) { }, messages: []string{ "\n==> Checking if Consul can be installed\n ✓ No existing Consul installations found.\n ✓ No existing Consul persistent volume claims found\n ✓ No existing Consul secrets found.\n", - "\n==> Consul Installation Summary\n Name: consul\n Namespace: consul\n \n Helm value overrides\n --------------------\n connectInject:\n enabled: true\n metrics:\n defaultEnableMerging: true\n defaultEnabled: true\n enableGatewayMetrics: true\n controller:\n enabled: true\n global:\n metrics:\n enableAgentMetrics: true\n enabled: true\n name: consul\n prometheus:\n enabled: true\n server:\n replicas: 1\n ui:\n enabled: true\n service:\n enabled: true\n \n", + "\n==> Consul Installation Summary\n Name: consul\n Namespace: consul\n \n Helm value overrides\n --------------------\n connectInject:\n enabled: true\n metrics:\n defaultEnableMerging: true\n defaultEnabled: true\n enableGatewayMetrics: true\n global:\n metrics:\n enableAgentMetrics: true\n enabled: true\n name: consul\n prometheus:\n enabled: true\n server:\n replicas: 1\n ui:\n enabled: true\n service:\n enabled: true\n \n", "\n==> Installing Consul\n ✓ Downloaded charts.\n ✓ Consul installed in namespace \"consul\".\n", }, helmActionsRunner: &helm.MockActionRunner{}, @@ -574,7 +574,7 @@ func TestInstall(t *testing.T) { }, messages: []string{ "\n==> Checking if Consul can be installed\n ✓ No existing Consul installations found.\n ✓ No existing Consul persistent volume claims found\n ✓ No existing Consul secrets found.\n", - "\n==> Consul Installation Summary\n Name: consul\n Namespace: consul\n \n Helm value overrides\n --------------------\n connectInject:\n enabled: true\n controller:\n enabled: true\n global:\n acls:\n manageSystemACLs: true\n gossipEncryption:\n autoGenerate: true\n name: consul\n tls:\n enableAutoEncrypt: true\n enabled: true\n server:\n replicas: 1\n \n", + "\n==> Consul Installation Summary\n Name: consul\n Namespace: consul\n \n Helm value overrides\n --------------------\n connectInject:\n enabled: true\n global:\n acls:\n manageSystemACLs: true\n gossipEncryption:\n autoGenerate: true\n name: consul\n tls:\n enableAutoEncrypt: true\n enabled: true\n server:\n replicas: 1\n \n", "\n==> Installing Consul\n ✓ Downloaded charts.\n ✓ Consul installed in namespace \"consul\".\n", }, helmActionsRunner: &helm.MockActionRunner{}, diff --git a/cli/cmd/status/status_test.go b/cli/cmd/status/status_test.go index 8666fd8493..e227d4e3cf 100644 --- a/cli/cmd/status/status_test.go +++ b/cli/cmd/status/status_test.go @@ -77,7 +77,7 @@ func TestStatus(t *testing.T) { "status with servers returns success": { input: []string{}, messages: []string{ - fmt.Sprintf("\n==> Consul Status Summary\nName\tNamespace\tStatus\tChart Version\tAppVersion\tRevision\tLast Updated \n \t \tREADY \t1.0.0 \t \t0 \t%s\t\n", notImeStr), + fmt.Sprintf("\n==> Consul Status Summary\nName\tNamespace\tStatus\tChart Version\tAppVersion\tRevision\tLast Updated \n \t \tREADY \t1.0.0 \t \t0 \t%s\t\n", notImeStr), "\n==> Config:\n {}\n \nConsul servers healthy 3/3\n", }, preProcessingFunc: func(k8s kubernetes.Interface) error { @@ -102,7 +102,7 @@ func TestStatus(t *testing.T) { "status with pre-install and pre-upgrade hooks returns success and outputs hook status": { input: []string{}, messages: []string{ - fmt.Sprintf("\n==> Consul Status Summary\nName\tNamespace\tStatus\tChart Version\tAppVersion\tRevision\tLast Updated \n \t \tREADY \t1.0.0 \t \t0 \t%s\t\n", notImeStr), + fmt.Sprintf("\n==> Consul Status Summary\nName\tNamespace\tStatus\tChart Version\tAppVersion\tRevision\tLast Updated \n \t \tREADY \t1.0.0 \t \t0 \t%s\t\n", notImeStr), "\n==> Config:\n {}\n \n", "\n==> Status Of Helm Hooks:\npre-install-hook pre-install: Succeeded\npre-upgrade-hook pre-upgrade: Succeeded\nConsul servers healthy 3/3\n", }, diff --git a/cli/cmd/upgrade/upgrade_test.go b/cli/cmd/upgrade/upgrade_test.go index 809cac1034..d21b17febf 100644 --- a/cli/cmd/upgrade/upgrade_test.go +++ b/cli/cmd/upgrade/upgrade_test.go @@ -452,7 +452,7 @@ func TestUpgrade(t *testing.T) { messages: []string{ "\n==> Checking if Consul can be upgraded\n ✓ Existing Consul installation found to be upgraded.\n Name: consul\n Namespace: consul\n", "\n==> Checking if Consul demo application can be upgraded\n No existing Consul demo application installation found.\n", - "\n==> Consul Upgrade Summary\n ✓ Downloaded charts.\n \n Difference between user overrides for current and upgraded charts\n -----------------------------------------------------------------\n + connectInject:\n + enabled: true\n + metrics:\n + defaultEnableMerging: true\n + defaultEnabled: true\n + enableGatewayMetrics: true\n + controller:\n + enabled: true\n + global:\n + metrics:\n + enableAgentMetrics: true\n + enabled: true\n + name: consul\n + prometheus:\n + enabled: true\n + server:\n + replicas: 1\n + ui:\n + enabled: true\n + service:\n + enabled: true\n \n", + "\n==> Consul Upgrade Summary\n ✓ Downloaded charts.\n \n Difference between user overrides for current and upgraded charts\n -----------------------------------------------------------------\n + connectInject:\n + enabled: true\n + metrics:\n + defaultEnableMerging: true\n + defaultEnabled: true\n + enableGatewayMetrics: true\n + global:\n + metrics:\n + enableAgentMetrics: true\n + enabled: true\n + name: consul\n + prometheus:\n + enabled: true\n + server:\n + replicas: 1\n + ui:\n + enabled: true\n + service:\n + enabled: true\n \n", "\n==> Upgrading Consul\n ✓ Consul upgraded in namespace \"consul\".\n", }, helmActionsRunner: &helm.MockActionRunner{ @@ -477,7 +477,7 @@ func TestUpgrade(t *testing.T) { messages: []string{ "\n==> Checking if Consul can be upgraded\n ✓ Existing Consul installation found to be upgraded.\n Name: consul\n Namespace: consul\n", "\n==> Checking if Consul demo application can be upgraded\n No existing Consul demo application installation found.\n", - "\n==> Consul Upgrade Summary\n ✓ Downloaded charts.\n \n Difference between user overrides for current and upgraded charts\n -----------------------------------------------------------------\n + connectInject:\n + enabled: true\n + controller:\n + enabled: true\n + global:\n + acls:\n + manageSystemACLs: true\n + gossipEncryption:\n + autoGenerate: true\n + name: consul\n + tls:\n + enableAutoEncrypt: true\n + enabled: true\n + server:\n + replicas: 1\n \n", + "\n==> Consul Upgrade Summary\n ✓ Downloaded charts.\n \n Difference between user overrides for current and upgraded charts\n -----------------------------------------------------------------\n + connectInject:\n + enabled: true\n + global:\n + acls:\n + manageSystemACLs: true\n + gossipEncryption:\n + autoGenerate: true\n + name: consul\n + tls:\n + enableAutoEncrypt: true\n + enabled: true\n + server:\n + replicas: 1\n \n", "\n==> Upgrading Consul\n ✓ Consul upgraded in namespace \"consul\".\n", }, helmActionsRunner: &helm.MockActionRunner{ diff --git a/cli/preset/demo.go b/cli/preset/demo.go index bf6c0bb122..ee1b100114 100644 --- a/cli/preset/demo.go +++ b/cli/preset/demo.go @@ -29,8 +29,6 @@ connectInject: enableGatewayMetrics: true server: replicas: 1 -controller: - enabled: true ui: enabled: true service: diff --git a/cli/preset/quickstart.go b/cli/preset/quickstart.go index 52b3f000b1..823a60e312 100644 --- a/cli/preset/quickstart.go +++ b/cli/preset/quickstart.go @@ -29,8 +29,6 @@ connectInject: enableGatewayMetrics: true server: replicas: 1 -controller: - enabled: true ui: enabled: true service: diff --git a/cli/preset/secure.go b/cli/preset/secure.go index ded436804c..6fccc956a6 100644 --- a/cli/preset/secure.go +++ b/cli/preset/secure.go @@ -29,8 +29,6 @@ server: replicas: 1 connectInject: enabled: true -controller: - enabled: true ` return config.ConvertToMap(values), nil From c12be28f132bf1c4bbe6e89a7d0b508617ef446e Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Tue, 6 Dec 2022 16:49:15 -0500 Subject: [PATCH 017/340] Set consul api = v1.18.0, sdk = v0.13.0 and imageConsul = 1.14.2 (#1769) * Update to consuls latest versions * go mod tidy --- charts/consul/Chart.yaml | 4 ++-- charts/consul/values.yaml | 2 +- control-plane/cni/go.mod | 2 +- control-plane/go.mod | 4 ++-- control-plane/go.sum | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/charts/consul/Chart.yaml b/charts/consul/Chart.yaml index a8e1505280..253291defc 100644 --- a/charts/consul/Chart.yaml +++ b/charts/consul/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 name: consul version: 1.1.0-dev -appVersion: 1.14.0 +appVersion: 1.14.2 kubeVersion: ">=1.21.0-0" description: Official HashiCorp Consul Chart home: https://www.consul.io @@ -13,7 +13,7 @@ annotations: artifacthub.io/prerelease: true artifacthub.io/images: | - name: consul - image: hashicorp/consul:1.14.0 + image: hashicorp/consul:1.14.2 - name: consul-k8s-control-plane image: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.1.0-dev - name: consul-dataplane diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 9122d62d53..d26316100e 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -63,7 +63,7 @@ global: # image: "hashicorp/consul-enterprise:1.10.0-ent" # ``` # @default: hashicorp/consul: - image: "hashicorp/consul:1.14.0" + image: "hashicorp/consul:1.14.2" # Array of objects containing image pull secret names that will be applied to each service account. # This can be used to reference image pull secrets if using a custom consul or consul-k8s-control-plane Docker image. diff --git a/control-plane/cni/go.mod b/control-plane/cni/go.mod index 5d3423eef7..2b43b784fd 100644 --- a/control-plane/cni/go.mod +++ b/control-plane/cni/go.mod @@ -3,7 +3,7 @@ module github.com/hashicorp/consul-k8s/control-plane/cni require ( github.com/containernetworking/cni v1.1.1 github.com/containernetworking/plugins v1.1.1 - github.com/hashicorp/consul/sdk v0.12.0 + github.com/hashicorp/consul/sdk v0.13.0 github.com/hashicorp/go-hclog v0.16.1 github.com/stretchr/testify v1.7.1 k8s.io/api v0.22.2 diff --git a/control-plane/go.mod b/control-plane/go.mod index 7e1128121a..a20937c694 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -10,8 +10,8 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af65262de8 github.com/hashicorp/consul-server-connection-manager v0.1.0 - github.com/hashicorp/consul/api v1.16.0 - github.com/hashicorp/consul/sdk v0.12.0 + github.com/hashicorp/consul/api v1.18.0 + github.com/hashicorp/consul/sdk v0.13.0 github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f github.com/hashicorp/go-hclog v1.2.2 github.com/hashicorp/go-multierror v1.1.1 diff --git a/control-plane/go.sum b/control-plane/go.sum index 0b746303bb..b5b77aa4b8 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -346,8 +346,8 @@ github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af6526 github.com/hashicorp/consul-server-connection-manager v0.1.0 h1:XCweGvMHzra88rYv2zxwwuUOjBUdcQmNKVrnQmt/muo= github.com/hashicorp/consul-server-connection-manager v0.1.0/go.mod h1:XVVlO+Yk7aiRpspiHZkrrFVn9BJIiOPnQIzqytPxGaU= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.16.0 h1:Vf/QVFIwz+PdHR4T4lSwYzLULtbHVq0BheXCUAKP50M= -github.com/hashicorp/consul/api v1.16.0/go.mod h1:GJI1Sif0Wc/iYyqg7EXHJV37IPush6eJTewvYdF9uO8= +github.com/hashicorp/consul/api v1.18.0 h1:R7PPNzTCeN6VuQNDwwhZWJvzCtGSrNpJqfb22h3yH9g= +github.com/hashicorp/consul/api v1.18.0/go.mod h1:owRRGJ9M5xReDC5nfT8FTJrNAPbT4NM6p/k+d03q2v4= github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3usQMXZF6ziK3SzXU= github.com/hashicorp/consul/proto-public v0.1.0/go.mod h1:vs2KkuWwtjkIgA5ezp4YKPzQp4GitV+q/+PvksrA92k= github.com/hashicorp/consul/sdk v0.4.1-0.20221021205723-cc843c4be892 h1:jw0NwPmNPr5CxAU04hACdj61JSaJBKZ0FdBo+kwfNp4= From 62204bf56725d0c84a172d97803e784e793fed65 Mon Sep 17 00:00:00 2001 From: David Yu Date: Wed, 7 Dec 2022 08:31:52 -0800 Subject: [PATCH 018/340] CHANGELOG: Add 0.49.x series to release notes and 1.0.2 (#1772) --- CHANGELOG.md | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01a82e871c..dccaa78fd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## UNRELEASED +## 1.0.2 (December 1, 2022) + IMPROVEMENTS: * Helm: * CNI: Add `connectInject.cni.namespace` stanza which allows the CNI plugin resources to be deployed in a namespace other than the namespace that Consul is installed. [[GH-1756](https://github.com/hashicorp/consul-k8s/pull/1756)] @@ -9,6 +11,18 @@ BUG FIXES: * Use the correct autogenerated cert for the API Gateway Controller when connecting to servers versus clients. [[GH-1753](https://github.com/hashicorp/consul-k8s/pull/1753)] * Don't mount the CA cert when `externalServers.useSystemRoots` is `true`. [[GH-1753](https://github.com/hashicorp/consul-k8s/pull/1753)] +## 0.49.2 (December 1, 2022) + +IMPROVEMENTS: +* Control Plane + * Bump Dockerfile base image for RedHat UBI `consul-k8s-control-plane` image to `ubi-minimal:9.1`. [[GH-1725](https://github.com/hashicorp/consul-k8s/pull/1725)] +* Helm + * Add fields `localConnectTimeoutMs` and `localRequestTimeoutMs` to the `ServiceDefaults` CRD. [[GH-1647](https://github.com/hashicorp/consul-k8s/pull/1647)] + +BUG FIXES: +* Helm: + * Disable PodSecurityPolicies templating for `gossip-encryption-autogenerate` and `partition-init` when `global.enablePodSecurityPolicies` is `false`. [[GH-1693](https://github.com/hashicorp/consul-k8s/pull/1693)] + ## 1.0.1 (November 21, 2022) BUG FIXES: @@ -110,7 +124,6 @@ IMPROVEMENTS: * API Gateway: Allow controller to read MeshServices for use as a route backend. [[GH-1574](https://github.com/hashicorp/consul-k8s/pull/1574)] * API Gateway: Add support for using dynamic server discovery strings when running without agents. [[GH-1732](https://github.com/hashicorp/consul-k8s/pull/1732)] - BUG FIXES: * CLI * Allow optional environment variables for use in the cloud preset to the CLI for cluster bootstrapping. [[GH-1608](https://github.com/hashicorp/consul-k8s/pull/1608)] @@ -121,6 +134,23 @@ BUG FIXES: * Helm: * Disable PodSecurityPolicies in all templates when `global.enablePodSecurityPolicies` is `false`. [[GH-1693](https://github.com/hashicorp/consul-k8s/pull/1693)] +## 0.49.1 (November 14, 2022) +BREAKING CHANGES: +* Peering: + * Rename `PeerName` to `Peer` in ExportedServices CRD. [[GH-1596](https://github.com/hashicorp/consul-k8s/pull/1596)] + +FEATURES: +* Ingress Gateway + * Add support for MaxConnections, MaxConcurrentRequests, and MaxPendingRequests to Ingress Gateway CRD. [[GH-1691](https://github.com/hashicorp/consul-k8s/pull/1691)] + +IMPROVEMENTS: +* Helm: + * Add `tolerations` and `nodeSelector` to Server ACL init jobs and `nodeSelector` to Webhook cert manager. [[GH-1581](https://github.com/hashicorp/consul-k8s/pull/1581)] + * API Gateway: Allow controller to read MeshServices for use as a route backend. [[GH-1574](https://github.com/hashicorp/consul-k8s/pull/1574)] + * API Gateway: Add `tolerations` to `apiGateway.managedGatewayClass` and `apiGateway.controller` [[GH-1650](https://github.com/hashicorp/consul-k8s/pull/1650)] + * API Gateway: Create PodSecurityPolicy for controller when `global.enablePodSecurityPolicies=true`. [[GH-1656](https://github.com/hashicorp/consul-k8s/pull/1656)] + * API Gateway: Create PodSecurityPolicy and allow controller to bind it to ServiceAccounts that it creates for Gateway Deployments when `global.enablePodSecurityPolicies=true`. [[GH-1672](https://github.com/hashicorp/consul-k8s/pull/1672)] + ## 0.49.0 (September 29, 2022) FEATURES: From 4f4e81dfeb14db54826c36a1c507a7cf4a58917a Mon Sep 17 00:00:00 2001 From: Kyle Schochenmaier Date: Wed, 7 Dec 2022 13:11:34 -0600 Subject: [PATCH 019/340] fix failing cli test (#1774) --- cli/cmd/status/status_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/cmd/status/status_test.go b/cli/cmd/status/status_test.go index e227d4e3cf..8666fd8493 100644 --- a/cli/cmd/status/status_test.go +++ b/cli/cmd/status/status_test.go @@ -77,7 +77,7 @@ func TestStatus(t *testing.T) { "status with servers returns success": { input: []string{}, messages: []string{ - fmt.Sprintf("\n==> Consul Status Summary\nName\tNamespace\tStatus\tChart Version\tAppVersion\tRevision\tLast Updated \n \t \tREADY \t1.0.0 \t \t0 \t%s\t\n", notImeStr), + fmt.Sprintf("\n==> Consul Status Summary\nName\tNamespace\tStatus\tChart Version\tAppVersion\tRevision\tLast Updated \n \t \tREADY \t1.0.0 \t \t0 \t%s\t\n", notImeStr), "\n==> Config:\n {}\n \nConsul servers healthy 3/3\n", }, preProcessingFunc: func(k8s kubernetes.Interface) error { @@ -102,7 +102,7 @@ func TestStatus(t *testing.T) { "status with pre-install and pre-upgrade hooks returns success and outputs hook status": { input: []string{}, messages: []string{ - fmt.Sprintf("\n==> Consul Status Summary\nName\tNamespace\tStatus\tChart Version\tAppVersion\tRevision\tLast Updated \n \t \tREADY \t1.0.0 \t \t0 \t%s\t\n", notImeStr), + fmt.Sprintf("\n==> Consul Status Summary\nName\tNamespace\tStatus\tChart Version\tAppVersion\tRevision\tLast Updated \n \t \tREADY \t1.0.0 \t \t0 \t%s\t\n", notImeStr), "\n==> Config:\n {}\n \n", "\n==> Status Of Helm Hooks:\npre-install-hook pre-install: Succeeded\npre-upgrade-hook pre-upgrade: Succeeded\nConsul servers healthy 3/3\n", }, From 112ad18e12683559413c058d8e52f28d66597b07 Mon Sep 17 00:00:00 2001 From: Chris Bruce Date: Wed, 7 Dec 2022 12:33:43 -0800 Subject: [PATCH 020/340] Add global.extraLabels values.yaml setting (#1771) * Add global.extraLabels values.yaml setting This setting lets you apply a set of labels to all pods created by the consul-k8s helm chart. * Also apply global extra labels to deployments/daemonsets/statefulsets/jobs * Add global extraLabels to sync catalog deployment --- .../api-gateway-controller-deployment.yaml | 6 ++ charts/consul/templates/client-daemonset.yaml | 6 ++ charts/consul/templates/cni-daemonset.yaml | 6 ++ .../templates/connect-inject-deployment.yaml | 6 ++ .../create-federation-secret-job.yaml | 6 ++ .../templates/enterprise-license-job.yaml | 6 ++ .../gossip-encryption-autogenerate-job.yaml | 6 ++ .../ingress-gateways-deployment.yaml | 6 ++ .../templates/mesh-gateway-deployment.yaml | 6 ++ .../consul/templates/partition-init-job.yaml | 6 ++ .../server-acl-init-cleanup-job.yaml | 6 ++ .../consul/templates/server-acl-init-job.yaml | 6 ++ .../consul/templates/server-statefulset.yaml | 6 ++ .../templates/sync-catalog-deployment.yaml | 6 ++ .../terminating-gateways-deployment.yaml | 6 ++ .../templates/tls-init-cleanup-job.yaml | 6 ++ charts/consul/templates/tls-init-job.yaml | 6 ++ .../webhook-cert-manager-deployment.yaml | 6 ++ .../api-gateway-controller-deployment.bats | 47 +++++++++++++++ charts/consul/test/unit/client-daemonset.bats | 30 ++++++++++ charts/consul/test/unit/cni-daemonset.bats | 47 +++++++++++++++ .../test/unit/connect-inject-deployment.bats | 32 ++++++++++ .../unit/create-federation-secret-job.bats | 56 ++++++++++++++++++ .../test/unit/enterprise-license-job.bats | 50 ++++++++++++++++ .../gossip-encryption-autogenerate-job.bats | 44 ++++++++++++++ .../unit/ingress-gateways-deployment.bats | 47 +++++++++++++++ .../test/unit/mesh-gateway-deployment.bats | 47 +++++++++++++++ .../consul/test/unit/partition-init-job.bats | 59 +++++++++++++++++++ .../unit/server-acl-init-cleanup-job.bats | 44 ++++++++++++++ .../consul/test/unit/server-acl-init-job.bats | 44 ++++++++++++++ .../consul/test/unit/server-statefulset.bats | 29 +++++++++ .../test/unit/sync-catalog-deployment.bats | 32 ++++++++++ .../unit/terminating-gateways-deployment.bats | 46 +++++++++++++++ .../test/unit/tls-init-cleanup-job.bats | 44 ++++++++++++++ charts/consul/test/unit/tls-init-job.bats | 44 ++++++++++++++ .../unit/webhook-cert-manager-deployment.bats | 41 +++++++++++++ charts/consul/values.yaml | 13 ++++ 37 files changed, 904 insertions(+) diff --git a/charts/consul/templates/api-gateway-controller-deployment.yaml b/charts/consul/templates/api-gateway-controller-deployment.yaml index c548b63e4d..ec64bc3631 100644 --- a/charts/consul/templates/api-gateway-controller-deployment.yaml +++ b/charts/consul/templates/api-gateway-controller-deployment.yaml @@ -15,6 +15,9 @@ metadata: heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: api-gateway-controller + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} spec: replicas: {{ .Values.apiGateway.controller.replicas }} selector: @@ -46,6 +49,9 @@ spec: chart: {{ template "consul.chart" . }} release: {{ .Release.Name }} component: api-gateway-controller + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} spec: serviceAccountName: {{ template "consul.fullname" . }}-api-gateway-controller containers: diff --git a/charts/consul/templates/client-daemonset.yaml b/charts/consul/templates/client-daemonset.yaml index 59252301f9..91af3821fc 100644 --- a/charts/consul/templates/client-daemonset.yaml +++ b/charts/consul/templates/client-daemonset.yaml @@ -24,6 +24,9 @@ metadata: heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: client + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} spec: {{- if .Values.client.updateStrategy }} updateStrategy: @@ -47,6 +50,9 @@ spec: {{- if .Values.client.extraLabels }} {{- toYaml .Values.client.extraLabels | nindent 8 }} {{- end }} + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} annotations: {{- if .Values.global.secretsBackend.vault.enabled }} "vault.hashicorp.com/agent-inject": "true" diff --git a/charts/consul/templates/cni-daemonset.yaml b/charts/consul/templates/cni-daemonset.yaml index e9a6807338..ae04d9e657 100644 --- a/charts/consul/templates/cni-daemonset.yaml +++ b/charts/consul/templates/cni-daemonset.yaml @@ -11,6 +11,9 @@ metadata: heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: cni + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} spec: {{- if .Values.connectInject.cni.updateStrategy }} updateStrategy: @@ -29,6 +32,9 @@ spec: chart: {{ template "consul.chart" . }} release: {{ .Release.Name }} component: cni + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} annotations: consul.hashicorp.com/connect-inject: "false" spec: diff --git a/charts/consul/templates/connect-inject-deployment.yaml b/charts/consul/templates/connect-inject-deployment.yaml index e6b4675876..2b52c1b81c 100644 --- a/charts/consul/templates/connect-inject-deployment.yaml +++ b/charts/consul/templates/connect-inject-deployment.yaml @@ -23,6 +23,9 @@ metadata: heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: connect-injector + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} spec: replicas: {{ .Values.connectInject.replicas }} selector: @@ -41,6 +44,9 @@ spec: {{- if .Values.connectInject.extraLabels }} {{- toYaml .Values.connectInject.extraLabels | nindent 8 }} {{- end }} + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" {{- if .Values.connectInject.annotations }} diff --git a/charts/consul/templates/create-federation-secret-job.yaml b/charts/consul/templates/create-federation-secret-job.yaml index 40b81957d1..4f83a1f82a 100644 --- a/charts/consul/templates/create-federation-secret-job.yaml +++ b/charts/consul/templates/create-federation-secret-job.yaml @@ -15,6 +15,9 @@ metadata: heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: create-federation-secret + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} annotations: "helm.sh/hook": post-install,post-upgrade {{- /* Hook weight needs to be 1 so that the service account is provisioned first */}} @@ -29,6 +32,9 @@ spec: chart: {{ template "consul.chart" . }} release: {{ .Release.Name }} component: create-federation-secret + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" spec: diff --git a/charts/consul/templates/enterprise-license-job.yaml b/charts/consul/templates/enterprise-license-job.yaml index 02921db3b0..0122690104 100644 --- a/charts/consul/templates/enterprise-license-job.yaml +++ b/charts/consul/templates/enterprise-license-job.yaml @@ -15,6 +15,9 @@ metadata: heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: license + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} annotations: "helm.sh/hook": post-install,post-upgrade "helm.sh/hook-weight": "100" @@ -31,6 +34,9 @@ spec: chart: {{ template "consul.chart" . }} release: {{ .Release.Name }} component: license + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" spec: diff --git a/charts/consul/templates/gossip-encryption-autogenerate-job.yaml b/charts/consul/templates/gossip-encryption-autogenerate-job.yaml index e1a6e49823..9d296478a1 100644 --- a/charts/consul/templates/gossip-encryption-autogenerate-job.yaml +++ b/charts/consul/templates/gossip-encryption-autogenerate-job.yaml @@ -14,6 +14,9 @@ metadata: heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: gossip-encryption-autogenerate + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} annotations: "helm.sh/hook": pre-install,pre-upgrade "helm.sh/hook-weight": "1" @@ -27,6 +30,9 @@ spec: chart: {{ template "consul.chart" . }} release: {{ .Release.Name }} component: gossip-encryption-autogenerate + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" spec: diff --git a/charts/consul/templates/ingress-gateways-deployment.yaml b/charts/consul/templates/ingress-gateways-deployment.yaml index 139055b818..4f72031855 100644 --- a/charts/consul/templates/ingress-gateways-deployment.yaml +++ b/charts/consul/templates/ingress-gateways-deployment.yaml @@ -46,6 +46,9 @@ metadata: release: {{ $root.Release.Name }} component: ingress-gateway ingress-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} + {{- if $root.Values.global.extraLabels }} + {{- toYaml $root.Values.global.extraLabels | nindent 4 }} + {{- end }} spec: replicas: {{ default $defaults.replicas .replicas }} selector: @@ -66,6 +69,9 @@ spec: component: ingress-gateway ingress-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} consul.hashicorp.com/connect-inject-managed-by: consul-k8s-endpoints-controller + {{- if $root.Values.global.extraLabels }} + {{- toYaml $root.Values.global.extraLabels | nindent 8 }} + {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" "consul.hashicorp.com/gateway-kind": "ingress-gateway" diff --git a/charts/consul/templates/mesh-gateway-deployment.yaml b/charts/consul/templates/mesh-gateway-deployment.yaml index 0ba66dbdec..2b2bdc8c2a 100644 --- a/charts/consul/templates/mesh-gateway-deployment.yaml +++ b/charts/consul/templates/mesh-gateway-deployment.yaml @@ -19,6 +19,9 @@ metadata: heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: mesh-gateway + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} spec: replicas: {{ .Values.meshGateway.replicas }} selector: @@ -35,6 +38,9 @@ spec: release: {{ .Release.Name }} component: mesh-gateway consul.hashicorp.com/connect-inject-managed-by: consul-k8s-endpoints-controller + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" "consul.hashicorp.com/gateway-kind": "mesh-gateway" diff --git a/charts/consul/templates/partition-init-job.yaml b/charts/consul/templates/partition-init-job.yaml index 082c48447b..db73ef783b 100644 --- a/charts/consul/templates/partition-init-job.yaml +++ b/charts/consul/templates/partition-init-job.yaml @@ -15,6 +15,9 @@ metadata: heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: partition-init + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} annotations: "helm.sh/hook": pre-install "helm.sh/hook-weight": "2" @@ -28,6 +31,9 @@ spec: chart: {{ template "consul.chart" . }} release: {{ .Release.Name }} component: partition-init + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" {{- if (and .Values.global.secretsBackend.vault.enabled (or .Values.global.tls.enabled .Values.global.acls.manageSystemACLs)) }} diff --git a/charts/consul/templates/server-acl-init-cleanup-job.yaml b/charts/consul/templates/server-acl-init-cleanup-job.yaml index 697427ab5f..35b0877ab4 100644 --- a/charts/consul/templates/server-acl-init-cleanup-job.yaml +++ b/charts/consul/templates/server-acl-init-cleanup-job.yaml @@ -23,6 +23,9 @@ metadata: heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: server-acl-init-cleanup + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} annotations: "helm.sh/hook": post-install,post-upgrade "helm.sh/hook-weight": "0" @@ -39,6 +42,9 @@ spec: chart: {{ template "consul.chart" . }} release: {{ .Release.Name }} component: server-acl-init-cleanup + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" spec: diff --git a/charts/consul/templates/server-acl-init-job.yaml b/charts/consul/templates/server-acl-init-job.yaml index 88a16b0472..440ab8bee0 100644 --- a/charts/consul/templates/server-acl-init-job.yaml +++ b/charts/consul/templates/server-acl-init-job.yaml @@ -29,6 +29,9 @@ metadata: heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: server-acl-init + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} spec: template: metadata: @@ -38,6 +41,9 @@ spec: chart: {{ template "consul.chart" . }} release: {{ .Release.Name }} component: server-acl-init + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" {{- if .Values.global.secretsBackend.vault.enabled }} diff --git a/charts/consul/templates/server-statefulset.yaml b/charts/consul/templates/server-statefulset.yaml index 23894c4a04..8b73306fd7 100644 --- a/charts/consul/templates/server-statefulset.yaml +++ b/charts/consul/templates/server-statefulset.yaml @@ -31,6 +31,9 @@ metadata: heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: server + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} spec: serviceName: {{ template "consul.fullname" . }}-server podManagementPolicy: Parallel @@ -59,6 +62,9 @@ spec: {{- if .Values.server.extraLabels }} {{- toYaml .Values.server.extraLabels | nindent 8 }} {{- end }} + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} annotations: {{- if .Values.global.secretsBackend.vault.enabled }} "vault.hashicorp.com/agent-inject": "true" diff --git a/charts/consul/templates/sync-catalog-deployment.yaml b/charts/consul/templates/sync-catalog-deployment.yaml index 26de143065..f2815d9627 100644 --- a/charts/consul/templates/sync-catalog-deployment.yaml +++ b/charts/consul/templates/sync-catalog-deployment.yaml @@ -14,6 +14,9 @@ metadata: heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: sync-catalog + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} spec: replicas: 1 selector: @@ -32,6 +35,9 @@ spec: {{- if .Values.syncCatalog.extraLabels }} {{- toYaml .Values.syncCatalog.extraLabels | nindent 8 }} {{- end }} + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" {{- if .Values.syncCatalog.annotations }} diff --git a/charts/consul/templates/terminating-gateways-deployment.yaml b/charts/consul/templates/terminating-gateways-deployment.yaml index 3efa789527..2f2cb9a921 100644 --- a/charts/consul/templates/terminating-gateways-deployment.yaml +++ b/charts/consul/templates/terminating-gateways-deployment.yaml @@ -48,6 +48,9 @@ metadata: release: {{ $root.Release.Name }} component: terminating-gateway terminating-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} + {{- if $root.Values.global.extraLabels }} + {{- toYaml $root.Values.global.extraLabels | nindent 4 }} + {{- end }} spec: replicas: {{ default $defaults.replicas .replicas }} selector: @@ -68,6 +71,9 @@ spec: component: terminating-gateway terminating-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} consul.hashicorp.com/connect-inject-managed-by: consul-k8s-endpoints-controller + {{- if $root.Values.global.extraLabels }} + {{- toYaml $root.Values.global.extraLabels | nindent 8 }} + {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" "consul.hashicorp.com/gateway-kind": "terminating-gateway" diff --git a/charts/consul/templates/tls-init-cleanup-job.yaml b/charts/consul/templates/tls-init-cleanup-job.yaml index 9a8898cc10..ba29bb84ae 100644 --- a/charts/consul/templates/tls-init-cleanup-job.yaml +++ b/charts/consul/templates/tls-init-cleanup-job.yaml @@ -13,6 +13,9 @@ metadata: heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: tls-init-cleanup + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} annotations: "helm.sh/hook": pre-delete "helm.sh/hook-delete-policy": hook-succeeded @@ -27,6 +30,9 @@ spec: chart: {{ template "consul.chart" . }} release: {{ .Release.Name }} component: tls-init-cleanup + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" spec: diff --git a/charts/consul/templates/tls-init-job.yaml b/charts/consul/templates/tls-init-job.yaml index 47dd6462b0..d002ae7a75 100644 --- a/charts/consul/templates/tls-init-job.yaml +++ b/charts/consul/templates/tls-init-job.yaml @@ -14,6 +14,9 @@ metadata: heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: tls-init + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} annotations: "helm.sh/hook": pre-install,pre-upgrade "helm.sh/hook-weight": "1" @@ -27,6 +30,9 @@ spec: chart: {{ template "consul.chart" . }} release: {{ .Release.Name }} component: tls-init + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" spec: diff --git a/charts/consul/templates/webhook-cert-manager-deployment.yaml b/charts/consul/templates/webhook-cert-manager-deployment.yaml index be22d4da7a..dd93c039d2 100644 --- a/charts/consul/templates/webhook-cert-manager-deployment.yaml +++ b/charts/consul/templates/webhook-cert-manager-deployment.yaml @@ -11,6 +11,9 @@ metadata: heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: webhook-cert-manager + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} spec: replicas: 1 selector: @@ -28,6 +31,9 @@ spec: heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: webhook-cert-manager + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" "consul.hashicorp.com/config-checksum": {{ include (print $.Template.BasePath "/webhook-cert-manager-configmap.yaml") . | sha256sum }} diff --git a/charts/consul/test/unit/api-gateway-controller-deployment.bats b/charts/consul/test/unit/api-gateway-controller-deployment.bats index 83d296b15d..b71b51aee0 100755 --- a/charts/consul/test/unit/api-gateway-controller-deployment.bats +++ b/charts/consul/test/unit/api-gateway-controller-deployment.bats @@ -1544,3 +1544,50 @@ load _helpers yq '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "consul-auto-encrypt-ca-cert") | .mountPath' | tee /dev/stderr) [ "${actual}" = '"/consul/tls/ca"' ] } + +#-------------------------------------------------------------------- +# extraLabels + +@test "apiGateway/Deployment: no extra labels defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/api-gateway-controller-deployment.yaml \ + --set 'apiGateway.enabled=true' \ + --set 'apiGateway.image=bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.labels | del(."app") | del(."chart") | del(."release") | del(."component")' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "apiGateway/Deployment: extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/api-gateway-controller-deployment.yaml \ + --set 'apiGateway.enabled=true' \ + --set 'apiGateway.image=bar' \ + --set 'global.extraLabels.foo=bar' \ + . | tee /dev/stderr) + local actualBar=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + [ "${actualBar}" = "bar" ] + local actualTemplateBar=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + [ "${actualTemplateBar}" = "bar" ] +} + +@test "apiGateway/Deployment: multiple global extra labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/api-gateway-controller-deployment.yaml \ + --set 'apiGateway.enabled=true' \ + --set 'apiGateway.image=bar' \ + --set 'global.extraLabels.foo=bar' \ + --set 'global.extraLabels.baz=qux' \ + . | tee /dev/stderr) + local actualFoo=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + local actualBaz=$(echo "${actual}" | yq -r '.metadata.labels.baz' | tee /dev/stderr) + [ "${actualFoo}" = "bar" ] + [ "${actualBaz}" = "qux" ] + local actualTemplateFoo=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + local actualTemplateBaz=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.baz' | tee /dev/stderr) + [ "${actualTemplateFoo}" = "bar" ] + [ "${actualTemplateBaz}" = "qux" ] +} diff --git a/charts/consul/test/unit/client-daemonset.bats b/charts/consul/test/unit/client-daemonset.bats index 0f417bf4ab..6e7a030cb1 100755 --- a/charts/consul/test/unit/client-daemonset.bats +++ b/charts/consul/test/unit/client-daemonset.bats @@ -490,6 +490,36 @@ load _helpers [ "${actualBaz}" = "qux" ] } +@test "client/DaemonSet: extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/client-daemonset.yaml \ + --set 'client.enabled=true' \ + --set 'global.extraLabels.foo=bar' \ + . | tee /dev/stderr) + local actualBar=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + [ "${actualBar}" = "bar" ] + local actualTemplateBar=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + [ "${actualTemplateBar}" = "bar" ] +} + +@test "client/DaemonSet: multiple extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/client-daemonset.yaml \ + --set 'client.enabled=true' \ + --set 'global.extraLabels.foo=bar' \ + --set 'global.extraLabels.baz=qux' \ + . | tee /dev/stderr) + local actualFoo=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + local actualBaz=$(echo "${actual}" | yq -r '.metadata.labels.baz' | tee /dev/stderr) + [ "${actualFoo}" = "bar" ] + [ "${actualBaz}" = "qux" ] + local actualTemplateFoo=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + local actualTemplateBaz=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.baz' | tee /dev/stderr) + [ "${actualTemplateFoo}" = "bar" ] + [ "${actualTemplateBaz}" = "qux" ] +} #-------------------------------------------------------------------- # annotations diff --git a/charts/consul/test/unit/cni-daemonset.bats b/charts/consul/test/unit/cni-daemonset.bats index 3b5e046a67..675d6b877f 100644 --- a/charts/consul/test/unit/cni-daemonset.bats +++ b/charts/consul/test/unit/cni-daemonset.bats @@ -340,3 +340,50 @@ rollingUpdate: yq -r -c '.metadata.namespace' | tee /dev/stderr) [[ "${actual}" == "foo" ]] } + +#-------------------------------------------------------------------- +# extraLabels + +@test "cni/DaemonSet: no extra labels defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/cni-daemonset.yaml \ + --set 'connectInject.cni.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.labels | del(."app") | del(."chart") | del(."release") | del(."component")' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "cni/DaemonSet: extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/cni-daemonset.yaml \ + --set 'connectInject.cni.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'global.extraLabels.foo=bar' \ + . | tee /dev/stderr) + local actualBar=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + [ "${actualBar}" = "bar" ] + local actualTemplateBar=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + [ "${actualTemplateBar}" = "bar" ] +} + +@test "cni/DaemonSet: multiple global extra labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/cni-daemonset.yaml \ + --set 'connectInject.cni.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'global.extraLabels.foo=bar' \ + --set 'global.extraLabels.baz=qux' \ + . | tee /dev/stderr) + local actualFoo=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + local actualBaz=$(echo "${actual}" | yq -r '.metadata.labels.baz' | tee /dev/stderr) + [ "${actualFoo}" = "bar" ] + [ "${actualBaz}" = "qux" ] + local actualTemplateFoo=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + local actualTemplateBaz=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.baz' | tee /dev/stderr) + [ "${actualTemplateFoo}" = "bar" ] + [ "${actualTemplateBaz}" = "qux" ] +} diff --git a/charts/consul/test/unit/connect-inject-deployment.bats b/charts/consul/test/unit/connect-inject-deployment.bats index 4879049c49..9da24a7568 100755 --- a/charts/consul/test/unit/connect-inject-deployment.bats +++ b/charts/consul/test/unit/connect-inject-deployment.bats @@ -1272,6 +1272,38 @@ load _helpers [ "${actual}" = "bar" ] } +@test "connectInject/Deployment: can set extra global labels" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + --set 'global.extraLabels.foo=bar' \ + . | tee /dev/stderr) + local actualBar=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + [ "${actualBar}" = "bar" ] + local actualTemplateBar=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + [ "${actualTemplateBar}" = "bar" ] +} + +@test "connectInject/Deployment: can set multiple extra global labels" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + --set 'global.extraLabels.foo=bar' \ + --set 'global.extraLabels.baz=qux' \ + . | tee /dev/stderr) + + local actualFoo=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + local actualBaz=$(echo "${actual}" | yq -r '.metadata.labels.baz' | tee /dev/stderr) + [ "${actualFoo}" = "bar" ] + [ "${actualBaz}" = "qux" ] + local actualTemplateFoo=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + local actualTemplateBaz=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.baz' | tee /dev/stderr) + [ "${actualTemplateFoo}" = "bar" ] + [ "${actualTemplateBaz}" = "qux" ] +} + #-------------------------------------------------------------------- # annotations diff --git a/charts/consul/test/unit/create-federation-secret-job.bats b/charts/consul/test/unit/create-federation-secret-job.bats index 41e401f485..e528f28f0e 100644 --- a/charts/consul/test/unit/create-federation-secret-job.bats +++ b/charts/consul/test/unit/create-federation-secret-job.bats @@ -362,3 +362,59 @@ load _helpers yq -r '.spec.template.spec.nodeSelector' | tee /dev/stderr) [ "${actual}" = "testing" ] } + +#-------------------------------------------------------------------- +# extraLabels + +@test "createFederationSecret/Job: no extra labels defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/create-federation-secret-job.yaml \ + --set 'global.federation.enabled=true' \ + --set 'meshGateway.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'global.tls.enabled=true' \ + --set 'global.federation.createFederationSecret=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.labels | del(."app") | del(."chart") | del(."release") | del(."component")' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "createFederationSecret/Job: extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/create-federation-secret-job.yaml \ + --set 'global.federation.enabled=true' \ + --set 'meshGateway.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'global.tls.enabled=true' \ + --set 'global.federation.createFederationSecret=true' \ + --set 'global.extraLabels.foo=bar' \ + . | tee /dev/stderr) + local actualBar=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + [ "${actualBar}" = "bar" ] + local actualTemplateBar=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + [ "${actualTemplateBar}" = "bar" ] +} + +@test "createFederationSecret/Job: multiple global extra labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/create-federation-secret-job.yaml \ + --set 'global.federation.enabled=true' \ + --set 'meshGateway.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'global.tls.enabled=true' \ + --set 'global.federation.createFederationSecret=true' \ + --set 'global.extraLabels.foo=bar' \ + --set 'global.extraLabels.baz=qux' \ + . | tee /dev/stderr) + local actualFoo=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + local actualBaz=$(echo "${actual}" | yq -r '.metadata.labels.baz' | tee /dev/stderr) + [ "${actualFoo}" = "bar" ] + [ "${actualBaz}" = "qux" ] + local actualTemplateFoo=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + local actualTemplateBaz=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.baz' | tee /dev/stderr) + [ "${actualTemplateFoo}" = "bar" ] + [ "${actualTemplateBaz}" = "qux" ] +} diff --git a/charts/consul/test/unit/enterprise-license-job.bats b/charts/consul/test/unit/enterprise-license-job.bats index 5652419bb3..dcc844eb30 100644 --- a/charts/consul/test/unit/enterprise-license-job.bats +++ b/charts/consul/test/unit/enterprise-license-job.bats @@ -212,3 +212,53 @@ load _helpers actual=$(echo $ca_cert_volume | jq -r '.secret.items[0].key' | tee /dev/stderr) [ "${actual}" = "key" ] } + +#-------------------------------------------------------------------- +# extraLabels + +@test "enterpriseLicense/Job: no extra labels defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/enterprise-license-job.yaml \ + --set 'global.enterpriseLicense.secretName=foo' \ + --set 'global.enterpriseLicense.secretKey=bar' \ + --set 'global.enterpriseLicense.enableLicenseAutoload=false' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.labels | del(."app") | del(."chart") | del(."release") | del(."component") | del(."app.kubernetes.io/managed-by") | del(."app.kubernetes.io/instance") | del(."helm.sh/chart")' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "enterpriseLicense/Job: extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/enterprise-license-job.yaml \ + --set 'global.enterpriseLicense.secretName=foo' \ + --set 'global.enterpriseLicense.secretKey=bar' \ + --set 'global.enterpriseLicense.enableLicenseAutoload=false' \ + --set 'global.extraLabels.foo=bar' \ + . | tee /dev/stderr) + local actualBar=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + [ "${actualBar}" = "bar" ] + local actualTemplateBar=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + [ "${actualTemplateBar}" = "bar" ] +} + +@test "enterpriseLicense/Job: multiple global extra labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/enterprise-license-job.yaml \ + --set 'global.enterpriseLicense.secretName=foo' \ + --set 'global.enterpriseLicense.secretKey=bar' \ + --set 'global.enterpriseLicense.enableLicenseAutoload=false' \ + --set 'global.extraLabels.foo=bar' \ + --set 'global.extraLabels.baz=qux' \ + . | tee /dev/stderr) + local actualFoo=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + local actualBaz=$(echo "${actual}" | yq -r '.metadata.labels.baz' | tee /dev/stderr) + [ "${actualFoo}" = "bar" ] + [ "${actualBaz}" = "qux" ] + local actualTemplateFoo=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + local actualTemplateBaz=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.baz' | tee /dev/stderr) + [ "${actualTemplateFoo}" = "bar" ] + [ "${actualTemplateBaz}" = "qux" ] +} diff --git a/charts/consul/test/unit/gossip-encryption-autogenerate-job.bats b/charts/consul/test/unit/gossip-encryption-autogenerate-job.bats index 87cc5a5990..662b523bc0 100644 --- a/charts/consul/test/unit/gossip-encryption-autogenerate-job.bats +++ b/charts/consul/test/unit/gossip-encryption-autogenerate-job.bats @@ -61,3 +61,47 @@ load _helpers yq -r '.spec.template.spec | has("securityContext")' | tee /dev/stderr) [ "${has_security_context}" = "false" ] } + +#-------------------------------------------------------------------- +# extraLabels + +@test "gossipEncryptionAutogenerate/Job: no extra labels defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/gossip-encryption-autogenerate-job.yaml \ + --set 'global.gossipEncryption.autoGenerate=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.labels | del(."app") | del(."chart") | del(."release") | del(."component")' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "gossipEncryptionAutogenerate/Job: extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/gossip-encryption-autogenerate-job.yaml \ + --set 'global.gossipEncryption.autoGenerate=true' \ + --set 'global.extraLabels.foo=bar' \ + . | tee /dev/stderr) + local actualBar=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + [ "${actualBar}" = "bar" ] + local actualTemplateBar=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + [ "${actualTemplateBar}" = "bar" ] +} + +@test "gossipEncryptionAutogenerate/Job: multiple global extra labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/gossip-encryption-autogenerate-job.yaml \ + --set 'global.gossipEncryption.autoGenerate=true' \ + --set 'global.extraLabels.foo=bar' \ + --set 'global.extraLabels.baz=qux' \ + . | tee /dev/stderr) + local actualFoo=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + local actualBaz=$(echo "${actual}" | yq -r '.metadata.labels.baz' | tee /dev/stderr) + [ "${actualFoo}" = "bar" ] + [ "${actualBaz}" = "qux" ] + local actualTemplateFoo=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + local actualTemplateBaz=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.baz' | tee /dev/stderr) + [ "${actualTemplateFoo}" = "bar" ] + [ "${actualTemplateBaz}" = "qux" ] +} diff --git a/charts/consul/test/unit/ingress-gateways-deployment.bats b/charts/consul/test/unit/ingress-gateways-deployment.bats index 983a0b9edf..8ed76be13a 100644 --- a/charts/consul/test/unit/ingress-gateways-deployment.bats +++ b/charts/consul/test/unit/ingress-gateways-deployment.bats @@ -1457,3 +1457,50 @@ key2: value2' \ yq '.spec.template.spec.containers[0].args | any(contains("-tls-server-name=server.dc1.consul"))' | tee /dev/stderr) [ "${actual}" = "true" ] } + +#-------------------------------------------------------------------- +# extraLabels + +@test "ingressGateways/Deployment: no extra labels defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/ingress-gateways-deployment.yaml \ + --set 'ingressGateways.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.labels | del(."app") | del(."chart") | del(."release") | del(."component") | del(."heritage") | del(."ingress-gateway-name") | del(."consul.hashicorp.com/connect-inject-managed-by")' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "ingressGateways/Deployment: extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/ingress-gateways-deployment.yaml \ + --set 'ingressGateways.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'global.extraLabels.foo=bar' \ + . | tee /dev/stderr) + local actualBar=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + [ "${actualBar}" = "bar" ] + local actualTemplateBar=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + [ "${actualTemplateBar}" = "bar" ] +} + +@test "ingressGateways/Deployment: multiple global extra labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/ingress-gateways-deployment.yaml \ + --set 'ingressGateways.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'global.extraLabels.foo=bar' \ + --set 'global.extraLabels.baz=qux' \ + . | tee /dev/stderr) + local actualFoo=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + local actualBaz=$(echo "${actual}" | yq -r '.metadata.labels.baz' | tee /dev/stderr) + [ "${actualFoo}" = "bar" ] + [ "${actualBaz}" = "qux" ] + local actualTemplateFoo=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + local actualTemplateBaz=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.baz' | tee /dev/stderr) + [ "${actualTemplateFoo}" = "bar" ] + [ "${actualTemplateBaz}" = "qux" ] +} diff --git a/charts/consul/test/unit/mesh-gateway-deployment.bats b/charts/consul/test/unit/mesh-gateway-deployment.bats index 6f326f05a3..588b026d40 100755 --- a/charts/consul/test/unit/mesh-gateway-deployment.bats +++ b/charts/consul/test/unit/mesh-gateway-deployment.bats @@ -1597,3 +1597,50 @@ key2: value2' \ yq '.spec.template.spec.containers[0].args | any(contains("-tls-server-name=server.dc1.consul"))' | tee /dev/stderr) [ "${actual}" = "true" ] } + +#-------------------------------------------------------------------- +# extraLabels + +@test "meshGateway/Deployment: no extra labels defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/mesh-gateway-deployment.yaml \ + --set 'meshGateway.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.labels | del(."app") | del(."chart") | del(."release") | del(."component") | del(."consul.hashicorp.com/connect-inject-managed-by")' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "meshGateway/Deployment: extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/mesh-gateway-deployment.yaml \ + --set 'meshGateway.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'global.extraLabels.foo=bar' \ + . | tee /dev/stderr) + local actualBar=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + [ "${actualBar}" = "bar" ] + local actualTemplateBar=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + [ "${actualTemplateBar}" = "bar" ] +} + +@test "meshGateway/Deployment: multiple global extra labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/mesh-gateway-deployment.yaml \ + --set 'meshGateway.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'global.extraLabels.foo=bar' \ + --set 'global.extraLabels.baz=qux' \ + . | tee /dev/stderr) + local actualFoo=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + local actualBaz=$(echo "${actual}" | yq -r '.metadata.labels.baz' | tee /dev/stderr) + [ "${actualFoo}" = "bar" ] + [ "${actualBaz}" = "qux" ] + local actualTemplateFoo=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + local actualTemplateBaz=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.baz' | tee /dev/stderr) + [ "${actualTemplateFoo}" = "bar" ] + [ "${actualTemplateBaz}" = "qux" ] +} diff --git a/charts/consul/test/unit/partition-init-job.bats b/charts/consul/test/unit/partition-init-job.bats index 82ffc959fa..a3524090aa 100644 --- a/charts/consul/test/unit/partition-init-job.bats +++ b/charts/consul/test/unit/partition-init-job.bats @@ -879,3 +879,62 @@ reservedNameTest() { yq '.spec.template.spec.containers[0].command | any(contains("-tls-server-name=server.dc1.consul"))' | tee /dev/stderr) [ "${actual}" = "true" ] } + +#-------------------------------------------------------------------- +# extraLabels + +@test "partitionInit/Job: no extra labels defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/partition-init-job.yaml \ + --set 'global.adminPartitions.enabled=true' \ + --set 'global.enableConsulNamespaces=true' \ + --set 'server.enabled=false' \ + --set 'global.adminPartitions.name=bar' \ + --set 'externalServers.enabled=true' \ + --set 'externalServers.hosts[0]=foo' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.labels | del(."app") | del(."chart") | del(."release") | del(."component")' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "partitionInit/Job: extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/partition-init-job.yaml \ + --set 'global.adminPartitions.enabled=true' \ + --set 'global.enableConsulNamespaces=true' \ + --set 'server.enabled=false' \ + --set 'global.adminPartitions.name=bar' \ + --set 'externalServers.enabled=true' \ + --set 'externalServers.hosts[0]=foo' \ + --set 'global.extraLabels.foo=bar' \ + . | tee /dev/stderr) + local actualBar=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + [ "${actualBar}" = "bar" ] + local actualTemplateBar=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + [ "${actualTemplateBar}" = "bar" ] +} + +@test "partitionInit/Job: multiple global extra labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/partition-init-job.yaml \ + --set 'global.adminPartitions.enabled=true' \ + --set 'global.enableConsulNamespaces=true' \ + --set 'server.enabled=false' \ + --set 'global.adminPartitions.name=bar' \ + --set 'externalServers.enabled=true' \ + --set 'externalServers.hosts[0]=foo' \ + --set 'global.extraLabels.foo=bar' \ + --set 'global.extraLabels.baz=qux' \ + . | tee /dev/stderr) + local actualFoo=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + local actualBaz=$(echo "${actual}" | yq -r '.metadata.labels.baz' | tee /dev/stderr) + [ "${actualFoo}" = "bar" ] + [ "${actualBaz}" = "qux" ] + local actualTemplateFoo=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + local actualTemplateBaz=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.baz' | tee /dev/stderr) + [ "${actualTemplateFoo}" = "bar" ] + [ "${actualTemplateBaz}" = "qux" ] +} diff --git a/charts/consul/test/unit/server-acl-init-cleanup-job.bats b/charts/consul/test/unit/server-acl-init-cleanup-job.bats index cb57374116..947cfa9b42 100644 --- a/charts/consul/test/unit/server-acl-init-cleanup-job.bats +++ b/charts/consul/test/unit/server-acl-init-cleanup-job.bats @@ -115,3 +115,47 @@ load _helpers yq -r '.spec.template.spec.nodeSelector[0].key' | tee /dev/stderr) [ "${actual}" = "value" ] } + +#-------------------------------------------------------------------- +# extraLabels + +@test "serverACLInitCleanup/Job: no extra labels defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-cleanup-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.labels | del(."app") | del(."chart") | del(."release") | del(."component")' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "serverACLInitCleanup/Job: extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-cleanup-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.extraLabels.foo=bar' \ + . | tee /dev/stderr) + local actualBar=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + [ "${actualBar}" = "bar" ] + local actualTemplateBar=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + [ "${actualTemplateBar}" = "bar" ] +} + +@test "serverACLInitCleanup/Job: multiple global extra labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-cleanup-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.extraLabels.foo=bar' \ + --set 'global.extraLabels.baz=qux' \ + . | tee /dev/stderr) + local actualFoo=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + local actualBaz=$(echo "${actual}" | yq -r '.metadata.labels.baz' | tee /dev/stderr) + [ "${actualFoo}" = "bar" ] + [ "${actualBaz}" = "qux" ] + local actualTemplateFoo=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + local actualTemplateBaz=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.baz' | tee /dev/stderr) + [ "${actualTemplateFoo}" = "bar" ] + [ "${actualTemplateBaz}" = "qux" ] +} diff --git a/charts/consul/test/unit/server-acl-init-job.bats b/charts/consul/test/unit/server-acl-init-job.bats index 43c9589747..63450aa4c2 100644 --- a/charts/consul/test/unit/server-acl-init-job.bats +++ b/charts/consul/test/unit/server-acl-init-job.bats @@ -2166,3 +2166,47 @@ load _helpers yq '[.env[9].value] | any(contains("server.dc1.consul"))' | tee /dev/stderr) [ "${actual}" = "true" ] } + +#-------------------------------------------------------------------- +# extraLabels + +@test "serverACLInit/Job: no extra labels defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.labels | del(."app") | del(."chart") | del(."release") | del(."component")' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "serverACLInit/Job: extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.extraLabels.foo=bar' \ + . | tee /dev/stderr) + local actualBar=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + [ "${actualBar}" = "bar" ] + local actualTemplateBar=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + [ "${actualTemplateBar}" = "bar" ] +} + +@test "serverACLInit/Job: multiple global extra labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.extraLabels.foo=bar' \ + --set 'global.extraLabels.baz=qux' \ + . | tee /dev/stderr) + local actualFoo=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + local actualBaz=$(echo "${actual}" | yq -r '.metadata.labels.baz' | tee /dev/stderr) + [ "${actualFoo}" = "bar" ] + [ "${actualBaz}" = "qux" ] + local actualTemplateFoo=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + local actualTemplateBaz=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.baz' | tee /dev/stderr) + [ "${actualTemplateFoo}" = "bar" ] + [ "${actualTemplateBaz}" = "qux" ] +} diff --git a/charts/consul/test/unit/server-statefulset.bats b/charts/consul/test/unit/server-statefulset.bats index 147e9e31b7..2d21cf7c1e 100755 --- a/charts/consul/test/unit/server-statefulset.bats +++ b/charts/consul/test/unit/server-statefulset.bats @@ -590,6 +590,35 @@ load _helpers [ "${actualBaz}" = "qux" ] } +@test "server/StatefulSet: extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.extraLabels.foo=bar' \ + . | tee /dev/stderr) + local actualBar=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + [ "${actualBar}" = "bar" ] + local actualTemplateBar=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + [ "${actualTemplateBar}" = "bar" ] +} + +@test "server/StatefulSet: multiple extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.extraLabels.foo=bar' \ + --set 'global.extraLabels.baz=qux' \ + . | tee /dev/stderr) + local actualFoo=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + local actualBaz=$(echo "${actual}" | yq -r '.metadata.labels.baz' | tee /dev/stderr) + [ "${actualFoo}" = "bar" ] + [ "${actualBaz}" = "qux" ] + local actualTemplateFoo=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + local actualTemplateBaz=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.baz' | tee /dev/stderr) + [ "${actualTemplateFoo}" = "bar" ] + [ "${actualTemplateBaz}" = "qux" ] +} + #-------------------------------------------------------------------- # annotations diff --git a/charts/consul/test/unit/sync-catalog-deployment.bats b/charts/consul/test/unit/sync-catalog-deployment.bats index 2506d627f8..ae1fe1a854 100755 --- a/charts/consul/test/unit/sync-catalog-deployment.bats +++ b/charts/consul/test/unit/sync-catalog-deployment.bats @@ -895,6 +895,38 @@ load _helpers [ "${actual}" = "bar" ] } +@test "syncCatalog/Deployment: extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/sync-catalog-deployment.yaml \ + --set 'syncCatalog.enabled=true' \ + --set 'global.extraLabels.foo=bar' \ + . | tee /dev/stderr) + local actualBar=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + [ "${actualBar}" = "bar" ] + local actualTemplateBar=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + [ "${actualTemplateBar}" = "bar" ] +} + +@test "syncCatalog/Deployment: multiple extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/sync-catalog-deployment.yaml \ + --set 'syncCatalog.enabled=true' \ + --set 'global.extraLabels.foo=bar' \ + --set 'global.extraLabels.baz=qux' \ + . | tee /dev/stderr) + local actualFoo=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + local actualBaz=$(echo "${actual}" | yq -r '.metadata.labels.baz' | tee /dev/stderr) + [ "${actualFoo}" = "bar" ] + [ "${actualBaz}" = "qux" ] + local actualTemplateFoo=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + local actualTemplateBaz=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.baz' | tee /dev/stderr) + [ "${actualTemplateFoo}" = "bar" ] + [ "${actualTemplateBaz}" = "qux" ] +} + + #-------------------------------------------------------------------- # annotations diff --git a/charts/consul/test/unit/terminating-gateways-deployment.bats b/charts/consul/test/unit/terminating-gateways-deployment.bats index b7bbc0bf6d..523138a351 100644 --- a/charts/consul/test/unit/terminating-gateways-deployment.bats +++ b/charts/consul/test/unit/terminating-gateways-deployment.bats @@ -1458,3 +1458,49 @@ key2: value2' \ [ "${actual}" = "server.dc1.consul" ] } +#-------------------------------------------------------------------- +# extraLabels + +@test "terminatingGateways/Deployment: no extra labels defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/terminating-gateways-deployment.yaml \ + --set 'terminatingGateways.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.labels | del(."app") | del(."chart") | del(."release") | del(."component") | del(."heritage") | del(."terminating-gateway-name") | del(."consul.hashicorp.com/connect-inject-managed-by")' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "terminatingGateways/Deployment: extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/terminating-gateways-deployment.yaml \ + --set 'terminatingGateways.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'global.extraLabels.foo=bar' \ + . | tee /dev/stderr) + local actualBar=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + [ "${actualBar}" = "bar" ] + local actualTemplateBar=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + [ "${actualTemplateBar}" = "bar" ] +} + +@test "terminatingGateways/Deployment: multiple extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/terminating-gateways-deployment.yaml \ + --set 'terminatingGateways.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'global.extraLabels.foo=bar' \ + --set 'global.extraLabels.baz=qux' \ + . | tee /dev/stderr) + local actualFoo=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + local actualBaz=$(echo "${actual}" | yq -r '.metadata.labels.baz' | tee /dev/stderr) + [ "${actualFoo}" = "bar" ] + [ "${actualBaz}" = "qux" ] + local actualTemplateFoo=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + local actualTemplateBaz=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.baz' | tee /dev/stderr) + [ "${actualTemplateFoo}" = "bar" ] + [ "${actualTemplateBaz}" = "qux" ] +} diff --git a/charts/consul/test/unit/tls-init-cleanup-job.bats b/charts/consul/test/unit/tls-init-cleanup-job.bats index 76da65bfe5..04b4a2df31 100644 --- a/charts/consul/test/unit/tls-init-cleanup-job.bats +++ b/charts/consul/test/unit/tls-init-cleanup-job.bats @@ -75,3 +75,47 @@ load _helpers --set 'global.tls.enableAutoEncrypt=true' \ . } + +#-------------------------------------------------------------------- +# extraLabels + +@test "tlsInit/Job: no extra labels defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/tls-init-cleanup-job.yaml \ + --set 'global.tls.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.labels | del(."app") | del(."chart") | del(."release") | del(."component")' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "tlsInit/Job: extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/tls-init-cleanup-job.yaml \ + --set 'global.tls.enabled=true' \ + --set 'global.extraLabels.foo=bar' \ + . | tee /dev/stderr) + local actualBar=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + [ "${actualBar}" = "bar" ] + local actualTemplateBar=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + [ "${actualTemplateBar}" = "bar" ] +} + +@test "tlsInit/Job: multiple extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/tls-init-cleanup-job.yaml \ + --set 'global.tls.enabled=true' \ + --set 'global.extraLabels.foo=bar' \ + --set 'global.extraLabels.baz=qux' \ + . | tee /dev/stderr) + local actualFoo=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + local actualBaz=$(echo "${actual}" | yq -r '.metadata.labels.baz' | tee /dev/stderr) + [ "${actualFoo}" = "bar" ] + [ "${actualBaz}" = "qux" ] + local actualTemplateFoo=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + local actualTemplateBaz=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.baz' | tee /dev/stderr) + [ "${actualTemplateFoo}" = "bar" ] + [ "${actualTemplateBaz}" = "qux" ] +} diff --git a/charts/consul/test/unit/tls-init-job.bats b/charts/consul/test/unit/tls-init-job.bats index 6c148ed074..f9294915a5 100644 --- a/charts/consul/test/unit/tls-init-job.bats +++ b/charts/consul/test/unit/tls-init-job.bats @@ -163,3 +163,47 @@ load _helpers --set 'global.tls.enableAutoEncrypt=true' \ . } + +#-------------------------------------------------------------------- +# extraLabels + +@test "tlsInit/Job: no extra labels defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/tls-init-job.yaml \ + --set 'global.tls.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.labels | del(."app") | del(."chart") | del(."release") | del(."component")' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "tlsInit/Job: extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/tls-init-job.yaml \ + --set 'global.tls.enabled=true' \ + --set 'global.extraLabels.foo=bar' \ + . | tee /dev/stderr) + local actualBar=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + [ "${actualBar}" = "bar" ] + local actualTemplateBar=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + [ "${actualTemplateBar}" = "bar" ] +} + +@test "tlsInit/Job: multiple extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/tls-init-job.yaml \ + --set 'global.tls.enabled=true' \ + --set 'global.extraLabels.foo=bar' \ + --set 'global.extraLabels.baz=qux' \ + . | tee /dev/stderr) + local actualFoo=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + local actualBaz=$(echo "${actual}" | yq -r '.metadata.labels.baz' | tee /dev/stderr) + [ "${actualFoo}" = "bar" ] + [ "${actualBaz}" = "qux" ] + local actualTemplateFoo=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + local actualTemplateBaz=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.baz' | tee /dev/stderr) + [ "${actualTemplateFoo}" = "bar" ] + [ "${actualTemplateBaz}" = "qux" ] +} diff --git a/charts/consul/test/unit/webhook-cert-manager-deployment.bats b/charts/consul/test/unit/webhook-cert-manager-deployment.bats index e6549200cf..7d1a028d20 100644 --- a/charts/consul/test/unit/webhook-cert-manager-deployment.bats +++ b/charts/consul/test/unit/webhook-cert-manager-deployment.bats @@ -83,3 +83,44 @@ load _helpers --set 'global.secretsBackend.vault.consulCARole=test2' \ . } + +#-------------------------------------------------------------------- +# extraLabels + +@test "webhookCertManager/Deployment: no extra labels defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/webhook-cert-manager-deployment.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.labels | del(."app") | del(."chart") | del(."release") | del(."component") | del(."heritage")' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "webhookCertManager/Deployment: extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/webhook-cert-manager-deployment.yaml \ + --set 'global.extraLabels.foo=bar' \ + . | tee /dev/stderr) + local actualBar=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + [ "${actualBar}" = "bar" ] + local actualTemplateBar=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + [ "${actualTemplateBar}" = "bar" ] +} + +@test "webhookCertManager/Deployment: multiple global extra labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/webhook-cert-manager-deployment.yaml \ + --set 'global.extraLabels.foo=bar' \ + --set 'global.extraLabels.baz=qux' \ + . | tee /dev/stderr) + local actualFoo=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + local actualBaz=$(echo "${actual}" | yq -r '.metadata.labels.baz' | tee /dev/stderr) + [ "${actualFoo}" = "bar" ] + [ "${actualBaz}" = "qux" ] + local actualTemplateFoo=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + local actualTemplateBaz=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.baz' | tee /dev/stderr) + [ "${actualTemplateFoo}" = "bar" ] + [ "${actualTemplateBaz}" = "qux" ] +} diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index d26316100e..7bd8c5e549 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -622,6 +622,19 @@ global: # @type: string secretKey: null + # Extra labels to attach to all pods, deployments, daemonsets, statefulsets, and jobs. This should be a YAML map. + # + # Example: + # + # ```yaml + # extraLabels: + # labelKey: label-value + # anotherLabelKey: another-label-value + # ``` + # + # @type: map + extraLabels: {} + # Server, when enabled, configures a server cluster to run. This should # be disabled if you plan on connecting to a Consul cluster external to # the Kube cluster. From 08c014783378f8d243cc9790f8ce42143f1036d9 Mon Sep 17 00:00:00 2001 From: Kyle Schochenmaier Date: Wed, 7 Dec 2022 15:05:36 -0600 Subject: [PATCH 021/340] add usage of acceptance test suite to contributing guide for debugging (#1775) * add usage of acceptance test suite to contributing guide for debugging --- CONTRIBUTING.md | 194 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 193 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 229f6068f5..327399b89c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,9 +22,9 @@ 1. [Running the tests](#running-the-tests) 1. [Writing Unit tests](#writing-unit-tests) 1. [Writing Acceptance tests](#writing-acceptance-tests) +1. [Using the Acceptance Test Framework to Debug](#using-acceptance-test-framework-to-debug) 1. [Helm Reference Docs](#helm-reference-docs) - ## Contributing 101 ### Building and running `consul-k8s-control-plane` @@ -940,6 +940,198 @@ Here are some things to consider before adding a test: --- +## Using Acceptance Test Framework to Debug +### Acceptance Tests + +The [consul-k8s](https://github.com/hashicorp/consul-k8s) repository has an extensive list of [acceptance](https://github.com/hashicorp/consul-k8s/tree/main/acceptance/tests) +tests that are used by CI to run per-PR and nightly acceptance tests. +It is built on its own framework that uses Helm and the consul-k8s CLI to deploy consul (and other tools) in various +configurations that provide test coverage for most features that exist and provides coverage for more advanced deployments +than are typically covered in guides. +Importantly, it is **automated**, so you are able to rapidly deploy known working +configurations in known working environments. +It can be very helpful for bootstrapping complex environments such as when using Vault as a CA for Consul or for federating test clusters. + +The tests are organized like this : +```shell +demo $ tree -L 1 -d acceptance/tests +acceptance/tests +├── basic +├── cli +├── config-entries +├── connect +├── consul-dns +├── example +├── fixtures +├── ingress-gateway +├── metrics +├── partitions +├── peering +├── snapshot-agent +├── sync +├── terminating-gateway +├── vault +└── wan-federation +``` + +### Basic Running of Tests +Any given test can be run either through GoLand or another IDE, or via command line using `go test -run`. + +To run all of the connect tests from command line: +```shell +$ cd acceptance/test +$ go test ./connect/... -p 1 -timeout 2h -failfast -use-kind -no-cleanup-on-failure -kubecontext=kind-dc1 -secondary-kubecontext=kind-dc2 -enable-enterprise -enable-multi-cluster -debug-directory=/tmp/debug -consul-k8s-image=kyleschochenmaier/consul-k8s-acls +``` + +When running from command line a few things are important: +* Some tests use Enterprise features, in which case you need: + * Set environment variables `CONSUL_ENT_LICENSE` and possibly `VAULT_LICENSE`. + * Use `-enable-enterprise` on command line when running the test. +* Multi-cluster tests require `-enable-multi-cluster` + `-kubecontext=` + `-secondary-kubecontext=` +* Using `.//...` is required as part of the command-line to pick up necessary environmental config. + +### Using the framework to debug in an environment +=> NOTE: It is helpful to tune the docker desktop resource settings so that docker has at least 4GB memory, plenty of cpu cores and 2GB of swap. + +* If using Kind, `-use-kind` should be added, and be sure you cluster is up and running: +```shell +$ kind create cluster --name=dc1 && kind create cluster --name=dc2 +``` +* Pick a test which replicates the environment you are wanting to work with. + Ex: pick a test from `partitions/` or `vault/` or `connect/`. +* If you need the environment to persist, add a `time.Sleep(1*time.Hour)` to the end of the test in the test file. +* Use the following flags if you need to use or test out a specific consul/k8s image: + `-consul-k8s-image=` && `-consul-image=` +* You can set custom helm flags by modifying the test file directly in the respective directory. + +Finally, run the test like shown above: +```shell +$ cd acceptance/tests +$ go test -run Vault_WANFederationViaGateways ./vault/... -p 1 -timeout 2h -failfast -use-kind -no-cleanup-on-failure -kubecontext=kind-dc1 -secondary-kubecontext=kind-dc2 -enable-multi-cluster -debug-directory=/tmp/debug +``` +You can interact with the running kubernetes clusters now using `kubectl [COMMAND] --context=` + +* `kind delete clusters --all` is helpful for cleanup! + +### Example Debugging session using the acceptance test framework to bootstrap and debug a Vault backed federated Consul installation: +This test utilizes the `consul-k8s` acceptance test framework, with a custom consul-k8s branch which: +* Modifies the acceptance test to use custom consul+consul-k8s images and sleeps at the end of the test to allow analysis. +* Modifies the helm chart to pass in `connect_ca.intermediate_cert_ttl` and `connect_ca.leaf_cert_ttl` in the `server-configmap` + +1. First clone the consul-k8s repo and then check out the branch locally: `git checkout origin/consul-vault-provider-wanfed-acceptance`. +2. Start the kind clusters: `kind create cluster --name=dc1 && kind create cluster --name=dc2` +3. run the `TestVault_WANFederationViaGateways` acceptance test in `acceptance/tests/vault/vault_wan_fed_test.go` - I use goland, but this command should get you most of the way: +```shell +$ cd acceptance/tests +$ go test -run Vault_WANFederationViaGateways ./vault/... -p 1 -timeout 2h -failfast -use-kind -no-cleanup-on-failure -kubecontext=kind-dc1 -secondary-kubecontext=kind-dc2 -enable-multi-cluster -debug-directory=/tmp/debug +``` +NOTE: This specific acceptance test is considered FLAKY with Kind, if things don't come up it's best to run against GKE/AKS/etc, in which case you just modify the `kubecontext` command parameters to point to your clusters. It is worth noting that you will need to setup any necessary networking for non-Kind clusters manually. + +NOTE: This test requires a VAULT_LICENSE set as an environment variable in the shell where you run `go test` + +4. Wait 10-20 minutes to allow the first intermediate ca renewal, this test is particularly resource intensive so it can take time for everything to come online on a laptop, use `kubectl get pods` to validate that `static-server` and `static-client` have been deployed and are online. + +You can validate the ICA rotation by doing: +```shell +# Fetch the vault root token: +$ kubectl get secrets -root-token -o json //----> b64 decode the `data.token` field. +$ kubectl exec -it -- sh +$ export VAULT_TOKEN= +$ export VAULT_ADDR=https://-vault:8200 + +# Fetch the consul bootstrap token +$ vault kv get consul/secret/bootstrap + +# Examine the vault issuers, there should be 2 by now if ICA renewal has occured: +# NOTE: for a federated setup the issuers url for dc2 is `vault list dc2/connect_inter/issuers`! +$ vault list dc1/connect_inter/issuers + +Keys +---- +29bdffbd-87ec-cfe0-fd05-b78f99eba243 +344eea3c-f085-943a-c3ff-66721ef408f4 + +# Now login to the consul-server +$ kubectl exec -it -- sh +$ export CONSUL_HTTP_TOKEN= +$ export CONSUL_HTTP_ADDR=https://localhost:8501 +$ export CONSUL_HTTP_SSL_VERIFY=false + +# Read the `connect/ca/roots` endpoint: +# It should change + rotate with the expiration of the ICA (defined by `intermediate_cert_ttl` which is `15m` in the branch for this gist. + +$ curl -k --header "X-Consul-Token: 1428da53-5e88-db1a-6ad5-e50212b011da" https://127.0.0.1:8501/v1/agent/connect/ca/roots | jq + . + % Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed +100 3113 100 3113 0 0 6222 0 --:--:-- --:--:-- --:--:-- 7705 +{ + "ActiveRootID": "36:be:19:0e:56:d1:c2:1a:d8:54:22:97:88:3c:91:17:1d:d2:d3:e0", + "TrustDomain": "34a76791-b9b2-b93e-b0e4-1989ed11a28e.consul", + "Roots": [ + { + "ID": "36:be:19:0e:56:d1:c2:1a:d8:54:22:97:88:3c:91:17:1d:d2:d3:e0", + "Name": "Vault CA Primary Cert", + "SerialNumber": 15998414315735550000, + "SigningKeyID": "fe:b9:d6:0b:c6:ce:2c:25:4f:d8:59:cb:11:ea:a5:42:5f:8e:41:4b", + "ExternalTrustDomain": "34a76791-b9b2-b93e-b0e4-1989ed11a28e", + "NotBefore": "2022-11-16T20:16:15Z", + "NotAfter": "2032-11-13T20:16:45Z", + "RootCert": "-----BEGIN CERTIFICATE-----\nMIICLDCCAdKgAwIBAgIUKQ9BPHF9mtC7yFPC3gXJDpLxCHIwCgYIKoZIzj0EAwIw\nLzEtMCsGA1UEAxMkcHJpLTEwOTJudTEudmF1bHQuY2EuMzRhNzY3OTEuY29uc3Vs\nMB4XDTIyMTExNjIwMTYxNVoXDTMyMTExMzIwMTY0NVowLzEtMCsGA1UEAxMkcHJp\nLTEwOTJudTEudmF1bHQuY2EuMzRhNzY3OTEuY29uc3VsMFkwEwYHKoZIzj0CAQYI\nKoZIzj0DAQcDQgAETnpGixC1kW8ep2JcGjRR2jbdESvjlEm9nSIWVAcilemUGFwi\nJ0YW0XUmJeEzRyfwLXnOw6voPzXRf1zXKjdTD6OByzCByDAOBgNVHQ8BAf8EBAMC\nAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUtb6EjDxyI+myIjDc+7KbiN8u\n8XowHwYDVR0jBBgwFoAUtb6EjDxyI+myIjDc+7KbiN8u8XowZQYDVR0RBF4wXIIk\ncHJpLTEwOTJudTEudmF1bHQuY2EuMzRhNzY3OTEuY29uc3VshjRzcGlmZmU6Ly8z\nNGE3Njc5MS1iOWIyLWI5M2UtYjBlNC0xOTg5ZWQxMWEyOGUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCIHBezFSQAK5Nolf0rs3ErvlDcA8Z9esldh6gHupuGsNkAiEA\n9qL+P9PJAW4CrbTL0iF2yZUyJC2nwSSa2K0nYG8bXWQ=\n-----END CERTIFICATE-----\n", + "IntermediateCerts": [ + "-----BEGIN CERTIFICATE-----\nMIICLzCCAdSgAwIBAgIUbILCP3ODM4ScNBOm0jw59Fxju0swCgYIKoZIzj0EAwIw\nLzEtMCsGA1UEAxMkcHJpLTEwOTJudTEudmF1bHQuY2EuMzRhNzY3OTEuY29uc3Vs\nMB4XDTIyMTExNjIwMzIxNloXDTIyMTExNjIwNDc0NlowMDEuMCwGA1UEAxMlcHJp\nLTE4MThxNWlnLnZhdWx0LmNhLjM0YTc2NzkxLmNvbnN1bDBZMBMGByqGSM49AgEG\nCCqGSM49AwEHA0IABI30ikgrwTjbPaGgfNYkushvrEUUpxLzxMMEBlE82ilog1RW\nqwuEU29Qsa+N4SrfOf37xNv/Ey8SXPs5l2HmXJWjgcwwgckwDgYDVR0PAQH/BAQD\nAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFCZpC/BTdaggL2kj6Dfyk3+a\nNqBvMB8GA1UdIwQYMBaAFLW+hIw8ciPpsiIw3Puym4jfLvF6MGYGA1UdEQRfMF2C\nJXByaS0xODE4cTVpZy52YXVsdC5jYS4zNGE3Njc5MS5jb25zdWyGNHNwaWZmZTov\nLzM0YTc2NzkxLWI5YjItYjkzZS1iMGU0LTE5ODllZDExYTI4ZS5jb25zdWwwCgYI\nKoZIzj0EAwIDSQAwRgIhAJ8RHgR5qkyW2q866vGYJy+7BJ4zUXs3OJ76QLmxxU3K\nAiEA70S7wBEm1ZduTAk1ZfZPJEUGxvAXAcgy7EWeO/6MJ5o=\n-----END CERTIFICATE-----\n", + "-----BEGIN CERTIFICATE-----\nMIICLTCCAdKgAwIBAgIUU3qwESuhh4PgW3/tnHDn3qnBMrAwCgYIKoZIzj0EAwIw\nLzEtMCsGA1UEAxMkcHJpLTEwOTJudTEudmF1bHQuY2EuMzRhNzY3OTEuY29uc3Vs\nMB4XDTIyMTExNjIwNDAxNloXDTIyMTExNjIwNTU0NlowLzEtMCsGA1UEAxMkcHJp\nLTFkY2hkbGkudmF1bHQuY2EuMzRhNzY3OTEuY29uc3VsMFkwEwYHKoZIzj0CAQYI\nKoZIzj0DAQcDQgAEpj0BWPkcH82su9XGOo9rN5Zr5+Jyp68LiHy+qlIgH3L+OAir\nYgmXmJfuNwI8S2BB8cu0Gk3w5cTF7O0p/qAghaOByzCByDAOBgNVHQ8BAf8EBAMC\nAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU/rnWC8bOLCVP2FnLEeqlQl+O\nQUswHwYDVR0jBBgwFoAUtb6EjDxyI+myIjDc+7KbiN8u8XowZQYDVR0RBF4wXIIk\ncHJpLTFkY2hkbGkudmF1bHQuY2EuMzRhNzY3OTEuY29uc3VshjRzcGlmZmU6Ly8z\nNGE3Njc5MS1iOWIyLWI5M2UtYjBlNC0xOTg5ZWQxMWEyOGUuY29uc3VsMAoGCCqG\nSM49BAMCA0kAMEYCIQCtq4LiZzkiIKUES9MrzUEflg7wcwQf7Km+8RcOGQbz9QIh\nANWHWt1fe8Hl1wQ55qxsV5lSfOpGAox5WHpgnsBC7cwU\n-----END CERTIFICATE-----\n" + ], + "Active": true, + "PrivateKeyType": "ec", + "PrivateKeyBits": 256, + "CreateIndex": 11, + "ModifyIndex": 797 + } + ] +} + +# You can x509 decode the ICA certs to verify they have been updated and have correct expiry: +$ openssl x509 -in cert.crt -text -noout +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 53:7a:b0:11:2b:a1:87:83:e0:5b:7f:ed:9c:70:e7:de:a9:c1:32:b0 + Signature Algorithm: ecdsa-with-SHA256 + Issuer: CN=pri-1092nu1.vault.ca.34a76791.consul + Validity + Not Before: Nov 16 20:40:16 2022 GMT + Not After : Nov 16 20:55:46 2022 GMT + Subject: CN=pri-1dchdli.vault.ca.34a76791.consul + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:a6:3d:01:58:f9:1c:1f:cd:ac:bb:d5:c6:3a:8f: + 6b:37:96:6b:e7:e2:72:a7:af:0b:88:7c:be:aa:52: + 20:1f:72:fe:38:08:ab:62:09:97:98:97:ee:37:02: + 3c:4b:60:41:f1:cb:b4:1a:4d:f0:e5:c4:c5:ec:ed: + 29:fe:a0:20:85 + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Subject Key Identifier: + FE:B9:D6:0B:C6:CE:2C:25:4F:D8:59:CB:11:EA:A5:42:5F:8E:41:4B + X509v3 Authority Key Identifier: + keyid:B5:BE:84:8C:3C:72:23:E9:B2:22:30:DC:FB:B2:9B:88:DF:2E:F1:7A + + X509v3 Subject Alternative Name: + DNS:pri-1dchdli.vault.ca.34a76791.consul, URI:spiffe://34a76791-b9b2-b93e-b0e4-1989ed11a28e.consul + +``` + +--- + ## Helm Reference Docs The Helm reference docs (https://www.consul.io/docs/k8s/helm) are automatically From d1129a5eb62b3cea00afbc7fc8ba907bc5980864 Mon Sep 17 00:00:00 2001 From: David Yu Date: Thu, 8 Dec 2022 12:58:34 -0800 Subject: [PATCH 022/340] Update CHANGELOG.md (#1779) --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dccaa78fd5..1515b6d12f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## UNRELEASED +IMPROVEMENTS: +* Helm: + * Add a `global.extraLabels` stanza to allow setting global Kubernetes labels for all components deployed by the `consul-k8s` Helm chart. [[GH-1778](https://github.com/hashicorp/consul-k8s/pull/1778)] + ## 1.0.2 (December 1, 2022) IMPROVEMENTS: From d7c253f3da4c7a689d3b230ed60cdb85c9dac71c Mon Sep 17 00:00:00 2001 From: David Yu Date: Tue, 13 Dec 2022 09:50:49 -0800 Subject: [PATCH 023/340] Create JIRA Github Sync (#1782) * Create JIRA Github Sync --- .github/workflows/jira.yaml | 92 +++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 .github/workflows/jira.yaml diff --git a/.github/workflows/jira.yaml b/.github/workflows/jira.yaml new file mode 100644 index 0000000000..e86d54eb22 --- /dev/null +++ b/.github/workflows/jira.yaml @@ -0,0 +1,92 @@ +on: + issues: + types: [opened, closed, deleted, reopened] + pull_request_target: + types: [opened, closed, reopened] + issue_comment: + types: [created] + workflow_dispatch: + +name: Jira Sync + +jobs: + sync: + runs-on: ubuntu-latest + name: Jira sync + steps: + - name: Login + uses: atlassian/gajira-login@v3 + env: + JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} + JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} + JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} + + - name: Set ticket type + id: set-ticket-type + run: | + echo "::set-output name=type::GH Issue" + - name: Set ticket labels + if: github.event.action == 'opened' + id: set-ticket-labels + run: | + LABELS="[consul-k8s]" + echo "::set-output name=labels::${LABELS}" + + - name: Check if team member + if: github.event.action == 'opened' && steps.set-ticket-type.outputs.type == 'Task' + id: is-team-member + run: | + TEAM=consul-kubernetes + ROLE="$(hub api orgs/hashicorp/teams/${TEAM}/memberships/${{ github.actor }} | jq -r '.role | select(.!=null)')" + if [[ -n ${ROLE} ]]; then + echo "Actor ${{ github.actor }} is a ${TEAM} team member" + echo "::set-output name=message::true" + else + echo "Actor ${{ github.actor }} is NOT a ${TEAM} team member" + echo "::set-output name=message::false" + fi + env: + GITHUB_TOKEN: ${{ secrets.JIRA_SYNC_GITHUB_TOKEN }} + + - name: Create ticket + if: ( github.event.action == 'opened' && steps.set-ticket-type.outputs.type != 'Task' ) || ( github.event.action == 'opened' && steps.set-ticket-type.outputs.type == 'Task' && steps.is-team-member.outputs.message == 'false' ) + uses: tomhjp/gh-action-jira-create@v0.2.0 + with: + project: NET + issuetype: "${{ steps.set-ticket-type.outputs.type }}" + summary: "${{ github.event.repository.name }} [${{ steps.set-ticket-type.outputs.type }} #${{ github.event.issue.number || github.event.pull_request.number }}]: ${{ github.event.issue.title || github.event.pull_request.title }}" + description: "${{ github.event.issue.body || github.event.pull_request.body }}\n\n_Created in GitHub by ${{ github.actor }}._" + # customfield_10089 is "Issue Link", customfield_10371 is "Source" (use JIRA API to retrieve) + extraFields: '{ "customfield_10089": "${{ github.event.issue.html_url || github.event.pull_request.html_url }}", + "customfield_10371": { "value": "GitHub" }, + "components": [{ "name": "${{ github.event.repository.name }}" }], + "labels": ${{ steps.set-ticket-labels.outputs.labels }} }' + + - name: Search + if: github.event.action != 'opened' + id: search + uses: tomhjp/gh-action-jira-search@v0.2.1 + with: + # cf[10089] is Issue Link (use JIRA API to retrieve) + jql: 'issuetype = "${{ steps.set-ticket-type.outputs.type }}" and cf[10089] = "${{ github.event.issue.html_url || github.event.pull_request.html_url }}"' + + - name: Sync comment + if: github.event.action == 'created' && steps.search.outputs.issue + uses: tomhjp/gh-action-jira-comment@v0.1.0 + with: + issue: ${{ steps.search.outputs.issue }} + comment: "${{ github.actor }} ${{ github.event.review.state || 'commented' }}:\n\n${{ github.event.comment.body || github.event.review.body }}\n\n${{ github.event.comment.html_url || github.event.review.html_url }}" + + - name: Close ticket + if: ( github.event.action == 'closed' || github.event.action == 'deleted' ) && steps.search.outputs.issue + uses: atlassian/gajira-transition@v2.0.1 + with: + issue: ${{ steps.search.outputs.issue }} + transition: "Closed" + + - name: Reopen ticket + if: github.event.action == 'reopened' && steps.search.outputs.issue + uses: atlassian/gajira-transition@v2.0.1 + with: + issue: ${{ steps.search.outputs.issue }} + transition: "To Do" From f3cb000e90cdf6b35e2bf707c1c602c00e4c7b4a Mon Sep 17 00:00:00 2001 From: David Yu Date: Tue, 13 Dec 2022 11:11:26 -0800 Subject: [PATCH 024/340] JIRA sync: downgrade to tomhjp/gh-action-jira-create@v0.1.3 (#1784) * Update jira.yaml reset to 0.1.3 for create jira plugin and remove team member check --- .github/workflows/jira.yaml | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/.github/workflows/jira.yaml b/.github/workflows/jira.yaml index e86d54eb22..3a0f2af541 100644 --- a/.github/workflows/jira.yaml +++ b/.github/workflows/jira.yaml @@ -31,26 +31,10 @@ jobs: run: | LABELS="[consul-k8s]" echo "::set-output name=labels::${LABELS}" - - - name: Check if team member - if: github.event.action == 'opened' && steps.set-ticket-type.outputs.type == 'Task' - id: is-team-member - run: | - TEAM=consul-kubernetes - ROLE="$(hub api orgs/hashicorp/teams/${TEAM}/memberships/${{ github.actor }} | jq -r '.role | select(.!=null)')" - if [[ -n ${ROLE} ]]; then - echo "Actor ${{ github.actor }} is a ${TEAM} team member" - echo "::set-output name=message::true" - else - echo "Actor ${{ github.actor }} is NOT a ${TEAM} team member" - echo "::set-output name=message::false" - fi - env: - GITHUB_TOKEN: ${{ secrets.JIRA_SYNC_GITHUB_TOKEN }} - name: Create ticket - if: ( github.event.action == 'opened' && steps.set-ticket-type.outputs.type != 'Task' ) || ( github.event.action == 'opened' && steps.set-ticket-type.outputs.type == 'Task' && steps.is-team-member.outputs.message == 'false' ) - uses: tomhjp/gh-action-jira-create@v0.2.0 + if: ( github.event.action == 'opened' && steps.set-ticket-type.outputs.type != 'Task' ) || ( github.event.action == 'opened' && steps.set-ticket-type.outputs.type == 'Task' ) + uses: tomhjp/gh-action-jira-create@v0.1.3 with: project: NET issuetype: "${{ steps.set-ticket-type.outputs.type }}" From e4fde5364f2cf193cf717f7c94241c3ed257ab01 Mon Sep 17 00:00:00 2001 From: David Yu Date: Tue, 13 Dec 2022 12:33:42 -0800 Subject: [PATCH 025/340] JIRA: Use env when creating with custom fields (#1786) * Update jira.yaml --- .github/workflows/jira.yaml | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/.github/workflows/jira.yaml b/.github/workflows/jira.yaml index 3a0f2af541..fdd07264c2 100644 --- a/.github/workflows/jira.yaml +++ b/.github/workflows/jira.yaml @@ -15,7 +15,7 @@ jobs: name: Jira sync steps: - name: Login - uses: atlassian/gajira-login@v3 + uses: atlassian/gajira-login@v2.0.0 env: JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} @@ -24,17 +24,38 @@ jobs: - name: Set ticket type id: set-ticket-type run: | - echo "::set-output name=type::GH Issue" + if [[ "${{ github.event_name == 'pull_request' && github.event.action == 'opened' ]]; then + echo "::set-output name=type::PR" + else + echo "::set-output name=type::GH Issue" + fi + - name: Set ticket labels if: github.event.action == 'opened' id: set-ticket-labels run: | LABELS="[consul-k8s]" echo "::set-output name=labels::${LABELS}" + + - name: Check if team member + if: github.event.action == 'opened' && steps.set-ticket-type.outputs.type == 'Task' + id: is-team-member + run: | + TEAM=consul + ROLE="$(hub api orgs/hashicorp/teams/${TEAM}/memberships/${{ github.actor }} | jq -r '.role | select(.!=null)')" + if [[ -n ${ROLE} ]]; then + echo "Actor ${{ github.actor }} is a ${TEAM} team member" + echo "::set-output name=message::true" + else + echo "Actor ${{ github.actor }} is NOT a ${TEAM} team member" + echo "::set-output name=message::false" + fi + env: + GITHUB_TOKEN: ${{ secrets.JIRA_SYNC_GITHUB_TOKEN }} - - name: Create ticket - if: ( github.event.action == 'opened' && steps.set-ticket-type.outputs.type != 'Task' ) || ( github.event.action == 'opened' && steps.set-ticket-type.outputs.type == 'Task' ) - uses: tomhjp/gh-action-jira-create@v0.1.3 + - name: Create ticket if an issue is filed, or if PR not by a team member is opened + if: ( github.event.action == 'opened' && steps.set-ticket-type.outputs.type == 'GH Issue' ) || ( github.event.action == 'opened' && steps.set-ticket-type.outputs.type == 'PR' && steps.is-team-member.outputs.message == 'false' ) + uses: tomhjp/gh-action-jira-create@v0.2.0 with: project: NET issuetype: "${{ steps.set-ticket-type.outputs.type }}" @@ -45,6 +66,10 @@ jobs: "customfield_10371": { "value": "GitHub" }, "components": [{ "name": "${{ github.event.repository.name }}" }], "labels": ${{ steps.set-ticket-labels.outputs.labels }} }' + env: + JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} + JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} + JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} - name: Search if: github.event.action != 'opened' From 904b5b9589caf923511fe52c83ec4613af5bf397 Mon Sep 17 00:00:00 2001 From: David Yu Date: Tue, 13 Dec 2022 12:40:42 -0800 Subject: [PATCH 026/340] Update jira.yaml (#1788) --- .github/workflows/jira.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/jira.yaml b/.github/workflows/jira.yaml index fdd07264c2..02bb34b39c 100644 --- a/.github/workflows/jira.yaml +++ b/.github/workflows/jira.yaml @@ -24,7 +24,7 @@ jobs: - name: Set ticket type id: set-ticket-type run: | - if [[ "${{ github.event_name == 'pull_request' && github.event.action == 'opened' ]]; then + if [[ "${{ github.event_name == 'pull_request' && github.event.action == 'opened' }}" == "true" ]]; then echo "::set-output name=type::PR" else echo "::set-output name=type::GH Issue" From c577be1f4b4c6fbeee1a9b3cd2daafeb0036ae68 Mon Sep 17 00:00:00 2001 From: David Yu Date: Tue, 13 Dec 2022 13:03:37 -0800 Subject: [PATCH 027/340] missing quotes (#1790) --- .github/workflows/jira.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/jira.yaml b/.github/workflows/jira.yaml index 02bb34b39c..ac1e12e3a9 100644 --- a/.github/workflows/jira.yaml +++ b/.github/workflows/jira.yaml @@ -15,7 +15,7 @@ jobs: name: Jira sync steps: - name: Login - uses: atlassian/gajira-login@v2.0.0 + uses: atlassian/gajira-login@v3.0.0 env: JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} @@ -65,7 +65,7 @@ jobs: extraFields: '{ "customfield_10089": "${{ github.event.issue.html_url || github.event.pull_request.html_url }}", "customfield_10371": { "value": "GitHub" }, "components": [{ "name": "${{ github.event.repository.name }}" }], - "labels": ${{ steps.set-ticket-labels.outputs.labels }} }' + "labels": "${{ steps.set-ticket-labels.outputs.labels }}" }' env: JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} From 72e5aeb37ef113a82b8f539e1c1a1a77453f4425 Mon Sep 17 00:00:00 2001 From: David Yu Date: Tue, 13 Dec 2022 14:13:00 -0800 Subject: [PATCH 028/340] JIRA: Extra field for labels needs arrays (#1792) * Update jira.yaml --- .github/workflows/jira.yaml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/jira.yaml b/.github/workflows/jira.yaml index ac1e12e3a9..f5c155604c 100644 --- a/.github/workflows/jira.yaml +++ b/.github/workflows/jira.yaml @@ -25,9 +25,9 @@ jobs: id: set-ticket-type run: | if [[ "${{ github.event_name == 'pull_request' && github.event.action == 'opened' }}" == "true" ]]; then - echo "::set-output name=type::PR" + echo "TYPE=PR" >> $GITHUB_OUTPUT else - echo "::set-output name=type::GH Issue" + echo "TYPE=GH Issue" >> $GITHUB_OUTPUT fi - name: Set ticket labels @@ -35,37 +35,37 @@ jobs: id: set-ticket-labels run: | LABELS="[consul-k8s]" - echo "::set-output name=labels::${LABELS}" + echo "LABELS=${LABELS}" >> $GITHUB_OUTPUT - name: Check if team member - if: github.event.action == 'opened' && steps.set-ticket-type.outputs.type == 'Task' + if: github.event.action == 'opened' && steps.set-ticket-type.outputs.TYPE == 'PR' id: is-team-member run: | TEAM=consul ROLE="$(hub api orgs/hashicorp/teams/${TEAM}/memberships/${{ github.actor }} | jq -r '.role | select(.!=null)')" if [[ -n ${ROLE} ]]; then echo "Actor ${{ github.actor }} is a ${TEAM} team member" - echo "::set-output name=message::true" + echo "MESSAGE=true" >> $GITHUB_OUTPUT else echo "Actor ${{ github.actor }} is NOT a ${TEAM} team member" - echo "::set-output name=message::false" + echo "MESSAGE=false" >> $GITHUB_OUTPUT fi env: GITHUB_TOKEN: ${{ secrets.JIRA_SYNC_GITHUB_TOKEN }} - name: Create ticket if an issue is filed, or if PR not by a team member is opened - if: ( github.event.action == 'opened' && steps.set-ticket-type.outputs.type == 'GH Issue' ) || ( github.event.action == 'opened' && steps.set-ticket-type.outputs.type == 'PR' && steps.is-team-member.outputs.message == 'false' ) + if: ( github.event.action == 'opened' && steps.set-ticket-type.outputs.TYPE == 'GH Issue' ) || ( github.event.action == 'opened' && steps.is-team-member.outputs.MESSAGE == 'false' ) uses: tomhjp/gh-action-jira-create@v0.2.0 with: project: NET - issuetype: "${{ steps.set-ticket-type.outputs.type }}" - summary: "${{ github.event.repository.name }} [${{ steps.set-ticket-type.outputs.type }} #${{ github.event.issue.number || github.event.pull_request.number }}]: ${{ github.event.issue.title || github.event.pull_request.title }}" + issuetype: "${{ steps.set-ticket-type.outputs.TYPE }}" + summary: "${{ github.event.repository.name }} [${{ steps.set-ticket-type.outputs.TYPE }} #${{ github.event.issue.number || github.event.pull_request.number }}]: ${{ github.event.issue.title || github.event.pull_request.title }}" description: "${{ github.event.issue.body || github.event.pull_request.body }}\n\n_Created in GitHub by ${{ github.actor }}._" # customfield_10089 is "Issue Link", customfield_10371 is "Source" (use JIRA API to retrieve) extraFields: '{ "customfield_10089": "${{ github.event.issue.html_url || github.event.pull_request.html_url }}", "customfield_10371": { "value": "GitHub" }, "components": [{ "name": "${{ github.event.repository.name }}" }], - "labels": "${{ steps.set-ticket-labels.outputs.labels }}" }' + "labels": [{ "${{ steps.set-ticket-labels.outputs.LABELS }}" }] }' env: JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} @@ -77,7 +77,7 @@ jobs: uses: tomhjp/gh-action-jira-search@v0.2.1 with: # cf[10089] is Issue Link (use JIRA API to retrieve) - jql: 'issuetype = "${{ steps.set-ticket-type.outputs.type }}" and cf[10089] = "${{ github.event.issue.html_url || github.event.pull_request.html_url }}"' + jql: 'issuetype = "${{ steps.set-ticket-type.outputs.TYPE }}" and cf[10089] = "${{ github.event.issue.html_url || github.event.pull_request.html_url }}"' - name: Sync comment if: github.event.action == 'created' && steps.search.outputs.issue From d673fb8afba03ddf4c4e0e961977ba6a48bca019 Mon Sep 17 00:00:00 2001 From: David Yu Date: Tue, 13 Dec 2022 15:24:25 -0800 Subject: [PATCH 029/340] JIRA: Re-use previous structure from labels (#1794) * Update jira.yaml re-using previous structure from labels --- .github/workflows/jira.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/jira.yaml b/.github/workflows/jira.yaml index f5c155604c..4092a1898a 100644 --- a/.github/workflows/jira.yaml +++ b/.github/workflows/jira.yaml @@ -34,7 +34,10 @@ jobs: if: github.event.action == 'opened' id: set-ticket-labels run: | - LABELS="[consul-k8s]" + LABELS="[" + if [[ "${{ contains(github.event.issue.labels.*.name, 'type/bug') }}" == "true" ]]; then LABELS+="\"type/bug\", "; fi + if [[ "${{ contains(github.event.issue.labels.*.name, 'type/enhancement') }}" == "true" ]]; then LABELS+="\"type/enhancement\", "; fi + if [[ ${#LABELS} != 1 ]]; then LABELS=${LABELS::-2}"]"; else LABELS+="]"; fi echo "LABELS=${LABELS}" >> $GITHUB_OUTPUT - name: Check if team member @@ -65,7 +68,7 @@ jobs: extraFields: '{ "customfield_10089": "${{ github.event.issue.html_url || github.event.pull_request.html_url }}", "customfield_10371": { "value": "GitHub" }, "components": [{ "name": "${{ github.event.repository.name }}" }], - "labels": [{ "${{ steps.set-ticket-labels.outputs.LABELS }}" }] }' + "labels": ${{ steps.set-ticket-labels.outputs.LABELS }} }' env: JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} From 9069014e5ed947d7c79ad1d63f9ec4f9ee7ccd1e Mon Sep 17 00:00:00 2001 From: David Yu Date: Wed, 14 Dec 2022 08:39:02 -0800 Subject: [PATCH 030/340] JIRA: Add default work stream when syncing JIRA issues (#1796) * Update jira.yaml Split out issue vs pr workflow and fix some issues with work stream field --- .github/workflows/jira-issues.yaml | 83 +++++++++++++++++++ .github/workflows/{jira.yaml => jira-pr.yaml} | 20 ++--- 2 files changed, 89 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/jira-issues.yaml rename .github/workflows/{jira.yaml => jira-pr.yaml} (82%) diff --git a/.github/workflows/jira-issues.yaml b/.github/workflows/jira-issues.yaml new file mode 100644 index 0000000000..18705db8e8 --- /dev/null +++ b/.github/workflows/jira-issues.yaml @@ -0,0 +1,83 @@ +on: + issues: + types: [opened, closed, deleted, reopened] + issue_comment: + types: [created] + workflow_dispatch: + +name: Jira Community Issue Sync + +jobs: + sync: + runs-on: ubuntu-latest + name: Jira Community Issue sync + steps: + - name: Login + uses: atlassian/gajira-login@v3.0.0 + env: + JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} + JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} + JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} + + - name: Set ticket type + id: set-ticket-type + run: | + echo "TYPE=GH Issue" >> $GITHUB_OUTPUT + + - name: Set ticket labels + if: github.event.action == 'opened' + id: set-ticket-labels + run: | + LABELS="[" + if [[ "${{ contains(github.event.issue.labels.*.name, 'type/bug') }}" == "true" ]]; then LABELS+="\"type/bug\", "; fi + if [[ "${{ contains(github.event.issue.labels.*.name, 'type/enhancement') }}" == "true" ]]; then LABELS+="\"type/enhancement\", "; fi + if [[ ${#LABELS} != 1 ]]; then LABELS=${LABELS::-2}"]"; else LABELS+="]"; fi + echo "LABELS=${LABELS}" >> $GITHUB_OUTPUT + + - name: Create ticket if an issue is filed, or if PR not by a team member is opened + if: github.event.action == 'opened' + uses: tomhjp/gh-action-jira-create@v0.2.0 + with: + project: NET + issuetype: "${{ steps.set-ticket-type.outputs.TYPE }}" + summary: "${{ github.event.repository.name }} [${{ steps.set-ticket-type.outputs.TYPE }} #${{ github.event.issue.number }}]: ${{ github.event.issue.title }}" + description: "${{ github.event.issue.body || github.event.pull_request.body }}\n\n_Created in GitHub by ${{ github.actor }}._" + # customfield_10089 is "Issue Link", customfield_10371 is "Source" (use JIRA API to retrieve) + extraFields: '{ "customfield_10089": "${{ github.event.issue.html_url || github.event.pull_request.html_url }}", + "customfield_10371": { "value": "GitHub" }, + "customfield_10535": [{ "value": "Service Mesh" }], + "components": [{ "name": "${{ github.event.repository.name }}" }], + "labels": ${{ steps.set-ticket-labels.outputs.LABELS }} }' + env: + JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} + JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} + JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} + + - name: Search + if: github.event.action != 'opened' + id: search + uses: tomhjp/gh-action-jira-search@v0.2.1 + with: + # cf[10089] is Issue Link (use JIRA API to retrieve) + jql: 'issuetype = "${{ steps.set-ticket-type.outputs.TYPE }}" and cf[10089] = "${{ github.event.issue.html_url || github.event.pull_request.html_url }}"' + + - name: Sync comment + if: github.event.action == 'created' && steps.search.outputs.issue + uses: tomhjp/gh-action-jira-comment@v0.1.0 + with: + issue: ${{ steps.search.outputs.issue }} + comment: "${{ github.actor }} ${{ github.event.review.state || 'commented' }}:\n\n${{ github.event.comment.body || github.event.review.body }}\n\n${{ github.event.comment.html_url || github.event.review.html_url }}" + + - name: Close ticket + if: ( github.event.action == 'closed' || github.event.action == 'deleted' ) && steps.search.outputs.issue + uses: atlassian/gajira-transition@v2.0.1 + with: + issue: ${{ steps.search.outputs.issue }} + transition: "Closed" + + - name: Reopen ticket + if: github.event.action == 'reopened' && steps.search.outputs.issue + uses: atlassian/gajira-transition@v2.0.1 + with: + issue: ${{ steps.search.outputs.issue }} + transition: "To Do" diff --git a/.github/workflows/jira.yaml b/.github/workflows/jira-pr.yaml similarity index 82% rename from .github/workflows/jira.yaml rename to .github/workflows/jira-pr.yaml index 4092a1898a..5d0664fdcb 100644 --- a/.github/workflows/jira.yaml +++ b/.github/workflows/jira-pr.yaml @@ -1,13 +1,9 @@ on: - issues: - types: [opened, closed, deleted, reopened] pull_request_target: types: [opened, closed, reopened] - issue_comment: - types: [created] workflow_dispatch: -name: Jira Sync +name: Jira Community PR Sync jobs: sync: @@ -24,11 +20,7 @@ jobs: - name: Set ticket type id: set-ticket-type run: | - if [[ "${{ github.event_name == 'pull_request' && github.event.action == 'opened' }}" == "true" ]]; then - echo "TYPE=PR" >> $GITHUB_OUTPUT - else - echo "TYPE=GH Issue" >> $GITHUB_OUTPUT - fi + echo "TYPE=Github Issue" >> $GITHUB_OUTPUT - name: Set ticket labels if: github.event.action == 'opened' @@ -41,7 +33,7 @@ jobs: echo "LABELS=${LABELS}" >> $GITHUB_OUTPUT - name: Check if team member - if: github.event.action == 'opened' && steps.set-ticket-type.outputs.TYPE == 'PR' + if: github.event.action == 'opened' id: is-team-member run: | TEAM=consul @@ -57,15 +49,15 @@ jobs: GITHUB_TOKEN: ${{ secrets.JIRA_SYNC_GITHUB_TOKEN }} - name: Create ticket if an issue is filed, or if PR not by a team member is opened - if: ( github.event.action == 'opened' && steps.set-ticket-type.outputs.TYPE == 'GH Issue' ) || ( github.event.action == 'opened' && steps.is-team-member.outputs.MESSAGE == 'false' ) + if: ( github.event.action == 'opened' && steps.is-team-member.outputs.MESSAGE == 'false' ) uses: tomhjp/gh-action-jira-create@v0.2.0 with: project: NET issuetype: "${{ steps.set-ticket-type.outputs.TYPE }}" - summary: "${{ github.event.repository.name }} [${{ steps.set-ticket-type.outputs.TYPE }} #${{ github.event.issue.number || github.event.pull_request.number }}]: ${{ github.event.issue.title || github.event.pull_request.title }}" + summary: "${{ github.event.repository.name }} [${{ steps.set-ticket-type.outputs.TYPE }} #${{ github.event.pull_request.number }}]: ${{ github.event.pull_request.title }}" description: "${{ github.event.issue.body || github.event.pull_request.body }}\n\n_Created in GitHub by ${{ github.actor }}._" # customfield_10089 is "Issue Link", customfield_10371 is "Source" (use JIRA API to retrieve) - extraFields: '{ "customfield_10089": "${{ github.event.issue.html_url || github.event.pull_request.html_url }}", + extraFields: '{ "customfield_10089": "${{ github.event.pull_request.html_url }}", "customfield_10371": { "value": "GitHub" }, "components": [{ "name": "${{ github.event.repository.name }}" }], "labels": ${{ steps.set-ticket-labels.outputs.LABELS }} }' From a3decf08d4fa23983e025a1b4938b7a9455f5903 Mon Sep 17 00:00:00 2001 From: David Yu Date: Thu, 15 Dec 2022 13:21:17 -0800 Subject: [PATCH 031/340] JIRA fix for search issue (#1801) --- .github/workflows/jira-pr.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/jira-pr.yaml b/.github/workflows/jira-pr.yaml index 5d0664fdcb..0a2fc37a74 100644 --- a/.github/workflows/jira-pr.yaml +++ b/.github/workflows/jira-pr.yaml @@ -20,7 +20,7 @@ jobs: - name: Set ticket type id: set-ticket-type run: | - echo "TYPE=Github Issue" >> $GITHUB_OUTPUT + echo "TYPE=GH Issue" >> $GITHUB_OUTPUT - name: Set ticket labels if: github.event.action == 'opened' From c32b649dee05c36df29e919b6c1629033f9edfa2 Mon Sep 17 00:00:00 2001 From: David Yu Date: Mon, 19 Dec 2022 11:00:07 -0800 Subject: [PATCH 032/340] Add work stream to PR sync (#1803) --- .github/workflows/jira-pr.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/jira-pr.yaml b/.github/workflows/jira-pr.yaml index 0a2fc37a74..5c0ba71cd2 100644 --- a/.github/workflows/jira-pr.yaml +++ b/.github/workflows/jira-pr.yaml @@ -58,7 +58,8 @@ jobs: description: "${{ github.event.issue.body || github.event.pull_request.body }}\n\n_Created in GitHub by ${{ github.actor }}._" # customfield_10089 is "Issue Link", customfield_10371 is "Source" (use JIRA API to retrieve) extraFields: '{ "customfield_10089": "${{ github.event.pull_request.html_url }}", - "customfield_10371": { "value": "GitHub" }, + "customfield_10371": { "value": "GitHub" }, + "customfield_10535": [{ "value": "Service Mesh" }], "components": [{ "name": "${{ github.event.repository.name }}" }], "labels": ${{ steps.set-ticket-labels.outputs.LABELS }} }' env: From a2ba8916201af55cf2f57c8ed84506dcb1345aa9 Mon Sep 17 00:00:00 2001 From: Kyle Havlovitz Date: Fri, 16 Dec 2022 14:42:02 -0800 Subject: [PATCH 033/340] ignore partition/namespace on SourceIntention to match top-level config entry --- control-plane/api/v1alpha1/serviceintentions_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/control-plane/api/v1alpha1/serviceintentions_types.go b/control-plane/api/v1alpha1/serviceintentions_types.go index 6488a3615c..a0a240639a 100644 --- a/control-plane/api/v1alpha1/serviceintentions_types.go +++ b/control-plane/api/v1alpha1/serviceintentions_types.go @@ -239,7 +239,7 @@ func (in *ServiceIntentions) MatchesConsul(candidate api.ConfigEntry) bool { in.ToConsul(""), configEntry, cmpopts.IgnoreFields(capi.ServiceIntentionsConfigEntry{}, "Partition", "Namespace", "Meta", "ModifyIndex", "CreateIndex"), - cmpopts.IgnoreFields(capi.SourceIntention{}, "LegacyID", "LegacyMeta", "LegacyCreateTime", "LegacyUpdateTime", "Precedence", "Type"), + cmpopts.IgnoreFields(capi.SourceIntention{}, "Partition", "Namespace", "LegacyID", "LegacyMeta", "LegacyCreateTime", "LegacyUpdateTime", "Precedence", "Type"), cmpopts.IgnoreUnexported(), cmpopts.EquateEmpty(), // Consul will sort the sources by precedence when returning the resource From 730024577108cf318964ddfa1ebdb14a950d22b9 Mon Sep 17 00:00:00 2001 From: Kyle Havlovitz Date: Mon, 19 Dec 2022 11:26:31 -0800 Subject: [PATCH 034/340] Add changelog note --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1515b6d12f..330b1dc2c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ BUG FIXES: * Helm: * Use the correct autogenerated cert for the API Gateway Controller when connecting to servers versus clients. [[GH-1753](https://github.com/hashicorp/consul-k8s/pull/1753)] * Don't mount the CA cert when `externalServers.useSystemRoots` is `true`. [[GH-1753](https://github.com/hashicorp/consul-k8s/pull/1753)] +* Control Plane + * Don't incorrectly diff intention config entries when upgrading from Consul pre-1.12 to 1.12+ [[GH-1804](https://github.com/hashicorp/consul-k8s/pull/1804)] ## 0.49.2 (December 1, 2022) From 6ec4db232515017b160689f4fcf82e0346747230 Mon Sep 17 00:00:00 2001 From: David Yu Date: Tue, 20 Dec 2022 15:54:41 -0800 Subject: [PATCH 035/340] values.yaml - Fix helm docs for 1.0.x (#1810) * Fix helm docs for 1.0.x --- charts/consul/values.yaml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 7bd8c5e549..f2f909fa81 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -236,7 +236,7 @@ global: {} connectInject: - # Configuration to the Vault Secret that Kubernetes will use on + # Configuration to the Vault Secret that Kubernetes uses on # Kubernetes pod creation, deletion, and update, to get CA certificates # used issued from vault to send webhooks to the ConnectInject. caCert: @@ -245,7 +245,7 @@ global: # @type: string secretName: null - # Configuration to the Vault Secret that Kubernetes will use on + # Configuration to the Vault Secret that Kubernetes uses on # Kubernetes pod creation, deletion, and update, to get TLS certificates # used issued from vault to send webhooks to the ConnectInject. tlsCert: @@ -300,7 +300,7 @@ global: # If true, the Helm chart will enable TLS for Consul # servers and clients and all consul-k8s-control-plane components, as well as generate certificate # authority (optional) and server and client certificates. - # This setting is required for [Cluster Peering](/docs/connect/cluster-peering/k8s). + # This setting is required for [Cluster Peering](https://developer.hashicorp.com/consul/docs/connect/cluster-peering/k8s). enabled: false # If true, turns on the auto-encrypt feature on clients and servers. @@ -832,9 +832,9 @@ server: # This configures the PodDisruptionBudget (https://kubernetes.io/docs/tasks/run-application/configure-pdb/) # for the server cluster. disruptionBudget: - # This will enable/disable registering a PodDisruptionBudget for the server - # cluster. If this is enabled, it will only register the budget so long as - # the server cluster is enabled. + # Enables registering a PodDisruptionBudget for the server + # cluster. If enabled, it only registers the budget so long as + # the server cluster is enabled. To disable, set to `false`. enabled: true # The maximum number of unavailable pods. By default, this will be @@ -1924,7 +1924,7 @@ connectInject: # Configures consul-cni plugin for Consul Service mesh services cni: - # If true, then all traffic redirection setup will use the consul-cni plugin. + # If true, then all traffic redirection setup uses the consul-cni plugin. # Requires connectInject.enabled to also be true. # @type: boolean enabled: false @@ -2318,11 +2318,11 @@ connectInject: memory: "150Mi" cpu: "50m" -# [Mesh Gateways](/docs/connect/gateways/mesh-gateway) enable Consul Connect to work across Consul datacenters. +# [Mesh Gateways](https://developer.hashicorp.com/consul/docs/connect/gateways/mesh-gateway) enable Consul Connect to work across Consul datacenters. meshGateway: - # If [mesh gateways](/docs/connect/gateways/mesh-gateway) are enabled, a Deployment will be created that runs + # If [mesh gateways](https://developer.hashicorp.com/consul/docs/connect/gateways/mesh-gateway) are enabled, a Deployment will be created that runs # gateways and Consul Connect will be configured to use gateways. - # This setting is required for [Cluster Peering](/docs/connect/cluster-peering/k8s). + # This setting is required for [Cluster Peering](https://developer.hashicorp.com/consul/docs/connect/cluster-peering/k8s). # Requirements: consul 1.6.0+ if using `global.acls.manageSystemACLs``. enabled: false @@ -2871,9 +2871,9 @@ apiGateway: # @type: string nodeSelector: null - # This value defines the tolerations that will be assigned to a gateway pod. + # Toleration settings for gateway pods created with the managed gateway class. # This should be a multi-line string matching the - # Tolerations (https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) array in a Pod spec. + # [Tolerations](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) array in a Pod spec. # # @type: string tolerations: null From 21d3b04bfc2a4714173596bd859198e48f253ef8 Mon Sep 17 00:00:00 2001 From: DanStough Date: Wed, 21 Dec 2022 11:53:44 -0500 Subject: [PATCH 036/340] feat: access logs update for proxy-defaults CRD --- CHANGELOG.md | 1 + .../consul/templates/crd-proxydefaults.yaml | 32 ++++++ .../api/v1alpha1/proxydefaults_types.go | 92 ++++++++++++++++ .../api/v1alpha1/proxydefaults_types_test.go | 100 +++++++++++++++++- .../api/v1alpha1/zz_generated.deepcopy.go | 20 ++++ .../consul.hashicorp.com_proxydefaults.yaml | 32 ++++++ control-plane/go.mod | 2 +- control-plane/go.sum | 2 + 8 files changed, 279 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 330b1dc2c7..79cf1931b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ IMPROVEMENTS: * Helm: * Add a `global.extraLabels` stanza to allow setting global Kubernetes labels for all components deployed by the `consul-k8s` Helm chart. [[GH-1778](https://github.com/hashicorp/consul-k8s/pull/1778)] + * Add the `accessLogs` field to the `ProxyDefaults` CRD. [[GH-1816](https://github.com/hashicorp/consul-k8s/pull/1816)] ## 1.0.2 (December 1, 2022) diff --git a/charts/consul/templates/crd-proxydefaults.yaml b/charts/consul/templates/crd-proxydefaults.yaml index e66543637f..1a63cfef36 100644 --- a/charts/consul/templates/crd-proxydefaults.yaml +++ b/charts/consul/templates/crd-proxydefaults.yaml @@ -57,6 +57,38 @@ spec: spec: description: ProxyDefaultsSpec defines the desired state of ProxyDefaults. properties: + accessLogs: + description: AccessLogs controls all envoy instances' access logging + configuration. + properties: + disableListenerLogs: + description: DisableListenerLogs turns off just listener logs + for connections rejected by Envoy because they don't have a + matching listener filter. + type: boolean + enabled: + description: Enabled turns on all access logging + type: boolean + jsonFormat: + description: 'JSONFormat is a JSON-formatted string of an Envoy + access log format dictionary. See for more info on formatting: + https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#format-dictionaries + Defining JSONFormat and TextFormat is invalid.' + type: string + path: + description: Path is the output file to write logs for file-type + logging + type: string + textFormat: + description: 'TextFormat is a representation of Envoy access logs + format. See for more info on formatting: https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#format-strings + Defining JSONFormat and TextFormat is invalid.' + type: string + type: + description: Type selects the output for logs one of "file", "stderr". + "stdout" + type: string + type: object config: description: Config is an arbitrary map of configuration values used by Connect proxies. Any values that your proxy allows can be configured diff --git a/control-plane/api/v1alpha1/proxydefaults_types.go b/control-plane/api/v1alpha1/proxydefaults_types.go index 215ec708ff..82fcde3687 100644 --- a/control-plane/api/v1alpha1/proxydefaults_types.go +++ b/control-plane/api/v1alpha1/proxydefaults_types.go @@ -75,6 +75,8 @@ type ProxyDefaultsSpec struct { MeshGateway MeshGateway `json:"meshGateway,omitempty"` // Expose controls the default expose path configuration for Envoy. Expose Expose `json:"expose,omitempty"` + // AccessLogs controls all envoy instances' access logging configuration. + AccessLogs *AccessLogs `json:"accessLogs,omitempty"` } func (in *ProxyDefaults) GetObjectMeta() metav1.ObjectMeta { @@ -165,6 +167,7 @@ func (in *ProxyDefaults) ToConsul(datacenter string) capi.ConfigEntry { Expose: in.Spec.Expose.toConsul(), Config: consulConfig, TransparentProxy: in.Spec.TransparentProxy.toConsul(), + AccessLogs: in.Spec.AccessLogs.toConsul(), Meta: meta(datacenter), } } @@ -195,6 +198,9 @@ func (in *ProxyDefaults) Validate(_ common.ConsulMeta) error { if err := in.validateConfig(path.Child("config")); err != nil { allErrs = append(allErrs, err) } + if err := in.Spec.AccessLogs.validate(path.Child("accessLogs")); err != nil { + allErrs = append(allErrs, err) + } allErrs = append(allErrs, in.Spec.Expose.validate(path.Child("expose"))...) if len(allErrs) > 0 { return apierrors.NewInvalid( @@ -237,3 +243,89 @@ func (in *ProxyDefaults) validateConfig(path *field.Path) *field.Error { } return nil } + +// LogSinkType represents the destination for Envoy access logs. +// One of "file", "stderr", or "stdout". +type LogSinkType string + +const ( + DefaultLogSinkType LogSinkType = "" + FileLogSinkType LogSinkType = "file" + StdErrLogSinkType LogSinkType = "stderr" + StdOutLogSinkType LogSinkType = "stdout" +) + +// AccessLogs describes the access logging configuration for all Envoy proxies in the mesh. +type AccessLogs struct { + // Enabled turns on all access logging + Enabled bool `json:"enabled,omitempty"` + + // DisableListenerLogs turns off just listener logs for connections rejected by Envoy because they don't + // have a matching listener filter. + DisableListenerLogs bool `json:"disableListenerLogs,omitempty"` + + // Type selects the output for logs + // one of "file", "stderr". "stdout" + Type LogSinkType `json:"type,omitempty"` + + // Path is the output file to write logs for file-type logging + Path string `json:"path,omitempty"` + + // JSONFormat is a JSON-formatted string of an Envoy access log format dictionary. + // See for more info on formatting: https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#format-dictionaries + // Defining JSONFormat and TextFormat is invalid. + JSONFormat string `json:"jsonFormat,omitempty"` + + // TextFormat is a representation of Envoy access logs format. + // See for more info on formatting: https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#format-strings + // Defining JSONFormat and TextFormat is invalid. + TextFormat string `json:"textFormat,omitempty"` +} + +func (in *AccessLogs) validate(path *field.Path) *field.Error { + if in == nil { + return nil + } + + switch in.Type { + case DefaultLogSinkType, StdErrLogSinkType, StdOutLogSinkType: + // OK + case FileLogSinkType: + if in.Path == "" { + return field.Invalid(path.Child("path"), in.Path, "path must be specified when using file type access logs") + } + default: + return field.Invalid(path.Child("type"), in.Type, "invalid access log type (must be one of \"stdout\", \"stderr\", \"file\"") + } + + if in.JSONFormat != "" && in.TextFormat != "" { + return field.Invalid(path.Child("textFormat"), in.TextFormat, "cannot specify both access log jsonFormat and textFormat") + } + + if in.Type != FileLogSinkType && in.Path != "" { + return field.Invalid(path.Child("path"), in.Path, "path is only valid for file type access logs") + } + + if in.JSONFormat != "" { + msg := json.RawMessage{} + if err := json.Unmarshal([]byte(in.JSONFormat), &msg); err != nil { + return field.Invalid(path.Child("jsonFormat"), in.JSONFormat, "invalid access log json") + } + } + + return nil +} + +func (in *AccessLogs) toConsul() *capi.AccessLogsConfig { + if in == nil { + return nil + } + return &capi.AccessLogsConfig{ + Enabled: in.Enabled, + DisableListenerLogs: in.DisableListenerLogs, + JSONFormat: in.JSONFormat, + Path: in.Path, + TextFormat: in.TextFormat, + Type: capi.LogSinkType(in.Type), + } +} diff --git a/control-plane/api/v1alpha1/proxydefaults_types_test.go b/control-plane/api/v1alpha1/proxydefaults_types_test.go index 2950a3a36e..794906b05e 100644 --- a/control-plane/api/v1alpha1/proxydefaults_types_test.go +++ b/control-plane/api/v1alpha1/proxydefaults_types_test.go @@ -71,6 +71,13 @@ func TestProxyDefaults_MatchesConsul(t *testing.T) { OutboundListenerPort: 1000, DialedDirectly: true, }, + AccessLogs: &AccessLogs{ + Enabled: true, + DisableListenerLogs: true, + Type: FileLogSinkType, + Path: "/var/log/envoy.logs", + TextFormat: "ITS WORKING %START_TIME%", + }, }, }, Theirs: &capi.ProxyConfigEntry{ @@ -103,6 +110,13 @@ func TestProxyDefaults_MatchesConsul(t *testing.T) { OutboundListenerPort: 1000, DialedDirectly: true, }, + AccessLogs: &capi.AccessLogsConfig{ + Enabled: true, + DisableListenerLogs: true, + Type: capi.FileLogSinkType, + Path: "/var/log/envoy.logs", + TextFormat: "ITS WORKING %START_TIME%", + }, }, Matches: true, }, @@ -236,6 +250,13 @@ func TestProxyDefaults_ToConsul(t *testing.T) { OutboundListenerPort: 1000, DialedDirectly: true, }, + AccessLogs: &AccessLogs{ + Enabled: true, + DisableListenerLogs: true, + Type: FileLogSinkType, + Path: "/var/log/envoy.logs", + TextFormat: "ITS WORKING %START_TIME%", + }, }, }, Exp: &capi.ProxyConfigEntry{ @@ -269,6 +290,13 @@ func TestProxyDefaults_ToConsul(t *testing.T) { OutboundListenerPort: 1000, DialedDirectly: true, }, + AccessLogs: &capi.AccessLogsConfig{ + Enabled: true, + DisableListenerLogs: true, + Type: capi.FileLogSinkType, + Path: "/var/log/envoy.logs", + TextFormat: "ITS WORKING %START_TIME%", + }, Meta: map[string]string{ common.SourceKey: common.SourceValue, common.DatacenterKey: "datacenter", @@ -366,6 +394,72 @@ func TestProxyDefaults_Validate(t *testing.T) { }, "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.mode: Invalid value: \"transparent\": use the annotation `consul.hashicorp.com/transparent-proxy` to configure the Transparent Proxy Mode", }, + "accessLogs.type": { + &ProxyDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "global", + }, + Spec: ProxyDefaultsSpec{ + AccessLogs: &AccessLogs{ + Type: "foo", + }, + }, + }, + "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.accessLogs.type: Invalid value: \"foo\": invalid access log type (must be one of \"stdout\", \"stderr\", \"file\"", + }, + "accessLogs.path missing": { + &ProxyDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "global", + }, + Spec: ProxyDefaultsSpec{ + AccessLogs: &AccessLogs{ + Type: "file", + }, + }, + }, + "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.accessLogs.path: Invalid value: \"\": path must be specified when using file type access logs", + }, + "accessLogs.path for wrong type": { + &ProxyDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "global", + }, + Spec: ProxyDefaultsSpec{ + AccessLogs: &AccessLogs{ + Path: "/var/log/envoy.logs", + }, + }, + }, + "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.accessLogs.path: Invalid value: \"/var/log/envoy.logs\": path is only valid for file type access logs", + }, + "accessLogs.jsonFormat": { + &ProxyDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "global", + }, + Spec: ProxyDefaultsSpec{ + AccessLogs: &AccessLogs{ + JSONFormat: "{ \"start_time\": \"%START_TIME\"", // intentionally missing the closing brace + }, + }, + }, + "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.accessLogs.jsonFormat: Invalid value: \"{ \\\"start_time\\\": \\\"%START_TIME\\\"\": invalid access log json", + }, + "accessLogs.textFormat": { + &ProxyDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "global", + }, + Spec: ProxyDefaultsSpec{ + AccessLogs: &AccessLogs{ + JSONFormat: "{ \"start_time\": \"%START_TIME\" }", + TextFormat: "MY START TIME %START_TIME", + }, + }, + }, + "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.accessLogs.textFormat: Invalid value: \"MY START TIME %START_TIME\": cannot specify both access log jsonFormat and textFormat", + }, "multi-error": { &ProxyDefaults{ ObjectMeta: metav1.ObjectMeta{ @@ -386,10 +480,14 @@ func TestProxyDefaults_Validate(t *testing.T) { TransparentProxy: &TransparentProxy{ OutboundListenerPort: 1000, }, + AccessLogs: &AccessLogs{ + JSONFormat: "{ \"start_time\": \"%START_TIME\" }", + TextFormat: "MY START TIME %START_TIME", + }, Mode: proxyModeRef("transparent"), }, }, - "proxydefaults.consul.hashicorp.com \"global\" is invalid: [spec.meshGateway.mode: Invalid value: \"invalid-mode\": must be one of \"remote\", \"local\", \"none\", \"\", spec.transparentProxy.outboundListenerPort: Invalid value: 1000: use the annotation `consul.hashicorp.com/transparent-proxy-outbound-listener-port` to configure the Outbound Listener Port, spec.mode: Invalid value: \"transparent\": use the annotation `consul.hashicorp.com/transparent-proxy` to configure the Transparent Proxy Mode, spec.expose.paths[0].path: Invalid value: \"invalid-path\": must begin with a '/', spec.expose.paths[0].protocol: Invalid value: \"invalid-protocol\": must be one of \"http\", \"http2\"]", + "proxydefaults.consul.hashicorp.com \"global\" is invalid: [spec.meshGateway.mode: Invalid value: \"invalid-mode\": must be one of \"remote\", \"local\", \"none\", \"\", spec.transparentProxy.outboundListenerPort: Invalid value: 1000: use the annotation `consul.hashicorp.com/transparent-proxy-outbound-listener-port` to configure the Outbound Listener Port, spec.mode: Invalid value: \"transparent\": use the annotation `consul.hashicorp.com/transparent-proxy` to configure the Transparent Proxy Mode, spec.accessLogs.textFormat: Invalid value: \"MY START TIME %START_TIME\": cannot specify both access log jsonFormat and textFormat, spec.expose.paths[0].path: Invalid value: \"invalid-path\": must begin with a '/', spec.expose.paths[0].protocol: Invalid value: \"invalid-protocol\": must be one of \"http\", \"http2\"]", }, } for name, testCase := range cases { diff --git a/control-plane/api/v1alpha1/zz_generated.deepcopy.go b/control-plane/api/v1alpha1/zz_generated.deepcopy.go index a51bf63d0d..34c385329e 100644 --- a/control-plane/api/v1alpha1/zz_generated.deepcopy.go +++ b/control-plane/api/v1alpha1/zz_generated.deepcopy.go @@ -10,6 +10,21 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AccessLogs) DeepCopyInto(out *AccessLogs) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AccessLogs. +func (in *AccessLogs) DeepCopy() *AccessLogs { + if in == nil { + return nil + } + out := new(AccessLogs) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Condition) DeepCopyInto(out *Condition) { *out = *in @@ -1204,6 +1219,11 @@ func (in *ProxyDefaultsSpec) DeepCopyInto(out *ProxyDefaultsSpec) { } out.MeshGateway = in.MeshGateway in.Expose.DeepCopyInto(&out.Expose) + if in.AccessLogs != nil { + in, out := &in.AccessLogs, &out.AccessLogs + *out = new(AccessLogs) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyDefaultsSpec. diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml index 6b9628cd74..4a309ebbbc 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml @@ -50,6 +50,38 @@ spec: spec: description: ProxyDefaultsSpec defines the desired state of ProxyDefaults. properties: + accessLogs: + description: AccessLogs controls all envoy instances' access logging + configuration. + properties: + disableListenerLogs: + description: DisableListenerLogs turns off just listener logs + for connections rejected by Envoy because they don't have a + matching listener filter. + type: boolean + enabled: + description: Enabled turns on all access logging + type: boolean + jsonFormat: + description: 'JSONFormat is a JSON-formatted string of an Envoy + access log format dictionary. See for more info on formatting: + https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#format-dictionaries + Defining JSONFormat and TextFormat is invalid.' + type: string + path: + description: Path is the output file to write logs for file-type + logging + type: string + textFormat: + description: 'TextFormat is a representation of Envoy access logs + format. See for more info on formatting: https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#format-strings + Defining JSONFormat and TextFormat is invalid.' + type: string + type: + description: Type selects the output for logs one of "file", "stderr". + "stdout" + type: string + type: object config: description: Config is an arbitrary map of configuration values used by Connect proxies. Any values that your proxy allows can be configured diff --git a/control-plane/go.mod b/control-plane/go.mod index a20937c694..6c3e48d2ba 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -10,7 +10,7 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af65262de8 github.com/hashicorp/consul-server-connection-manager v0.1.0 - github.com/hashicorp/consul/api v1.18.0 + github.com/hashicorp/consul/api v1.10.1-0.20221220195433-629878a6879c github.com/hashicorp/consul/sdk v0.13.0 github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f github.com/hashicorp/go-hclog v1.2.2 diff --git a/control-plane/go.sum b/control-plane/go.sum index b5b77aa4b8..ef065d85b3 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -346,6 +346,8 @@ github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af6526 github.com/hashicorp/consul-server-connection-manager v0.1.0 h1:XCweGvMHzra88rYv2zxwwuUOjBUdcQmNKVrnQmt/muo= github.com/hashicorp/consul-server-connection-manager v0.1.0/go.mod h1:XVVlO+Yk7aiRpspiHZkrrFVn9BJIiOPnQIzqytPxGaU= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/api v1.10.1-0.20221220195433-629878a6879c h1:dIy67NF/J5gm0wdGxTA8dCFeQDz8TatuuMEzC7n3aDk= +github.com/hashicorp/consul/api v1.10.1-0.20221220195433-629878a6879c/go.mod h1:c1u8FzGHcavbEtRW/p1YditvfMgn4QsKNgz2rnCDF7c= github.com/hashicorp/consul/api v1.18.0 h1:R7PPNzTCeN6VuQNDwwhZWJvzCtGSrNpJqfb22h3yH9g= github.com/hashicorp/consul/api v1.18.0/go.mod h1:owRRGJ9M5xReDC5nfT8FTJrNAPbT4NM6p/k+d03q2v4= github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3usQMXZF6ziK3SzXU= From 7fa7336b6bd002efed5fea88990c3f6d2f62d94f Mon Sep 17 00:00:00 2001 From: David Yu Date: Thu, 22 Dec 2022 10:58:30 -0800 Subject: [PATCH 037/340] move PR 1804 to unreleased (#1812) --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79cf1931b1..a6e1a511c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ IMPROVEMENTS: * Add a `global.extraLabels` stanza to allow setting global Kubernetes labels for all components deployed by the `consul-k8s` Helm chart. [[GH-1778](https://github.com/hashicorp/consul-k8s/pull/1778)] * Add the `accessLogs` field to the `ProxyDefaults` CRD. [[GH-1816](https://github.com/hashicorp/consul-k8s/pull/1816)] +BUG FIXES: +* Control Plane + * Don't incorrectly diff intention config entries when upgrading from Consul pre-1.12 to 1.12+ [[GH-1804](https://github.com/hashicorp/consul-k8s/pull/1804)] + ## 1.0.2 (December 1, 2022) IMPROVEMENTS: @@ -15,8 +19,6 @@ BUG FIXES: * Helm: * Use the correct autogenerated cert for the API Gateway Controller when connecting to servers versus clients. [[GH-1753](https://github.com/hashicorp/consul-k8s/pull/1753)] * Don't mount the CA cert when `externalServers.useSystemRoots` is `true`. [[GH-1753](https://github.com/hashicorp/consul-k8s/pull/1753)] -* Control Plane - * Don't incorrectly diff intention config entries when upgrading from Consul pre-1.12 to 1.12+ [[GH-1804](https://github.com/hashicorp/consul-k8s/pull/1804)] ## 0.49.2 (December 1, 2022) From 3b7fa09f44ea229b44376e30811c59adbcebb229 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Thu, 12 Jan 2023 14:22:21 -0500 Subject: [PATCH 038/340] Create annotation to allows users to use proxy health check (#1824) --- CHANGELOG.md | 2 ++ .../connect-inject/constants/annotations_and_labels.go | 5 +++++ .../connect-inject/webhook/consul_dataplane_sidecar.go | 1 + 3 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6e1a511c5..d0596484f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ IMPROVEMENTS: * Helm: * Add a `global.extraLabels` stanza to allow setting global Kubernetes labels for all components deployed by the `consul-k8s` Helm chart. [[GH-1778](https://github.com/hashicorp/consul-k8s/pull/1778)] * Add the `accessLogs` field to the `ProxyDefaults` CRD. [[GH-1816](https://github.com/hashicorp/consul-k8s/pull/1816)] +* Control-Plane + * Add support for the annotation `consul.hashicorp.com/use-proxy-health-check`. [[GH-1824](https://github.com/hashicorp/consul-k8s/pull/1824)] BUG FIXES: * Control Plane diff --git a/control-plane/connect-inject/constants/annotations_and_labels.go b/control-plane/connect-inject/constants/annotations_and_labels.go index c3ba29ace2..637e028202 100644 --- a/control-plane/connect-inject/constants/annotations_and_labels.go +++ b/control-plane/connect-inject/constants/annotations_and_labels.go @@ -86,6 +86,11 @@ const ( // e.g. consul.hashicorp.com/service-meta-foo:bar. AnnotationMeta = "consul.hashicorp.com/service-meta-" + // AnnotationUseProxyHealthCheck creates a readiness listener on the sidecar proxy and + // queries this instead of the application health check for the status of the application. + // Enable this only if the application does not support health checks. + AnnotationUseProxyHealthCheck = "consul.hashicorp.com/use-proxy-health-check" + // annotations for sidecar proxy resource limits. AnnotationSidecarProxyCPULimit = "consul.hashicorp.com/sidecar-proxy-cpu-limit" AnnotationSidecarProxyCPURequest = "consul.hashicorp.com/sidecar-proxy-cpu-request" diff --git a/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go b/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go index 9d9c4af7d4..d0cd232c84 100644 --- a/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go +++ b/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go @@ -55,6 +55,7 @@ func (w *MeshWebhook) consulDataplaneSidecar(namespace corev1.Namespace, pod cor }, InitialDelaySeconds: 1, } + container := corev1.Container{ Name: containerName, Image: w.ImageConsulDataplane, From a5cc951825295cbf285b4b0d1bf37490087c3bb3 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Thu, 12 Jan 2023 12:58:59 -0800 Subject: [PATCH 039/340] Add envoyExtensions field to serviceDefaults and proxyDefaults CRDs (#1823) * updated consul api to the latest * added new ServiceDefault fields to the serviceDefaults CRD - Added fields to support EnvoyExtensions - Added missing BalanceInboundConnections field * added EnvoyExtensions to proxyDefaults CRD - most of these changes are parallel to what was done in serviceDefaults - proxyDefaults makes use of the envoyExtension(s) definition in serviceDefaults and also makes use of the validation/toConsul logic defined there * added tests for new fields - Added tests for proxyDefault - Added tests for serviceDefault - EnvoyExtension test cases should be basically the same for both CRDs * generate the manifests and generate deepcopy * updated the changelog * added CI test to catch bad terraform formatting * formatted terraform files * update contributing doc --- .github/workflows/test.yml | 14 ++ CHANGELOG.md | 2 + CONTRIBUTING.md | 17 +- Makefile | 10 + .../consul/templates/crd-proxydefaults.yaml | 16 ++ .../consul/templates/crd-servicedefaults.yaml | 36 ++- charts/consul/test/terraform/aks/variables.tf | 2 +- charts/consul/test/terraform/eks/main.tf | 4 +- charts/consul/test/terraform/eks/variables.tf | 2 +- charts/consul/test/terraform/gke/variables.tf | 2 +- .../test/terraform/openshift/variables.tf | 2 +- .../api/v1alpha1/proxydefaults_types.go | 7 +- .../api/v1alpha1/proxydefaults_types_test.go | 189 ++++++++++++++-- .../v1alpha1/proxydefaults_webhook_test.go | 2 +- .../api/v1alpha1/servicedefaults_types.go | 43 ++-- .../v1alpha1/servicedefaults_types_test.go | 205 ++++++++++++++++++ control-plane/api/v1alpha1/shared_types.go | 66 ++++++ .../api/v1alpha1/zz_generated.deepcopy.go | 55 +++++ .../scripts/terraformfmtcheck.sh | 14 ++ .../consul.hashicorp.com_proxydefaults.yaml | 16 ++ .../consul.hashicorp.com_servicedefaults.yaml | 36 ++- control-plane/go.mod | 2 +- control-plane/go.sum | 2 + 23 files changed, 679 insertions(+), 65 deletions(-) create mode 100755 control-plane/build-support/scripts/terraformfmtcheck.sh diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3bbaf5bfe7..3967d1c816 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,6 +10,20 @@ env: CONSUL_ENT_DOCKER_IMAGE: hashicorppreview/consul-enterprise:1.14-dev # Consul's enterprise version to use in tests jobs: + terraform-fmt-check: + name: "Terraform format check" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Setup Terraform + uses: hashicorp/setup-terraform@v2 + with: + terraform_version: TERRAFORM_VERSION + terraform_wrapper: false + - name: Run Terraform checks + run: | + make terraform-fmt-check TERRAFORM_DIR="${{ github.workspace }}" + get-go-version: name: "Determine Go toolchain version" runs-on: ubuntu-latest diff --git a/CHANGELOG.md b/CHANGELOG.md index d0596484f3..3599155056 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ IMPROVEMENTS: * Helm: * Add a `global.extraLabels` stanza to allow setting global Kubernetes labels for all components deployed by the `consul-k8s` Helm chart. [[GH-1778](https://github.com/hashicorp/consul-k8s/pull/1778)] * Add the `accessLogs` field to the `ProxyDefaults` CRD. [[GH-1816](https://github.com/hashicorp/consul-k8s/pull/1816)] + * Add the `envoyExtensions` field to the `ProxyDefaults` and `ServiceDefaults` CRD. [[GH-1823]](https://github.com/hashicorp/consul-k8s/pull/1823) + * Add the `balanceInboundConnections` field to the `ServiceDefaults` CRD. [[GH-1823]](https://github.com/hashicorp/consul-k8s/pull/1823) * Control-Plane * Add support for the annotation `consul.hashicorp.com/use-proxy-health-check`. [[GH-1824](https://github.com/hashicorp/consul-k8s/pull/1824)] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 327399b89c..61f65f21a3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -424,20 +424,27 @@ rebase the branch on main, fixing any conflicts along the way before the code ca manage your resource type. ### Testing A New CRD -1. Build a Docker image for consul-k8s via `make dev-docker` and tagging your image appropriately. Remember to CD into the `control-plane` directory! +1. Build a Docker image for consul-k8s via `make control-plane-dev-docker` and push to a docker repository: + ``` + docker tag consul-k8s-control-plane-dev /consul-k8s-control-plane-dev: + docker push /consul-k8s-control-plane-dev: + ``` 1. Install using the updated Helm repository, with a values like: ```yaml global: - imageK8S: ghcr.io/lkysow/consul-k8s-dev:nov26 + imageK8S: lkysow/consul-k8s-control-plane-dev:nov26 name: consul server: replicas: 1 bootstrapExpect: 1 - controller: + ui: + enabled: true + connectInject: enabled: true ``` -1. `kubectl apply` your sample CRD. -1. Check its synced status: +1. Create a sample CRD +1. Run `kubectl apply -f ` to apply your sample CRD. +1. Check its synced status (for example CRD called ingressgateway): ```bash kubectl get ingressgateway NAME SYNCED AGE diff --git a/Makefile b/Makefile index 43a8e16b0d..6ad59a2a91 100644 --- a/Makefile +++ b/Makefile @@ -75,6 +75,16 @@ kind-cni: kind create cluster --config=$(CURDIR)/acceptance/framework/environment/cni-kind/kind.config --name dc2 --image kindest/node:v1.23.6 make kind-cni-calico +# Perform a terraform fmt check but don't change anything +terraform-fmt-check: + @$(CURDIR)/control-plane/build-support/scripts/terraformfmtcheck.sh $(TERRAFORM_DIR) +.PHONY: terraform-fmt-check + +# Format all terraform files according to terraform fmt +terraform-fmt: + @terraform fmt -recursive +.PHONY: terraform-fmt + # ===========> CLI Targets diff --git a/charts/consul/templates/crd-proxydefaults.yaml b/charts/consul/templates/crd-proxydefaults.yaml index 1a63cfef36..749f2e4257 100644 --- a/charts/consul/templates/crd-proxydefaults.yaml +++ b/charts/consul/templates/crd-proxydefaults.yaml @@ -95,6 +95,22 @@ spec: globally here. Supports JSON config values. See https://www.consul.io/docs/connect/proxies/envoy#configuration-formatting type: object x-kubernetes-preserve-unknown-fields: true + envoyExtensions: + description: EnvoyExtensions are a list of extensions to modify Envoy + proxy configuration. + items: + description: EnvoyExtension has configuration for an extension that + patches Envoy resources. + properties: + arguments: + type: object + x-kubernetes-preserve-unknown-fields: true + name: + type: string + required: + type: boolean + type: object + type: array expose: description: Expose controls the default expose path configuration for Envoy. diff --git a/charts/consul/templates/crd-servicedefaults.yaml b/charts/consul/templates/crd-servicedefaults.yaml index f1ebdc8d2c..128884c454 100644 --- a/charts/consul/templates/crd-servicedefaults.yaml +++ b/charts/consul/templates/crd-servicedefaults.yaml @@ -57,6 +57,12 @@ spec: spec: description: ServiceDefaultsSpec defines the desired state of ServiceDefaults. properties: + balanceInboundConnections: + description: BalanceInboundConnections sets the strategy for allocating + inbound connections to the service across proxy threads. The only + supported value is exact_balance. By default, no connection balancing + is used. Refer to the Envoy Connection Balance config for details. + type: string destination: description: Destination is an address(es)/port combination that represents an endpoint outside the mesh. This is only valid when the mesh is @@ -76,6 +82,22 @@ spec: format: int32 type: integer type: object + envoyExtensions: + description: EnvoyExtensions are a list of extensions to modify Envoy + proxy configuration. + items: + description: EnvoyExtension has configuration for an extension that + patches Envoy resources. + properties: + arguments: + type: object + x-kubernetes-preserve-unknown-fields: true + name: + type: string + required: + type: boolean + type: object + type: array expose: description: Expose controls the default expose path configuration for Envoy. @@ -114,15 +136,15 @@ spec: with an external system. type: string localConnectTimeoutMs: - description: The number of milliseconds allowed to make connections - to the local application instance before timing out. Defaults to - 5000. + description: LocalConnectTimeoutMs is the number of milliseconds allowed + to make connections to the local application instance before timing + out. Defaults to 5000. type: integer localRequestTimeoutMs: - description: In milliseconds, the timeout for HTTP requests to the - local application instance. Applies to HTTP-based protocols only. - If not specified, inherits the Envoy default for route timeouts - (15s). + description: LocalRequestTimeoutMs is the timeout for HTTP requests + to the local application instance in milliseconds. Applies to HTTP-based + protocols only. If not specified, inherits the Envoy default for + route timeouts (15s). type: integer maxInboundConnections: description: MaxInboundConnections is the maximum number of concurrent diff --git a/charts/consul/test/terraform/aks/variables.tf b/charts/consul/test/terraform/aks/variables.tf index 1651ce7b09..bb9dbef537 100644 --- a/charts/consul/test/terraform/aks/variables.tf +++ b/charts/consul/test/terraform/aks/variables.tf @@ -27,7 +27,7 @@ variable "cluster_count" { } variable "tags" { - type = map + type = map(any) default = {} description = "Tags to attach to the created resources." } diff --git a/charts/consul/test/terraform/eks/main.tf b/charts/consul/test/terraform/eks/main.tf index c466334315..9ccc2cdd2b 100644 --- a/charts/consul/test/terraform/eks/main.tf +++ b/charts/consul/test/terraform/eks/main.tf @@ -53,8 +53,8 @@ module "vpc" { module "eks" { count = var.cluster_count - source = "terraform-aws-modules/eks/aws" - version = "17.24.0" + source = "terraform-aws-modules/eks/aws" + version = "17.24.0" kubeconfig_api_version = "client.authentication.k8s.io/v1beta1" cluster_name = "consul-k8s-${random_id.suffix[count.index].dec}" diff --git a/charts/consul/test/terraform/eks/variables.tf b/charts/consul/test/terraform/eks/variables.tf index 361a5f5c45..05f383168b 100644 --- a/charts/consul/test/terraform/eks/variables.tf +++ b/charts/consul/test/terraform/eks/variables.tf @@ -21,7 +21,7 @@ variable "role_arn" { } variable "tags" { - type = map + type = map(any) default = {} description = "Tags to attach to the created resources." } diff --git a/charts/consul/test/terraform/gke/variables.tf b/charts/consul/test/terraform/gke/variables.tf index 04d214cedb..ef4a429116 100644 --- a/charts/consul/test/terraform/gke/variables.tf +++ b/charts/consul/test/terraform/gke/variables.tf @@ -30,7 +30,7 @@ variable "cluster_count" { } variable "labels" { - type = map + type = map(any) default = {} description = "Labels to attach to the created resources." } diff --git a/charts/consul/test/terraform/openshift/variables.tf b/charts/consul/test/terraform/openshift/variables.tf index f2479e3229..1df518f8ed 100644 --- a/charts/consul/test/terraform/openshift/variables.tf +++ b/charts/consul/test/terraform/openshift/variables.tf @@ -9,7 +9,7 @@ variable "cluster_count" { } variable "tags" { - type = map + type = map(any) default = {} description = "Tags to attach to the created resources." } diff --git a/control-plane/api/v1alpha1/proxydefaults_types.go b/control-plane/api/v1alpha1/proxydefaults_types.go index 82fcde3687..543d8f7ae4 100644 --- a/control-plane/api/v1alpha1/proxydefaults_types.go +++ b/control-plane/api/v1alpha1/proxydefaults_types.go @@ -77,6 +77,8 @@ type ProxyDefaultsSpec struct { Expose Expose `json:"expose,omitempty"` // AccessLogs controls all envoy instances' access logging configuration. AccessLogs *AccessLogs `json:"accessLogs,omitempty"` + // EnvoyExtensions are a list of extensions to modify Envoy proxy configuration. + EnvoyExtensions EnvoyExtensions `json:"envoyExtensions,omitempty"` } func (in *ProxyDefaults) GetObjectMeta() metav1.ObjectMeta { @@ -168,6 +170,7 @@ func (in *ProxyDefaults) ToConsul(datacenter string) capi.ConfigEntry { Config: consulConfig, TransparentProxy: in.Spec.TransparentProxy.toConsul(), AccessLogs: in.Spec.AccessLogs.toConsul(), + EnvoyExtensions: in.Spec.EnvoyExtensions.toConsul(), Meta: meta(datacenter), } } @@ -202,6 +205,8 @@ func (in *ProxyDefaults) Validate(_ common.ConsulMeta) error { allErrs = append(allErrs, err) } allErrs = append(allErrs, in.Spec.Expose.validate(path.Child("expose"))...) + allErrs = append(allErrs, in.Spec.EnvoyExtensions.validate(path.Child("envoyExtensions"))...) + if len(allErrs) > 0 { return apierrors.NewInvalid( schema.GroupKind{Group: ConsulHashicorpGroup, Kind: ProxyDefaultsKubeKind}, @@ -239,7 +244,7 @@ func (in *ProxyDefaults) validateConfig(path *field.Path) *field.Error { } var outConfig map[string]interface{} if err := json.Unmarshal(in.Spec.Config, &outConfig); err != nil { - return field.Invalid(path, in.Spec.Config, fmt.Sprintf(`must be valid map value: %s`, err)) + return field.Invalid(path, string(in.Spec.Config), fmt.Sprintf(`must be valid map value: %s`, err)) } return nil } diff --git a/control-plane/api/v1alpha1/proxydefaults_types_test.go b/control-plane/api/v1alpha1/proxydefaults_types_test.go index 794906b05e..225068c136 100644 --- a/control-plane/api/v1alpha1/proxydefaults_types_test.go +++ b/control-plane/api/v1alpha1/proxydefaults_types_test.go @@ -78,6 +78,18 @@ func TestProxyDefaults_MatchesConsul(t *testing.T) { Path: "/var/log/envoy.logs", TextFormat: "ITS WORKING %START_TIME%", }, + EnvoyExtensions: EnvoyExtensions{ + EnvoyExtension{ + Name: "aws_request_signing", + Arguments: json.RawMessage(`{"AWSServiceName": "s3", "Region": "us-west-2"}`), + Required: false, + }, + EnvoyExtension{ + Name: "zipkin", + Arguments: json.RawMessage(`{"ClusterName": "zipkin_cluster", "Port": "9411", "CollectorEndpoint":"/api/v2/spans"}`), + Required: true, + }, + }, }, }, Theirs: &capi.ProxyConfigEntry{ @@ -117,6 +129,25 @@ func TestProxyDefaults_MatchesConsul(t *testing.T) { Path: "/var/log/envoy.logs", TextFormat: "ITS WORKING %START_TIME%", }, + EnvoyExtensions: []capi.EnvoyExtension{ + { + Name: "aws_request_signing", + Arguments: map[string]interface{}{ + "AWSServiceName": "s3", + "Region": "us-west-2", + }, + Required: false, + }, + { + Name: "zipkin", + Arguments: map[string]interface{}{ + "ClusterName": "zipkin_cluster", + "Port": "9411", + "CollectorEndpoint": "/api/v2/spans", + }, + Required: true, + }, + }, }, Matches: true, }, @@ -257,6 +288,18 @@ func TestProxyDefaults_ToConsul(t *testing.T) { Path: "/var/log/envoy.logs", TextFormat: "ITS WORKING %START_TIME%", }, + EnvoyExtensions: EnvoyExtensions{ + EnvoyExtension{ + Name: "aws_request_signing", + Arguments: json.RawMessage(`{"AWSServiceName": "s3", "Region": "us-west-2"}`), + Required: false, + }, + EnvoyExtension{ + Name: "zipkin", + Arguments: json.RawMessage(`{"ClusterName": "zipkin_cluster", "Port": "9411", "CollectorEndpoint":"/api/v2/spans"}`), + Required: true, + }, + }, }, }, Exp: &capi.ProxyConfigEntry{ @@ -297,6 +340,25 @@ func TestProxyDefaults_ToConsul(t *testing.T) { Path: "/var/log/envoy.logs", TextFormat: "ITS WORKING %START_TIME%", }, + EnvoyExtensions: []capi.EnvoyExtension{ + { + Name: "aws_request_signing", + Arguments: map[string]interface{}{ + "AWSServiceName": "s3", + "Region": "us-west-2", + }, + Required: false, + }, + { + Name: "zipkin", + Arguments: map[string]interface{}{ + "ClusterName": "zipkin_cluster", + "Port": "9411", + "CollectorEndpoint": "/api/v2/spans", + }, + Required: true, + }, + }, Meta: map[string]string{ common.SourceKey: common.SourceValue, common.DatacenterKey: "datacenter", @@ -321,8 +383,30 @@ func TestProxyDefaults_Validate(t *testing.T) { input *ProxyDefaults expectedErrMsg string }{ + "valid envoyExtension": { + input: &ProxyDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "global", + }, + Spec: ProxyDefaultsSpec{ + EnvoyExtensions: EnvoyExtensions{ + EnvoyExtension{ + Name: "aws_request_signing", + Arguments: json.RawMessage(`{"AWSServiceName": "s3", "Region": "us-west-2"}`), + Required: false, + }, + EnvoyExtension{ + Name: "zipkin", + Arguments: json.RawMessage(`{"ClusterName": "zipkin_cluster", "Port": "9411", "CollectorEndpoint":"/api/v2/spans"}`), + Required: true, + }, + }, + }, + }, + expectedErrMsg: "", + }, "meshgateway.mode": { - &ProxyDefaults{ + input: &ProxyDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "global", }, @@ -332,10 +416,10 @@ func TestProxyDefaults_Validate(t *testing.T) { }, }, }, - `proxydefaults.consul.hashicorp.com "global" is invalid: spec.meshGateway.mode: Invalid value: "foobar": must be one of "remote", "local", "none", ""`, + expectedErrMsg: `proxydefaults.consul.hashicorp.com "global" is invalid: spec.meshGateway.mode: Invalid value: "foobar": must be one of "remote", "local", "none", ""`, }, "expose.paths[].protocol": { - &ProxyDefaults{ + input: &ProxyDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "global", }, @@ -350,10 +434,10 @@ func TestProxyDefaults_Validate(t *testing.T) { }, }, }, - `proxydefaults.consul.hashicorp.com "global" is invalid: spec.expose.paths[0].protocol: Invalid value: "invalid-protocol": must be one of "http", "http2"`, + expectedErrMsg: `proxydefaults.consul.hashicorp.com "global" is invalid: spec.expose.paths[0].protocol: Invalid value: "invalid-protocol": must be one of "http", "http2"`, }, "expose.paths[].path": { - &ProxyDefaults{ + input: &ProxyDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "global", }, @@ -368,10 +452,10 @@ func TestProxyDefaults_Validate(t *testing.T) { }, }, }, - `proxydefaults.consul.hashicorp.com "global" is invalid: spec.expose.paths[0].path: Invalid value: "invalid-path": must begin with a '/'`, + expectedErrMsg: `proxydefaults.consul.hashicorp.com "global" is invalid: spec.expose.paths[0].path: Invalid value: "invalid-path": must begin with a '/'`, }, "transparentProxy.outboundListenerPort": { - &ProxyDefaults{ + input: &ProxyDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "global", }, @@ -381,10 +465,10 @@ func TestProxyDefaults_Validate(t *testing.T) { }, }, }, - "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.transparentProxy.outboundListenerPort: Invalid value: 1000: use the annotation `consul.hashicorp.com/transparent-proxy-outbound-listener-port` to configure the Outbound Listener Port", + expectedErrMsg: "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.transparentProxy.outboundListenerPort: Invalid value: 1000: use the annotation `consul.hashicorp.com/transparent-proxy-outbound-listener-port` to configure the Outbound Listener Port", }, "mode": { - &ProxyDefaults{ + input: &ProxyDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "global", }, @@ -392,10 +476,10 @@ func TestProxyDefaults_Validate(t *testing.T) { Mode: proxyModeRef("transparent"), }, }, - "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.mode: Invalid value: \"transparent\": use the annotation `consul.hashicorp.com/transparent-proxy` to configure the Transparent Proxy Mode", + expectedErrMsg: "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.mode: Invalid value: \"transparent\": use the annotation `consul.hashicorp.com/transparent-proxy` to configure the Transparent Proxy Mode", }, "accessLogs.type": { - &ProxyDefaults{ + input: &ProxyDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "global", }, @@ -405,10 +489,10 @@ func TestProxyDefaults_Validate(t *testing.T) { }, }, }, - "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.accessLogs.type: Invalid value: \"foo\": invalid access log type (must be one of \"stdout\", \"stderr\", \"file\"", + expectedErrMsg: "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.accessLogs.type: Invalid value: \"foo\": invalid access log type (must be one of \"stdout\", \"stderr\", \"file\"", }, "accessLogs.path missing": { - &ProxyDefaults{ + input: &ProxyDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "global", }, @@ -418,10 +502,10 @@ func TestProxyDefaults_Validate(t *testing.T) { }, }, }, - "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.accessLogs.path: Invalid value: \"\": path must be specified when using file type access logs", + expectedErrMsg: "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.accessLogs.path: Invalid value: \"\": path must be specified when using file type access logs", }, "accessLogs.path for wrong type": { - &ProxyDefaults{ + input: &ProxyDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "global", }, @@ -431,10 +515,10 @@ func TestProxyDefaults_Validate(t *testing.T) { }, }, }, - "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.accessLogs.path: Invalid value: \"/var/log/envoy.logs\": path is only valid for file type access logs", + expectedErrMsg: "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.accessLogs.path: Invalid value: \"/var/log/envoy.logs\": path is only valid for file type access logs", }, "accessLogs.jsonFormat": { - &ProxyDefaults{ + input: &ProxyDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "global", }, @@ -444,10 +528,10 @@ func TestProxyDefaults_Validate(t *testing.T) { }, }, }, - "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.accessLogs.jsonFormat: Invalid value: \"{ \\\"start_time\\\": \\\"%START_TIME\\\"\": invalid access log json", + expectedErrMsg: "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.accessLogs.jsonFormat: Invalid value: \"{ \\\"start_time\\\": \\\"%START_TIME\\\"\": invalid access log json", }, "accessLogs.textFormat": { - &ProxyDefaults{ + input: &ProxyDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "global", }, @@ -458,10 +542,71 @@ func TestProxyDefaults_Validate(t *testing.T) { }, }, }, - "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.accessLogs.textFormat: Invalid value: \"MY START TIME %START_TIME\": cannot specify both access log jsonFormat and textFormat", + expectedErrMsg: "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.accessLogs.textFormat: Invalid value: \"MY START TIME %START_TIME\": cannot specify both access log jsonFormat and textFormat", + }, + "envoyExtension.arguments single empty": { + input: &ProxyDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "global", + }, + Spec: ProxyDefaultsSpec{ + EnvoyExtensions: EnvoyExtensions{ + EnvoyExtension{ + Name: "aws_request_signing", + Arguments: json.RawMessage(`{"AWSServiceName": "s3", "Region": "us-west-2"}`), + Required: false, + }, + EnvoyExtension{ + Name: "zipkin", + Arguments: nil, + Required: true, + }, + }, + }, + }, + expectedErrMsg: `proxydefaults.consul.hashicorp.com "global" is invalid: spec.envoyExtensions.envoyExtension[1].arguments: Required value: arguments must be defined`, + }, + "envoyExtension.arguments multi empty": { + input: &ProxyDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "global", + }, + Spec: ProxyDefaultsSpec{ + EnvoyExtensions: EnvoyExtensions{ + EnvoyExtension{ + Name: "aws_request_signing", + Arguments: nil, + Required: false, + }, + EnvoyExtension{ + Name: "zipkin", + Arguments: nil, + Required: true, + }, + }, + }, + }, + expectedErrMsg: `proxydefaults.consul.hashicorp.com "global" is invalid: [spec.envoyExtensions.envoyExtension[0].arguments: Required value: arguments must be defined, spec.envoyExtensions.envoyExtension[1].arguments: Required value: arguments must be defined]`, + }, + "envoyExtension.arguments invalid json": { + input: &ProxyDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "global", + }, + Spec: ProxyDefaultsSpec{ + EnvoyExtensions: EnvoyExtensions{ + EnvoyExtension{ + Name: "aws_request_signing", + Arguments: json.RawMessage(`{"SOME_INVALID_JSON"}`), + Required: false, + }, + }, + }, + }, + expectedErrMsg: `proxydefaults.consul.hashicorp.com "global" is invalid: spec.envoyExtensions.envoyExtension[0].arguments: Invalid value: "{\"SOME_INVALID_JSON\"}": must be valid map value: invalid character '}' after object key`, }, "multi-error": { - &ProxyDefaults{ + input: &ProxyDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "global", }, @@ -487,7 +632,7 @@ func TestProxyDefaults_Validate(t *testing.T) { Mode: proxyModeRef("transparent"), }, }, - "proxydefaults.consul.hashicorp.com \"global\" is invalid: [spec.meshGateway.mode: Invalid value: \"invalid-mode\": must be one of \"remote\", \"local\", \"none\", \"\", spec.transparentProxy.outboundListenerPort: Invalid value: 1000: use the annotation `consul.hashicorp.com/transparent-proxy-outbound-listener-port` to configure the Outbound Listener Port, spec.mode: Invalid value: \"transparent\": use the annotation `consul.hashicorp.com/transparent-proxy` to configure the Transparent Proxy Mode, spec.accessLogs.textFormat: Invalid value: \"MY START TIME %START_TIME\": cannot specify both access log jsonFormat and textFormat, spec.expose.paths[0].path: Invalid value: \"invalid-path\": must begin with a '/', spec.expose.paths[0].protocol: Invalid value: \"invalid-protocol\": must be one of \"http\", \"http2\"]", + expectedErrMsg: "proxydefaults.consul.hashicorp.com \"global\" is invalid: [spec.meshGateway.mode: Invalid value: \"invalid-mode\": must be one of \"remote\", \"local\", \"none\", \"\", spec.transparentProxy.outboundListenerPort: Invalid value: 1000: use the annotation `consul.hashicorp.com/transparent-proxy-outbound-listener-port` to configure the Outbound Listener Port, spec.mode: Invalid value: \"transparent\": use the annotation `consul.hashicorp.com/transparent-proxy` to configure the Transparent Proxy Mode, spec.accessLogs.textFormat: Invalid value: \"MY START TIME %START_TIME\": cannot specify both access log jsonFormat and textFormat, spec.expose.paths[0].path: Invalid value: \"invalid-path\": must begin with a '/', spec.expose.paths[0].protocol: Invalid value: \"invalid-protocol\": must be one of \"http\", \"http2\"]", }, } for name, testCase := range cases { diff --git a/control-plane/api/v1alpha1/proxydefaults_webhook_test.go b/control-plane/api/v1alpha1/proxydefaults_webhook_test.go index cc36d994dd..3136540089 100644 --- a/control-plane/api/v1alpha1/proxydefaults_webhook_test.go +++ b/control-plane/api/v1alpha1/proxydefaults_webhook_test.go @@ -46,7 +46,7 @@ func TestValidateProxyDefault(t *testing.T) { }, expAllow: false, // This error message is because the value "1" is valid JSON but is an invalid map - expErrMessage: "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.config: Invalid value: json.RawMessage{0x31}: must be valid map value: json: cannot unmarshal number into Go value of type map[string]interface {}", + expErrMessage: "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.config: Invalid value: \"1\": must be valid map value: json: cannot unmarshal number into Go value of type map[string]interface {}", }, "proxy default exists": { existingResources: []runtime.Object{&ProxyDefaults{ diff --git a/control-plane/api/v1alpha1/servicedefaults_types.go b/control-plane/api/v1alpha1/servicedefaults_types.go index 0a4020e010..2682f6a28a 100644 --- a/control-plane/api/v1alpha1/servicedefaults_types.go +++ b/control-plane/api/v1alpha1/servicedefaults_types.go @@ -88,13 +88,19 @@ type ServiceDefaultsSpec struct { // MaxInboundConnections is the maximum number of concurrent inbound connections to // each service instance. Defaults to 0 (using consul's default) if not set. MaxInboundConnections int `json:"maxInboundConnections,omitempty"` - // The number of milliseconds allowed to make connections to the local application + // LocalConnectTimeoutMs is the number of milliseconds allowed to make connections to the local application // instance before timing out. Defaults to 5000. LocalConnectTimeoutMs int `json:"localConnectTimeoutMs,omitempty"` - // In milliseconds, the timeout for HTTP requests to the local application instance. + // LocalRequestTimeoutMs is the timeout for HTTP requests to the local application instance in milliseconds. // Applies to HTTP-based protocols only. If not specified, inherits the Envoy default for // route timeouts (15s). LocalRequestTimeoutMs int `json:"localRequestTimeoutMs,omitempty"` + // BalanceInboundConnections sets the strategy for allocating inbound connections to the service across + // proxy threads. The only supported value is exact_balance. By default, no connection balancing is used. + // Refer to the Envoy Connection Balance config for details. + BalanceInboundConnections string `json:"balanceInboundConnections,omitempty"` + // EnvoyExtensions are a list of extensions to modify Envoy proxy configuration. + EnvoyExtensions EnvoyExtensions `json:"envoyExtensions,omitempty"` } type Upstreams struct { @@ -260,19 +266,21 @@ func (in *ServiceDefaults) SyncedConditionStatus() corev1.ConditionStatus { // ToConsul converts the entry into it's Consul equivalent struct. func (in *ServiceDefaults) ToConsul(datacenter string) capi.ConfigEntry { return &capi.ServiceConfigEntry{ - Kind: in.ConsulKind(), - Name: in.ConsulName(), - Protocol: in.Spec.Protocol, - MeshGateway: in.Spec.MeshGateway.toConsul(), - Expose: in.Spec.Expose.toConsul(), - ExternalSNI: in.Spec.ExternalSNI, - TransparentProxy: in.Spec.TransparentProxy.toConsul(), - UpstreamConfig: in.Spec.UpstreamConfig.toConsul(), - Destination: in.Spec.Destination.toConsul(), - Meta: meta(datacenter), - MaxInboundConnections: in.Spec.MaxInboundConnections, - LocalConnectTimeoutMs: in.Spec.LocalConnectTimeoutMs, - LocalRequestTimeoutMs: in.Spec.LocalRequestTimeoutMs, + Kind: in.ConsulKind(), + Name: in.ConsulName(), + Protocol: in.Spec.Protocol, + MeshGateway: in.Spec.MeshGateway.toConsul(), + Expose: in.Spec.Expose.toConsul(), + ExternalSNI: in.Spec.ExternalSNI, + TransparentProxy: in.Spec.TransparentProxy.toConsul(), + UpstreamConfig: in.Spec.UpstreamConfig.toConsul(), + Destination: in.Spec.Destination.toConsul(), + Meta: meta(datacenter), + MaxInboundConnections: in.Spec.MaxInboundConnections, + LocalConnectTimeoutMs: in.Spec.LocalConnectTimeoutMs, + LocalRequestTimeoutMs: in.Spec.LocalRequestTimeoutMs, + BalanceInboundConnections: in.Spec.BalanceInboundConnections, + EnvoyExtensions: in.Spec.EnvoyExtensions.toConsul(), } } @@ -311,8 +319,13 @@ func (in *ServiceDefaults) Validate(consulMeta common.ConsulMeta) error { allErrs = append(allErrs, field.Invalid(path.Child("localRequestTimeoutMs"), in.Spec.LocalRequestTimeoutMs, "LocalRequestTimeoutMs must be > 0")) } + if in.Spec.BalanceInboundConnections != "" && in.Spec.BalanceInboundConnections != "exact_balance" { + allErrs = append(allErrs, field.Invalid(path.Child("balanceInboundConnections"), in.Spec.BalanceInboundConnections, "BalanceInboundConnections must be an empty string or exact_balance")) + } + allErrs = append(allErrs, in.Spec.UpstreamConfig.validate(path.Child("upstreamConfig"), consulMeta.PartitionsEnabled)...) allErrs = append(allErrs, in.Spec.Expose.validate(path.Child("expose"))...) + allErrs = append(allErrs, in.Spec.EnvoyExtensions.validate(path.Child("envoyExtensions"))...) if len(allErrs) > 0 { return apierrors.NewInvalid( diff --git a/control-plane/api/v1alpha1/servicedefaults_types_test.go b/control-plane/api/v1alpha1/servicedefaults_types_test.go index e7fdae2575..fb29cf15cc 100644 --- a/control-plane/api/v1alpha1/servicedefaults_types_test.go +++ b/control-plane/api/v1alpha1/servicedefaults_types_test.go @@ -1,6 +1,7 @@ package v1alpha1 import ( + "encoding/json" "testing" "time" @@ -141,6 +142,19 @@ func TestServiceDefaults_ToConsul(t *testing.T) { }, }, }, + BalanceInboundConnections: "exact_balance", + EnvoyExtensions: EnvoyExtensions{ + EnvoyExtension{ + Name: "aws_request_signing", + Arguments: json.RawMessage(`{"AWSServiceName": "s3", "Region": "us-west-2"}`), + Required: false, + }, + EnvoyExtension{ + Name: "zipkin", + Arguments: json.RawMessage(`{"ClusterName": "zipkin_cluster", "Port": "9411", "CollectorEndpoint":"/api/v2/spans"}`), + Required: true, + }, + }, Destination: &ServiceDefaultsDestination{ Addresses: []string{"api.google.com"}, Port: 443, @@ -249,6 +263,26 @@ func TestServiceDefaults_ToConsul(t *testing.T) { }, }, }, + BalanceInboundConnections: "exact_balance", + EnvoyExtensions: []capi.EnvoyExtension{ + { + Name: "aws_request_signing", + Arguments: map[string]interface{}{ + "AWSServiceName": "s3", + "Region": "us-west-2", + }, + Required: false, + }, + { + Name: "zipkin", + Arguments: map[string]interface{}{ + "ClusterName": "zipkin_cluster", + "Port": "9411", + "CollectorEndpoint": "/api/v2/spans", + }, + Required: true, + }, + }, Destination: &capi.DestinationConfig{ Addresses: []string{"api.google.com"}, Port: 443, @@ -402,6 +436,19 @@ func TestServiceDefaults_MatchesConsul(t *testing.T) { }, }, }, + BalanceInboundConnections: "exact_balance", + EnvoyExtensions: EnvoyExtensions{ + EnvoyExtension{ + Name: "aws_request_signing", + Arguments: json.RawMessage(`{"AWSServiceName": "s3", "Region": "us-west-2"}`), + Required: false, + }, + EnvoyExtension{ + Name: "zipkin", + Arguments: json.RawMessage(`{"ClusterName": "zipkin_cluster", "Port": "9411", "CollectorEndpoint":"/api/v2/spans"}`), + Required: true, + }, + }, Destination: &ServiceDefaultsDestination{ Addresses: []string{"api.google.com"}, Port: 443, @@ -503,6 +550,26 @@ func TestServiceDefaults_MatchesConsul(t *testing.T) { }, }, }, + BalanceInboundConnections: "exact_balance", + EnvoyExtensions: []capi.EnvoyExtension{ + { + Name: "aws_request_signing", + Arguments: map[string]interface{}{ + "AWSServiceName": "s3", + "Region": "us-west-2", + }, + Required: false, + }, + { + Name: "zipkin", + Arguments: map[string]interface{}{ + "ClusterName": "zipkin_cluster", + "Port": "9411", + "CollectorEndpoint": "/api/v2/spans", + }, + Required: true, + }, + }, Destination: &capi.DestinationConfig{ Addresses: []string{"api.google.com"}, Port: 443, @@ -638,6 +705,39 @@ func TestServiceDefaults_Validate(t *testing.T) { }, expectedErrMsg: "", }, + "valid - balanceInboundConnections": { + input: &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-service", + }, + Spec: ServiceDefaultsSpec{ + BalanceInboundConnections: "exact_balance", + }, + }, + expectedErrMsg: "", + }, + "valid - envoyExtension": { + input: &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-service", + }, + Spec: ServiceDefaultsSpec{ + EnvoyExtensions: EnvoyExtensions{ + EnvoyExtension{ + Name: "aws_request_signing", + Arguments: json.RawMessage(`{"AWSServiceName": "s3", "Region": "us-west-2"}`), + Required: false, + }, + EnvoyExtension{ + Name: "zipkin", + Arguments: json.RawMessage(`{"ClusterName": "zipkin_cluster", "Port": "9411", "CollectorEndpoint":"/api/v2/spans"}`), + Required: true, + }, + }, + }, + }, + expectedErrMsg: "", + }, "protocol": { input: &ServiceDefaults{ ObjectMeta: metav1.ObjectMeta{ @@ -915,6 +1015,111 @@ func TestServiceDefaults_Validate(t *testing.T) { }, expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.destination.port: Invalid value: 0x0: invalid port number`, }, + "MaxInboundConnections (invalid value)": { + input: &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-service", + }, + Spec: ServiceDefaultsSpec{ + MaxInboundConnections: -1, + }, + }, + expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.maxinboundconnections: Invalid value: -1: MaxInboundConnections must be > 0`, + }, + "LocalConnectTimeoutMs (invalid value)": { + input: &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-service", + }, + Spec: ServiceDefaultsSpec{ + LocalConnectTimeoutMs: -1, + }, + }, + expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.localConnectTimeoutMs: Invalid value: -1: LocalConnectTimeoutMs must be > 0`, + }, + "LocalRequestTimeoutMs (invalid value)": { + input: &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-service", + }, + Spec: ServiceDefaultsSpec{ + LocalRequestTimeoutMs: -1, + }, + }, + expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.localRequestTimeoutMs: Invalid value: -1: LocalRequestTimeoutMs must be > 0`, + }, + "balanceInboundConnections (invalid value)": { + input: &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-service", + }, + Spec: ServiceDefaultsSpec{ + BalanceInboundConnections: "not_exact_balance", + }, + }, + expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.balanceInboundConnections: Invalid value: "not_exact_balance": BalanceInboundConnections must be an empty string or exact_balance`, + }, + "envoyExtension.arguments (single empty)": { + input: &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-service", + }, + Spec: ServiceDefaultsSpec{ + EnvoyExtensions: EnvoyExtensions{ + EnvoyExtension{ + Name: "aws_request_signing", + Arguments: json.RawMessage(`{"AWSServiceName": "s3", "Region": "us-west-2"}`), + Required: false, + }, + EnvoyExtension{ + Name: "zipkin", + Arguments: nil, + Required: true, + }, + }, + }, + }, + expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.envoyExtensions.envoyExtension[1].arguments: Required value: arguments must be defined`, + }, + "envoyExtension.arguments (multi empty)": { + input: &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-service", + }, + Spec: ServiceDefaultsSpec{ + EnvoyExtensions: EnvoyExtensions{ + EnvoyExtension{ + Name: "aws_request_signing", + Arguments: nil, + Required: false, + }, + EnvoyExtension{ + Name: "aws_request_signing", + Arguments: nil, + Required: false, + }, + }, + }, + }, + expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: [spec.envoyExtensions.envoyExtension[0].arguments: Required value: arguments must be defined, spec.envoyExtensions.envoyExtension[1].arguments: Required value: arguments must be defined]`, + }, + "envoyExtension.arguments (invalid json)": { + input: &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-service", + }, + Spec: ServiceDefaultsSpec{ + EnvoyExtensions: EnvoyExtensions{ + EnvoyExtension{ + Name: "aws_request_signing", + Arguments: json.RawMessage(`{"SOME_INVALID_JSON"}`), + Required: false, + }, + }, + }, + }, + expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.envoyExtensions.envoyExtension[0].arguments: Invalid value: "{\"SOME_INVALID_JSON\"}": must be valid map value: invalid character '}' after object key`, + }, } for name, testCase := range cases { diff --git a/control-plane/api/v1alpha1/shared_types.go b/control-plane/api/v1alpha1/shared_types.go index 9b884cf476..edcaba5a46 100644 --- a/control-plane/api/v1alpha1/shared_types.go +++ b/control-plane/api/v1alpha1/shared_types.go @@ -1,6 +1,7 @@ package v1alpha1 import ( + "encoding/json" "fmt" "strings" @@ -79,6 +80,19 @@ type HTTPHeaderModifiers struct { Remove []string `json:"remove,omitempty"` } +// EnvoyExtension has configuration for an extension that patches Envoy resources. +type EnvoyExtension struct { + Name string `json:"name,omitempty"` + Required bool `json:"required,omitempty"` + // +kubebuilder:validation:Type=object + // +kubebuilder:validation:Schemaless + // +kubebuilder:pruning:PreserveUnknownFields + Arguments json.RawMessage `json:"arguments,omitempty"` +} + +// EnvoyExtensions represents a list of the EnvoyExtension configuration. +type EnvoyExtensions []EnvoyExtension + func (in MeshGateway) toConsul() capi.MeshGatewayConfig { mode := capi.MeshGatewayMode(in.Mode) switch mode { @@ -176,6 +190,58 @@ func (in *HTTPHeaderModifiers) toConsul() *capi.HTTPHeaderModifiers { } } +func (in EnvoyExtensions) toConsul() []capi.EnvoyExtension { + if in == nil { + return nil + } + + outConfig := make([]capi.EnvoyExtension, 0) + + for _, e := range in { + consulExtension := capi.EnvoyExtension{ + Name: e.Name, + Required: e.Required, + } + + // We already validate that arguments is present + var args map[string]interface{} + _ = json.Unmarshal(e.Arguments, &args) + consulExtension.Arguments = args + outConfig = append(outConfig, consulExtension) + } + + return outConfig +} + +func (in EnvoyExtensions) validate(path *field.Path) field.ErrorList { + if len(in) == 0 { + return nil + } + + var errs field.ErrorList + for i, e := range in { + if err := e.validate(path.Child("envoyExtension").Index(i)); err != nil { + errs = append(errs, err) + } + } + + return errs +} + +func (in EnvoyExtension) validate(path *field.Path) *field.Error { + // Validate that the arguments are not nil + if in.Arguments == nil { + err := field.Required(path.Child("arguments"), "arguments must be defined") + return err + } + // Validate that the arguments are valid json + var outConfig map[string]interface{} + if err := json.Unmarshal(in.Arguments, &outConfig); err != nil { + return field.Invalid(path.Child("arguments"), string(in.Arguments), fmt.Sprintf(`must be valid map value: %s`, err)) + } + return nil +} + func notInSliceMessage(slice []string) string { return fmt.Sprintf(`must be one of "%s"`, strings.Join(slice, `", "`)) } diff --git a/control-plane/api/v1alpha1/zz_generated.deepcopy.go b/control-plane/api/v1alpha1/zz_generated.deepcopy.go index 34c385329e..d12db29d14 100644 --- a/control-plane/api/v1alpha1/zz_generated.deepcopy.go +++ b/control-plane/api/v1alpha1/zz_generated.deepcopy.go @@ -78,6 +78,47 @@ func (in *CookieConfig) DeepCopy() *CookieConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyExtension) DeepCopyInto(out *EnvoyExtension) { + *out = *in + if in.Arguments != nil { + in, out := &in.Arguments, &out.Arguments + *out = make(json.RawMessage, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyExtension. +func (in *EnvoyExtension) DeepCopy() *EnvoyExtension { + if in == nil { + return nil + } + out := new(EnvoyExtension) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in EnvoyExtensions) DeepCopyInto(out *EnvoyExtensions) { + { + in := &in + *out = make(EnvoyExtensions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyExtensions. +func (in EnvoyExtensions) DeepCopy() EnvoyExtensions { + if in == nil { + return nil + } + out := new(EnvoyExtensions) + in.DeepCopyInto(out) + return *out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExportedService) DeepCopyInto(out *ExportedService) { *out = *in @@ -1224,6 +1265,13 @@ func (in *ProxyDefaultsSpec) DeepCopyInto(out *ProxyDefaultsSpec) { *out = new(AccessLogs) **out = **in } + if in.EnvoyExtensions != nil { + in, out := &in.EnvoyExtensions, &out.EnvoyExtensions + *out = make(EnvoyExtensions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyDefaultsSpec. @@ -1401,6 +1449,13 @@ func (in *ServiceDefaultsSpec) DeepCopyInto(out *ServiceDefaultsSpec) { *out = new(ServiceDefaultsDestination) (*in).DeepCopyInto(*out) } + if in.EnvoyExtensions != nil { + in, out := &in.EnvoyExtensions, &out.EnvoyExtensions + *out = make(EnvoyExtensions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceDefaultsSpec. diff --git a/control-plane/build-support/scripts/terraformfmtcheck.sh b/control-plane/build-support/scripts/terraformfmtcheck.sh new file mode 100755 index 0000000000..0f962a1c1b --- /dev/null +++ b/control-plane/build-support/scripts/terraformfmtcheck.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +# Check terraform fmt +echo "==> Checking that code complies with terraform fmt requirements..." +tffmt_files=$(terraform fmt -check -recursive "$1") +if [[ -n ${tffmt_files} ]]; then + echo 'terraform fmt needs to be run on the following files:' + echo "${tffmt_files}" + echo "You can use the command: \`make terraform-fmt\` to reformat all terraform code." + exit 1 +fi + +echo "==> Check code compile completed successfully" +exit 0 \ No newline at end of file diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml index 4a309ebbbc..2563cbcf77 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml @@ -88,6 +88,22 @@ spec: globally here. Supports JSON config values. See https://www.consul.io/docs/connect/proxies/envoy#configuration-formatting type: object x-kubernetes-preserve-unknown-fields: true + envoyExtensions: + description: EnvoyExtensions are a list of extensions to modify Envoy + proxy configuration. + items: + description: EnvoyExtension has configuration for an extension that + patches Envoy resources. + properties: + arguments: + type: object + x-kubernetes-preserve-unknown-fields: true + name: + type: string + required: + type: boolean + type: object + type: array expose: description: Expose controls the default expose path configuration for Envoy. diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml index 944f494f98..8b05eeb025 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml @@ -50,6 +50,12 @@ spec: spec: description: ServiceDefaultsSpec defines the desired state of ServiceDefaults. properties: + balanceInboundConnections: + description: BalanceInboundConnections sets the strategy for allocating + inbound connections to the service across proxy threads. The only + supported value is exact_balance. By default, no connection balancing + is used. Refer to the Envoy Connection Balance config for details. + type: string destination: description: Destination is an address(es)/port combination that represents an endpoint outside the mesh. This is only valid when the mesh is @@ -69,6 +75,22 @@ spec: format: int32 type: integer type: object + envoyExtensions: + description: EnvoyExtensions are a list of extensions to modify Envoy + proxy configuration. + items: + description: EnvoyExtension has configuration for an extension that + patches Envoy resources. + properties: + arguments: + type: object + x-kubernetes-preserve-unknown-fields: true + name: + type: string + required: + type: boolean + type: object + type: array expose: description: Expose controls the default expose path configuration for Envoy. @@ -107,15 +129,15 @@ spec: with an external system. type: string localConnectTimeoutMs: - description: The number of milliseconds allowed to make connections - to the local application instance before timing out. Defaults to - 5000. + description: LocalConnectTimeoutMs is the number of milliseconds allowed + to make connections to the local application instance before timing + out. Defaults to 5000. type: integer localRequestTimeoutMs: - description: In milliseconds, the timeout for HTTP requests to the - local application instance. Applies to HTTP-based protocols only. - If not specified, inherits the Envoy default for route timeouts - (15s). + description: LocalRequestTimeoutMs is the timeout for HTTP requests + to the local application instance in milliseconds. Applies to HTTP-based + protocols only. If not specified, inherits the Envoy default for + route timeouts (15s). type: integer maxInboundConnections: description: MaxInboundConnections is the maximum number of concurrent diff --git a/control-plane/go.mod b/control-plane/go.mod index 6c3e48d2ba..2cd0300557 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -10,7 +10,7 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af65262de8 github.com/hashicorp/consul-server-connection-manager v0.1.0 - github.com/hashicorp/consul/api v1.10.1-0.20221220195433-629878a6879c + github.com/hashicorp/consul/api v1.10.1-0.20230106171340-8d923c178919 github.com/hashicorp/consul/sdk v0.13.0 github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f github.com/hashicorp/go-hclog v1.2.2 diff --git a/control-plane/go.sum b/control-plane/go.sum index ef065d85b3..098d32835d 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -348,6 +348,8 @@ github.com/hashicorp/consul-server-connection-manager v0.1.0/go.mod h1:XVVlO+Yk7 github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.10.1-0.20221220195433-629878a6879c h1:dIy67NF/J5gm0wdGxTA8dCFeQDz8TatuuMEzC7n3aDk= github.com/hashicorp/consul/api v1.10.1-0.20221220195433-629878a6879c/go.mod h1:c1u8FzGHcavbEtRW/p1YditvfMgn4QsKNgz2rnCDF7c= +github.com/hashicorp/consul/api v1.10.1-0.20230106171340-8d923c178919 h1:8aVegJMSv7PIAAa1zqQQ0CT4TKv+Nf7I4rhE6+uDa1U= +github.com/hashicorp/consul/api v1.10.1-0.20230106171340-8d923c178919/go.mod h1:c1u8FzGHcavbEtRW/p1YditvfMgn4QsKNgz2rnCDF7c= github.com/hashicorp/consul/api v1.18.0 h1:R7PPNzTCeN6VuQNDwwhZWJvzCtGSrNpJqfb22h3yH9g= github.com/hashicorp/consul/api v1.18.0/go.mod h1:owRRGJ9M5xReDC5nfT8FTJrNAPbT4NM6p/k+d03q2v4= github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3usQMXZF6ziK3SzXU= From 3ccee4c4093d4aa28001a8fcb6a3862f56e50357 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Fri, 13 Jan 2023 09:05:32 -0800 Subject: [PATCH 040/340] updated tests to use 1.15-dev (#1831) * updated tests to use 1.15-dev - 1.15-dev is currently broken, so tagging it to a working sha for the moment * add EnvoyExtensions to proxy and service defaults - Also include balanceInboundConnections in serviceDefaults --- .circleci/config.yml | 20 +++++++++---------- .../bases/crds-oss/proxydefaults.yaml | 11 ++++++++++ .../bases/crds-oss/servicedefaults.yaml | 14 ++++++++++++- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 34bbc520a5..df5dd49573 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -589,7 +589,7 @@ jobs: - run: mkdir -p $TEST_RESULTS - run-acceptance-tests: failfast: true - additional-flags: -use-kind -kubecontext="kind-dc1" -secondary-kubecontext="kind-dc2" -consul-image=docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.14-dev + additional-flags: -use-kind -kubecontext="kind-dc1" -secondary-kubecontext="kind-dc2" -consul-image=docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.15-dev-23aaa4f83845d0e2eced9ea69f731d7eedf840d1 - store_test_results: path: /tmp/test-results - store_artifacts: @@ -622,7 +622,7 @@ jobs: - run: mkdir -p $TEST_RESULTS - run-acceptance-tests: failfast: true - additional-flags: -use-kind -kubecontext="kind-dc1" -secondary-kubecontext="kind-dc2" -enable-transparent-proxy -consul-image=docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.14-dev + additional-flags: -use-kind -kubecontext="kind-dc1" -secondary-kubecontext="kind-dc2" -enable-transparent-proxy -consul-image=docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.15-dev-23aaa4f83845d0e2eced9ea69f731d7eedf840d1 - store_test_results: path: /tmp/test-results - store_artifacts: @@ -655,7 +655,7 @@ jobs: - run: mkdir -p $TEST_RESULTS - run-acceptance-tests: failfast: true - additional-flags: -use-kind -kubecontext="kind-dc1" -secondary-kubecontext="kind-dc2" -enable-transparent-proxy -enable-cni -consul-image=docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.14-dev + additional-flags: -use-kind -kubecontext="kind-dc1" -secondary-kubecontext="kind-dc2" -enable-transparent-proxy -enable-cni -consul-image=docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.15-dev-23aaa4f83845d0e2eced9ea69f731d7eedf840d1 - store_test_results: path: /tmp/test-results - store_artifacts: @@ -773,7 +773,7 @@ jobs: - run: mkdir -p $TEST_RESULTS - run-acceptance-tests: - additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-pod-security-policies -enable-transparent-proxy -consul-image=hashicorppreview/consul-enterprise:1.14-dev + additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-pod-security-policies -enable-transparent-proxy -consul-image=hashicorppreview/consul-enterprise:1.15-dev-23aaa4f83845d0e2eced9ea69f731d7eedf840d1 - store_test_results: path: /tmp/test-results @@ -842,7 +842,7 @@ jobs: - run: mkdir -p $TEST_RESULTS - run-acceptance-tests: - additional-flags: -use-gke -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-pod-security-policies -enable-transparent-proxy -enable-cni -consul-image=hashicorppreview/consul-enterprise:1.14-dev + additional-flags: -use-gke -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-pod-security-policies -enable-transparent-proxy -enable-cni -consul-image=hashicorppreview/consul-enterprise:1.15-dev-23aaa4f83845d0e2eced9ea69f731d7eedf840d1 - store_test_results: path: /tmp/test-results @@ -899,7 +899,7 @@ jobs: - run: mkdir -p $TEST_RESULTS - run-acceptance-tests: - additional-flags: -use-aks -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -consul-image=hashicorppreview/consul-enterprise:1.14-dev + additional-flags: -use-aks -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -consul-image=hashicorppreview/consul-enterprise:1.15-dev-23aaa4f83845d0e2eced9ea69f731d7eedf840d1 - store_test_results: path: /tmp/test-results @@ -956,7 +956,7 @@ jobs: - run: mkdir -p $TEST_RESULTS - run-acceptance-tests: - additional-flags: -use-aks -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -enable-cni -consul-image=hashicorppreview/consul-enterprise:1.14-dev + additional-flags: -use-aks -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -enable-cni -consul-image=hashicorppreview/consul-enterprise:1.15-dev-23aaa4f83845d0e2eced9ea69f731d7eedf840d1 - store_test_results: path: /tmp/test-results @@ -1018,7 +1018,7 @@ jobs: - run: mkdir -p $TEST_RESULTS - run-acceptance-tests: - additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -consul-image=hashicorppreview/consul-enterprise:1.14-dev + additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -consul-image=hashicorppreview/consul-enterprise:1.15-dev-23aaa4f83845d0e2eced9ea69f731d7eedf840d1 - store_test_results: path: /tmp/test-results @@ -1081,7 +1081,7 @@ jobs: - run: mkdir -p $TEST_RESULTS - run-acceptance-tests: - additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -enable-cni -consul-image=hashicorppreview/consul-enterprise:1.14-dev + additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -enable-cni -consul-image=hashicorppreview/consul-enterprise:1.15-dev-23aaa4f83845d0e2eced9ea69f731d7eedf840d1 - store_test_results: path: /tmp/test-results @@ -1135,7 +1135,7 @@ jobs: - run: mkdir -p $TEST_RESULTS - run-acceptance-tests: - additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-openshift -enable-transparent-proxy -consul-image=hashicorppreview/consul-enterprise:1.14-dev + additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-openshift -enable-transparent-proxy -consul-image=hashicorppreview/consul-enterprise:1.15-dev-23aaa4f83845d0e2eced9ea69f731d7eedf840d1 - store_test_results: path: /tmp/test-results diff --git a/acceptance/tests/fixtures/bases/crds-oss/proxydefaults.yaml b/acceptance/tests/fixtures/bases/crds-oss/proxydefaults.yaml index 040da247f8..878b4c37e8 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/proxydefaults.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/proxydefaults.yaml @@ -19,3 +19,14 @@ spec: - path: /health listenerPort: 22000 localPathPort: 8080 + envoyExtensions: + - name: builtin/aws/lambda + required: false + arguments: + payloadPassthrough: false + region: us-west-2 + - name: builtin/aws/lambda + required: false + arguments: + payloadPassthrough: false + region: us-east-1 diff --git a/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml b/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml index 7b5f23eff5..33bc0e5096 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml @@ -24,4 +24,16 @@ spec: maxConnections: 5 passiveHealthCheck: interval: 10s - maxFailures: 2 \ No newline at end of file + maxFailures: 2 + balanceInboundConnections: "exact_balance" + envoyExtensions: + - name: builtin/aws/lambda + required: false + arguments: + payloadPassthrough: false + region: us-west-2 + - name: builtin/aws/lambda + required: false + arguments: + payloadPassthrough: false + region: us-east-1 \ No newline at end of file From 9ab7bea6a52043eb25b0673bb77562daa418251c Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Tue, 17 Jan 2023 14:33:42 -0500 Subject: [PATCH 041/340] Add health checks for services that are synced (#1821) * Add health checks for services that are synced - When the type of the service is ClusterIP, a health check will be added to the catalog registration with the health info of the service based on the state of the readiness probe of the pod associated with the service. - Replace `apiv1` with `corev1` to be consistent across the project. - Run `go mod tidy`. --- CHANGELOG.md | 1 + acceptance/tests/sync/sync_catalog_test.go | 8 ++- control-plane/catalog/to-consul/resource.go | 67 ++++++++++++------- .../catalog/to-consul/resource_test.go | 38 +++++++++++ control-plane/go.sum | 9 --- 5 files changed, 90 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3599155056..9b7c7dd852 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ IMPROVEMENTS: * Add the `balanceInboundConnections` field to the `ServiceDefaults` CRD. [[GH-1823]](https://github.com/hashicorp/consul-k8s/pull/1823) * Control-Plane * Add support for the annotation `consul.hashicorp.com/use-proxy-health-check`. [[GH-1824](https://github.com/hashicorp/consul-k8s/pull/1824)] + * Add health check for synced services based on the status of the Kubernetes readiness probe on synced pod. [[GH-1821](https://github.com/hashicorp/consul-k8s/pull/1821)] BUG FIXES: * Control Plane diff --git a/acceptance/tests/sync/sync_catalog_test.go b/acceptance/tests/sync/sync_catalog_test.go index 92b006cac6..c4f873fcbd 100644 --- a/acceptance/tests/sync/sync_catalog_test.go +++ b/acceptance/tests/sync/sync_catalog_test.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/logger" + "github.com/hashicorp/consul/api" "github.com/hashicorp/consul/sdk/testutil/retry" "github.com/stretchr/testify/require" ) @@ -65,8 +66,13 @@ func TestSyncCatalog(t *testing.T) { service, _, err := consulClient.Catalog().Service(syncedServiceName, "", nil) require.NoError(t, err) - require.Equal(t, 1, len(service)) + require.Len(t, service, 1) require.Equal(t, []string{"k8s"}, service[0].ServiceTags) + filter := fmt.Sprintf("ServiceID == %q", service[0].ServiceID) + healthChecks, _, err := consulClient.Health().Checks(syncedServiceName, &api.QueryOptions{Filter: filter}) + require.NoError(t, err) + require.Len(t, healthChecks, 1) + require.Equal(t, api.HealthPassing, healthChecks[0].Status) }) } } diff --git a/control-plane/catalog/to-consul/resource.go b/control-plane/catalog/to-consul/resource.go index 96538510d1..09d8aa6c5d 100644 --- a/control-plane/catalog/to-consul/resource.go +++ b/control-plane/catalog/to-consul/resource.go @@ -13,7 +13,7 @@ import ( "github.com/hashicorp/consul-k8s/control-plane/namespaces" consulapi "github.com/hashicorp/consul/api" "github.com/hashicorp/go-hclog" - apiv1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/watch" @@ -33,6 +33,12 @@ const ( ConsulK8SRefKind = "external-k8s-ref-kind" ConsulK8SRefValue = "external-k8s-ref-name" ConsulK8SNodeName = "external-k8s-node-name" + + // consulKubernetesCheckType is the type of health check in Consul for Kubernetes readiness status. + consulKubernetesCheckType = "kubernetes-readiness" + // consulKubernetesCheckName is the name of health check in Consul for Kubernetes readiness status. + consulKubernetesCheckName = "Kubernetes Readiness Check" + kubernetesSuccessReasonMsg = "Kubernetes health checks passing" ) type NodePortSyncType string @@ -131,11 +137,11 @@ type ServiceResource struct { // serviceMap holds services we should sync to Consul. Keys are the // in the form /. - serviceMap map[string]*apiv1.Service + serviceMap map[string]*corev1.Service // endpointsMap uses the same keys as serviceMap but maps to the endpoints // of each service. - endpointsMap map[string]*apiv1.Endpoints + endpointsMap map[string]*corev1.Endpoints // consulMap holds the services in Consul that we've registered from kube. // It's populated via Consul's API and lets us diff what is actually in @@ -157,7 +163,7 @@ func (t *ServiceResource) Informer() cache.SharedIndexInformer { return t.Client.CoreV1().Services(metav1.NamespaceAll).Watch(t.Ctx, options) }, }, - &apiv1.Service{}, + &corev1.Service{}, 0, cache.Indexers{}, ) @@ -166,7 +172,7 @@ func (t *ServiceResource) Informer() cache.SharedIndexInformer { // Upsert implements the controller.Resource interface. func (t *ServiceResource) Upsert(key string, raw interface{}) error { // We expect a Service. If it isn't a service then just ignore it. - service, ok := raw.(*apiv1.Service) + service, ok := raw.(*corev1.Service) if !ok { t.Log.Warn("upsert got invalid type", "raw", raw) return nil @@ -176,7 +182,7 @@ func (t *ServiceResource) Upsert(key string, raw interface{}) error { defer t.serviceLock.Unlock() if t.serviceMap == nil { - t.serviceMap = make(map[string]*apiv1.Service) + t.serviceMap = make(map[string]*corev1.Service) } if !t.shouldSync(service) { @@ -205,7 +211,7 @@ func (t *ServiceResource) Upsert(key string, raw interface{}) error { "err", err) } else { if t.endpointsMap == nil { - t.endpointsMap = make(map[string]*apiv1.Endpoints) + t.endpointsMap = make(map[string]*corev1.Endpoints) } t.endpointsMap[key] = endpoints t.Log.Debug("[ServiceResource.Upsert] adding service's endpoints to endpointsMap", "key", key, "service", service, "endpoints", endpoints) @@ -254,7 +260,7 @@ func (t *ServiceResource) Run(ch <-chan struct{}) { } // shouldSync returns true if resyncing should be enabled for the given service. -func (t *ServiceResource) shouldSync(svc *apiv1.Service) bool { +func (t *ServiceResource) shouldSync(svc *corev1.Service) bool { // Namespace logic // If in deny list, don't sync if t.DenyK8sNamespacesSet.Contains(svc.Namespace) { @@ -269,7 +275,7 @@ func (t *ServiceResource) shouldSync(svc *apiv1.Service) bool { } // Ignore ClusterIP services if ClusterIP sync is disabled - if svc.Spec.Type == apiv1.ServiceTypeClusterIP && !t.ClusterIPSync { + if svc.Spec.Type == corev1.ServiceTypeClusterIP && !t.ClusterIPSync { t.Log.Debug("[shouldSync] ignoring clusterip service", "svc.Namespace", svc.Namespace, "service", svc) return false } @@ -310,9 +316,9 @@ func (t *ServiceResource) shouldTrackEndpoints(key string) bool { return false } - return svc.Spec.Type == apiv1.ServiceTypeNodePort || - svc.Spec.Type == apiv1.ServiceTypeClusterIP || - (t.LoadBalancerEndpointsSync && svc.Spec.Type == apiv1.ServiceTypeLoadBalancer) + return svc.Spec.Type == corev1.ServiceTypeNodePort || + svc.Spec.Type == corev1.ServiceTypeClusterIP || + (t.LoadBalancerEndpointsSync && svc.Spec.Type == corev1.ServiceTypeLoadBalancer) } // generateRegistrations generates the necessary Consul registrations for @@ -380,7 +386,7 @@ func (t *ServiceResource) generateRegistrations(key string) { var overridePortNumber int if len(svc.Spec.Ports) > 0 { var port int - isNodePort := svc.Spec.Type == apiv1.ServiceTypeNodePort + isNodePort := svc.Spec.Type == corev1.ServiceTypeNodePort // If a specific port is specified, then use that port value portAnnotation, ok := svc.Annotations[annotationServicePort] @@ -479,7 +485,7 @@ func (t *ServiceResource) generateRegistrations(key string) { // each LoadBalancer entry. We only support entries that have an IP // address assigned (not hostnames). // If LoadBalancerEndpointsSync is true sync LB endpoints instead of loadbalancer ingress. - case apiv1.ServiceTypeLoadBalancer: + case corev1.ServiceTypeLoadBalancer: if t.LoadBalancerEndpointsSync { t.registerServiceInstance(baseNode, baseService, key, overridePortName, overridePortNumber, false) } else { @@ -512,7 +518,7 @@ func (t *ServiceResource) generateRegistrations(key string) { // endpoint of the service, which corresponds to the nodes the service's // pods are running on. This way we don't register _every_ K8S // node as part of the service. - case apiv1.ServiceTypeNodePort: + case corev1.ServiceTypeNodePort: if t.endpointsMap == nil { return } @@ -538,11 +544,11 @@ func (t *ServiceResource) generateRegistrations(key string) { } // Set the expected node address type - var expectedType apiv1.NodeAddressType + var expectedType corev1.NodeAddressType if t.NodePortSync == InternalOnly { - expectedType = apiv1.NodeInternalIP + expectedType = corev1.NodeInternalIP } else { - expectedType = apiv1.NodeExternalIP + expectedType = corev1.NodeExternalIP } // Find the ip address for the node and @@ -571,7 +577,7 @@ func (t *ServiceResource) generateRegistrations(key string) { // use an InternalIP if t.NodePortSync == ExternalFirst && !found { for _, address := range node.Status.Addresses { - if address.Type == apiv1.NodeInternalIP { + if address.Type == corev1.NodeInternalIP { r := baseNode rs := baseService r.Service = &rs @@ -593,7 +599,7 @@ func (t *ServiceResource) generateRegistrations(key string) { // For ClusterIP services, we register a service instance // for each endpoint. - case apiv1.ServiceTypeClusterIP: + case corev1.ServiceTypeClusterIP: t.registerServiceInstance(baseNode, baseService, key, overridePortName, overridePortNumber, true) } } @@ -674,6 +680,16 @@ func (t *ServiceResource) registerServiceInstance( r.Service.Meta[ConsulK8SNodeName] = *subsetAddr.NodeName } + r.Check = &consulapi.AgentCheck{ + CheckID: consulHealthCheckID(endpoints.Namespace, serviceID(r.Service.Service, addr)), + Name: consulKubernetesCheckName, + Namespace: baseService.Namespace, + Type: consulKubernetesCheckType, + Status: consulapi.HealthPassing, + ServiceID: serviceID(r.Service.Service, addr), + Output: kubernetesSuccessReasonMsg, + } + t.consulMap[key] = append(t.consulMap[key], &r) } } @@ -723,7 +739,7 @@ func (t *serviceEndpointsResource) Informer() cache.SharedIndexInformer { Watch(t.Ctx, options) }, }, - &apiv1.Endpoints{}, + &corev1.Endpoints{}, 0, cache.Indexers{}, ) @@ -731,7 +747,7 @@ func (t *serviceEndpointsResource) Informer() cache.SharedIndexInformer { func (t *serviceEndpointsResource) Upsert(key string, raw interface{}) error { svc := t.Service - endpoints, ok := raw.(*apiv1.Endpoints) + endpoints, ok := raw.(*corev1.Endpoints) if !ok { svc.Log.Warn("upsert got invalid type", "raw", raw) return nil @@ -747,7 +763,7 @@ func (t *serviceEndpointsResource) Upsert(key string, raw interface{}) error { // We are tracking this service so let's keep track of the endpoints if svc.endpointsMap == nil { - svc.endpointsMap = make(map[string]*apiv1.Endpoints) + svc.endpointsMap = make(map[string]*corev1.Endpoints) } svc.endpointsMap[key] = endpoints @@ -788,3 +804,8 @@ func (t *ServiceResource) addPrefixAndK8SNamespace(name, namespace string) strin return name } + +// consulHealthCheckID deterministically generates a health check ID based on service ID and Kubernetes namespace. +func consulHealthCheckID(k8sNS string, serviceID string) string { + return fmt.Sprintf("%s/%s", k8sNS, serviceID) +} diff --git a/control-plane/catalog/to-consul/resource_test.go b/control-plane/catalog/to-consul/resource_test.go index 28335dea27..9ba94123ef 100644 --- a/control-plane/catalog/to-consul/resource_test.go +++ b/control-plane/catalog/to-consul/resource_test.go @@ -6,6 +6,7 @@ import ( mapset "github.com/deckarep/golang-set" "github.com/hashicorp/consul-k8s/control-plane/helper/controller" + consulapi "github.com/hashicorp/consul/api" "github.com/hashicorp/consul/sdk/testutil/retry" "github.com/hashicorp/go-hclog" "github.com/stretchr/testify/require" @@ -1005,6 +1006,43 @@ func TestServiceResource_clusterIP(t *testing.T) { }) } +// Test that the proper registrations with health checks are generated for a ClusterIP type. +func TestServiceResource_clusterIP_healthCheck(t *testing.T) { + t.Parallel() + client := fake.NewSimpleClientset() + syncer := newTestSyncer() + serviceResource := defaultServiceResource(client, syncer) + serviceResource.ClusterIPSync = true + + // Start the controller + closer := controller.TestControllerRun(&serviceResource) + defer closer() + + // Insert the service + svc := clusterIPService("foo", metav1.NamespaceDefault) + _, err := client.CoreV1().Services(metav1.NamespaceDefault).Create(context.Background(), svc, metav1.CreateOptions{}) + require.NoError(t, err) + + // Insert the endpoints + createEndpoints(t, client, "foo", metav1.NamespaceDefault) + + // Verify what we got + retry.Run(t, func(r *retry.R) { + syncer.Lock() + defer syncer.Unlock() + actual := syncer.Registrations + require.Len(r, actual, 2) + require.Equal(r, consulKubernetesCheckName, actual[0].Check.Name) + require.Equal(r, consulapi.HealthPassing, actual[0].Check.Status) + require.Equal(r, kubernetesSuccessReasonMsg, actual[0].Check.Output) + require.Equal(r, consulKubernetesCheckType, actual[0].Check.Type) + require.Equal(r, consulKubernetesCheckName, actual[1].Check.Name) + require.Equal(r, consulapi.HealthPassing, actual[1].Check.Status) + require.Equal(r, kubernetesSuccessReasonMsg, actual[1].Check.Output) + require.Equal(r, consulKubernetesCheckType, actual[1].Check.Type) + }) +} + // Test clusterIP with prefix. func TestServiceResource_clusterIPPrefix(t *testing.T) { t.Parallel() diff --git a/control-plane/go.sum b/control-plane/go.sum index 098d32835d..7542ac12d9 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -346,12 +346,8 @@ github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af6526 github.com/hashicorp/consul-server-connection-manager v0.1.0 h1:XCweGvMHzra88rYv2zxwwuUOjBUdcQmNKVrnQmt/muo= github.com/hashicorp/consul-server-connection-manager v0.1.0/go.mod h1:XVVlO+Yk7aiRpspiHZkrrFVn9BJIiOPnQIzqytPxGaU= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.10.1-0.20221220195433-629878a6879c h1:dIy67NF/J5gm0wdGxTA8dCFeQDz8TatuuMEzC7n3aDk= -github.com/hashicorp/consul/api v1.10.1-0.20221220195433-629878a6879c/go.mod h1:c1u8FzGHcavbEtRW/p1YditvfMgn4QsKNgz2rnCDF7c= github.com/hashicorp/consul/api v1.10.1-0.20230106171340-8d923c178919 h1:8aVegJMSv7PIAAa1zqQQ0CT4TKv+Nf7I4rhE6+uDa1U= github.com/hashicorp/consul/api v1.10.1-0.20230106171340-8d923c178919/go.mod h1:c1u8FzGHcavbEtRW/p1YditvfMgn4QsKNgz2rnCDF7c= -github.com/hashicorp/consul/api v1.18.0 h1:R7PPNzTCeN6VuQNDwwhZWJvzCtGSrNpJqfb22h3yH9g= -github.com/hashicorp/consul/api v1.18.0/go.mod h1:owRRGJ9M5xReDC5nfT8FTJrNAPbT4NM6p/k+d03q2v4= github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3usQMXZF6ziK3SzXU= github.com/hashicorp/consul/proto-public v0.1.0/go.mod h1:vs2KkuWwtjkIgA5ezp4YKPzQp4GitV+q/+PvksrA92k= github.com/hashicorp/consul/sdk v0.4.1-0.20221021205723-cc843c4be892 h1:jw0NwPmNPr5CxAU04hACdj61JSaJBKZ0FdBo+kwfNp4= @@ -384,7 +380,6 @@ github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5O github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= -github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -454,7 +449,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -495,7 +489,6 @@ github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXx github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -595,7 +588,6 @@ github.com/rs/zerolog v1.4.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKk github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/conswriter v0.0.0-20180208195008-f5ae3917a627/go.mod h1:7zjs06qF79/FKAJpBvFx3P8Ww4UTIMAe+lpNXDHziac= github.com/sean-/pager v0.0.0-20180208200047-666be9bf53b5/go.mod h1:BeybITEsBEg6qbIiqJ6/Bqeq25bCLbL7YFmpaFfJDuM= @@ -804,7 +796,6 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= From 7d8d4fdc0b59205d9ce687c3b378450db39ef57b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Lorenzo Date: Wed, 18 Jan 2023 10:28:15 -0300 Subject: [PATCH 042/340] [CONSUL-620] Refactor Package Import --- .../subcommand/inject-connect/command.go | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/control-plane/subcommand/inject-connect/command.go b/control-plane/subcommand/inject-connect/command.go index 5cbd2abdee..93566387cd 100644 --- a/control-plane/subcommand/inject-connect/command.go +++ b/control-plane/subcommand/inject-connect/command.go @@ -35,7 +35,6 @@ import ( "k8s.io/klog/v2" ctrl "sigs.k8s.io/controller-runtime" ctrlRuntimeWebhook "sigs.k8s.io/controller-runtime/pkg/webhook" - ctrlwebhook "sigs.k8s.io/controller-runtime/pkg/webhook" ) const WebhookCAFilename = "ca.crt" @@ -645,61 +644,61 @@ func (c *Command) Run(args []string) int { // Note: The path here should be identical to the one on the kubebuilder // annotation in each webhook file. mgr.GetWebhookServer().Register("/mutate-v1alpha1-servicedefaults", - &ctrlwebhook.Admission{Handler: &v1alpha1.ServiceDefaultsWebhook{ + &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.ServiceDefaultsWebhook{ Client: mgr.GetClient(), Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.ServiceDefaults), ConsulMeta: consulMeta, }}) mgr.GetWebhookServer().Register("/mutate-v1alpha1-serviceresolver", - &ctrlwebhook.Admission{Handler: &v1alpha1.ServiceResolverWebhook{ + &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.ServiceResolverWebhook{ Client: mgr.GetClient(), Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.ServiceResolver), ConsulMeta: consulMeta, }}) mgr.GetWebhookServer().Register("/mutate-v1alpha1-proxydefaults", - &ctrlwebhook.Admission{Handler: &v1alpha1.ProxyDefaultsWebhook{ + &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.ProxyDefaultsWebhook{ Client: mgr.GetClient(), Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.ProxyDefaults), ConsulMeta: consulMeta, }}) mgr.GetWebhookServer().Register("/mutate-v1alpha1-mesh", - &ctrlwebhook.Admission{Handler: &v1alpha1.MeshWebhook{ + &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.MeshWebhook{ Client: mgr.GetClient(), Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.Mesh), ConsulMeta: consulMeta, }}) mgr.GetWebhookServer().Register("/mutate-v1alpha1-exportedservices", - &ctrlwebhook.Admission{Handler: &v1alpha1.ExportedServicesWebhook{ + &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.ExportedServicesWebhook{ Client: mgr.GetClient(), Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.ExportedServices), ConsulMeta: consulMeta, }}) mgr.GetWebhookServer().Register("/mutate-v1alpha1-servicerouter", - &ctrlwebhook.Admission{Handler: &v1alpha1.ServiceRouterWebhook{ + &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.ServiceRouterWebhook{ Client: mgr.GetClient(), Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.ServiceRouter), ConsulMeta: consulMeta, }}) mgr.GetWebhookServer().Register("/mutate-v1alpha1-servicesplitter", - &ctrlwebhook.Admission{Handler: &v1alpha1.ServiceSplitterWebhook{ + &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.ServiceSplitterWebhook{ Client: mgr.GetClient(), Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.ServiceSplitter), ConsulMeta: consulMeta, }}) mgr.GetWebhookServer().Register("/mutate-v1alpha1-serviceintentions", - &ctrlwebhook.Admission{Handler: &v1alpha1.ServiceIntentionsWebhook{ + &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.ServiceIntentionsWebhook{ Client: mgr.GetClient(), Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.ServiceIntentions), ConsulMeta: consulMeta, }}) mgr.GetWebhookServer().Register("/mutate-v1alpha1-ingressgateway", - &ctrlwebhook.Admission{Handler: &v1alpha1.IngressGatewayWebhook{ + &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.IngressGatewayWebhook{ Client: mgr.GetClient(), Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.IngressGateway), ConsulMeta: consulMeta, }}) mgr.GetWebhookServer().Register("/mutate-v1alpha1-terminatinggateway", - &ctrlwebhook.Admission{Handler: &v1alpha1.TerminatingGatewayWebhook{ + &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.TerminatingGatewayWebhook{ Client: mgr.GetClient(), Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.TerminatingGateway), ConsulMeta: consulMeta, From 1821f08477724394b9819de04b5a43ade5b996b7 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Thu, 19 Jan 2023 11:21:44 -0500 Subject: [PATCH 043/340] When configured, use the health check of the proxy (#1841) * When a service is configured with the correct annotation, a readiness endpoint with be configured in Consul dataplane and the readiness probe of the sidecar will be configured to use that endpoint to determine the health of the system. Additionally, when t-proxy is enabled, that port shall be in the ExcludeList for inbound connections. --- CHANGELOG.md | 2 +- .../connect-inject/constants/constants.go | 3 + .../webhook/consul_dataplane_sidecar.go | 61 ++++++- .../webhook/consul_dataplane_sidecar_test.go | 154 +++++++++++++++++- .../webhook/redirect_traffic.go | 6 + .../webhook/redirect_traffic_test.go | 33 ++++ 6 files changed, 249 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b7c7dd852..d4cf05b4f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ IMPROVEMENTS: * Add the `envoyExtensions` field to the `ProxyDefaults` and `ServiceDefaults` CRD. [[GH-1823]](https://github.com/hashicorp/consul-k8s/pull/1823) * Add the `balanceInboundConnections` field to the `ServiceDefaults` CRD. [[GH-1823]](https://github.com/hashicorp/consul-k8s/pull/1823) * Control-Plane - * Add support for the annotation `consul.hashicorp.com/use-proxy-health-check`. [[GH-1824](https://github.com/hashicorp/consul-k8s/pull/1824)] + * Add support for the annotation `consul.hashicorp.com/use-proxy-health-check`. When this annotation is used by a service, it configures a readiness endpoint on Consul Dataplane and queries it instead of the proxy's inbound port which forwards requests to the application. [[GH-1824](https://github.com/hashicorp/consul-k8s/pull/1824)], [[GH-1841](https://github.com/hashicorp/consul-k8s/pull/1824)] * Add health check for synced services based on the status of the Kubernetes readiness probe on synced pod. [[GH-1821](https://github.com/hashicorp/consul-k8s/pull/1821)] BUG FIXES: diff --git a/control-plane/connect-inject/constants/constants.go b/control-plane/connect-inject/constants/constants.go index 62f21740c4..e371677629 100644 --- a/control-plane/connect-inject/constants/constants.go +++ b/control-plane/connect-inject/constants/constants.go @@ -7,6 +7,9 @@ const ( // ProxyDefaultInboundPort is the default inbound port for the proxy. ProxyDefaultInboundPort = 20000 + // ProxyDefaultHealthPort is the default HTTP health check port for the proxy. + ProxyDefaultHealthPort = 21000 + // MetaKeyKubeNS is the meta key name for Kubernetes namespace used for the Consul services. MetaKeyKubeNS = "k8s-namespace" diff --git a/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go b/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go index d0cd232c84..ad3333ba1b 100644 --- a/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go +++ b/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go @@ -47,13 +47,28 @@ func (w *MeshWebhook) consulDataplaneSidecar(namespace corev1.Namespace, pod cor containerName = fmt.Sprintf("%s-%s", sidecarContainer, mpi.serviceName) } - probe := &corev1.Probe{ - Handler: corev1.Handler{ - TCPSocket: &corev1.TCPSocketAction{ - Port: intstr.FromInt(constants.ProxyDefaultInboundPort + mpi.serviceIndex), + var probe *corev1.Probe + if useProxyHealthCheck(pod) { + // If using the proxy health check for a service, configure an HTTP handler + // that queries the '/ready' endpoint of the proxy. + probe = &corev1.Probe{ + Handler: corev1.Handler{ + HTTPGet: &corev1.HTTPGetAction{ + Port: intstr.FromInt(constants.ProxyDefaultHealthPort + mpi.serviceIndex), + Path: "/ready", + }, }, - }, - InitialDelaySeconds: 1, + InitialDelaySeconds: 1, + } + } else { + probe = &corev1.Probe{ + Handler: corev1.Handler{ + TCPSocket: &corev1.TCPSocketAction{ + Port: intstr.FromInt(constants.ProxyDefaultInboundPort + mpi.serviceIndex), + }, + }, + InitialDelaySeconds: 1, + } } container := corev1.Container{ @@ -89,13 +104,27 @@ func (w *MeshWebhook) consulDataplaneSidecar(namespace corev1.Namespace, pod cor }, Args: args, ReadinessProbe: probe, - LivenessProbe: probe, } if w.AuthMethod != "" { container.VolumeMounts = append(container.VolumeMounts, saTokenVolumeMount) } + if useProxyHealthCheck(pod) { + // Configure the Readiness Address for the proxy's health check to be the Pod IP. + container.Env = append(container.Env, corev1.EnvVar{ + Name: "DP_ENVOY_READY_BIND_ADDRESS", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{FieldPath: "status.podIP"}, + }, + }) + // Configure the port on which the readiness probe will query the proxy for its health. + container.Ports = append(container.Ports, corev1.ContainerPort{ + Name: fmt.Sprintf("%s-%d", "proxy-health", mpi.serviceIndex), + ContainerPort: int32(constants.ProxyDefaultHealthPort + mpi.serviceIndex), + }) + } + // Add any extra VolumeMounts. if userVolMount, ok := pod.Annotations[constants.AnnotationConsulSidecarUserVolumeMount]; ok { var volumeMounts []corev1.VolumeMount @@ -206,6 +235,11 @@ func (w *MeshWebhook) getContainerSidecarArgs(namespace corev1.Namespace, mpi mu args = append(args, "-tls-disabled") } + // Configure the readiness port on the dataplane sidecar if proxy health checks are enabled. + if useProxyHealthCheck(pod) { + args = append(args, fmt.Sprintf("%s=%d", "-envoy-ready-bind-port", constants.ProxyDefaultHealthPort+mpi.serviceIndex)) + } + if mpi.serviceName != "" { args = append(args, fmt.Sprintf("-envoy-admin-bind-port=%d", 19000+mpi.serviceIndex)) } @@ -383,3 +417,16 @@ func (w *MeshWebhook) sidecarResources(pod corev1.Pod) (corev1.ResourceRequireme return resources, nil } + +// useProxyHealthCheck returns true if the pod has the annotation 'consul.hashicorp.com/use-proxy-health-check' +// set to truthy values. +func useProxyHealthCheck(pod corev1.Pod) bool { + if v, ok := pod.Annotations[constants.AnnotationUseProxyHealthCheck]; ok { + useProxyHealthCheck, err := strconv.ParseBool(v) + if err != nil { + return false + } + return useProxyHealthCheck + } + return false +} diff --git a/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go b/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go index f6916bc3e2..37aa1619bf 100644 --- a/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go +++ b/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go @@ -214,7 +214,6 @@ func TestHandlerConsulDataplaneSidecar(t *testing.T) { InitialDelaySeconds: 1, } require.Equal(t, expectedProbe, container.ReadinessProbe) - require.Equal(t, expectedProbe, container.LivenessProbe) require.Nil(t, container.StartupProbe) require.Len(t, container.Env, 3) require.Equal(t, container.Env[0].Name, "TMPDIR") @@ -308,6 +307,158 @@ func TestHandlerConsulDataplaneSidecar_DNSProxy(t *testing.T) { require.Contains(t, container.Args, "-consul-dns-bind-port=8600") } +func TestHandlerConsulDataplaneSidecar_ProxyHealthCheck(t *testing.T) { + h := MeshWebhook{ + ConsulConfig: &consul.Config{HTTPPort: 8500, GRPCPort: 8502}, + ConsulAddress: "1.1.1.1", + LogLevel: "info", + } + pod := corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + constants.AnnotationUseProxyHealthCheck: "true", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "web", + }, + }, + }, + } + container, err := h.consulDataplaneSidecar(testNS, pod, multiPortInfo{}) + expectedProbe := &corev1.Probe{ + Handler: corev1.Handler{ + HTTPGet: &corev1.HTTPGetAction{ + Port: intstr.FromInt(21000), + Path: "/ready", + }, + }, + InitialDelaySeconds: 1, + } + require.NoError(t, err) + require.Contains(t, container.Args, "-envoy-ready-bind-port=21000") + require.Equal(t, expectedProbe, container.ReadinessProbe) + require.Contains(t, container.Env, corev1.EnvVar{ + Name: "DP_ENVOY_READY_BIND_ADDRESS", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{FieldPath: "status.podIP"}, + }, + }) + require.Contains(t, container.Ports, corev1.ContainerPort{ + Name: "proxy-health-0", + ContainerPort: 21000, + }) +} + +func TestHandlerConsulDataplaneSidecar_ProxyHealthCheck_Multiport(t *testing.T) { + h := MeshWebhook{ + ConsulConfig: &consul.Config{HTTPPort: 8500, GRPCPort: 8502}, + ConsulAddress: "1.1.1.1", + LogLevel: "info", + } + pod := corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-pod", + Annotations: map[string]string{ + constants.AnnotationService: "web,web-admin", + constants.AnnotationUseProxyHealthCheck: "true", + }, + }, + + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: "web-admin-service-account", + }, + }, + Containers: []corev1.Container{ + { + Name: "web", + }, + { + Name: "web-side", + }, + { + Name: "web-admin", + }, + { + Name: "web-admin-side", + }, + { + Name: "auth-method-secret", + VolumeMounts: []corev1.VolumeMount{ + { + Name: "service-account-secret", + MountPath: "/var/run/secrets/kubernetes.io/serviceaccount", + }, + }, + }, + }, + ServiceAccountName: "web", + }, + } + multiPortInfos := []multiPortInfo{ + { + serviceIndex: 0, + serviceName: "web", + }, + { + serviceIndex: 1, + serviceName: "web-admin", + }, + } + expectedArgs := []string{ + "-envoy-ready-bind-port=21000", + "-envoy-ready-bind-port=21001", + } + expectedProbe := []*corev1.Probe{ + { + Handler: corev1.Handler{ + HTTPGet: &corev1.HTTPGetAction{ + Port: intstr.FromInt(21000), + Path: "/ready", + }, + }, + InitialDelaySeconds: 1, + }, + { + Handler: corev1.Handler{ + HTTPGet: &corev1.HTTPGetAction{ + Port: intstr.FromInt(21001), + Path: "/ready", + }, + }, + InitialDelaySeconds: 1, + }, + } + expectedPort := []corev1.ContainerPort{ + { + Name: "proxy-health-0", + ContainerPort: 21000, + }, + { + Name: "proxy-health-1", + ContainerPort: 21001, + }, + } + expectedEnvVar := corev1.EnvVar{ + Name: "DP_ENVOY_READY_BIND_ADDRESS", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{FieldPath: "status.podIP"}, + }, + } + for i, info := range multiPortInfos { + container, err := h.consulDataplaneSidecar(testNS, pod, info) + require.NoError(t, err) + require.Contains(t, container.Args, expectedArgs[i]) + require.Equal(t, expectedProbe[i], container.ReadinessProbe) + require.Contains(t, container.Ports, expectedPort[i]) + require.Contains(t, container.Env, expectedEnvVar) + } +} + func TestHandlerConsulDataplaneSidecar_Multiport(t *testing.T) { for _, aclsEnabled := range []bool{false, true} { name := fmt.Sprintf("acls enabled: %t", aclsEnabled) @@ -430,7 +581,6 @@ func TestHandlerConsulDataplaneSidecar_Multiport(t *testing.T) { InitialDelaySeconds: 1, } require.Equal(t, expectedProbe, container.ReadinessProbe) - require.Equal(t, expectedProbe, container.LivenessProbe) require.Nil(t, container.StartupProbe) } }) diff --git a/control-plane/connect-inject/webhook/redirect_traffic.go b/control-plane/connect-inject/webhook/redirect_traffic.go index 73040b39e7..eab23a2b91 100644 --- a/control-plane/connect-inject/webhook/redirect_traffic.go +++ b/control-plane/connect-inject/webhook/redirect_traffic.go @@ -52,6 +52,12 @@ func (w *MeshWebhook) iptablesConfigJSON(pod corev1.Pod, ns corev1.Namespace) (s return "", err } + // Exclude the port on which the proxy health check port will be configured if + // using the proxy health check for a service. + if useProxyHealthCheck(pod) { + cfg.ExcludeInboundPorts = append(cfg.ExcludeInboundPorts, strconv.Itoa(constants.ProxyDefaultHealthPort)) + } + if overwriteProbes { for i, container := range pod.Spec.Containers { // skip the "envoy-sidecar" container from having its probes overridden diff --git a/control-plane/connect-inject/webhook/redirect_traffic_test.go b/control-plane/connect-inject/webhook/redirect_traffic_test.go index 5ed660d96d..2ad9940fbe 100644 --- a/control-plane/connect-inject/webhook/redirect_traffic_test.go +++ b/control-plane/connect-inject/webhook/redirect_traffic_test.go @@ -72,6 +72,39 @@ func TestAddRedirectTrafficConfig(t *testing.T) { ExcludeUIDs: []string{"5996"}, }, }, + { + name: "proxy health checks enabled", + webhook: MeshWebhook{ + Log: logrtest.TestLogger{T: t}, + AllowK8sNamespacesSet: mapset.NewSetWith("*"), + DenyK8sNamespacesSet: mapset.NewSet(), + decoder: decoder, + }, + pod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: defaultNamespace, + Name: defaultPodName, + Annotations: map[string]string{ + constants.AnnotationUseProxyHealthCheck: "true", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "test", + }, + }, + }, + }, + expCfg: iptables.Config{ + ConsulDNSIP: "", + ProxyUserID: strconv.Itoa(sidecarUserAndGroupID), + ProxyInboundPort: constants.ProxyDefaultInboundPort, + ProxyOutboundPort: iptables.DefaultTProxyOutboundPort, + ExcludeUIDs: []string{"5996"}, + ExcludeInboundPorts: []string{"21000"}, + }, + }, { name: "metrics enabled", webhook: MeshWebhook{ From dc0f645f92f915ef16b1ac6120f0b5a42148aa08 Mon Sep 17 00:00:00 2001 From: jm96441n Date: Thu, 19 Jan 2023 13:11:03 -0500 Subject: [PATCH 044/340] Added first test for log command --- cli/cmd/proxy/log/command.go | 1 + cli/cmd/proxy/log/command_test.go | 51 +++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 cli/cmd/proxy/log/command.go create mode 100644 cli/cmd/proxy/log/command_test.go diff --git a/cli/cmd/proxy/log/command.go b/cli/cmd/proxy/log/command.go new file mode 100644 index 0000000000..7330d54052 --- /dev/null +++ b/cli/cmd/proxy/log/command.go @@ -0,0 +1 @@ +package log diff --git a/cli/cmd/proxy/log/command_test.go b/cli/cmd/proxy/log/command_test.go new file mode 100644 index 0000000000..f2970feaf8 --- /dev/null +++ b/cli/cmd/proxy/log/command_test.go @@ -0,0 +1,51 @@ +package log + +import ( + "bytes" + "context" + "io" + "os" + "testing" + + "github.com/hashicorp/consul-k8s/cli/common" + "github.com/hashicorp/consul-k8s/cli/common/terminal" + "github.com/hashicorp/go-hclog" + "github.com/stretchr/testify/require" +) + +func TestFlagParsing(t *testing.T) { + testCases := map[string]struct { + args []string + out int + }{ + "No args": { + args: []string{}, + out: 1, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + c := setupCommand(bytes.NewBuffer([]byte{})) + out := c.Run(tc.args) + require.Equal(t, tc.out, out) + }) + } +} + +func setupCommand(buf io.Writer) *LogCommand { + log := hclog.New(&hclog.LoggerOptions{ + Name: "test", + Level: hclog.Debug, + Output: os.Stdout, + }) + + command := &LogCommand{ + BaseCommand: &common.BaseCommand{ + Log: log, + UI: terminal.NewUI(context.Background(), buf), + }, + } + command.init() + return command +} From 0543b9394aa7a8b8af7ebca61eccd363c2eac1de Mon Sep 17 00:00:00 2001 From: jm96441n Date: Thu, 19 Jan 2023 13:25:26 -0500 Subject: [PATCH 045/340] Added initial flag parsing --- cli/cmd/proxy/log/command.go | 62 +++++++++++++++++++++++++++++++ cli/cmd/proxy/log/command_test.go | 4 ++ 2 files changed, 66 insertions(+) diff --git a/cli/cmd/proxy/log/command.go b/cli/cmd/proxy/log/command.go index 7330d54052..4959c47eb2 100644 --- a/cli/cmd/proxy/log/command.go +++ b/cli/cmd/proxy/log/command.go @@ -1 +1,63 @@ package log + +import ( + "errors" + "strings" + "sync" + + "github.com/hashicorp/consul-k8s/cli/common" + "github.com/hashicorp/consul-k8s/cli/common/flag" +) + +type LogCommand struct { + *common.BaseCommand + set *flag.Sets + + // Command Flags + podName string + + once sync.Once + help string +} + +var ErrMissingPodName = errors.New("Exactly one positional argument is requied: ") + +func (l *LogCommand) init() { + l.set = flag.NewSets() +} + +func (l *LogCommand) Run(args []string) int { + l.once.Do(l.init) + l.Log.ResetNamed("log") + defer common.CloseWithError(l.BaseCommand) + + err := l.parseFlags(args) + if err != nil { + return 1 + } + return 0 +} + +func (l *LogCommand) parseFlags(args []string) error { + positional := []string{} + // Separate positional args from keyed args + for _, arg := range args { + if strings.HasPrefix(arg, "-") { + break + } + positional = append(positional, arg) + } + keyed := args[len(positional):] + + if len(positional) != 1 { + return ErrMissingPodName + } + + l.podName = positional[0] + + err := l.set.Parse(keyed) + if err != nil { + return err + } + return nil +} diff --git a/cli/cmd/proxy/log/command_test.go b/cli/cmd/proxy/log/command_test.go index f2970feaf8..fb6b57a8cb 100644 --- a/cli/cmd/proxy/log/command_test.go +++ b/cli/cmd/proxy/log/command_test.go @@ -22,6 +22,10 @@ func TestFlagParsing(t *testing.T) { args: []string{}, out: 1, }, + "With pod name": { + args: []string{"now-this-is-pod-racing"}, + out: 0, + }, } for name, tc := range testCases { From 68ac06ef6dc96a6503d95d37e27b3806f4f27215 Mon Sep 17 00:00:00 2001 From: jm96441n Date: Thu, 19 Jan 2023 16:38:28 -0500 Subject: [PATCH 046/340] able to print and format output for getting current log levels for a proxy in a pod --- cli/cmd/proxy/log/command.go | 63 ------- cli/cmd/proxy/log/command_test.go | 55 ------ cli/cmd/proxy/loglevel/command.go | 223 +++++++++++++++++++++++++ cli/cmd/proxy/loglevel/command_test.go | 164 ++++++++++++++++++ cli/commands.go | 7 +- 5 files changed, 393 insertions(+), 119 deletions(-) delete mode 100644 cli/cmd/proxy/log/command.go delete mode 100644 cli/cmd/proxy/log/command_test.go create mode 100644 cli/cmd/proxy/loglevel/command.go create mode 100644 cli/cmd/proxy/loglevel/command_test.go diff --git a/cli/cmd/proxy/log/command.go b/cli/cmd/proxy/log/command.go deleted file mode 100644 index 4959c47eb2..0000000000 --- a/cli/cmd/proxy/log/command.go +++ /dev/null @@ -1,63 +0,0 @@ -package log - -import ( - "errors" - "strings" - "sync" - - "github.com/hashicorp/consul-k8s/cli/common" - "github.com/hashicorp/consul-k8s/cli/common/flag" -) - -type LogCommand struct { - *common.BaseCommand - set *flag.Sets - - // Command Flags - podName string - - once sync.Once - help string -} - -var ErrMissingPodName = errors.New("Exactly one positional argument is requied: ") - -func (l *LogCommand) init() { - l.set = flag.NewSets() -} - -func (l *LogCommand) Run(args []string) int { - l.once.Do(l.init) - l.Log.ResetNamed("log") - defer common.CloseWithError(l.BaseCommand) - - err := l.parseFlags(args) - if err != nil { - return 1 - } - return 0 -} - -func (l *LogCommand) parseFlags(args []string) error { - positional := []string{} - // Separate positional args from keyed args - for _, arg := range args { - if strings.HasPrefix(arg, "-") { - break - } - positional = append(positional, arg) - } - keyed := args[len(positional):] - - if len(positional) != 1 { - return ErrMissingPodName - } - - l.podName = positional[0] - - err := l.set.Parse(keyed) - if err != nil { - return err - } - return nil -} diff --git a/cli/cmd/proxy/log/command_test.go b/cli/cmd/proxy/log/command_test.go deleted file mode 100644 index fb6b57a8cb..0000000000 --- a/cli/cmd/proxy/log/command_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package log - -import ( - "bytes" - "context" - "io" - "os" - "testing" - - "github.com/hashicorp/consul-k8s/cli/common" - "github.com/hashicorp/consul-k8s/cli/common/terminal" - "github.com/hashicorp/go-hclog" - "github.com/stretchr/testify/require" -) - -func TestFlagParsing(t *testing.T) { - testCases := map[string]struct { - args []string - out int - }{ - "No args": { - args: []string{}, - out: 1, - }, - "With pod name": { - args: []string{"now-this-is-pod-racing"}, - out: 0, - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - c := setupCommand(bytes.NewBuffer([]byte{})) - out := c.Run(tc.args) - require.Equal(t, tc.out, out) - }) - } -} - -func setupCommand(buf io.Writer) *LogCommand { - log := hclog.New(&hclog.LoggerOptions{ - Name: "test", - Level: hclog.Debug, - Output: os.Stdout, - }) - - command := &LogCommand{ - BaseCommand: &common.BaseCommand{ - Log: log, - UI: terminal.NewUI(context.Background(), buf), - }, - } - command.init() - return command -} diff --git a/cli/cmd/proxy/loglevel/command.go b/cli/cmd/proxy/loglevel/command.go new file mode 100644 index 0000000000..83504e018c --- /dev/null +++ b/cli/cmd/proxy/loglevel/command.go @@ -0,0 +1,223 @@ +package loglevel + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "net/http" + "strings" + "sync" + + "github.com/hashicorp/consul-k8s/cli/common" + "github.com/hashicorp/consul-k8s/cli/common/flag" + "github.com/hashicorp/consul-k8s/cli/common/terminal" + helmCLI "helm.sh/helm/v3/pkg/cli" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" +) + +const defaultAdminPort int = 19000 + +type LoggerConfig map[string]string + +type LogCommand struct { + *common.BaseCommand + + kubernetes kubernetes.Interface + set *flag.Sets + + // Command Flags + podName string + + once sync.Once + help string + restConfig *rest.Config + logLevelFetcher func(context.Context, common.PortForwarder) (LoggerConfig, error) +} + +var ErrMissingPodName = errors.New("Exactly one positional argument is required: ") + +func (l *LogCommand) init() { + l.set = flag.NewSets() + l.help = l.set.Help() +} + +func (l *LogCommand) Run(args []string) int { + l.once.Do(l.init) + l.Log.ResetNamed("loglevel") + defer common.CloseWithError(l.BaseCommand) + + err := l.parseFlags(args) + if err != nil { + fmt.Println(err) + return 1 + } + + if l.logLevelFetcher == nil { + l.logLevelFetcher = FetchLogLevel + } + + err = l.initKubernetes() + if err != nil { + fmt.Println(err) + return 1 + } + + adminPorts, err := l.fetchAdminPorts() + if err != nil { + fmt.Println(err) + return 1 + } + + logLevels, err := l.fetchLogLevels(adminPorts) + if err != nil { + fmt.Println(err) + return 1 + } + l.outputLevels(logLevels) + return 0 +} + +func (l *LogCommand) parseFlags(args []string) error { + positional := []string{} + // Separate positional args from keyed args + for _, arg := range args { + if strings.HasPrefix(arg, "-") { + break + } + positional = append(positional, arg) + } + keyed := args[len(positional):] + + if len(positional) != 1 { + return ErrMissingPodName + } + + l.podName = positional[0] + + err := l.set.Parse(keyed) + if err != nil { + return err + } + return nil +} + +func (l *LogCommand) initKubernetes() error { + settings := helmCLI.New() + var err error + + if l.restConfig == nil { + l.restConfig, err = settings.RESTClientGetter().ToRESTConfig() + if err != nil { + return fmt.Errorf("error creating Kubernetes REST config %v", err) + } + + } + + if l.kubernetes == nil { + l.kubernetes, err = kubernetes.NewForConfig(l.restConfig) + if err != nil { + return fmt.Errorf("error creating Kubernetes client %v", err) + } + } + return nil +} + +func (l *LogCommand) fetchAdminPorts() (map[string]int, error) { + adminPorts := make(map[string]int, 0) + // TODO: support different namespaces + pod, err := l.kubernetes.CoreV1().Pods("default").Get(l.Ctx, l.podName, metav1.GetOptions{}) + if err != nil { + return adminPorts, err + } + + connectService, isMultiport := pod.Annotations["consul.hashicorp.com/connect-service"] + + if !isMultiport { + // Return the default port configuration. + adminPorts[l.podName] = defaultAdminPort + return adminPorts, nil + } + + for idx, svc := range strings.Split(connectService, ",") { + adminPorts[svc] = defaultAdminPort + idx + } + + return adminPorts, nil +} + +func (l *LogCommand) fetchLogLevels(adminPorts map[string]int) (map[string]LoggerConfig, error) { + loggers := make(map[string]LoggerConfig, 0) + + for name, port := range adminPorts { + pf := common.PortForward{ + Namespace: "default", // TODO: change this to use the configurable namespace + PodName: l.podName, + RemotePort: port, + KubeClient: l.kubernetes, + RestConfig: l.restConfig, + } + + logLevels, err := l.logLevelFetcher(l.Ctx, &pf) + if err != nil { + return loggers, err + } + loggers[name] = logLevels + } + return loggers, nil +} + +func FetchLogLevel(ctx context.Context, portForward common.PortForwarder) (LoggerConfig, error) { + endpoint, err := portForward.Open(ctx) + if err != nil { + return nil, err + } + + defer portForward.Close() + + response, err := http.Post(fmt.Sprintf("http://%s/logging", endpoint), "application/json", bytes.NewBuffer([]byte{})) + if err != nil { + return nil, err + } + body, err := io.ReadAll(response.Body) + loggers := strings.Split(string(body), "\n") + logLevels := make(map[string]string) + var name string + var level string + + // the first line here is just a header + for _, logger := range loggers[1:] { + if len(logger) == 0 { + continue + } + fmt.Sscanf(logger, "%s %s", &name, &level) + name = strings.TrimRight(name, ":") + logLevels[name] = level + } + return logLevels, nil +} + +func (l *LogCommand) Help() string { + l.once.Do(l.init) + return fmt.Sprintf("%s\n\nUsage: consul-k8s proxy log [flags]\n\n%s", l.Synopsis(), l.help) +} + +func (l *LogCommand) Synopsis() string { + return "Inspect and Modify the Envoy Log configuration for a given Pod." +} + +func (l *LogCommand) outputLevels(logLevels map[string]LoggerConfig) { + l.UI.Output(fmt.Sprintf("Envoy log configuration for %s in namespace default:", l.podName)) + for n, levels := range logLevels { + l.UI.Output(fmt.Sprintf("Log Levels for %s", n), terminal.WithHeaderStyle()) + table := terminal.NewTable("Name", "Level") + for name, level := range levels { + table.AddRow([]string{name, level}, []string{}) + } + l.UI.Table(table) + l.UI.Output("") + } +} diff --git a/cli/cmd/proxy/loglevel/command_test.go b/cli/cmd/proxy/loglevel/command_test.go new file mode 100644 index 0000000000..1cb575ba7c --- /dev/null +++ b/cli/cmd/proxy/loglevel/command_test.go @@ -0,0 +1,164 @@ +package loglevel + +import ( + "bytes" + "context" + "fmt" + "io" + "os" + "regexp" + "testing" + + "github.com/hashicorp/consul-k8s/cli/common" + "github.com/hashicorp/consul-k8s/cli/common/terminal" + "github.com/hashicorp/go-hclog" + "github.com/stretchr/testify/require" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/fake" +) + +func TestFlagParsing(t *testing.T) { + t.Parallel() + testCases := map[string]struct { + args []string + out int + }{ + "No args": { + args: []string{}, + out: 1, + }, + "With pod name": { + args: []string{"now-this-is-pod-racing"}, + out: 0, + }, + } + podName := "now-this-is-pod-racing" + fakePod := v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: podName, + Namespace: "default", + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + c := setupCommand(bytes.NewBuffer([]byte{})) + c.kubernetes = fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{fakePod}}) + c.logLevelFetcher = func(context.Context, common.PortForwarder) (LoggerConfig, error) { + return testLogConfig, nil + } + + out := c.Run(tc.args) + require.Equal(t, tc.out, out) + }) + } +} + +func TestOutputForGettingLogLevel(t *testing.T) { + t.Parallel() + podName := "now-this-is-pod-racing" + expectedHeader := fmt.Sprintf("Envoy log configuration for %s in namespace default:", podName) + fakePod := v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: podName, + Namespace: "default", + }, + } + + buf := bytes.NewBuffer([]byte{}) + c := setupCommand(buf) + c.logLevelFetcher = func(context.Context, common.PortForwarder) (LoggerConfig, error) { + return testLogConfig, nil + } + c.kubernetes = fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{fakePod}}) + + args := []string{podName} + out := c.Run(args) + require.Equal(t, 0, out) + + actual := buf.String() + + require.Regexp(t, expectedHeader, actual) + require.Regexp(t, "Log Levels for now-this-is-pod-racing", actual) + for logger, level := range testLogConfig { + require.Regexp(t, regexp.MustCompile(logger+`\s*`+level), actual) + } +} + +var testLogConfig = LoggerConfig{ + "admin": "debug", + "alternate_protocols_cache": "debug", + "aws": "debug", + "assert": "debug", + "backtrace": "debug", + "cache_filter": "debug", + "client": "debug", + "config": "debug", + "connection": "debug", + "conn_handler": "debug", + "decompression": "debug", + "dns": "debug", + "dubbo": "debug", + "envoy_bug": "debug", + "ext_authz": "debug", + "ext_proc": "debug", + "rocketmq": "debug", + "file": "debug", + "filter": "debug", + "forward_proxy": "debug", + "grpc": "debug", + "happy_eyeballs": "debug", + "hc": "debug", + "health_checker": "debug", + "http": "debug", + "http2": "debug", + "hystrix": "debug", + "init": "debug", + "io": "debug", + "jwt": "debug", + "kafka": "debug", + "key_value_store": "debug", + "lua": "debug", + "main": "debug", + "matcher": "debug", + "misc": "debug", + "mongo": "debug", + "multi_connection": "debug", + "oauth2": "debug", + "quic": "debug", + "quic_stream": "debug", + "pool": "debug", + "rbac": "debug", + "rds": "debug", + "redis": "debug", + "router": "debug", + "runtime": "debug", + "stats": "debug", + "secret": "debug", + "tap": "debug", + "testing": "debug", + "thrift": "debug", + "tracing": "debug", + "upstream": "debug", + "udp": "debug", + "wasm": "debug", + "websocket": "debug", +} + +func setupCommand(buf io.Writer) *LogCommand { + log := hclog.New(&hclog.LoggerOptions{ + Name: "test", + Level: hclog.Debug, + Output: os.Stdout, + }) + + command := &LogCommand{ + BaseCommand: &common.BaseCommand{ + Log: log, + UI: terminal.NewUI(context.Background(), buf), + }, + } + command.init() + return command +} diff --git a/cli/commands.go b/cli/commands.go index d29b4a8ad7..dc132eef9a 100644 --- a/cli/commands.go +++ b/cli/commands.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/consul-k8s/cli/cmd/install" "github.com/hashicorp/consul-k8s/cli/cmd/proxy" "github.com/hashicorp/consul-k8s/cli/cmd/proxy/list" + "github.com/hashicorp/consul-k8s/cli/cmd/proxy/loglevel" "github.com/hashicorp/consul-k8s/cli/cmd/proxy/read" "github.com/hashicorp/consul-k8s/cli/cmd/status" "github.com/hashicorp/consul-k8s/cli/cmd/uninstall" @@ -19,7 +20,6 @@ import ( ) func initializeCommands(ctx context.Context, log hclog.Logger) (*common.BaseCommand, map[string]cli.CommandFactory) { - baseCommand := &common.BaseCommand{ Ctx: ctx, Log: log, @@ -68,6 +68,11 @@ func initializeCommands(ctx context.Context, log hclog.Logger) (*common.BaseComm BaseCommand: baseCommand, }, nil }, + "proxy log": func() (cli.Command, error) { + return &loglevel.LogCommand{ + BaseCommand: baseCommand, + }, nil + }, } return baseCommand, commands From 27d148893d63d6be436453850c4ea684feef8ac4 Mon Sep 17 00:00:00 2001 From: jm96441n Date: Thu, 19 Jan 2023 17:05:37 -0500 Subject: [PATCH 047/340] Add test for FetchLogLevel --- cli/cmd/proxy/loglevel/command_test.go | 39 ++++++++++++ .../loglevel/testdata/fetch_debug_levels.txt | 59 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 cli/cmd/proxy/loglevel/testdata/fetch_debug_levels.txt diff --git a/cli/cmd/proxy/loglevel/command_test.go b/cli/cmd/proxy/loglevel/command_test.go index 1cb575ba7c..10f444f74e 100644 --- a/cli/cmd/proxy/loglevel/command_test.go +++ b/cli/cmd/proxy/loglevel/command_test.go @@ -5,8 +5,11 @@ import ( "context" "fmt" "io" + "net/http" + "net/http/httptest" "os" "regexp" + "strings" "testing" "github.com/hashicorp/consul-k8s/cli/common" @@ -86,6 +89,42 @@ func TestOutputForGettingLogLevel(t *testing.T) { } } +func TestHelp(t *testing.T) { + buf := bytes.NewBuffer([]byte{}) + c := setupCommand(buf) + expectedSynposis := "Inspect and Modify the Envoy Log configuration for a given Pod." + expectedUsage := `Usage: consul-k8s proxy log \[flags\]` + actual := c.Help() + require.Regexp(t, expectedSynposis, actual) + require.Regexp(t, expectedUsage, actual) +} + +func TestFetchLogLevel(t *testing.T) { + rawLogLevels, err := os.ReadFile("testdata/fetch_debug_levels.txt") + require.NoError(t, err) + mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write(rawLogLevels) + })) + + defer mockServer.Close() + + mpf := &mockPortForwarder{ + openBehavior: func(ctx context.Context) (string, error) { + return strings.Replace(mockServer.URL, "http://", "", 1), nil + }, + } + logLevels, err := FetchLogLevel(context.Background(), mpf) + require.NoError(t, err) + require.Equal(t, testLogConfig, logLevels) +} + +type mockPortForwarder struct { + openBehavior func(context.Context) (string, error) +} + +func (m *mockPortForwarder) Open(ctx context.Context) (string, error) { return m.openBehavior(ctx) } +func (m *mockPortForwarder) Close() {} + var testLogConfig = LoggerConfig{ "admin": "debug", "alternate_protocols_cache": "debug", diff --git a/cli/cmd/proxy/loglevel/testdata/fetch_debug_levels.txt b/cli/cmd/proxy/loglevel/testdata/fetch_debug_levels.txt new file mode 100644 index 0000000000..6b059dc1aa --- /dev/null +++ b/cli/cmd/proxy/loglevel/testdata/fetch_debug_levels.txt @@ -0,0 +1,59 @@ +active loggers: + admin: debug + alternate_protocols_cache: debug + aws: debug + assert: debug + backtrace: debug + cache_filter: debug + client: debug + config: debug + connection: debug + conn_handler: debug + decompression: debug + dns: debug + dubbo: debug + envoy_bug: debug + ext_authz: debug + ext_proc: debug + rocketmq: debug + file: debug + filter: debug + forward_proxy: debug + grpc: debug + happy_eyeballs: debug + hc: debug + health_checker: debug + http: debug + http2: debug + hystrix: debug + init: debug + io: debug + jwt: debug + kafka: debug + key_value_store: debug + lua: debug + main: debug + matcher: debug + misc: debug + mongo: debug + multi_connection: debug + oauth2: debug + quic: debug + quic_stream: debug + pool: debug + rbac: debug + rds: debug + redis: debug + router: debug + runtime: debug + stats: debug + secret: debug + tap: debug + testing: debug + thrift: debug + tracing: debug + upstream: debug + udp: debug + wasm: debug + websocket: debug + From 98cde9b6c805f7d4795058f1bf34862244ae4b09 Mon Sep 17 00:00:00 2001 From: jm96441n Date: Fri, 20 Jan 2023 10:55:58 -0500 Subject: [PATCH 048/340] Add color to log level output to help differentiate levels --- cli/cmd/proxy/loglevel/command.go | 16 +++++++++++++--- cli/cmd/proxy/loglevel/command_test.go | 2 +- cli/common/terminal/table.go | 18 ++++++++++++------ 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/cli/cmd/proxy/loglevel/command.go b/cli/cmd/proxy/loglevel/command.go index 83504e018c..f80eff08cc 100644 --- a/cli/cmd/proxy/loglevel/command.go +++ b/cli/cmd/proxy/loglevel/command.go @@ -23,6 +23,18 @@ const defaultAdminPort int = 19000 type LoggerConfig map[string]string +var ErrMissingPodName = errors.New("Exactly one positional argument is required: ") + +var levelToColor = map[string]string{ + "trace": terminal.Green, + "debug": terminal.HiWhite, + "info": terminal.Blue, + "warning": terminal.Yellow, + "error": terminal.Red, + "critical": terminal.Magenta, + "off": "", +} + type LogCommand struct { *common.BaseCommand @@ -38,8 +50,6 @@ type LogCommand struct { logLevelFetcher func(context.Context, common.PortForwarder) (LoggerConfig, error) } -var ErrMissingPodName = errors.New("Exactly one positional argument is required: ") - func (l *LogCommand) init() { l.set = flag.NewSets() l.help = l.set.Help() @@ -215,7 +225,7 @@ func (l *LogCommand) outputLevels(logLevels map[string]LoggerConfig) { l.UI.Output(fmt.Sprintf("Log Levels for %s", n), terminal.WithHeaderStyle()) table := terminal.NewTable("Name", "Level") for name, level := range levels { - table.AddRow([]string{name, level}, []string{}) + table.AddRow([]string{name, level}, []string{"", levelToColor[level]}) } l.UI.Table(table) l.UI.Output("") diff --git a/cli/cmd/proxy/loglevel/command_test.go b/cli/cmd/proxy/loglevel/command_test.go index 10f444f74e..f35ec6b715 100644 --- a/cli/cmd/proxy/loglevel/command_test.go +++ b/cli/cmd/proxy/loglevel/command_test.go @@ -85,7 +85,7 @@ func TestOutputForGettingLogLevel(t *testing.T) { require.Regexp(t, expectedHeader, actual) require.Regexp(t, "Log Levels for now-this-is-pod-racing", actual) for logger, level := range testLogConfig { - require.Regexp(t, regexp.MustCompile(logger+`\s*`+level), actual) + require.Regexp(t, regexp.MustCompile(logger+`.*`+level), actual) } } diff --git a/cli/common/terminal/table.go b/cli/common/terminal/table.go index 67278931a4..ac9a762609 100644 --- a/cli/common/terminal/table.go +++ b/cli/common/terminal/table.go @@ -5,15 +5,21 @@ import ( ) const ( - Yellow = "yellow" - Green = "green" - Red = "red" + Yellow = "yellow" + Green = "green" + Red = "red" + Blue = "blue" + Magenta = "magenta" + HiWhite = "hiwhite" ) var colorMapping = map[string]int{ - Green: tablewriter.FgGreenColor, - Yellow: tablewriter.FgYellowColor, - Red: tablewriter.FgRedColor, + Green: tablewriter.FgGreenColor, + Yellow: tablewriter.FgYellowColor, + Red: tablewriter.FgRedColor, + Blue: tablewriter.FgBlueColor, + Magenta: tablewriter.FgMagentaColor, + HiWhite: tablewriter.FgHiWhiteColor, } // Passed to UI.Table to provide a nicely formatted table. From 2dfc6c9ff54d9777f6d5a6f2a84b3bedbfa81f11 Mon Sep 17 00:00:00 2001 From: jm96441n Date: Fri, 20 Jan 2023 11:45:03 -0500 Subject: [PATCH 049/340] Added args for kubeconfig, kubecontext, and namespace, added comments, changed command name to `LogLevelCommand` --- cli/cmd/proxy/loglevel/command.go | 136 ++++++++++++++++++++----- cli/cmd/proxy/loglevel/command_test.go | 57 ++++++++++- cli/commands.go | 2 +- 3 files changed, 163 insertions(+), 32 deletions(-) diff --git a/cli/cmd/proxy/loglevel/command.go b/cli/cmd/proxy/loglevel/command.go index f80eff08cc..c622955bb4 100644 --- a/cli/cmd/proxy/loglevel/command.go +++ b/cli/cmd/proxy/loglevel/command.go @@ -13,13 +13,20 @@ import ( "github.com/hashicorp/consul-k8s/cli/common" "github.com/hashicorp/consul-k8s/cli/common/flag" "github.com/hashicorp/consul-k8s/cli/common/terminal" + "github.com/posener/complete" helmCLI "helm.sh/helm/v3/pkg/cli" + "k8s.io/apimachinery/pkg/api/validation" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" ) -const defaultAdminPort int = 19000 +const ( + defaultAdminPort int = 19000 + flagNameNamespace = "namespace" + flagNameKubeConfig = "kubeconfig" + flagNameKubeContext = "context" +) type LoggerConfig map[string]string @@ -35,14 +42,17 @@ var levelToColor = map[string]string{ "off": "", } -type LogCommand struct { +type LogLevelCommand struct { *common.BaseCommand kubernetes kubernetes.Interface set *flag.Sets // Command Flags - podName string + podName string + namespace string + kubeConfig string + kubeContext string once sync.Once help string @@ -50,19 +60,48 @@ type LogCommand struct { logLevelFetcher func(context.Context, common.PortForwarder) (LoggerConfig, error) } -func (l *LogCommand) init() { +func (l *LogLevelCommand) init() { l.set = flag.NewSets() + f := l.set.NewSet("Command Options") + f.StringVar(&flag.StringVar{ + Name: flagNameNamespace, + Target: &l.namespace, + Usage: "The namespace where the target Pod can be found.", + Aliases: []string{"n"}, + }) + + f = l.set.NewSet("Global Options") + f.StringVar(&flag.StringVar{ + Name: flagNameKubeConfig, + Aliases: []string{"c"}, + Target: &l.kubeConfig, + Usage: "Set the path to kubeconfig file.", + }) + f.StringVar(&flag.StringVar{ + Name: flagNameKubeContext, + Target: &l.kubeContext, + Usage: "Set the Kubernetes context to use.", + }) + l.help = l.set.Help() } -func (l *LogCommand) Run(args []string) int { +func (l *LogLevelCommand) Run(args []string) int { l.once.Do(l.init) l.Log.ResetNamed("loglevel") defer common.CloseWithError(l.BaseCommand) err := l.parseFlags(args) if err != nil { - fmt.Println(err) + l.UI.Output(err.Error(), terminal.WithErrorStyle()) + l.UI.Output(fmt.Sprintf("\n%s", l.Help())) + return 1 + } + + err = l.validateFlags() + if err != nil { + l.UI.Output(err.Error(), terminal.WithErrorStyle()) + l.UI.Output(fmt.Sprintf("\n%s", l.Help())) return 1 } @@ -72,26 +111,29 @@ func (l *LogCommand) Run(args []string) int { err = l.initKubernetes() if err != nil { - fmt.Println(err) + l.UI.Output(err.Error(), terminal.WithErrorStyle()) + l.UI.Output(fmt.Sprintf("\n%s", l.Help())) return 1 } adminPorts, err := l.fetchAdminPorts() if err != nil { - fmt.Println(err) + l.UI.Output(err.Error(), terminal.WithErrorStyle()) + l.UI.Output(fmt.Sprintf("\n%s", l.Help())) return 1 } logLevels, err := l.fetchLogLevels(adminPorts) if err != nil { - fmt.Println(err) + l.UI.Output(err.Error(), terminal.WithErrorStyle()) + l.UI.Output(fmt.Sprintf("\n%s", l.Help())) return 1 } l.outputLevels(logLevels) return 0 } -func (l *LogCommand) parseFlags(args []string) error { +func (l *LogLevelCommand) parseFlags(args []string) error { positional := []string{} // Separate positional args from keyed args for _, arg := range args { @@ -115,10 +157,25 @@ func (l *LogCommand) parseFlags(args []string) error { return nil } -func (l *LogCommand) initKubernetes() error { +func (l *LogLevelCommand) validateFlags() error { + if errs := validation.ValidateNamespaceName(l.namespace, false); l.namespace != "" && len(errs) > 0 { + return fmt.Errorf("invalid namespace name passed for -namespace/-n: %v", strings.Join(errs, "; ")) + } + return nil +} + +func (l *LogLevelCommand) initKubernetes() error { settings := helmCLI.New() var err error + if l.kubeConfig != "" { + settings.KubeConfig = l.kubeConfig + } + + if l.kubeContext != "" { + settings.KubeContext = l.kubeContext + } + if l.restConfig == nil { l.restConfig, err = settings.RESTClientGetter().ToRESTConfig() if err != nil { @@ -133,13 +190,17 @@ func (l *LogCommand) initKubernetes() error { return fmt.Errorf("error creating Kubernetes client %v", err) } } + if l.namespace == "" { + l.namespace = settings.Namespace() + } + return nil } -func (l *LogCommand) fetchAdminPorts() (map[string]int, error) { +// fetchAdminPorts retrieves all admin ports for Envoy Proxies running in a pod given namespace +func (l *LogLevelCommand) fetchAdminPorts() (map[string]int, error) { adminPorts := make(map[string]int, 0) - // TODO: support different namespaces - pod, err := l.kubernetes.CoreV1().Pods("default").Get(l.Ctx, l.podName, metav1.GetOptions{}) + pod, err := l.kubernetes.CoreV1().Pods(l.namespace).Get(l.Ctx, l.podName, metav1.GetOptions{}) if err != nil { return adminPorts, err } @@ -159,12 +220,12 @@ func (l *LogCommand) fetchAdminPorts() (map[string]int, error) { return adminPorts, nil } -func (l *LogCommand) fetchLogLevels(adminPorts map[string]int) (map[string]LoggerConfig, error) { +func (l *LogLevelCommand) fetchLogLevels(adminPorts map[string]int) (map[string]LoggerConfig, error) { loggers := make(map[string]LoggerConfig, 0) for name, port := range adminPorts { pf := common.PortForward{ - Namespace: "default", // TODO: change this to use the configurable namespace + Namespace: l.namespace, PodName: l.podName, RemotePort: port, KubeClient: l.kubernetes, @@ -180,6 +241,8 @@ func (l *LogCommand) fetchLogLevels(adminPorts map[string]int) (map[string]Logge return loggers, nil } +// FetchLogLevel requests the logging endpoint from Envoy Admin Interface for a given port +// more can be read about that endpoint https://www.envoyproxy.io/docs/envoy/latest/operations/admin#post--logging func FetchLogLevel(ctx context.Context, portForward common.PortForwarder) (LoggerConfig, error) { endpoint, err := portForward.Open(ctx) if err != nil { @@ -188,7 +251,8 @@ func FetchLogLevel(ctx context.Context, portForward common.PortForwarder) (Logge defer portForward.Close() - response, err := http.Post(fmt.Sprintf("http://%s/logging", endpoint), "application/json", bytes.NewBuffer([]byte{})) + // this endpoint does not support returning json, so we've gotta parse the plain text + response, err := http.Post(fmt.Sprintf("http://%s/logging", endpoint), "text/plain", bytes.NewBuffer([]byte{})) if err != nil { return nil, err } @@ -210,16 +274,7 @@ func FetchLogLevel(ctx context.Context, portForward common.PortForwarder) (Logge return logLevels, nil } -func (l *LogCommand) Help() string { - l.once.Do(l.init) - return fmt.Sprintf("%s\n\nUsage: consul-k8s proxy log [flags]\n\n%s", l.Synopsis(), l.help) -} - -func (l *LogCommand) Synopsis() string { - return "Inspect and Modify the Envoy Log configuration for a given Pod." -} - -func (l *LogCommand) outputLevels(logLevels map[string]LoggerConfig) { +func (l *LogLevelCommand) outputLevels(logLevels map[string]LoggerConfig) { l.UI.Output(fmt.Sprintf("Envoy log configuration for %s in namespace default:", l.podName)) for n, levels := range logLevels { l.UI.Output(fmt.Sprintf("Log Levels for %s", n), terminal.WithHeaderStyle()) @@ -231,3 +286,30 @@ func (l *LogCommand) outputLevels(logLevels map[string]LoggerConfig) { l.UI.Output("") } } + +func (l *LogLevelCommand) Help() string { + l.once.Do(l.init) + return fmt.Sprintf("%s\n\nUsage: consul-k8s proxy log [flags]\n\n%s", l.Synopsis(), l.help) +} + +func (l *LogLevelCommand) Synopsis() string { + return "Inspect and Modify the Envoy Log configuration for a given Pod." +} + +// AutocompleteFlags returns a mapping of supported flags and autocomplete +// options for this command. The map key for the Flags map should be the +// complete flag such as "-foo" or "--foo". +func (l *LogLevelCommand) AutocompleteFlags() complete.Flags { + return complete.Flags{ + fmt.Sprintf("-%s", flagNameNamespace): complete.PredictNothing, + fmt.Sprintf("-%s", flagNameKubeConfig): complete.PredictFiles("*"), + fmt.Sprintf("-%s", flagNameKubeContext): complete.PredictNothing, + } +} + +// AutocompleteArgs returns the argument predictor for this command. +// Since argument completion is not supported, this will return +// complete.PredictNothing. +func (l *LogLevelCommand) AutocompleteArgs() complete.Predictor { + return complete.PredictNothing +} diff --git a/cli/cmd/proxy/loglevel/command_test.go b/cli/cmd/proxy/loglevel/command_test.go index f35ec6b715..9206ea1ba6 100644 --- a/cli/cmd/proxy/loglevel/command_test.go +++ b/cli/cmd/proxy/loglevel/command_test.go @@ -21,7 +21,7 @@ import ( "k8s.io/client-go/kubernetes/fake" ) -func TestFlagParsing(t *testing.T) { +func TestFlagParsingFails(t *testing.T) { t.Parallel() testCases := map[string]struct { args []string @@ -31,9 +31,17 @@ func TestFlagParsing(t *testing.T) { args: []string{}, out: 1, }, - "With pod name": { - args: []string{"now-this-is-pod-racing"}, - out: 0, + "Multiple podnames passed": { + args: []string{"podname", "podname2"}, + out: 1, + }, + "Nonexistent flag passed, -foo bar": { + args: []string{"podName", "-foo", "bar"}, + out: 1, + }, + "Invalid argument passed, -namespace YOLO": { + args: []string{"podName", "-namespace", "YOLO"}, + out: 1, }, } podName := "now-this-is-pod-racing" @@ -58,6 +66,47 @@ func TestFlagParsing(t *testing.T) { } } +func TestFlagParsingSucceeds(t *testing.T) { + t.Parallel() + testCases := map[string]struct { + args []string + podNamespace string + out int + }{ + "With single pod name": { + args: []string{"now-this-is-pod-racing"}, + podNamespace: "default", + out: 0, + }, + "With single pod name and namespace": { + args: []string{"now-this-is-pod-racing", "-n", "another"}, + podNamespace: "another", + out: 0, + }, + } + podName := "now-this-is-pod-racing" + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + fakePod := v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: podName, + Namespace: tc.podNamespace, + }, + } + + c := setupCommand(bytes.NewBuffer([]byte{})) + c.kubernetes = fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{fakePod}}) + c.logLevelFetcher = func(context.Context, common.PortForwarder) (LoggerConfig, error) { + return testLogConfig, nil + } + + out := c.Run(tc.args) + require.Equal(t, tc.out, out) + }) + } +} + func TestOutputForGettingLogLevel(t *testing.T) { t.Parallel() podName := "now-this-is-pod-racing" diff --git a/cli/commands.go b/cli/commands.go index dc132eef9a..2946873e51 100644 --- a/cli/commands.go +++ b/cli/commands.go @@ -69,7 +69,7 @@ func initializeCommands(ctx context.Context, log hclog.Logger) (*common.BaseComm }, nil }, "proxy log": func() (cli.Command, error) { - return &loglevel.LogCommand{ + return &loglevel.LogLevelCommand{ BaseCommand: baseCommand, }, nil }, From bcedf88b368d79df7d2daff8433a9b03382f6e01 Mon Sep 17 00:00:00 2001 From: jm96441n Date: Fri, 20 Jan 2023 11:52:37 -0500 Subject: [PATCH 050/340] Clean up, ran linter --- cli/cmd/proxy/loglevel/command.go | 5 ++++- cli/cmd/proxy/loglevel/command_test.go | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cli/cmd/proxy/loglevel/command.go b/cli/cmd/proxy/loglevel/command.go index c622955bb4..4b4ea2d347 100644 --- a/cli/cmd/proxy/loglevel/command.go +++ b/cli/cmd/proxy/loglevel/command.go @@ -197,7 +197,7 @@ func (l *LogLevelCommand) initKubernetes() error { return nil } -// fetchAdminPorts retrieves all admin ports for Envoy Proxies running in a pod given namespace +// fetchAdminPorts retrieves all admin ports for Envoy Proxies running in a pod given namespace. func (l *LogLevelCommand) fetchAdminPorts() (map[string]int, error) { adminPorts := make(map[string]int, 0) pod, err := l.kubernetes.CoreV1().Pods(l.namespace).Get(l.Ctx, l.podName, metav1.GetOptions{}) @@ -257,6 +257,9 @@ func FetchLogLevel(ctx context.Context, portForward common.PortForwarder) (Logge return nil, err } body, err := io.ReadAll(response.Body) + if err != nil { + return nil, fmt.Errorf("failed to reach envoy: %v", err) + } loggers := strings.Split(string(body), "\n") logLevels := make(map[string]string) var name string diff --git a/cli/cmd/proxy/loglevel/command_test.go b/cli/cmd/proxy/loglevel/command_test.go index 9206ea1ba6..083416d836 100644 --- a/cli/cmd/proxy/loglevel/command_test.go +++ b/cli/cmd/proxy/loglevel/command_test.go @@ -234,14 +234,14 @@ var testLogConfig = LoggerConfig{ "websocket": "debug", } -func setupCommand(buf io.Writer) *LogCommand { +func setupCommand(buf io.Writer) *LogLevelCommand { log := hclog.New(&hclog.LoggerOptions{ Name: "test", Level: hclog.Debug, Output: os.Stdout, }) - command := &LogCommand{ + command := &LogLevelCommand{ BaseCommand: &common.BaseCommand{ Log: log, UI: terminal.NewUI(context.Background(), buf), From 0706360c1f23e02ad17799b9252a3bf2902b7788 Mon Sep 17 00:00:00 2001 From: jm96441n Date: Fri, 20 Jan 2023 12:03:26 -0500 Subject: [PATCH 051/340] Clean up variable usage in test --- cli/cmd/proxy/loglevel/command_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/cmd/proxy/loglevel/command_test.go b/cli/cmd/proxy/loglevel/command_test.go index 083416d836..c79eba9726 100644 --- a/cli/cmd/proxy/loglevel/command_test.go +++ b/cli/cmd/proxy/loglevel/command_test.go @@ -68,23 +68,23 @@ func TestFlagParsingFails(t *testing.T) { func TestFlagParsingSucceeds(t *testing.T) { t.Parallel() + podName := "now-this-is-pod-racing" testCases := map[string]struct { args []string podNamespace string out int }{ "With single pod name": { - args: []string{"now-this-is-pod-racing"}, + args: []string{podName}, podNamespace: "default", out: 0, }, "With single pod name and namespace": { - args: []string{"now-this-is-pod-racing", "-n", "another"}, + args: []string{podName, "-n", "another"}, podNamespace: "another", out: 0, }, } - podName := "now-this-is-pod-racing" for name, tc := range testCases { t.Run(name, func(t *testing.T) { From 1f877511b1338c462eae9fd549a18d446df74b3f Mon Sep 17 00:00:00 2001 From: jm96441n Date: Fri, 20 Jan 2023 13:05:23 -0500 Subject: [PATCH 052/340] Removed unnecessary variable type --- cli/cmd/proxy/loglevel/command.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cli/cmd/proxy/loglevel/command.go b/cli/cmd/proxy/loglevel/command.go index 4b4ea2d347..0b4e7fb5df 100644 --- a/cli/cmd/proxy/loglevel/command.go +++ b/cli/cmd/proxy/loglevel/command.go @@ -22,10 +22,10 @@ import ( ) const ( - defaultAdminPort int = 19000 - flagNameNamespace = "namespace" - flagNameKubeConfig = "kubeconfig" - flagNameKubeContext = "context" + defaultAdminPort = 19000 + flagNameNamespace = "namespace" + flagNameKubeConfig = "kubeconfig" + flagNameKubeContext = "context" ) type LoggerConfig map[string]string From ee55c0167e11ae957e54c42df1798f4da4f7b7ac Mon Sep 17 00:00:00 2001 From: jm96441n Date: Fri, 20 Jan 2023 17:24:37 -0500 Subject: [PATCH 053/340] PR feedback, DRY-ing up some error handling logic, cleaning up validation, check bounds of returned slice for envoy parsing, check status code of response from envoy --- cli/cmd/proxy/loglevel/command.go | 57 +++++++++++++++++--------- cli/cmd/proxy/loglevel/command_test.go | 2 + 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/cli/cmd/proxy/loglevel/command.go b/cli/cmd/proxy/loglevel/command.go index 0b4e7fb5df..aa47d6c391 100644 --- a/cli/cmd/proxy/loglevel/command.go +++ b/cli/cmd/proxy/loglevel/command.go @@ -30,7 +30,10 @@ const ( type LoggerConfig map[string]string -var ErrMissingPodName = errors.New("Exactly one positional argument is required: ") +var ( + ErrIncorrectArgFormat = errors.New("Exactly one positional argument is required: ") + ErrNoLoggersReturned = errors.New("No loggers were returned from Envoy") +) var levelToColor = map[string]string{ "trace": terminal.Green, @@ -93,16 +96,11 @@ func (l *LogLevelCommand) Run(args []string) int { err := l.parseFlags(args) if err != nil { - l.UI.Output(err.Error(), terminal.WithErrorStyle()) - l.UI.Output(fmt.Sprintf("\n%s", l.Help())) - return 1 + return l.logOutputAndDie(err) } - err = l.validateFlags() if err != nil { - l.UI.Output(err.Error(), terminal.WithErrorStyle()) - l.UI.Output(fmt.Sprintf("\n%s", l.Help())) - return 1 + return l.logOutputAndDie(err) } if l.logLevelFetcher == nil { @@ -111,29 +109,27 @@ func (l *LogLevelCommand) Run(args []string) int { err = l.initKubernetes() if err != nil { - l.UI.Output(err.Error(), terminal.WithErrorStyle()) - l.UI.Output(fmt.Sprintf("\n%s", l.Help())) - return 1 + return l.logOutputAndDie(err) } adminPorts, err := l.fetchAdminPorts() if err != nil { - l.UI.Output(err.Error(), terminal.WithErrorStyle()) - l.UI.Output(fmt.Sprintf("\n%s", l.Help())) - return 1 + return l.logOutputAndDie(err) } logLevels, err := l.fetchLogLevels(adminPorts) if err != nil { - l.UI.Output(err.Error(), terminal.WithErrorStyle()) - l.UI.Output(fmt.Sprintf("\n%s", l.Help())) - return 1 + return l.logOutputAndDie(err) } l.outputLevels(logLevels) return 0 } func (l *LogLevelCommand) parseFlags(args []string) error { + if len(args) == 0 { + return ErrIncorrectArgFormat + } + positional := []string{} // Separate positional args from keyed args for _, arg := range args { @@ -145,7 +141,7 @@ func (l *LogLevelCommand) parseFlags(args []string) error { keyed := args[len(positional):] if len(positional) != 1 { - return ErrMissingPodName + return ErrIncorrectArgFormat } l.podName = positional[0] @@ -158,9 +154,15 @@ func (l *LogLevelCommand) parseFlags(args []string) error { } func (l *LogLevelCommand) validateFlags() error { - if errs := validation.ValidateNamespaceName(l.namespace, false); l.namespace != "" && len(errs) > 0 { + if l.namespace == "" { + return nil + } + + errs := validation.ValidateNamespaceName(l.namespace, false) + if len(errs) > 0 { return fmt.Errorf("invalid namespace name passed for -namespace/-n: %v", strings.Join(errs, "; ")) } + return nil } @@ -256,11 +258,21 @@ func FetchLogLevel(ctx context.Context, portForward common.PortForwarder) (Logge if err != nil { return nil, err } + body, err := io.ReadAll(response.Body) if err != nil { return nil, fmt.Errorf("failed to reach envoy: %v", err) } + + if response.StatusCode >= 400 { + return nil, fmt.Errorf("call to envoy failed with status code: %d, and message: %s", response.StatusCode, body) + } + loggers := strings.Split(string(body), "\n") + if len(loggers) == 0 { + return nil, ErrNoLoggersReturned + } + logLevels := make(map[string]string) var name string var level string @@ -274,6 +286,7 @@ func FetchLogLevel(ctx context.Context, portForward common.PortForwarder) (Logge name = strings.TrimRight(name, ":") logLevels[name] = level } + return logLevels, nil } @@ -316,3 +329,9 @@ func (l *LogLevelCommand) AutocompleteFlags() complete.Flags { func (l *LogLevelCommand) AutocompleteArgs() complete.Predictor { return complete.PredictNothing } + +func (l *LogLevelCommand) logOutputAndDie(err error) int { + l.UI.Output(err.Error(), terminal.WithErrorStyle()) + l.UI.Output(fmt.Sprintf("\n%s", l.Help())) + return 1 +} diff --git a/cli/cmd/proxy/loglevel/command_test.go b/cli/cmd/proxy/loglevel/command_test.go index c79eba9726..6207270c20 100644 --- a/cli/cmd/proxy/loglevel/command_test.go +++ b/cli/cmd/proxy/loglevel/command_test.go @@ -139,6 +139,7 @@ func TestOutputForGettingLogLevel(t *testing.T) { } func TestHelp(t *testing.T) { + t.Parallel() buf := bytes.NewBuffer([]byte{}) c := setupCommand(buf) expectedSynposis := "Inspect and Modify the Envoy Log configuration for a given Pod." @@ -149,6 +150,7 @@ func TestHelp(t *testing.T) { } func TestFetchLogLevel(t *testing.T) { + t.Parallel() rawLogLevels, err := os.ReadFile("testdata/fetch_debug_levels.txt") require.NoError(t, err) mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { From 7425f125b475543321fceb864d86b5dcce0a0d1c Mon Sep 17 00:00:00 2001 From: jm96441n Date: Fri, 20 Jan 2023 17:27:15 -0500 Subject: [PATCH 054/340] Move log name setting to the init function --- cli/cmd/proxy/loglevel/command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/cmd/proxy/loglevel/command.go b/cli/cmd/proxy/loglevel/command.go index aa47d6c391..80444d7339 100644 --- a/cli/cmd/proxy/loglevel/command.go +++ b/cli/cmd/proxy/loglevel/command.go @@ -64,6 +64,7 @@ type LogLevelCommand struct { } func (l *LogLevelCommand) init() { + l.Log.ResetNamed("loglevel") l.set = flag.NewSets() f := l.set.NewSet("Command Options") f.StringVar(&flag.StringVar{ @@ -91,7 +92,6 @@ func (l *LogLevelCommand) init() { func (l *LogLevelCommand) Run(args []string) int { l.once.Do(l.init) - l.Log.ResetNamed("loglevel") defer common.CloseWithError(l.BaseCommand) err := l.parseFlags(args) From d47cfd3b9739d1210d00501784ff4276cfee46b5 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Fri, 27 Jan 2023 11:41:18 -0500 Subject: [PATCH 055/340] Bump Kubernetes versions for clouds for acceptance tests (#1852) --- .circleci/config.yml | 57 ++++++++++++------------ CHANGELOG.md | 3 +- README.md | 2 +- charts/consul/README.md | 2 +- charts/consul/test/terraform/aks/main.tf | 19 ++++---- charts/consul/test/terraform/eks/main.tf | 48 ++++++++++++++++++-- charts/consul/test/terraform/gke/main.tf | 13 ++---- 7 files changed, 90 insertions(+), 54 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index df5dd49573..f55e540f13 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -573,7 +573,7 @@ jobs: - checkout - install-prereqs - create-kind-clusters: - version: "v1.25.3" + version: "v1.26.0" - restore_cache: keys: - consul-helm-modcache-v2-{{ checksum "acceptance/go.mod" }} @@ -606,7 +606,7 @@ jobs: - checkout - install-prereqs - create-kind-clusters: - version: "v1.25.3" + version: "v1.26.0" - restore_cache: keys: - consul-helm-modcache-v2-{{ checksum "acceptance/go.mod" }} @@ -723,7 +723,7 @@ jobs: ############################# # CLOUD ACCEPTANCE TEST JOBS ############################# - acceptance-gke-1-23: + acceptance-gke-1-25: parallelism: 2 environment: - TEST_RESULTS: /tmp/test-results @@ -773,7 +773,7 @@ jobs: - run: mkdir -p $TEST_RESULTS - run-acceptance-tests: - additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-pod-security-policies -enable-transparent-proxy -consul-image=hashicorppreview/consul-enterprise:1.15-dev-23aaa4f83845d0e2eced9ea69f731d7eedf840d1 + additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -consul-image=hashicorppreview/consul-enterprise:1.15-dev-23aaa4f83845d0e2eced9ea69f731d7eedf840d1 - store_test_results: path: /tmp/test-results @@ -792,7 +792,7 @@ jobs: fail_only: true failure_message: "GKE acceptance tests failed. Check the logs at: ${CIRCLE_BUILD_URL}" - acceptance-gke-cni-1-23: + acceptance-gke-cni-1-25: parallelism: 2 environment: - TEST_RESULTS: /tmp/test-results @@ -842,7 +842,7 @@ jobs: - run: mkdir -p $TEST_RESULTS - run-acceptance-tests: - additional-flags: -use-gke -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-pod-security-policies -enable-transparent-proxy -enable-cni -consul-image=hashicorppreview/consul-enterprise:1.15-dev-23aaa4f83845d0e2eced9ea69f731d7eedf840d1 + additional-flags: -use-gke -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -enable-cni -consul-image=hashicorppreview/consul-enterprise:1.15-dev-23aaa4f83845d0e2eced9ea69f731d7eedf840d1 - store_test_results: path: /tmp/test-results @@ -861,7 +861,7 @@ jobs: fail_only: true failure_message: "GKE CNI acceptance tests failed. Check the logs at: ${CIRCLE_BUILD_URL}" - acceptance-aks-1-22: + acceptance-aks-1-24: parallelism: 3 environment: - TEST_RESULTS: /tmp/test-results @@ -918,7 +918,7 @@ jobs: fail_only: true failure_message: "AKS acceptance tests failed. Check the logs at: ${CIRCLE_BUILD_URL}" - acceptance-aks-cni-1-22: + acceptance-aks-cni-1-24: parallelism: 3 environment: - TEST_RESULTS: /tmp/test-results @@ -974,7 +974,7 @@ jobs: fail_only: true failure_message: "AKS CNI acceptance tests failed. Check the logs at: ${CIRCLE_BUILD_URL}" - acceptance-eks-1-21: + acceptance-eks-1-23: parallelism: 3 environment: - TEST_RESULTS: /tmp/test-results @@ -1037,7 +1037,7 @@ jobs: fail_only: true failure_message: "EKS acceptance tests failed. Check the logs at: ${CIRCLE_BUILD_URL}" - acceptance-eks-cni-1-21: + acceptance-eks-cni-1-23: parallelism: 3 environment: - TEST_RESULTS: /tmp/test-results @@ -1193,7 +1193,7 @@ jobs: - slack/status: channel: *slack-channel fail_only: true - failure_message: "Acceptance tests against Kind with Kubernetes v1.23 with Consul 1.12 nightly failed. Check the logs at: ${CIRCLE_BUILD_URL}" + failure_message: "Acceptance tests against Kind with Kubernetes v1.25 with Consul 1.12 nightly failed. Check the logs at: ${CIRCLE_BUILD_URL}" acceptance-kind-1-23-consul-compat-nightly-1-13: environment: @@ -1234,7 +1234,7 @@ jobs: - slack/status: channel: *slack-channel fail_only: true - failure_message: "Acceptance tests against Kind with Kubernetes v1.23 with Consul 1.13 nightly failed. Check the logs at: ${CIRCLE_BUILD_URL}" + failure_message: "Acceptance tests against Kind with Kubernetes v1.25 with Consul 1.13 nightly failed. Check the logs at: ${CIRCLE_BUILD_URL}" ######################## # WORKFLOWS @@ -1264,16 +1264,17 @@ workflows: - acceptance: context: consul-ci requires: - - dev-upload-docker + - dev-upload-docker - acceptance-tproxy-cni: context: consul-ci requires: - - dev-upload-docker + - dev-upload-docker - acceptance-tproxy: context: consul-ci requires: - dev-upload-docker + nightly-cleanup: triggers: - schedule: @@ -1310,15 +1311,15 @@ workflows: - build-distros-linux # Disable until we can use UBI images. # - acceptance-openshift - - acceptance-gke-1-23: + - acceptance-gke-1-25: requires: - - dev-upload-docker - - acceptance-gke-cni-1-23: + - dev-upload-docker + - acceptance-gke-cni-1-25: requires: - - acceptance-gke-1-23 + - acceptance-gke-1-25 - acceptance-tproxy: requires: - - dev-upload-docker + - dev-upload-docker nightly-acceptance-tests-main: description: | @@ -1342,24 +1343,24 @@ workflows: - build-distros-linux # Disable until we can use UBI images. # - acceptance-openshift - - acceptance-gke-1-23: + - acceptance-gke-1-25: requires: - dev-upload-docker - - acceptance-gke-cni-1-23: + - acceptance-gke-cni-1-25: requires: - - acceptance-gke-1-23 - - acceptance-eks-1-21: + - acceptance-gke-1-25 + - acceptance-eks-1-23: requires: - dev-upload-docker - - acceptance-eks-cni-1-21: + - acceptance-eks-cni-1-23: requires: - - acceptance-eks-1-21 - - acceptance-aks-1-22: + - acceptance-eks-1-23 + - acceptance-aks-1-24: requires: - dev-upload-docker - - acceptance-aks-cni-1-22: + - acceptance-aks-cni-1-24: requires: - - acceptance-aks-1-22 + - acceptance-aks-1-24 - acceptance-tproxy: requires: - dev-upload-docker diff --git a/CHANGELOG.md b/CHANGELOG.md index d4cf05b4f6..bf1197c5ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,13 @@ IMPROVEMENTS: * Helm: + * Kubernetes v1.26 is now supported. Minimum tested version of Kubernetes is now v1.23. [[GH-1852](https://github.com/hashicorp/consul-k8s/pull/1852)] * Add a `global.extraLabels` stanza to allow setting global Kubernetes labels for all components deployed by the `consul-k8s` Helm chart. [[GH-1778](https://github.com/hashicorp/consul-k8s/pull/1778)] * Add the `accessLogs` field to the `ProxyDefaults` CRD. [[GH-1816](https://github.com/hashicorp/consul-k8s/pull/1816)] * Add the `envoyExtensions` field to the `ProxyDefaults` and `ServiceDefaults` CRD. [[GH-1823]](https://github.com/hashicorp/consul-k8s/pull/1823) * Add the `balanceInboundConnections` field to the `ServiceDefaults` CRD. [[GH-1823]](https://github.com/hashicorp/consul-k8s/pull/1823) * Control-Plane - * Add support for the annotation `consul.hashicorp.com/use-proxy-health-check`. When this annotation is used by a service, it configures a readiness endpoint on Consul Dataplane and queries it instead of the proxy's inbound port which forwards requests to the application. [[GH-1824](https://github.com/hashicorp/consul-k8s/pull/1824)], [[GH-1841](https://github.com/hashicorp/consul-k8s/pull/1824)] + * Add support for the annotation `consul.hashicorp.com/use-proxy-health-check`. When this annotation is used by a service, it configures a readiness endpoint on Consul Dataplane and queries it instead of the proxy's inbound port which forwards requests to the application. [[GH-1824](https://github.com/hashicorp/consul-k8s/pull/1824)], [[GH-1841](https://github.com/hashicorp/consul-k8s/pull/1841)] * Add health check for synced services based on the status of the Kubernetes readiness probe on synced pod. [[GH-1821](https://github.com/hashicorp/consul-k8s/pull/1821)] BUG FIXES: diff --git a/README.md b/README.md index aafddfbc29..1d3a3733ab 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ by contacting us at [security@hashicorp.com](mailto:security@hashicorp.com). The following pre-requisites must be met before installing Consul on Kubernetes. - * **Kubernetes 1.22.x - 1.25.x** - This represents the earliest versions of Kubernetes tested. + * **Kubernetes 1.23.x - 1.26.x** - This represents the earliest versions of Kubernetes tested. It is possible that this chart works with earlier versions, but it is untested. * Helm install diff --git a/charts/consul/README.md b/charts/consul/README.md index 79b3fc4a68..e7d7fd9285 100644 --- a/charts/consul/README.md +++ b/charts/consul/README.md @@ -42,7 +42,7 @@ by contacting us at [security@hashicorp.com](mailto:security@hashicorp.com). The following pre-requisites must be met before installing Consul on Kubernetes. - * **Kubernetes 1.22.x - 1.25.x** - This represents the earliest versions of Kubernetes tested. + * **Kubernetes 1.23.x - 1.26.x** - This represents the earliest versions of Kubernetes tested. It is possible that this chart works with earlier versions, but it is untested. * Helm install diff --git a/charts/consul/test/terraform/aks/main.tf b/charts/consul/test/terraform/aks/main.tf index 784a60d9ef..1db5145531 100644 --- a/charts/consul/test/terraform/aks/main.tf +++ b/charts/consul/test/terraform/aks/main.tf @@ -1,5 +1,5 @@ provider "azurerm" { - version = "2.90.0" + version = "3.40.0" features {} } @@ -40,12 +40,13 @@ resource "azurerm_virtual_network_peering" "default" { } resource "azurerm_kubernetes_cluster" "default" { - count = var.cluster_count - name = "consul-k8s-${random_id.suffix[count.index].dec}" - location = azurerm_resource_group.default[count.index].location - resource_group_name = azurerm_resource_group.default[count.index].name - dns_prefix = "consul-k8s-${random_id.suffix[count.index].dec}" - kubernetes_version = "1.22.11" + count = var.cluster_count + name = "consul-k8s-${random_id.suffix[count.index].dec}" + location = azurerm_resource_group.default[count.index].location + resource_group_name = azurerm_resource_group.default[count.index].name + dns_prefix = "consul-k8s-${random_id.suffix[count.index].dec}" + kubernetes_version = "1.24.6" + role_based_access_control_enabled = true // We're setting the network plugin and other network properties explicitly // here even though they are the same as defaults to ensure that none of these CIDRs @@ -77,10 +78,6 @@ resource "azurerm_kubernetes_cluster" "default" { client_secret = var.client_secret } - role_based_access_control { - enabled = true - } - tags = var.tags } diff --git a/charts/consul/test/terraform/eks/main.tf b/charts/consul/test/terraform/eks/main.tf index 9ccc2cdd2b..ca48a5a8fe 100644 --- a/charts/consul/test/terraform/eks/main.tf +++ b/charts/consul/test/terraform/eks/main.tf @@ -3,8 +3,8 @@ provider "aws" { region = var.region assume_role { - role_arn = var.role_arn - duration_seconds = 2700 + role_arn = var.role_arn + duration = "2700s" } } @@ -58,8 +58,9 @@ module "eks" { kubeconfig_api_version = "client.authentication.k8s.io/v1beta1" cluster_name = "consul-k8s-${random_id.suffix[count.index].dec}" - cluster_version = "1.21" + cluster_version = "1.23" subnets = module.vpc[count.index].private_subnets + enable_irsa = true vpc_id = module.vpc[count.index].vpc_id @@ -80,6 +81,47 @@ module "eks" { tags = var.tags } +resource "aws_iam_role" "csi-driver-role" { + count = var.cluster_count + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Action = "sts:AssumeRoleWithWebIdentity", + Principal = { + Federated = module.eks[count.index].oidc_provider_arn + }, + Condition = { + StringEquals = { + join(":", [trimprefix(module.eks[count.index].cluster_oidc_issuer_url, "https://"), "aud"]) = ["sts.amazonaws.com"], + join(":", [trimprefix(module.eks[count.index].cluster_oidc_issuer_url, "https://"), "sub"]) = ["system:serviceaccount:kube-system:ebs-csi-controller-sa"], + } + } + } + ] + }) +} + +data "aws_iam_policy" "csi-driver-policy" { + name = "AmazonEBSCSIDriverPolicy" +} + +resource "aws_iam_role_policy_attachment" "csi" { + count = var.cluster_count + role = aws_iam_role.csi-driver-role[count.index].name + policy_arn = data.aws_iam_policy.csi-driver-policy.arn +} + +resource "aws_eks_addon" "csi-driver" { + count = var.cluster_count + cluster_name = module.eks[count.index].cluster_id + addon_name = "aws-ebs-csi-driver" + addon_version = "v1.15.0-eksbuild.1" + service_account_role_arn = aws_iam_role.csi-driver-role[count.index].arn + resolve_conflicts = "OVERWRITE" +} + data "aws_eks_cluster" "cluster" { count = var.cluster_count name = module.eks[count.index].cluster_id diff --git a/charts/consul/test/terraform/gke/main.tf b/charts/consul/test/terraform/gke/main.tf index 1574df36b3..1bd574ce2c 100644 --- a/charts/consul/test/terraform/gke/main.tf +++ b/charts/consul/test/terraform/gke/main.tf @@ -1,4 +1,4 @@ -provider "google-beta" { +provider "google" { project = var.project version = "~> 3.49.0" } @@ -10,13 +10,12 @@ resource "random_id" "suffix" { data "google_container_engine_versions" "main" { location = var.zone - version_prefix = "1.23." + version_prefix = "1.25." } resource "google_container_cluster" "cluster" { - provider = "google-beta" - - count = var.cluster_count + provider = "google" + count = var.cluster_count name = "consul-k8s-${random_id.suffix[count.index].dec}" project = var.project @@ -28,10 +27,6 @@ resource "google_container_cluster" "cluster" { tags = ["consul-k8s-${random_id.suffix[count.index].dec}"] machine_type = "e2-standard-4" } - pod_security_policy_config { - enabled = true - } - resource_labels = var.labels } From d531159748727cd6cb956f938ef3387ecf5fc126 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Wed, 1 Feb 2023 14:42:17 -0500 Subject: [PATCH 056/340] Update main to latest consul & consul-dataplane versions (late Jan 2023) (#1865) --- CHANGELOG.md | 26 ++++++++++++++++++++++++++ charts/consul/Chart.yaml | 6 +++--- charts/consul/values.yaml | 4 ++-- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf1197c5ae..db8162167e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,32 @@ BUG FIXES: * Control Plane * Don't incorrectly diff intention config entries when upgrading from Consul pre-1.12 to 1.12+ [[GH-1804](https://github.com/hashicorp/consul-k8s/pull/1804)] +## 1.0.3 (January 30, 2023) + +IMPROVEMENTS: +* Helm: + * Kubernetes v1.26 is now supported. Minimum tested version of Kubernetes is now v1.23. [[GH-1852](https://github.com/hashicorp/consul-k8s/pull/1852)] + * Add a `global.extraLabels` stanza to allow setting global Kubernetes labels for all components deployed by the `consul-k8s` Helm chart. [[GH-1778](https://github.com/hashicorp/consul-k8s/pull/1778)] +* Control-Plane + * Add support for the annotation `consul.hashicorp.com/use-proxy-health-check`. When this annotation is used by a service, it configures a readiness endpoint on Consul Dataplane and queries it instead of the proxy's inbound port which forwards requests to the application. [[GH-1824](https://github.com/hashicorp/consul-k8s/pull/1824)], [[GH-1841](https://github.com/hashicorp/consul-k8s/pull/1841)] + * Add health check for synced services based on the status of the Kubernetes readiness probe on synced pod. [[GH-1821](https://github.com/hashicorp/consul-k8s/pull/1821)] + +BUG FIXES: +* Control Plane + +## 0.49.3 (January 30, 2023) + +IMPROVEMENTS: +* Helm: + * Add a `global.extraLabels` stanza to allow setting global Kubernetes labels for all components deployed by the `consul-k8s` Helm chart. [[GH-1778](https://github.com/hashicorp/consul-k8s/pull/1778)] +* Control-Plane + * Add support for the annotation `consul.hashicorp.com/use-proxy-health-check`. When this annotation is used by a service, it configures a readiness endpoint on Consul Dataplane and queries it instead of the proxy's inbound port which forwards requests to the application. [[GH-1824](https://github.com/hashicorp/consul-k8s/pull/1824)], [[GH-1843](https://github.com/hashicorp/consul-k8s/pull/1843)] + * Add health check for synced services based on the status of the Kubernetes readiness probe on synced pod. [[GH-1821](https://github.com/hashicorp/consul-k8s/pull/1821)] + +BUG FIXES: +* Control Plane + * Don't incorrectly diff intention config entries when upgrading from Consul pre-1.12 to 1.12+ [[GH-1804](https://github.com/hashicorp/consul-k8s/pull/1804)] + ## 1.0.2 (December 1, 2022) IMPROVEMENTS: diff --git a/charts/consul/Chart.yaml b/charts/consul/Chart.yaml index 253291defc..41b95611e6 100644 --- a/charts/consul/Chart.yaml +++ b/charts/consul/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 name: consul version: 1.1.0-dev -appVersion: 1.14.2 +appVersion: 1.14.4 kubeVersion: ">=1.21.0-0" description: Official HashiCorp Consul Chart home: https://www.consul.io @@ -13,11 +13,11 @@ annotations: artifacthub.io/prerelease: true artifacthub.io/images: | - name: consul - image: hashicorp/consul:1.14.2 + image: hashicorp/consul:1.14.4 - name: consul-k8s-control-plane image: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.1.0-dev - name: consul-dataplane - image: hashicorp/consul-dataplane:1.0.0 + image: hashicorp/consul-dataplane:1.0.1 - name: envoy image: envoyproxy/envoy:v1.23.1 artifacthub.io/license: MPL-2.0 diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index f2f909fa81..ed1ed4cccb 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -63,7 +63,7 @@ global: # image: "hashicorp/consul-enterprise:1.10.0-ent" # ``` # @default: hashicorp/consul: - image: "hashicorp/consul:1.14.2" + image: "hashicorp/consul:1.14.4" # Array of objects containing image pull secret names that will be applied to each service account. # This can be used to reference image pull secrets if using a custom consul or consul-k8s-control-plane Docker image. @@ -542,7 +542,7 @@ global: # The name (and tag) of the consul-dataplane Docker image used for the # connect-injected sidecar proxies and mesh, terminating, and ingress gateways. # @default: hashicorp/consul-dataplane: - imageConsulDataplane: "hashicorp/consul-dataplane:1.0.0" + imageConsulDataplane: "hashicorp/consul-dataplane:1.0.1" # Configuration for running this Helm chart on the Red Hat OpenShift platform. # This Helm chart currently supports OpenShift v4.x+. From eea6c5b5eeb1eed14bb86cb2daa9cb5f03c1a7f7 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Thu, 2 Feb 2023 13:01:54 -0500 Subject: [PATCH 057/340] missing a line in the changelog (#1868) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index db8162167e..3dc03f3479 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ IMPROVEMENTS: BUG FIXES: * Control Plane + * Don't incorrectly diff intention config entries when upgrading from Consul pre-1.12 to 1.12+ [[GH-1804](https://github.com/hashicorp/consul-k8s/pull/1804)] ## 0.49.3 (January 30, 2023) From 02896eb2e1db7ddd394f2251bc74ede52cd85f1c Mon Sep 17 00:00:00 2001 From: Luke Kysow <1034429+lkysow@users.noreply.github.com> Date: Thu, 2 Feb 2023 11:38:24 -0800 Subject: [PATCH 058/340] Exclude openebs namespace from injection. (#1869) * Exclude openebs namespace from injection. OpenEBS is a Kubernetes storage solution. When you spin up a PVC, under the hood OpenEBS creates a pod to handle the necessary storage operations. If the openebs namespace is not excluded from injection, that pod can't start because our mutatingwebhook config requires all pod scheduling requests make it to our webhook and our webhook isn't running yet because the consul servers aren't running. This is a breaking change but I think it's worth it because it's very unlikely anyone is using the openebs namespace for anything other than openebs. * Changelog --- CHANGELOG.md | 15 +++++++++++++++ charts/consul/values.yaml | 8 ++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dc03f3479..9d1aafcc01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ ## UNRELEASED +BREAKING CHANGES: +* Helm: + * Change defaults to exclude the `openebs` namespace from sidecar injection. If you previously had pods in that namespace + that you wanted to be injected, you must now set `namespaceSelector` as follows: + + ```yaml + connectInject: + namespaceSelector: | + matchExpressions: + - key: "kubernetes.io/metadata.name" + operator: "NotIn" + values: ["kube-system","local-path-storage"] + ``` + [[GH-1869](https://github.com/hashicorp/consul-k8s/pull/1869)] + IMPROVEMENTS: * Helm: * Kubernetes v1.26 is now supported. Minimum tested version of Kubernetes is now v1.23. [[GH-1852](https://github.com/hashicorp/consul-k8s/pull/1852)] diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index ed1ed4cccb..63c2a63cbf 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -2140,9 +2140,9 @@ connectInject: # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector # for more details. # - # By default, we exclude the kube-system namespace since usually users won't - # want those pods injected and also the local-path-storage namespace so that - # Kind (Kubernetes In Docker) can provision Pods used to create PVCs. + # By default, we exclude kube-system since usually users won't + # want those pods injected and local-path-storage and openebs so that + # Kind (Kubernetes In Docker) and OpenEBS (https://openebs.io/) respectively can provision Pods used to create PVCs. # Note that this exclusion is only supported in Kubernetes v1.21.1+. # # Example: @@ -2157,7 +2157,7 @@ connectInject: matchExpressions: - key: "kubernetes.io/metadata.name" operator: "NotIn" - values: ["kube-system","local-path-storage"] + values: ["kube-system","local-path-storage","openebs"] # List of k8s namespaces to allow Connect sidecar # injection in. If a k8s namespace is not included or is listed in `k8sDenyNamespaces`, From d08dd73cab4884a362b4bb554476c5ce44ed1c93 Mon Sep 17 00:00:00 2001 From: DanStough Date: Thu, 26 Jan 2023 18:11:03 -0500 Subject: [PATCH 059/340] feat: add peer to service-defaults overrides --- CHANGELOG.md | 1 + .../consul/templates/crd-servicedefaults.yaml | 20 +++-- .../api/v1alpha1/servicedefaults_types.go | 27 ++++++- .../v1alpha1/servicedefaults_types_test.go | 73 ++++++++++++++++++- .../consul.hashicorp.com_servicedefaults.yaml | 20 +++-- control-plane/go.mod | 2 +- control-plane/go.sum | 4 + 7 files changed, 128 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d1aafcc01..98335b1481 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ IMPROVEMENTS: * Add the `accessLogs` field to the `ProxyDefaults` CRD. [[GH-1816](https://github.com/hashicorp/consul-k8s/pull/1816)] * Add the `envoyExtensions` field to the `ProxyDefaults` and `ServiceDefaults` CRD. [[GH-1823]](https://github.com/hashicorp/consul-k8s/pull/1823) * Add the `balanceInboundConnections` field to the `ServiceDefaults` CRD. [[GH-1823]](https://github.com/hashicorp/consul-k8s/pull/1823) + * Add the `upstreamConfig.overrides[].peer` field to the `ServiceDefaults` CRD. [[GH-1853]](https://github.com/hashicorp/consul-k8s/pull/1853) * Control-Plane * Add support for the annotation `consul.hashicorp.com/use-proxy-health-check`. When this annotation is used by a service, it configures a readiness endpoint on Consul Dataplane and queries it instead of the proxy's inbound port which forwards requests to the application. [[GH-1824](https://github.com/hashicorp/consul-k8s/pull/1824)], [[GH-1841](https://github.com/hashicorp/consul-k8s/pull/1841)] * Add health check for synced services based on the status of the Kubernetes readiness probe on synced pod. [[GH-1821](https://github.com/hashicorp/consul-k8s/pull/1821)] diff --git a/charts/consul/templates/crd-servicedefaults.yaml b/charts/consul/templates/crd-servicedefaults.yaml index 128884c454..5c6ecc7476 100644 --- a/charts/consul/templates/crd-servicedefaults.yaml +++ b/charts/consul/templates/crd-servicedefaults.yaml @@ -258,15 +258,15 @@ spec: type: string type: object name: - description: Name is only accepted within a service-defaults + description: Name is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides config entry. type: string namespace: - description: Namespace is only accepted within a service-defaults + description: Namespace is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides config entry. type: string partition: - description: Partition is only accepted within a service-defaults + description: Partition is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides config entry. type: string passiveHealthCheck: @@ -291,6 +291,10 @@ spec: format: int32 type: integer type: object + peer: + description: Peer is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides + config entry. + type: string protocol: description: Protocol describes the upstream's service protocol. Valid values are "tcp", "http" and "grpc". Anything else @@ -357,15 +361,15 @@ spec: type: string type: object name: - description: Name is only accepted within a service-defaults + description: Name is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides config entry. type: string namespace: - description: Namespace is only accepted within a service-defaults + description: Namespace is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides config entry. type: string partition: - description: Partition is only accepted within a service-defaults + description: Partition is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides config entry. type: string passiveHealthCheck: @@ -392,6 +396,10 @@ spec: format: int32 type: integer type: object + peer: + description: Peer is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides + config entry. + type: string protocol: description: Protocol describes the upstream's service protocol. Valid values are "tcp", "http" and "grpc". Anything else diff --git a/control-plane/api/v1alpha1/servicedefaults_types.go b/control-plane/api/v1alpha1/servicedefaults_types.go index 2682f6a28a..06da8b1d2c 100644 --- a/control-plane/api/v1alpha1/servicedefaults_types.go +++ b/control-plane/api/v1alpha1/servicedefaults_types.go @@ -7,7 +7,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "github.com/hashicorp/consul-k8s/control-plane/api/common" capi "github.com/hashicorp/consul/api" "github.com/miekg/dns" corev1 "k8s.io/api/core/v1" @@ -15,6 +14,8 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" + + "github.com/hashicorp/consul-k8s/control-plane/api/common" ) const ( @@ -113,12 +114,14 @@ type Upstreams struct { } type Upstream struct { - // Name is only accepted within a service-defaults config entry. + // Name is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides config entry. Name string `json:"name,omitempty"` - // Namespace is only accepted within a service-defaults config entry. + // Namespace is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides config entry. Namespace string `json:"namespace,omitempty"` - // Partition is only accepted within a service-defaults config entry. + // Partition is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides config entry. Partition string `json:"partition,omitempty"` + // Peer is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides config entry. + Peer string `json:"peer,omitempty"` // EnvoyListenerJSON is a complete override ("escape hatch") for the upstream's // listener. // Note: This escape hatch is NOT compatible with the discovery chain and @@ -374,10 +377,25 @@ func (in *Upstream) validate(path *field.Path, kind string, partitionsEnabled bo if in.Name != "" { errs = append(errs, field.Invalid(path.Child("name"), in.Name, "upstream.name for a default upstream must be \"\"")) } + if in.Namespace != "" { + errs = append(errs, field.Invalid(path.Child("namespace"), in.Namespace, "upstream.namespace for a default upstream must be \"\"")) + } + if in.Partition != "" { + errs = append(errs, field.Invalid(path.Child("partition"), in.Partition, "upstream.partition for a default upstream must be \"\"")) + } + if in.Peer != "" { + errs = append(errs, field.Invalid(path.Child("peer"), in.Peer, "upstream.peer for a default upstream must be \"\"")) + } } else if kind == overrideUpstream { if in.Name == "" { errs = append(errs, field.Invalid(path.Child("name"), in.Name, "upstream.name for an override upstream cannot be \"\"")) } + if in.Namespace != "" && in.Peer != "" { + errs = append(errs, field.Invalid(path, in, "both namespace and peer cannot be specified.")) + } + if in.Partition != "" && in.Peer != "" { + errs = append(errs, field.Invalid(path, in, "both partition and peer cannot be specified.")) + } } if !partitionsEnabled && in.Partition != "" { errs = append(errs, field.Invalid(path.Child("partition"), in.Partition, "Consul Enterprise Admin Partitions must be enabled to set upstream.partition")) @@ -396,6 +414,7 @@ func (in *Upstream) toConsul() *capi.UpstreamConfig { Name: in.Name, Namespace: in.Namespace, Partition: in.Partition, + Peer: in.Peer, EnvoyListenerJSON: in.EnvoyListenerJSON, EnvoyClusterJSON: in.EnvoyClusterJSON, Protocol: in.Protocol, diff --git a/control-plane/api/v1alpha1/servicedefaults_types_test.go b/control-plane/api/v1alpha1/servicedefaults_types_test.go index fb29cf15cc..33ec6d2f40 100644 --- a/control-plane/api/v1alpha1/servicedefaults_types_test.go +++ b/control-plane/api/v1alpha1/servicedefaults_types_test.go @@ -5,12 +5,13 @@ import ( "testing" "time" - "github.com/hashicorp/consul-k8s/control-plane/api/common" capi "github.com/hashicorp/consul/api" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/pointer" + + "github.com/hashicorp/consul-k8s/control-plane/api/common" ) func TestServiceDefaults_ToConsul(t *testing.T) { @@ -854,6 +855,21 @@ func TestServiceDefaults_Validate(t *testing.T) { }, expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.upstreamConfig.defaults.name: Invalid value: "foobar": upstream.name for a default upstream must be ""`, }, + "upstreamConfig.defaults.namespace": { + input: &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-service", + }, + Spec: ServiceDefaultsSpec{ + UpstreamConfig: &Upstreams{ + Defaults: &Upstream{ + Namespace: "foobar", + }, + }, + }, + }, + expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.upstreamConfig.defaults.namespace: Invalid value: "foobar": upstream.namespace for a default upstream must be ""`, + }, "upstreamConfig.defaults.partition": { input: &ServiceDefaults{ ObjectMeta: metav1.ObjectMeta{ @@ -868,7 +884,22 @@ func TestServiceDefaults_Validate(t *testing.T) { }, }, partitionsEnabled: false, - expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.upstreamConfig.defaults.partition: Invalid value: "upstream": Consul Enterprise Admin Partitions must be enabled to set upstream.partition`, + expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: [spec.upstreamConfig.defaults.partition: Invalid value: "upstream": upstream.partition for a default upstream must be "", spec.upstreamConfig.defaults.partition: Invalid value: "upstream": Consul Enterprise Admin Partitions must be enabled to set upstream.partition]`, + }, + "upstreamConfig.defaults.peer": { + input: &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-service", + }, + Spec: ServiceDefaultsSpec{ + UpstreamConfig: &Upstreams{ + Defaults: &Upstream{ + Peer: "foobar", + }, + }, + }, + }, + expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.upstreamConfig.defaults.peer: Invalid value: "foobar": upstream.peer for a default upstream must be ""`, }, "upstreamConfig.overrides.meshGateway": { input: &ServiceDefaults{ @@ -925,6 +956,44 @@ func TestServiceDefaults_Validate(t *testing.T) { }, expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.upstreamConfig.overrides[0].partition: Invalid value: "upstream": Consul Enterprise Admin Partitions must be enabled to set upstream.partition`, }, + "upstreamConfig.overrides.partition and namespace": { + input: &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-service", + }, + Spec: ServiceDefaultsSpec{ + UpstreamConfig: &Upstreams{ + Overrides: []*Upstream{ + { + Name: "service", + Namespace: "namespace", + Peer: "peer", + }, + }, + }, + }, + }, + expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.upstreamConfig.overrides[0]: Invalid value: v1alpha1.Upstream{Name:"service", Namespace:"namespace", Partition:"", Peer:"peer", EnvoyListenerJSON:"", EnvoyClusterJSON:"", Protocol:"", ConnectTimeoutMs:0, Limits:(*v1alpha1.UpstreamLimits)(nil), PassiveHealthCheck:(*v1alpha1.PassiveHealthCheck)(nil), MeshGateway:v1alpha1.MeshGateway{Mode:""}}: both namespace and peer cannot be specified.`, + }, + "upstreamConfig.overrides.partition and peer": { + input: &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-service", + }, + Spec: ServiceDefaultsSpec{ + UpstreamConfig: &Upstreams{ + Overrides: []*Upstream{ + { + Name: "service", + Partition: "upstream", + Peer: "peer", + }, + }, + }, + }, + }, + expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: [spec.upstreamConfig.overrides[0]: Invalid value: v1alpha1.Upstream{Name:"service", Namespace:"", Partition:"upstream", Peer:"peer", EnvoyListenerJSON:"", EnvoyClusterJSON:"", Protocol:"", ConnectTimeoutMs:0, Limits:(*v1alpha1.UpstreamLimits)(nil), PassiveHealthCheck:(*v1alpha1.PassiveHealthCheck)(nil), MeshGateway:v1alpha1.MeshGateway{Mode:""}}: both partition and peer cannot be specified., spec.upstreamConfig.overrides[0].partition: Invalid value: "upstream": Consul Enterprise Admin Partitions must be enabled to set upstream.partition]`, + }, "multi-error": { input: &ServiceDefaults{ ObjectMeta: metav1.ObjectMeta{ diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml index 8b05eeb025..4f335a923d 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml @@ -251,15 +251,15 @@ spec: type: string type: object name: - description: Name is only accepted within a service-defaults + description: Name is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides config entry. type: string namespace: - description: Namespace is only accepted within a service-defaults + description: Namespace is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides config entry. type: string partition: - description: Partition is only accepted within a service-defaults + description: Partition is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides config entry. type: string passiveHealthCheck: @@ -284,6 +284,10 @@ spec: format: int32 type: integer type: object + peer: + description: Peer is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides + config entry. + type: string protocol: description: Protocol describes the upstream's service protocol. Valid values are "tcp", "http" and "grpc". Anything else @@ -350,15 +354,15 @@ spec: type: string type: object name: - description: Name is only accepted within a service-defaults + description: Name is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides config entry. type: string namespace: - description: Namespace is only accepted within a service-defaults + description: Namespace is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides config entry. type: string partition: - description: Partition is only accepted within a service-defaults + description: Partition is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides config entry. type: string passiveHealthCheck: @@ -385,6 +389,10 @@ spec: format: int32 type: integer type: object + peer: + description: Peer is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides + config entry. + type: string protocol: description: Protocol describes the upstream's service protocol. Valid values are "tcp", "http" and "grpc". Anything else diff --git a/control-plane/go.mod b/control-plane/go.mod index 2cd0300557..d5805fe558 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -10,7 +10,7 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af65262de8 github.com/hashicorp/consul-server-connection-manager v0.1.0 - github.com/hashicorp/consul/api v1.10.1-0.20230106171340-8d923c178919 + github.com/hashicorp/consul/api v1.10.1-0.20230203155153-2f149d60ccbf github.com/hashicorp/consul/sdk v0.13.0 github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f github.com/hashicorp/go-hclog v1.2.2 diff --git a/control-plane/go.sum b/control-plane/go.sum index 7542ac12d9..d1473ae24f 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -348,6 +348,10 @@ github.com/hashicorp/consul-server-connection-manager v0.1.0/go.mod h1:XVVlO+Yk7 github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.10.1-0.20230106171340-8d923c178919 h1:8aVegJMSv7PIAAa1zqQQ0CT4TKv+Nf7I4rhE6+uDa1U= github.com/hashicorp/consul/api v1.10.1-0.20230106171340-8d923c178919/go.mod h1:c1u8FzGHcavbEtRW/p1YditvfMgn4QsKNgz2rnCDF7c= +github.com/hashicorp/consul/api v1.10.1-0.20230126204442-a43eadd1225b h1:dNIQYhru10Hg+E1oEL8f9CX6MC+8CW5JuQ4jk3g70LA= +github.com/hashicorp/consul/api v1.10.1-0.20230126204442-a43eadd1225b/go.mod h1:c1u8FzGHcavbEtRW/p1YditvfMgn4QsKNgz2rnCDF7c= +github.com/hashicorp/consul/api v1.10.1-0.20230203155153-2f149d60ccbf h1:vvsHghmX3LyNUaDe7onYKHyDiny+ystdHKIEujbNj4Q= +github.com/hashicorp/consul/api v1.10.1-0.20230203155153-2f149d60ccbf/go.mod h1:c1u8FzGHcavbEtRW/p1YditvfMgn4QsKNgz2rnCDF7c= github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3usQMXZF6ziK3SzXU= github.com/hashicorp/consul/proto-public v0.1.0/go.mod h1:vs2KkuWwtjkIgA5ezp4YKPzQp4GitV+q/+PvksrA92k= github.com/hashicorp/consul/sdk v0.4.1-0.20221021205723-cc843c4be892 h1:jw0NwPmNPr5CxAU04hACdj61JSaJBKZ0FdBo+kwfNp4= From f562a092ea6dcc1ec8243930a0c9dc62af44971a Mon Sep 17 00:00:00 2001 From: DanStough Date: Thu, 26 Jan 2023 18:11:44 -0500 Subject: [PATCH 060/340] chore: fix generation for peering CRDs --- hack/copy-crds-to-chart/main.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/hack/copy-crds-to-chart/main.go b/hack/copy-crds-to-chart/main.go index 7276c468f8..7085bdb9e6 100644 --- a/hack/copy-crds-to-chart/main.go +++ b/hack/copy-crds-to-chart/main.go @@ -9,6 +9,14 @@ import ( "strings" ) +var ( + // HACK IT! + requiresPeering = map[string]struct{}{ + "consul.hashicorp.com_peeringacceptors.yaml": {}, + "consul.hashicorp.com_peeringdialers.yaml": {}, + } +) + func main() { if len(os.Args) != 1 { fmt.Println("Usage: go run ./...") @@ -43,8 +51,13 @@ func realMain(helmPath string) error { // Strip leading newline. contents = strings.TrimPrefix(contents, "\n") - // Add {{- if .Values.connectInject.enabled }} {{- end }} wrapper. - contents = fmt.Sprintf("{{- if .Values.connectInject.enabled }}\n%s{{- end }}\n", contents) + if _, ok := requiresPeering[info.Name()]; ok { + // Add {{- if and .Values.connectInject.enabled .Values.global.peering.enabled }} {{- end }} wrapper. + contents = fmt.Sprintf("{{- if and .Values.connectInject.enabled .Values.global.peering.enabled }}\n%s{{- end }}\n", contents) + } else { + // Add {{- if .Values.connectInject.enabled }} {{- end }} wrapper. + contents = fmt.Sprintf("{{- if .Values.connectInject.enabled }}\n%s{{- end }}\n", contents) + } // Add labels, this is hacky because we're relying on the line number // but it means we don't need to regex or yaml parse. From 10e188c2db299e871510bd051637512e68021a69 Mon Sep 17 00:00:00 2001 From: Jared Kirschner <85913323+jkirschner-hashicorp@users.noreply.github.com> Date: Sun, 5 Feb 2023 21:03:39 -0500 Subject: [PATCH 061/340] Refine server TLS Vault PKI role config (#1877) The generate_lease=true configuration is unnecessary and generates a note about performance implications in Vault logs. Remove this configuration so that the default value of generate_lease=false is used instead. --- acceptance/framework/vault/helpers.go | 1 - 1 file changed, 1 deletion(-) diff --git a/acceptance/framework/vault/helpers.go b/acceptance/framework/vault/helpers.go index 6ebfb5cf62..4726e246ae 100644 --- a/acceptance/framework/vault/helpers.go +++ b/acceptance/framework/vault/helpers.go @@ -40,7 +40,6 @@ func ConfigurePKICerts(t *testing.T, "allow_bare_domains": "true", "allow_localhost": "true", "allow_subdomains": "true", - "generate_lease": "true", "max_ttl": maxTTL, } From c61785fe37338c4860f2714f876c897850e3a9f3 Mon Sep 17 00:00:00 2001 From: David Yu Date: Mon, 6 Feb 2023 18:00:55 -0800 Subject: [PATCH 062/340] Dockerfile: Remove gnupg from Consul k8s container (#1882) * Remove gnupg * Update CHANGELOG.md --- CHANGELOG.md | 1 + control-plane/Dockerfile | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98335b1481..277535e2fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ IMPROVEMENTS: * Control-Plane * Add support for the annotation `consul.hashicorp.com/use-proxy-health-check`. When this annotation is used by a service, it configures a readiness endpoint on Consul Dataplane and queries it instead of the proxy's inbound port which forwards requests to the application. [[GH-1824](https://github.com/hashicorp/consul-k8s/pull/1824)], [[GH-1841](https://github.com/hashicorp/consul-k8s/pull/1841)] * Add health check for synced services based on the status of the Kubernetes readiness probe on synced pod. [[GH-1821](https://github.com/hashicorp/consul-k8s/pull/1821)] + * Remove extraneous `gnupg` depdency from `consul-k8s-control-plane` since it is no longer needed for validating binary artifacts prior to release. [[GH-1882](https://github.com/hashicorp/consul-k8s/pull/1882)] BUG FIXES: * Control Plane diff --git a/control-plane/Dockerfile b/control-plane/Dockerfile index 3e31c92ef6..ee20f8bb61 100644 --- a/control-plane/Dockerfile +++ b/control-plane/Dockerfile @@ -89,7 +89,7 @@ LABEL name=${BIN_NAME} \ ENV BIN_NAME=${BIN_NAME} ENV VERSION=${PRODUCT_VERSION} -RUN apk add --no-cache ca-certificates gnupg libcap openssl su-exec iputils libc6-compat iptables +RUN apk add --no-cache ca-certificates libcap openssl su-exec iputils libc6-compat iptables # TARGETOS and TARGETARCH are set automatically when --platform is provided. ARG TARGETOS @@ -152,7 +152,7 @@ ARG TARGETARCH # Copy license for Red Hat certification. COPY LICENSE /licenses/mozilla.txt -RUN microdnf install -y ca-certificates gnupg libcap openssl shadow-utils iptables +RUN microdnf install -y ca-certificates libcap openssl shadow-utils iptables # Create a non-root user to run the software. On OpenShift, this # will not matter since the container is run as a random user and group From 05ffee3896d890186bc8cc160ad1617c9baa53ec Mon Sep 17 00:00:00 2001 From: David Yu Date: Mon, 6 Feb 2023 20:02:14 -0800 Subject: [PATCH 063/340] Dockerfile: remove `gnupg` from dev image (#1885) * Dockerfile: remove `gnupg` from dev image * Update CHANGELOG.md --- CHANGELOG.md | 2 +- control-plane/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 277535e2fc..4aecdf96d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,7 @@ IMPROVEMENTS: * Control-Plane * Add support for the annotation `consul.hashicorp.com/use-proxy-health-check`. When this annotation is used by a service, it configures a readiness endpoint on Consul Dataplane and queries it instead of the proxy's inbound port which forwards requests to the application. [[GH-1824](https://github.com/hashicorp/consul-k8s/pull/1824)], [[GH-1841](https://github.com/hashicorp/consul-k8s/pull/1841)] * Add health check for synced services based on the status of the Kubernetes readiness probe on synced pod. [[GH-1821](https://github.com/hashicorp/consul-k8s/pull/1821)] - * Remove extraneous `gnupg` depdency from `consul-k8s-control-plane` since it is no longer needed for validating binary artifacts prior to release. [[GH-1882](https://github.com/hashicorp/consul-k8s/pull/1882)] + * Remove extraneous `gnupg` dependency from `consul-k8s-control-plane` since it is no longer needed for validating binary artifacts prior to release. [[GH-1882](https://github.com/hashicorp/consul-k8s/pull/1882)] BUG FIXES: * Control Plane diff --git a/control-plane/Dockerfile b/control-plane/Dockerfile index ee20f8bb61..2989712a5f 100644 --- a/control-plane/Dockerfile +++ b/control-plane/Dockerfile @@ -41,7 +41,7 @@ LABEL name=${BIN_NAME} \ ENV BIN_NAME=${BIN_NAME} ENV VERSION=${VERSION} -RUN apk add --no-cache ca-certificates gnupg libcap openssl su-exec iputils libc6-compat iptables +RUN apk add --no-cache ca-certificates libcap openssl su-exec iputils libc6-compat iptables # Create a non-root user to run the software. RUN addgroup ${BIN_NAME} && \ From 013d7bd1893e2b94d82461b1b28a1a7b9152f95f Mon Sep 17 00:00:00 2001 From: Tu Nguyen Date: Tue, 7 Feb 2023 13:10:55 -0800 Subject: [PATCH 064/340] Update links to support devdot --- charts/consul/values.yaml | 86 +++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 63c2a63cbf..7bd4d27e99 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -25,7 +25,7 @@ global: name: null # The domain Consul will answer DNS queries for - # (see `-domain` (https://www.consul.io/docs/agent/config/cli-flags#_domain)) and the domain services synced from + # (Refer to [`-domain`](https://developer.hashicorp.com/consul/docs/agent/config/cli-flags#_domain)) and the domain services synced from # Consul into Kubernetes will have, e.g. `service-name.service.consul`. domain: consul @@ -67,7 +67,7 @@ global: # Array of objects containing image pull secret names that will be applied to each service account. # This can be used to reference image pull secrets if using a custom consul or consul-k8s-control-plane Docker image. - # See https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry for reference. + # Refer to https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry. # # Example: # @@ -92,13 +92,13 @@ global: datacenter: dc1 # Controls whether pod security policies are created for the Consul components - # created by this chart. See https://kubernetes.io/docs/concepts/policy/pod-security-policy/. + # created by this chart. Refer to https://kubernetes.io/docs/concepts/policy/pod-security-policy/. enablePodSecurityPolicies: false # secretsBackend is used to configure Vault as the secrets backend for the Consul on Kubernetes installation. # The Vault cluster needs to have the Kubernetes Auth Method, KV2 and PKI secrets engines enabled # and have necessary secrets, policies and roles created prior to installing Consul. - # See https://www.consul.io/docs/k8s/installation/vault for full instructions. + # Refer to https://developer.hashicorp.com/consul/docs/k8s/deployment-configurations/vault for full instructions. # # The Vault cluster _must_ not have the Consul cluster installed by this Helm chart as its storage backend # as that would cause a circular dependency. @@ -198,7 +198,7 @@ global: # The provider will be configured to use the Vault Kubernetes auth method # and therefore requires the role provided by `global.secretsBackend.vault.consulServerRole` # to have permissions to the root and intermediate PKI paths. - # Please see https://www.consul.io/docs/connect/ca/vault#vault-acl-policies + # Please refer to https://developer.hashicorp.com/consul/docs/connect/ca/vault#vault-acl-policies # for information on how to configure the Vault policies. connectCA: # The address of the Vault server. @@ -208,15 +208,15 @@ global: authMethodPath: "kubernetes" # The path to a PKI secrets engine for the root certificate. - # For more details, please refer to [Vault Connect CA configuration](https://www.consul.io/docs/connect/ca/vault#rootpkipath). + # For more details, please refer to [Vault Connect CA configuration](https://developer.hashicorp.com/consul/docs/connect/ca/vault#rootpkipath). rootPKIPath: "" # The path to a PKI secrets engine for the generated intermediate certificate. - # For more details, please refer to [Vault Connect CA configuration](https://www.consul.io/docs/connect/ca/vault#intermediatepkipath). + # For more details, please refer to [Vault Connect CA configuration](https://developer.hashicorp.com/consul/docs/connect/ca/vault#intermediatepkipath). intermediatePKIPath: "" # Additional Connect CA configuration in JSON format. - # Please refer to [Vault Connect CA configuration](https://www.consul.io/docs/connect/ca/vault#configuration) + # Please refer to [Vault Connect CA configuration](https://developer.hashicorp.com/consul/docs/connect/ca/vault#configuration) # for all configuration options available for that provider. # # Example: @@ -255,7 +255,7 @@ global: secretName: null # Configures Consul's gossip encryption key. - # (see `-encrypt` (https://www.consul.io/docs/agent/config/cli-flags#_encrypt)). + # (Refer to [`-encrypt`](https://developer.hashicorp.com/consul/docs/agent/config/cli-flags#_encrypt)). # By default, gossip encryption is not enabled. The gossip encryption key may be set automatically or manually. # The recommended method is to automatically generate the key. # To automatically generate and set a gossip encryption key, set autoGenerate to true. @@ -263,7 +263,7 @@ global: # To manually generate a gossip encryption key, set secretName and secretKey and use Consul to generate # a key, saving this as a Kubernetes secret or Vault secret path and key. # If `global.secretsBackend.vault.enabled=true`, be sure to add the "data" component of the secretName path as required by - # the Vault KV-2 secrets engine [see example]. + # the Vault KV-2 secrets engine [refer to example]. # # ```shell-session # $ kubectl create secret generic consul-gossip-encryption-key --from-literal=key=$(consul keygen) @@ -288,12 +288,12 @@ global: # A list of addresses of upstream DNS servers that are used to recursively resolve DNS queries. # These values are given as `-recursor` flags to Consul servers and clients. - # See https://www.consul.io/docs/agent/config/cli-flags#_recursor for more details. + # Refer to https://developer.hashicorp.com/consul/docs/agent/config/cli-flags#_recursor for more details. # If this is an empty array (the default), then Consul DNS will only resolve queries for the Consul top level domain (by default `.consul`). # @type: array recursors: [ ] - # Enables TLS (https://learn.hashicorp.com/tutorials/consul/tls-encryption-secure) + # Enables TLS (https://developer.hashicorp.com/consul/tutorials/security/tls-encryption-secure) # across the cluster to verify authenticity of the Consul servers and clients. # Requires Consul v1.4.1+. tls: @@ -323,7 +323,7 @@ global: # If true, `verify_outgoing`, `verify_server_hostname`, # and `verify_incoming` for internal RPC communication will be set to `true` for Consul servers and clients. # Set this to false to incrementally roll out TLS on an existing Consul cluster. - # Please see https://consul.io/docs/k8s/operations/tls-on-existing-cluster + # Please refer to https://developer.hashicorp.com/consul/docs/k8s/operations/tls-on-existing-cluster # for more details. verify: true @@ -501,7 +501,7 @@ global: # This address must be reachable from the Consul servers in the primary datacenter. # This auth method will be used to provision ACL tokens for Consul components and is different # from the one used by the Consul Service Mesh. - # Please see the [Kubernetes Auth Method documentation](https://consul.io/docs/acl/auth-methods/kubernetes). + # Please refer to the [Kubernetes Auth Method documentation](https://developer.hashicorp.com/consul/docs/security/acl/auth-methods/kubernetes). # # You can retrieve this value from your `kubeconfig` by running: # @@ -653,7 +653,7 @@ server: image: null # The number of server agents to run. This determines the fault tolerance of - # the cluster. Please see the deployment table (https://consul.io/docs/internals/consensus#deployment-table) + # the cluster. Please refer to the deployment table (https://developer.hashicorp.com/consul/docs/architecture/consensus#deployment-table) # for more information. replicas: 1 @@ -695,8 +695,8 @@ server: # Vault Secrets backend: # If you are using Vault as a secrets backend, a Vault Policy must be created which allows `["create", "update"]` # capabilities on the PKI issuing endpoint, which is usually of the form `pki/issue/consul-server`. - # Please see the following guide for steps to generate a compatible certificate: - # https://learn.hashicorp.com/tutorials/consul/vault-pki-consul-secure-tls + # Please refer to the following guide for steps to generate a compatible certificate: + # https://developer.hashicorp.com/consul/tutorials/vault-secure/vault-pki-consul-secure-tls # Note: when using TLS, both the `server.serverCert` and `global.tls.caCert` which points to the CA endpoint of this PKI engine # must be provided. serverCert: @@ -739,17 +739,17 @@ server: # storage classes, the PersistentVolumeClaims would need to be manually created. # A `null` value will use the Kubernetes cluster's default StorageClass. If a default # StorageClass does not exist, you will need to create one. - # Refer to the [Read/Write Tuning](https://www.consul.io/docs/install/performance#read-write-tuning) + # Refer to the [Read/Write Tuning](https://developer.hashicorp.com/consul/docs/install/performance#read-write-tuning) # section of the Server Performance Requirements documentation for considerations # around choosing a performant storage class. # - # ~> **Note:** The [Reference Architecture](https://learn.hashicorp.com/tutorials/consul/reference-architecture#hardware-sizing-for-consul-servers) + # ~> **Note:** The [Reference Architecture](https://developer.hashicorp.com/consul/tutorials/production-deploy/reference-architecture#hardware-sizing-for-consul-servers) # contains best practices and recommendations for selecting suitable # hardware sizes for your Consul servers. # @type: string storageClass: null - # This will enable/disable Connect (https://consul.io/docs/connect). Setting this to true + # This will enable/disable Connect (https://developer.hashicorp.com/consul/docs/connect). Setting this to true # _will not_ automatically secure pod communication, this # setting will only enable usage of the feature. Consul will automatically initialize # a new CA and set of certificates. Additional Connect settings can be configured @@ -826,7 +826,7 @@ server: # control a rolling update of Consul server agents. This value specifies the # partition (https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions) # for performing a rolling update. Please read the linked Kubernetes documentation - # and https://www.consul.io/docs/k8s/upgrade#upgrading-consul-servers for more information. + # and https://developer.hashicorp.com/consul/docs/k8s/upgrade#upgrading-consul-servers for more information. updatePartition: 0 # This configures the PodDisruptionBudget (https://kubernetes.io/docs/tasks/run-application/configure-pdb/) @@ -845,7 +845,7 @@ server: # @type: integer maxUnavailable: null - # A raw string of extra JSON configuration (https://consul.io/docs/agent/options) for Consul + # A raw string of extra JSON configuration (https://developer.hashicorp.com/consul/docs/agent/config/config-files) for Consul # servers. This will be saved as-is into a ConfigMap that is read by the Consul # server agents. This can be used to add additional configuration that # isn't directly exposed by the chart. @@ -1063,21 +1063,21 @@ server: extraEnvironmentVars: { } # [Enterprise Only] Values for setting up and running snapshot agents - # (https://consul.io/commands/snapshot/agent) + # (https://developer.hashicorp.com/consul/commands/snapshot/agent) # within the Consul clusters. They run as a sidecar with Consul servers. snapshotAgent: # If true, the chart will install resources necessary to run the snapshot agent. enabled: false # Interval at which to perform snapshots. - # See https://www.consul.io/commands/snapshot/agent#interval + # Refer to https://developer.hashicorp.com/consul/commands/snapshot/agent#interval # @type: string interval: 1h # A Kubernetes or Vault secret that should be manually created to contain the entire # config to be used on the snapshot agent. # This is the preferred method of configuration since there are usually storage - # credentials present. Please see Snapshot agent config (https://consul.io/commands/snapshot/agent#config-file-options) + # credentials present. Please refer to the [Snapshot agent config](https://developer.hashicorp.com/consul/commands/snapshot/agent#config-file-options) # for details. configSecret: # The name of the Kubernetes secret or Vault secret path that holds the snapshot agent config. @@ -1155,7 +1155,7 @@ externalServers: # If you are setting `global.acls.manageSystemACLs` and # `connectInject.enabled` to true, set `k8sAuthMethodHost` to the address of the Kubernetes API server. # This address must be reachable from the Consul servers. - # Please see the Kubernetes Auth Method documentation (https://consul.io/docs/acl/auth-methods/kubernetes). + # Please refer to the [Kubernetes Auth Method documentation](https://developer.hashicorp.com/consul/docs/security/acl/auth-methods/kubernetes). # # You could retrieve this value from your `kubeconfig` by running: # @@ -1184,7 +1184,7 @@ client: # @type: string image: null - # A list of valid `-retry-join` values (https://www.consul.io/docs/agent/config/cli-flags#_retry_join). + # A list of valid `-retry-join` values (https://developer.hashicorp.com/consul/docs/agent/config/cli-flags#_retry_join). # If this is `null` (default), then the clients will attempt to automatically # join the server cluster running within Kubernetes. # This means that with `server.enabled` set to true, clients will automatically @@ -1210,7 +1210,7 @@ client: grpc: true # nodeMeta specifies an arbitrary metadata key/value pair to associate with the node - # (see https://www.consul.io/docs/agent/config/cli-flags#_node_meta) + # (refer to https://developer.hashicorp.com/consul/docs/agent/config/cli-flags#_node_meta) nodeMeta: pod-name: ${HOSTNAME} host-ip: ${HOST_IP} @@ -1281,7 +1281,7 @@ client: # @recurse: false tlsInit: null - # A raw string of extra JSON configuration (https://consul.io/docs/agent/options) for Consul + # A raw string of extra JSON configuration (https://developer.hashicorp.com/consul/docs/agent/config/config-files) for Consul # clients. This will be saved as-is into a ConfigMap that is read by the Consul # client agents. This can be used to add additional configuration that # isn't directly exposed by the chart. @@ -1435,7 +1435,7 @@ client: hostNetwork: false # updateStrategy for the DaemonSet. - # See https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#daemonset-update-strategy. + # Refer to https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#daemonset-update-strategy. # This should be a multi-line string mapping directly to the updateStrategy # # Example: @@ -1563,7 +1563,7 @@ ui: # Optionally set the ingressClassName. ingressClassName: "" - # pathType override - see: https://kubernetes.io/docs/concepts/services-networking/ingress/#path-types + # pathType override - refer to: https://kubernetes.io/docs/concepts/services-networking/ingress/#path-types pathType: Prefix # hosts is a list of host name to create Ingress rules. @@ -1609,8 +1609,8 @@ ui: # @type: boolean # @default: global.metrics.enabled enabled: "-" - # Provider for metrics. See - # https://www.consul.io/docs/agent/options#ui_config_metrics_provider + # Provider for metrics. Refer to + # https://developer.hashicorp.com/consul/docs/agent/config/config-files#ui_config_metrics_provider # This value is only used if `ui.enabled` is set to true. # @type: string provider: "prometheus" @@ -1620,9 +1620,9 @@ ui: # @type: string baseURL: http://prometheus-server - # Corresponds to https://www.consul.io/docs/agent/options#ui_config_dashboard_url_templates configuration. + # Corresponds to https://developer.hashicorp.com/consul/docs/agent/config/config-files#ui_config_dashboard_url_templates configuration. dashboardURLTemplates: - # Sets https://www.consul.io/docs/agent/options#ui_config_dashboard_url_templates_service. + # Sets https://developer.hashicorp.com/consul/docs/agent/config/config-files#ui_config_dashboard_url_templates_service. service: "" # Configure the catalog sync process to sync K8S with Consul @@ -1645,7 +1645,7 @@ syncCatalog: # If true, all valid services in K8S are # synced by default. If false, the service must be annotated - # (https://consul.io/docs/k8s/service-sync#sync-enable-disable) properly to sync. + # (https://developer.hashicorp.com/consul/docs/k8s/service-sync#sync-enable-disable) properly to sync. # In either case an annotation can override the default. default: true @@ -1880,7 +1880,7 @@ connectInject: # If true, the injector will inject the # Connect sidecar into all pods by default. Otherwise, pods must specify the - # injection annotation (https://consul.io/docs/k8s/connect#consul-hashicorp-com-connect-inject) + # injection annotation (https://developer.hashicorp.com/consul/docs/k8s/connect#consul-hashicorp-com-connect-inject) # to opt-in to Connect injection. If this is true, pods can use the same annotation # to explicitly opt-out of injection. default: false @@ -1992,7 +1992,7 @@ connectInject: runAsUser: 0 # updateStrategy for the CNI installer DaemonSet. - # See https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#daemonset-update-strategy. + # Refer to https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#daemonset-update-strategy. # This should be a multi-line string mapping directly to the updateStrategy # # Example: @@ -2137,7 +2137,7 @@ connectInject: # Selector for restricting the webhook to only specific namespaces. # Use with `connectInject.default: true` to automatically inject all pods in namespaces that match the selector. This should be set to a multiline string. - # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector + # Refer to https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector # for more details. # # By default, we exclude kube-system since usually users won't @@ -2243,8 +2243,8 @@ connectInject: # If set to an empty string all service accounts can log in. # This only has effect if ACLs are enabled. # - # See https://www.consul.io/docs/acl/acl-auth-methods.html#binding-rules - # and https://www.consul.io/docs/acl/auth-methods/kubernetes.html#trusted-identity-attributes + # Refer to https://developer.hashicorp.com/consul/docs/security/acl/auth-methods#binding-rules + # and https://developer.hashicorp.com/consul/docs/security/acl/auth-methods/kubernetes#trusted-identity-attributes # for more details. # Requires Consul >= v1.5. aclBindingRuleSelector: "serviceaccount.name!=default" @@ -2274,7 +2274,7 @@ connectInject: # leads to unnecessary thread and memory usage and leaves unnecessary idle connections open. It is # advised to keep this number low for sidecars and high for edge proxies. # This will control the `--concurrency` flag to Envoy. - # For additional information see also: https://blog.envoyproxy.io/envoy-threading-model-a8d44b922310 + # For additional information, refer to https://blog.envoyproxy.io/envoy-threading-model-a8d44b922310 # # This setting can be overridden on a per-pod basis via this annotation: # - `consul.hashicorp.com/consul-envoy-proxy-concurrency` @@ -2359,7 +2359,7 @@ meshGateway: # Port that gets registered for WAN traffic. # If source is set to "Service" then this setting will have no effect. - # See the documentation for source as to which port will be used in that + # Refer to the documentation for source as to which port will be used in that # case. port: 443 From ebee53dfce2d8a697bdca3fa4fff58631e1fafe9 Mon Sep 17 00:00:00 2001 From: Tu Nguyen Date: Tue, 7 Feb 2023 21:47:50 -0800 Subject: [PATCH 065/340] opportunistic updates - add markdown links --- charts/consul/values.yaml | 130 ++++++++++++++++++++------------------ 1 file changed, 70 insertions(+), 60 deletions(-) diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 7bd4d27e99..1bf3ce122c 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -98,7 +98,8 @@ global: # secretsBackend is used to configure Vault as the secrets backend for the Consul on Kubernetes installation. # The Vault cluster needs to have the Kubernetes Auth Method, KV2 and PKI secrets engines enabled # and have necessary secrets, policies and roles created prior to installing Consul. - # Refer to https://developer.hashicorp.com/consul/docs/k8s/deployment-configurations/vault for full instructions. + # Refer to [Vault as the Secrets Backend](https://developer.hashicorp.com/consul/docs/k8s/deployment-configurations/vault) + # documentation for full instructions. # # The Vault cluster _must_ not have the Consul cluster installed by this Helm chart as its storage backend # as that would cause a circular dependency. @@ -198,8 +199,8 @@ global: # The provider will be configured to use the Vault Kubernetes auth method # and therefore requires the role provided by `global.secretsBackend.vault.consulServerRole` # to have permissions to the root and intermediate PKI paths. - # Please refer to https://developer.hashicorp.com/consul/docs/connect/ca/vault#vault-acl-policies - # for information on how to configure the Vault policies. + # Please refer to [Vault ACL policies](https://developer.hashicorp.com/consul/docs/connect/ca/vault#vault-acl-policies) + # documentation for information on how to configure the Vault policies. connectCA: # The address of the Vault server. address: "" @@ -288,12 +289,12 @@ global: # A list of addresses of upstream DNS servers that are used to recursively resolve DNS queries. # These values are given as `-recursor` flags to Consul servers and clients. - # Refer to https://developer.hashicorp.com/consul/docs/agent/config/cli-flags#_recursor for more details. + # Refer to [`-recursor`](https://developer.hashicorp.com/consul/docs/agent/config/cli-flags#_recursor) for more details. # If this is an empty array (the default), then Consul DNS will only resolve queries for the Consul top level domain (by default `.consul`). # @type: array recursors: [ ] - # Enables TLS (https://developer.hashicorp.com/consul/tutorials/security/tls-encryption-secure) + # Enables [TLS](https://developer.hashicorp.com/consul/tutorials/security/tls-encryption-secure) # across the cluster to verify authenticity of the Consul servers and clients. # Requires Consul v1.4.1+. tls: @@ -323,7 +324,7 @@ global: # If true, `verify_outgoing`, `verify_server_hostname`, # and `verify_incoming` for internal RPC communication will be set to `true` for Consul servers and clients. # Set this to false to incrementally roll out TLS on an existing Consul cluster. - # Please refer to https://developer.hashicorp.com/consul/docs/k8s/operations/tls-on-existing-cluster + # Please refer to [TLS on existing clusters](https://developer.hashicorp.com/consul/docs/k8s/operations/tls-on-existing-cluster) # for more details. verify: true @@ -436,10 +437,10 @@ global: # tolerations configures the taints and tolerations for the server-acl-init # and server-acl-init-cleanup jobs. This should be a multi-line string matching the - # Tolerations (https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) array in a Pod spec. + # [Tolerations](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) array in a Pod spec. tolerations: "" - # This value defines `nodeSelector` (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) + # This value defines [`nodeSelector`](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) # labels for the server-acl-init and server-acl-init-cleanup jobs pod assignment, formatted as a multi-line string. # # Example: @@ -653,7 +654,7 @@ server: image: null # The number of server agents to run. This determines the fault tolerance of - # the cluster. Please refer to the deployment table (https://developer.hashicorp.com/consul/docs/architecture/consensus#deployment-table) + # the cluster. Please refer to the [deployment table](https://developer.hashicorp.com/consul/docs/architecture/consensus#deployment-table) # for more information. replicas: 1 @@ -695,8 +696,8 @@ server: # Vault Secrets backend: # If you are using Vault as a secrets backend, a Vault Policy must be created which allows `["create", "update"]` # capabilities on the PKI issuing endpoint, which is usually of the form `pki/issue/consul-server`. - # Please refer to the following guide for steps to generate a compatible certificate: - # https://developer.hashicorp.com/consul/tutorials/vault-secure/vault-pki-consul-secure-tls + # Complete [this tutorial](https://developer.hashicorp.com/consul/tutorials/vault-secure/vault-pki-consul-secure-tls) + # to learn how to generate a compatible certificate. # Note: when using TLS, both the `server.serverCert` and `global.tls.caCert` which points to the CA endpoint of this PKI engine # must be provided. serverCert: @@ -749,7 +750,7 @@ server: # @type: string storageClass: null - # This will enable/disable Connect (https://developer.hashicorp.com/consul/docs/connect). Setting this to true + # This will enable/disable [Connect](https://developer.hashicorp.com/consul/docs/connect). Setting this to true # _will not_ automatically secure pod communication, this # setting will only enable usage of the feature. Consul will automatically initialize # a new CA and set of certificates. Additional Connect settings can be configured @@ -771,7 +772,7 @@ server: # The resource requests (CPU, memory, etc.) # for each of the server agents. This should be a YAML map corresponding to a Kubernetes - # ResourceRequirements (https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#resourcerequirements-v1-core) + # [`ResourceRequirements``](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#resourcerequirements-v1-core) # object. NOTE: The use of a YAML string is deprecated. # # Example: @@ -824,12 +825,13 @@ server: # This value is used to carefully # control a rolling update of Consul server agents. This value specifies the - # partition (https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions) - # for performing a rolling update. Please read the linked Kubernetes documentation - # and https://developer.hashicorp.com/consul/docs/k8s/upgrade#upgrading-consul-servers for more information. + # [partition](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions) + # for performing a rolling update. Please read the linked Kubernetes + # and [Upgrade Consul](https://developer.hashicorp.com/consul/docs/k8s/upgrade#upgrading-consul-servers) + # documentation for more information. updatePartition: 0 - # This configures the PodDisruptionBudget (https://kubernetes.io/docs/tasks/run-application/configure-pdb/) + # This configures the [`PodDisruptionBudget`](https://kubernetes.io/docs/tasks/run-application/configure-pdb/) # for the server cluster. disruptionBudget: # Enables registering a PodDisruptionBudget for the server @@ -845,7 +847,7 @@ server: # @type: integer maxUnavailable: null - # A raw string of extra JSON configuration (https://developer.hashicorp.com/consul/docs/agent/config/config-files) for Consul + # A raw string of extra [JSON configuration](https://developer.hashicorp.com/consul/docs/agent/config/config-files) for Consul # servers. This will be saved as-is into a ConfigMap that is read by the Consul # server agents. This can be used to add additional configuration that # isn't directly exposed by the chart. @@ -908,7 +910,7 @@ server: # @type: array extraContainers: [ ] - # This value defines the affinity (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) + # This value defines the [affinity](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) # for server pods. It defaults to allowing only a single server pod on each node, which # minimizes risk of the cluster becoming unusable if a node is lost. If you need # to run more pods per node (for example, testing on Minikube), set this value @@ -938,13 +940,15 @@ server: topologyKey: kubernetes.io/hostname # Toleration settings for server pods. This - # should be a multi-line string matching the Tolerations - # (https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) array in a Pod spec. + # should be a multi-line string matching the + # [Tolerations](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) + # array in a Pod spec. tolerations: "" # Pod topology spread constraints for server pods. - # This should be a multi-line YAML string matching the `topologySpreadConstraints` array - # (https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) in a Pod Spec. + # This should be a multi-line YAML string matching the + # [`topologySpreadConstraints`](https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) + # array in a Pod Spec. # # This requires K8S >= 1.18 (beta) or 1.19 (stable). # @@ -963,7 +967,7 @@ server: # ``` topologySpreadConstraints: "" - # This value defines `nodeSelector` (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) + # This value defines [`nodeSelector`](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) # labels for server pod assignment, formatted as a multi-line string. # # Example: @@ -977,7 +981,7 @@ server: nodeSelector: null # This value references an existing - # Kubernetes `priorityClassName` (https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#pod-priority) + # Kubernetes [`priorityClassName`](https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#pod-priority) # that can be assigned to server pods. priorityClassName: "" @@ -1062,15 +1066,15 @@ server: # @type: map extraEnvironmentVars: { } - # [Enterprise Only] Values for setting up and running snapshot agents - # (https://developer.hashicorp.com/consul/commands/snapshot/agent) + # [Enterprise Only] Values for setting up and running + # [snapshot agents](https://developer.hashicorp.com/consul/commands/snapshot/agent) # within the Consul clusters. They run as a sidecar with Consul servers. snapshotAgent: # If true, the chart will install resources necessary to run the snapshot agent. enabled: false # Interval at which to perform snapshots. - # Refer to https://developer.hashicorp.com/consul/commands/snapshot/agent#interval + # Refer to [`interval`](https://developer.hashicorp.com/consul/commands/snapshot/agent#interval) # @type: string interval: 1h @@ -1184,7 +1188,7 @@ client: # @type: string image: null - # A list of valid `-retry-join` values (https://developer.hashicorp.com/consul/docs/agent/config/cli-flags#_retry_join). + # A list of valid [`-retry-join` values](https://developer.hashicorp.com/consul/docs/agent/config/cli-flags#_retry_join). # If this is `null` (default), then the clients will attempt to automatically # join the server cluster running within Kubernetes. # This means that with `server.enabled` set to true, clients will automatically @@ -1210,7 +1214,7 @@ client: grpc: true # nodeMeta specifies an arbitrary metadata key/value pair to associate with the node - # (refer to https://developer.hashicorp.com/consul/docs/agent/config/cli-flags#_node_meta) + # (refer to [`-node-meta`](https://developer.hashicorp.com/consul/docs/agent/config/cli-flags#_node_meta)) nodeMeta: pod-name: ${HOSTNAME} host-ip: ${HOST_IP} @@ -1281,7 +1285,7 @@ client: # @recurse: false tlsInit: null - # A raw string of extra JSON configuration (https://developer.hashicorp.com/consul/docs/agent/config/config-files) for Consul + # A raw string of extra [JSON configuration](https://developer.hashicorp.com/consul/docs/agent/config/config-files) for Consul # clients. This will be saved as-is into a ConfigMap that is read by the Consul # client agents. This can be used to add additional configuration that # isn't directly exposed by the chart. @@ -1386,7 +1390,7 @@ client: affinity: null # This value references an existing - # Kubernetes `priorityClassName` (https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#pod-priority) + # Kubernetes [`priorityClassName`](https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#pod-priority) # that can be assigned to client pods. priorityClassName: "" @@ -1422,7 +1426,7 @@ client: # @type: map extraEnvironmentVars: { } - # This value defines the Pod DNS policy (https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy) + # This value defines the [Pod DNS policy](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy) # for client pods to use. # @type: string dnsPolicy: null @@ -1435,7 +1439,8 @@ client: hostNetwork: false # updateStrategy for the DaemonSet. - # Refer to https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#daemonset-update-strategy. + # Refer to the Kubernetes [Daemonset upgrade strategy](https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#daemonset-update-strategy) + # documentation. # This should be a multi-line string mapping directly to the updateStrategy # # Example: @@ -1610,7 +1615,7 @@ ui: # @default: global.metrics.enabled enabled: "-" # Provider for metrics. Refer to - # https://developer.hashicorp.com/consul/docs/agent/config/config-files#ui_config_metrics_provider + # [`metrics_provider`](https://developer.hashicorp.com/consul/docs/agent/config/config-files#ui_config_metrics_provider) # This value is only used if `ui.enabled` is set to true. # @type: string provider: "prometheus" @@ -1620,9 +1625,10 @@ ui: # @type: string baseURL: http://prometheus-server - # Corresponds to https://developer.hashicorp.com/consul/docs/agent/config/config-files#ui_config_dashboard_url_templates configuration. + # Corresponds to [`dashboard_url_templates`](https://developer.hashicorp.com/consul/docs/agent/config/config-files#ui_config_dashboard_url_templates) + # configuration. dashboardURLTemplates: - # Sets https://developer.hashicorp.com/consul/docs/agent/config/config-files#ui_config_dashboard_url_templates_service. + # Sets [`dashboardURLTemplates.service`](https://developer.hashicorp.com/consul/docs/agent/config/config-files#ui_config_dashboard_url_templates_service). service: "" # Configure the catalog sync process to sync K8S with Consul @@ -1644,8 +1650,8 @@ syncCatalog: image: null # If true, all valid services in K8S are - # synced by default. If false, the service must be annotated - # (https://developer.hashicorp.com/consul/docs/k8s/service-sync#sync-enable-disable) properly to sync. + # synced by default. If false, the service must be [annotated](https://developer.hashicorp.com/consul/docs/k8s/service-sync#enable-and-disable-sync) + # properly to sync. # In either case an annotation can override the default. default: true @@ -1784,7 +1790,7 @@ syncCatalog: # @type: string secretKey: null - # This value defines `nodeSelector` (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) + # This value defines [`nodeSelector`](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) # labels for catalog sync pod assignment, formatted as a multi-line string. # # Example: @@ -1880,7 +1886,7 @@ connectInject: # If true, the injector will inject the # Connect sidecar into all pods by default. Otherwise, pods must specify the - # injection annotation (https://developer.hashicorp.com/consul/docs/k8s/connect#consul-hashicorp-com-connect-inject) + # [injection annotation](https://developer.hashicorp.com/consul/docs/k8s/connect#consul-hashicorp-com-connect-inject) # to opt-in to Connect injection. If this is true, pods can use the same annotation # to explicitly opt-out of injection. default: false @@ -1901,7 +1907,7 @@ connectInject: # Note: This value has no effect if transparent proxy is disabled on the pod. defaultOverwriteProbes: true - # This configures the PodDisruptionBudget (https://kubernetes.io/docs/tasks/run-application/configure-pdb/) + # This configures the [`PodDisruptionBudget`](https://kubernetes.io/docs/tasks/run-application/configure-pdb/) # for the service mesh sidecar injector. disruptionBudget: # This will enable/disable registering a PodDisruptionBudget for the @@ -1992,7 +1998,8 @@ connectInject: runAsUser: 0 # updateStrategy for the CNI installer DaemonSet. - # Refer to https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#daemonset-update-strategy. + # Refer to the Kubernetes [Daemonset upgrade strategy](https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#daemonset-update-strategy) + # documentation. # This should be a multi-line string mapping directly to the updateStrategy # # Example: @@ -2142,7 +2149,7 @@ connectInject: # # By default, we exclude kube-system since usually users won't # want those pods injected and local-path-storage and openebs so that - # Kind (Kubernetes In Docker) and OpenEBS (https://openebs.io/) respectively can provision Pods used to create PVCs. + # Kind (Kubernetes In Docker) and [OpenEBS](https://openebs.io/) respectively can provision Pods used to create PVCs. # Note that this exclusion is only supported in Kubernetes v1.21.1+. # # Example: @@ -2243,8 +2250,8 @@ connectInject: # If set to an empty string all service accounts can log in. # This only has effect if ACLs are enabled. # - # Refer to https://developer.hashicorp.com/consul/docs/security/acl/auth-methods#binding-rules - # and https://developer.hashicorp.com/consul/docs/security/acl/auth-methods/kubernetes#trusted-identity-attributes + # Refer to Auth methods [Binding rules](https://developer.hashicorp.com/consul/docs/security/acl/auth-methods#binding-rules) + # and [Trusted identiy attributes](https://developer.hashicorp.com/consul/docs/security/acl/auth-methods/kubernetes#trusted-identity-attributes) # for more details. # Requires Consul >= v1.5. aclBindingRuleSelector: "serviceaccount.name!=default" @@ -2462,7 +2469,7 @@ meshGateway: memory: "50Mi" cpu: "50m" - # This value defines the affinity (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) + # This value defines the [affinity](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) # for mesh gateway pods. It defaults to `null` thereby allowing multiple gateway pods on each node. But if one would prefer # a mode which minimizes risk of the cluster becoming unusable if a node is lost, set this value # to the value in the example below. @@ -2488,8 +2495,9 @@ meshGateway: tolerations: null # Pod topology spread constraints for mesh gateway pods. - # This should be a multi-line YAML string matching the `topologySpreadConstraints` array - # (https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) in a Pod Spec. + # This should be a multi-line YAML string matching the + # [`topologySpreadConstraints`](https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) + # array in a Pod Spec. # # This requires K8S >= 1.18 (beta) or 1.19 (stable). # @@ -2609,7 +2617,7 @@ ingressGateways: memory: "100Mi" cpu: "100m" - # This value defines the affinity (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) + # This value defines the [affinity](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) # for ingress gateway pods. It defaults to `null` thereby allowing multiple gateway pods on each node. But if one would prefer # a mode which minimizes risk of the cluster becoming unusable if a node is lost, set this value # to the value in the example below. @@ -2635,8 +2643,9 @@ ingressGateways: tolerations: null # Pod topology spread constraints for ingress gateway pods. - # This should be a multi-line YAML string matching the `topologySpreadConstraints` array - # (https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) in a Pod Spec. + # This should be a multi-line YAML string matching the + # [`topologySpreadConstraints`](https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) + # array in a Pod Spec. # # This requires K8S >= 1.18 (beta) or 1.19 (stable). # @@ -2739,7 +2748,7 @@ terminatingGateways: memory: "100Mi" cpu: "100m" - # This value defines the affinity (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) + # This value defines the [affinity](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) # for terminating gateway pods. It defaults to `null` thereby allowing multiple gateway pods on each node. But if one would prefer # a mode which minimizes risk of the cluster becoming unusable if a node is lost, set this value # to the value in the example below. @@ -2765,8 +2774,9 @@ terminatingGateways: tolerations: null # Pod topology spread constraints for terminating gateway pods. - # This should be a multi-line YAML string matching the `topologySpreadConstraints` array - # (https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) in a Pod Spec. + # This should be a multi-line YAML string matching the + # [`topologySpreadConstraints`](https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) + # array in a Pod Spec. # # This requires K8S >= 1.18 (beta) or 1.19 (stable). # @@ -2858,7 +2868,7 @@ apiGateway: # When true a GatewayClass is configured to automatically work with Consul as installed by helm. enabled: true - # This value defines `nodeSelector` (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) + # This value defines [`nodeSelector`](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) # labels for gateway pod assignment, formatted as a multi-line string. # # Example: @@ -2943,11 +2953,11 @@ apiGateway: annotations: null # This value references an existing - # Kubernetes `priorityClassName` (https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#pod-priority) + # Kubernetes [`priorityClassName`](https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#pod-priority) # that can be assigned to api-gateway-controller pods. priorityClassName: "" - # This value defines `nodeSelector` (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) + # This value defines [`nodeSelector`](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) # labels for api-gateway-controller pod assignment, formatted as a multi-line string. # # Example: @@ -2961,7 +2971,7 @@ apiGateway: nodeSelector: null # This value defines the tolerations for api-gateway-controller pod, this should be a multi-line string matching the - # Tolerations (https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) array in a Pod spec. + # [Tolerations](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) array in a Pod spec. # # @type: string tolerations: null @@ -3011,7 +3021,7 @@ webhookCertManager: # @type: string tolerations: null - # This value defines `nodeSelector` (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) + # This value defines [`nodeSelector`](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) # labels for the webhook-cert-manager pod assignment, formatted as a multi-line string. # # Example: From 0ce9fb4f2d3450e4709800c33283d31b4f5bd35c Mon Sep 17 00:00:00 2001 From: Melisa Griffin Date: Wed, 8 Feb 2023 15:41:31 -0500 Subject: [PATCH 066/340] NET-1750 Fixes ACL init command Consul Login Datacenter mixup (#1881) --- .../api-gateway-controller-deployment.yaml | 8 ++++- charts/consul/templates/client-daemonset.yaml | 6 ++++ .../api-gateway-controller-deployment.bats | 32 +++++++++++++++++-- charts/consul/test/unit/client-daemonset.bats | 23 +++++++++++++ control-plane/subcommand/acl-init/command.go | 2 +- 5 files changed, 67 insertions(+), 4 deletions(-) diff --git a/charts/consul/templates/api-gateway-controller-deployment.yaml b/charts/consul/templates/api-gateway-controller-deployment.yaml index ec64bc3631..a9f1806cc8 100644 --- a/charts/consul/templates/api-gateway-controller-deployment.yaml +++ b/charts/consul/templates/api-gateway-controller-deployment.yaml @@ -75,6 +75,7 @@ spec: {{- if .Values.global.acls.manageSystemACLs }} - name: CONSUL_HTTP_TOKEN_FILE value: "/consul/login/acl-token" + # CONSUL_LOGIN_DATACENTER is passed to the gateway that gets created. The controller does not use this to log in - name: CONSUL_LOGIN_DATACENTER value: {{ .Values.global.datacenter }} {{- end }} @@ -240,6 +241,12 @@ spec: fieldPath: metadata.name - name: CONSUL_LOGIN_META value: "component=api-gateway-controller,pod=$(NAMESPACE)/$(POD_NAME)" + - name: CONSUL_LOGIN_DATACENTER + {{- if and .Values.global.federation.enabled .Values.global.federation.primaryDatacenter }} + value: {{ .Values.global.federation.primaryDatacenter }} + {{- else }} + value: {{ .Values.global.datacenter }} + {{- end}} {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 8 }} image: {{ .Values.global.imageK8S }} volumeMounts: @@ -260,7 +267,6 @@ spec: consul-k8s-control-plane acl-init \ {{- if and .Values.global.federation.enabled .Values.global.federation.primaryDatacenter }} -auth-method-name={{ template "consul.fullname" . }}-k8s-component-auth-method-{{ .Values.global.datacenter }} \ - -datacenter={{ .Values.global.federation.primaryDatacenter }} \ {{- else }} -auth-method-name={{ template "consul.fullname" . }}-k8s-component-auth-method \ {{- end }} diff --git a/charts/consul/templates/client-daemonset.yaml b/charts/consul/templates/client-daemonset.yaml index 91af3821fc..09a70b394e 100644 --- a/charts/consul/templates/client-daemonset.yaml +++ b/charts/consul/templates/client-daemonset.yaml @@ -509,6 +509,12 @@ spec: - name: CONSUL_LOGIN_META value: "component=client,pod=$(NAMESPACE)/$(POD_NAME)" {{- end }} + - name: CONSUL_LOGIN_DATACENTER + {{- if and .Values.global.federation.enabled .Values.global.federation.primaryDatacenter }} + value: {{ .Values.global.federation.primaryDatacenter }} + {{- else }} + value: {{ .Values.global.datacenter }} + {{- end}} command: - "/bin/sh" - "-ec" diff --git a/charts/consul/test/unit/api-gateway-controller-deployment.bats b/charts/consul/test/unit/api-gateway-controller-deployment.bats index b71b51aee0..2dbcb9e0f1 100755 --- a/charts/consul/test/unit/api-gateway-controller-deployment.bats +++ b/charts/consul/test/unit/api-gateway-controller-deployment.bats @@ -346,7 +346,11 @@ load _helpers [ "${actual}" = "true" ] local actual=$(echo $object | - yq -r '[.env[7].value] | any(contains("5s"))' | tee /dev/stderr) + yq '[.env[3].name] | any(contains("CONSUL_LOGIN_DATACENTER"))' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq -r '[.env[8].value] | any(contains("5s"))' | tee /dev/stderr) [ "${actual}" = "true" ] } @@ -494,6 +498,30 @@ load _helpers [ "${actual}" = "true" ] } +@test "apiGateway/Deployment: consul login datacenter is set to primary when when federation enabled in non-primary datacenter" { + cd `chart_dir` + local object=$(helm template \ + -s templates/api-gateway-controller-deployment.yaml \ + --set 'apiGateway.enabled=true' \ + --set 'apiGateway.image=foo' \ + --set 'meshGateway.enabled=true' \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.datacenter=dc1' \ + --set 'global.federation.enabled=true' \ + --set 'global.federation.primaryDatacenter=dc2' \ + --set 'global.tls.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.initContainers[1]' | tee /dev/stderr) + + local actual=$(echo $object | + yq '[.env[3].name] | any(contains("CONSUL_LOGIN_DATACENTER"))' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq '[.env[3].value] | any(contains("dc2"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + @test "apiGateway/Deployment: primary-datacenter flag provided when federation enabled in non-primary datacenter" { cd `chart_dir` local object=$(helm template \ @@ -546,7 +574,7 @@ load _helpers [ "${actual}" = "true" ] local actual=$(echo $object | - yq -r '.command | any(contains("-datacenter=dc1"))' | tee /dev/stderr) + yq '[.env[3].value] | any(contains("dc1"))' | tee /dev/stderr) [ "${actual}" = "true" ] } diff --git a/charts/consul/test/unit/client-daemonset.bats b/charts/consul/test/unit/client-daemonset.bats index 6e7a030cb1..4c38207635 100755 --- a/charts/consul/test/unit/client-daemonset.bats +++ b/charts/consul/test/unit/client-daemonset.bats @@ -2127,6 +2127,29 @@ rollingUpdate: [[ "$output" =~ "If global.federation.enabled is true, global.adminPartitions.enabled must be false because they are mutually exclusive" ]] } +@test "client/DaemonSet: consul login datacenter is set to primary when when federation enabled in non-primary datacenter" { + cd `chart_dir` + local object=$(helm template \ + -s templates/client-daemonset.yaml \ + --set 'client.enabled=true' \ + --set 'meshGateway.enabled=true' \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.datacenter=dc1' \ + --set 'global.federation.enabled=true' \ + --set 'global.federation.primaryDatacenter=dc2' \ + --set 'global.tls.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.initContainers[] | select(.name == "client-acl-init")' | tee /dev/stderr) + + local actual=$(echo $object | + yq '[.env[11].name] | any(contains("CONSUL_LOGIN_DATACENTER"))' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq '[.env[11].value] | any(contains("dc2"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + #-------------------------------------------------------------------- # extraContainers diff --git a/control-plane/subcommand/acl-init/command.go b/control-plane/subcommand/acl-init/command.go index 2745470da8..af85128ea8 100644 --- a/control-plane/subcommand/acl-init/command.go +++ b/control-plane/subcommand/acl-init/command.go @@ -167,7 +167,7 @@ func (c *Command) Run(args []string) int { loginParams := common.LoginParams{ AuthMethod: c.consul.ConsulLogin.AuthMethod, - Datacenter: c.consul.Datacenter, + Datacenter: c.consul.ConsulLogin.Datacenter, BearerTokenFile: c.consul.ConsulLogin.BearerTokenFile, TokenSinkFile: c.flagTokenSinkFile, Meta: c.consul.ConsulLogin.Meta, From 69ae0ca3351d16fd56aef20436321bf097978a14 Mon Sep 17 00:00:00 2001 From: Semir Patel Date: Thu, 9 Feb 2023 09:16:21 -0600 Subject: [PATCH 067/340] Minor fixes to docs for acceptance tests (#1895) --- CONTRIBUTING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 61f65f21a3..510d4c3b3f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -986,15 +986,15 @@ Any given test can be run either through GoLand or another IDE, or via command l To run all of the connect tests from command line: ```shell -$ cd acceptance/test -$ go test ./connect/... -p 1 -timeout 2h -failfast -use-kind -no-cleanup-on-failure -kubecontext=kind-dc1 -secondary-kubecontext=kind-dc2 -enable-enterprise -enable-multi-cluster -debug-directory=/tmp/debug -consul-k8s-image=kyleschochenmaier/consul-k8s-acls +$ cd acceptance/tests +$ go test ./connect/... -v -p 1 -timeout 2h -failfast -use-kind -no-cleanup-on-failure -kubecontext=kind-dc1 -secondary-kubecontext=kind-dc2 -enable-enterprise -enable-multi-cluster -debug-directory=/tmp/debug -consul-k8s-image=kyleschochenmaier/consul-k8s-acls ``` When running from command line a few things are important: * Some tests use Enterprise features, in which case you need: * Set environment variables `CONSUL_ENT_LICENSE` and possibly `VAULT_LICENSE`. * Use `-enable-enterprise` on command line when running the test. -* Multi-cluster tests require `-enable-multi-cluster` + `-kubecontext=` + `-secondary-kubecontext=` +* Multi-cluster tests require `-enable-multi-cluster -kubecontext=kind-dc1 -secondary-kubecontext=kind-dc2` * Using `.//...` is required as part of the command-line to pick up necessary environmental config. ### Using the framework to debug in an environment From e71a71cbe7426d04d2410bad7205b79788cf418d Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Sun, 12 Feb 2023 22:06:35 -0500 Subject: [PATCH 068/340] Service to service troubleshooting (#1851) - Add troubleshooting commands for 'upstream' and 'proxy' to allow troubleshooting of envoy config. --- acceptance/tests/cli/cli_install_test.go | 42 ++- cli/cmd/troubleshoot/command.go | 26 ++ cli/cmd/troubleshoot/proxy/proxy.go | 282 +++++++++++++++ cli/cmd/troubleshoot/proxy/proxy_test.go | 72 ++++ cli/cmd/troubleshoot/upstreams/upstreams.go | 265 ++++++++++++++ .../troubleshoot/upstreams/upstreams_test.go | 127 +++++++ cli/commands.go | 22 +- cli/go.mod | 85 +++-- cli/go.sum | 342 +++++++++--------- 9 files changed, 1053 insertions(+), 210 deletions(-) create mode 100644 cli/cmd/troubleshoot/command.go create mode 100644 cli/cmd/troubleshoot/proxy/proxy.go create mode 100644 cli/cmd/troubleshoot/proxy/proxy_test.go create mode 100644 cli/cmd/troubleshoot/upstreams/upstreams.go create mode 100644 cli/cmd/troubleshoot/upstreams/upstreams_test.go diff --git a/acceptance/tests/cli/cli_install_test.go b/acceptance/tests/cli/cli_install_test.go index f8c40d3194..d45093dd59 100644 --- a/acceptance/tests/cli/cli_install_test.go +++ b/acceptance/tests/cli/cli_install_test.go @@ -1,6 +1,7 @@ package cli import ( + "context" "fmt" "strings" "testing" @@ -12,6 +13,7 @@ import ( "github.com/hashicorp/consul-k8s/acceptance/framework/logger" "github.com/hashicorp/consul/sdk/testutil/retry" "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) const ipv4RegEx = "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)" @@ -21,9 +23,11 @@ const ipv4RegEx = "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9] func TestInstall(t *testing.T) { cases := map[string]struct { secure bool + tproxy bool }{ - "not-secure": {secure: false}, - "secure": {secure: true}, + "not-secure": {secure: false, tproxy: false}, + "secure": {secure: true, tproxy: false}, + "not-secure-tproxy": {secure: false, tproxy: true}, } for name, c := range cases { @@ -32,6 +36,7 @@ func TestInstall(t *testing.T) { require.NoError(t, err) cfg := suite.Config() + cfg.EnableTransparentProxy = c.tproxy ctx := suite.Environment().DefaultContext(t) connHelper := connhelper.ConnectHelper{ @@ -83,6 +88,39 @@ func TestInstall(t *testing.T) { } }) + // Troubleshoot: Get the client pod so we can portForward to it and get the 'troubleshoot upstreams' output + clientPod, err := connHelper.Ctx.KubernetesClient(t).CoreV1().Pods(connHelper.Ctx.KubectlOptions(t).Namespace).List(context.Background(), metav1.ListOptions{ + LabelSelector: "app=static-client", + }) + require.NoError(t, err) + + clientPodName := clientPod.Items[0].Name + upstreamsOut, err := cli.Run(t, ctx.KubectlOptions(t), "troubleshoot", "upstreams", "-pod", clientPodName) + logger.Log(t, string(upstreamsOut)) + require.NoError(t, err) + + if c.tproxy { + // If tproxy is enabled we are looking for the upstream ip which is the ClusterIP of the Kubernetes Service + serverService, err := connHelper.Ctx.KubernetesClient(t).CoreV1().Services(connHelper.Ctx.KubectlOptions(t).Namespace).List(context.Background(), metav1.ListOptions{ + FieldSelector: "metadata.name=static-server", + }) + require.NoError(t, err) + serverIP := serverService.Items[0].Spec.ClusterIP + + proxyOut, err := cli.Run(t, ctx.KubectlOptions(t), "troubleshoot", "proxy", "-pod", clientPodName, "-upstream-ip", serverIP) + require.NoError(t, err) + require.Regexp(t, "upstream resources are valid", string(proxyOut)) + logger.Log(t, string(proxyOut)) + } else { + // With tproxy disabled and explicit upstreams we need the envoy-id of the server + require.Regexp(t, "static-server", string(upstreamsOut)) + + proxyOut, err := cli.Run(t, ctx.KubectlOptions(t), "troubleshoot", "proxy", "-pod", clientPodName, "-upstream-envoy-id", "static-server") + require.NoError(t, err) + require.Regexp(t, "upstream resources are valid", string(proxyOut)) + logger.Log(t, string(proxyOut)) + } + connHelper.TestConnectionSuccess(t) connHelper.TestConnectionFailureWhenUnhealthy(t) }) diff --git a/cli/cmd/troubleshoot/command.go b/cli/cmd/troubleshoot/command.go new file mode 100644 index 0000000000..d37c66e998 --- /dev/null +++ b/cli/cmd/troubleshoot/command.go @@ -0,0 +1,26 @@ +package troubleshoot + +import ( + "fmt" + + "github.com/hashicorp/consul-k8s/cli/common" + "github.com/mitchellh/cli" +) + +// TroubleshootCommand provides a synopsis for the troubleshoot subcommands (e.g. proxy, upstreams). +type TroubleshootCommand struct { + *common.BaseCommand +} + +// Run prints out information about the subcommands. +func (c *TroubleshootCommand) Run([]string) int { + return cli.RunResultHelp +} + +func (c *TroubleshootCommand) Help() string { + return fmt.Sprintf("%s\n\nUsage: consul-k8s troubleshoot ", c.Synopsis()) +} + +func (c *TroubleshootCommand) Synopsis() string { + return "Troubleshoot network and security configurations." +} diff --git a/cli/cmd/troubleshoot/proxy/proxy.go b/cli/cmd/troubleshoot/proxy/proxy.go new file mode 100644 index 0000000000..d17c491f5d --- /dev/null +++ b/cli/cmd/troubleshoot/proxy/proxy.go @@ -0,0 +1,282 @@ +package proxy + +import ( + "fmt" + "net" + "strings" + "sync" + + "github.com/hashicorp/consul-k8s/cli/common" + "github.com/hashicorp/consul-k8s/cli/common/flag" + "github.com/hashicorp/consul-k8s/cli/common/terminal" + troubleshoot "github.com/hashicorp/consul/troubleshoot/proxy" + "github.com/posener/complete" + helmCLI "helm.sh/helm/v3/pkg/cli" + "k8s.io/apimachinery/pkg/api/validation" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" +) + +const ( + defaultAdminPort int = 19000 + flagNameKubeConfig = "kubeconfig" + flagNameKubeContext = "context" + flagNameNamespace = "namespace" + flagNamePod = "pod" + flagNameUpstreamEnvoyID = "upstream-envoy-id" + flagNameUpstreamIP = "upstream-ip" + DebugColor = "\033[0;36m%s\033[0m" +) + +type ProxyCommand struct { + *common.BaseCommand + + kubernetes kubernetes.Interface + + set *flag.Sets + + flagKubeConfig string + flagKubeContext string + flagNamespace string + + flagPod string + flagUpstreamEnvoyID string + flagUpstreamIP string + + restConfig *rest.Config + + once sync.Once + help string +} + +// init sets up flags and help text for the command. +func (c *ProxyCommand) init() { + c.set = flag.NewSets() + f := c.set.NewSet("Command Options") + + f.StringVar(&flag.StringVar{ + Name: flagNamePod, + Target: &c.flagPod, + Usage: "The pod to port-forward to.", + Aliases: []string{"p"}, + }) + + f.StringVar(&flag.StringVar{ + Name: flagNameUpstreamEnvoyID, + Target: &c.flagUpstreamEnvoyID, + Usage: "The envoy identifier of the upstream service that receives the communication. (explicit upstreams only)", + Aliases: []string{"id"}, + }) + + f.StringVar(&flag.StringVar{ + Name: flagNameUpstreamIP, + Target: &c.flagUpstreamIP, + Usage: "The IP address of the upstream service that receives the communication. (transparent proxy only)", + Aliases: []string{"ip"}, + }) + + f = c.set.NewSet("Global Options") + f.StringVar(&flag.StringVar{ + Name: flagNameKubeConfig, + Aliases: []string{"c"}, + Target: &c.flagKubeConfig, + Default: "", + Usage: "Set the path to kubeconfig file.", + }) + f.StringVar(&flag.StringVar{ + Name: flagNameKubeContext, + Target: &c.flagKubeContext, + Default: "", + Usage: "Set the Kubernetes context to use.", + }) + + f.StringVar(&flag.StringVar{ + Name: flagNameNamespace, + Target: &c.flagNamespace, + Usage: "The namespace the pod is in.", + Aliases: []string{"n"}, + }) + + c.help = c.set.Help() +} + +// Run executes the list command. +func (c *ProxyCommand) Run(args []string) int { + c.once.Do(c.init) + c.Log.ResetNamed("list") + defer common.CloseWithError(c.BaseCommand) + + // Parse the command line flags. + if err := c.set.Parse(args); err != nil { + c.UI.Output("Error parsing arguments: %v", err.Error(), terminal.WithErrorStyle()) + return 1 + } + + // Validate the command line flags. + if err := c.validateFlags(); err != nil { + c.UI.Output("Invalid argument: %v", err.Error(), terminal.WithErrorStyle()) + return 1 + } + + if c.kubernetes == nil { + if err := c.initKubernetes(); err != nil { + c.UI.Output("Error initializing Kubernetes client: %v", err.Error(), terminal.WithErrorStyle()) + return 1 + } + } + + if err := c.Troubleshoot(); err != nil { + c.UI.Output("Error running troubleshoot: %v", err.Error(), terminal.WithErrorStyle()) + return 1 + } + + return 0 +} + +// validateFlags ensures that the flags passed in by the can be used. +func (c *ProxyCommand) validateFlags() error { + + if (c.flagUpstreamEnvoyID == "" && c.flagUpstreamIP == "") || (c.flagUpstreamEnvoyID != "" && c.flagUpstreamIP != "") { + return fmt.Errorf("-upstream-envoy-id OR -upstream-ip is required.\n Please run `consul troubleshoot upstreams` to find the corresponding upstream.") + } + + if c.flagPod == "" { + return fmt.Errorf("-pod flag is required") + } + + if errs := validation.ValidateNamespaceName(c.flagNamespace, false); c.flagNamespace != "" && len(errs) > 0 { + return fmt.Errorf("invalid namespace name passed for -namespace/-n: %v", strings.Join(errs, "; ")) + } + + return nil +} + +// initKubernetes initializes the Kubernetes client. +func (c *ProxyCommand) initKubernetes() (err error) { + settings := helmCLI.New() + + if c.flagKubeConfig != "" { + settings.KubeConfig = c.flagKubeConfig + } + + if c.flagKubeContext != "" { + settings.KubeContext = c.flagKubeContext + } + + if c.restConfig == nil { + if c.restConfig, err = settings.RESTClientGetter().ToRESTConfig(); err != nil { + return fmt.Errorf("error creating Kubernetes REST config %v", err) + } + } + + if c.kubernetes == nil { + if c.kubernetes, err = kubernetes.NewForConfig(c.restConfig); err != nil { + return fmt.Errorf("error creating Kubernetes client %v", err) + } + } + + if c.flagNamespace == "" { + c.flagNamespace = settings.Namespace() + } + + return nil +} + +func (c *ProxyCommand) Troubleshoot() error { + pf := common.PortForward{ + Namespace: c.flagNamespace, + PodName: c.flagPod, + RemotePort: defaultAdminPort, + KubeClient: c.kubernetes, + RestConfig: c.restConfig, + } + + endpoint, err := pf.Open(c.Ctx) + if err != nil { + return err + } + defer pf.Close() + + adminAddr, adminPort, err := net.SplitHostPort(endpoint) + if err != nil { + return err + } + + adminAddrIP, err := net.ResolveIPAddr("ip", adminAddr) + if err != nil { + return err + } + + t, err := troubleshoot.NewTroubleshoot(adminAddrIP, adminPort) + if err != nil { + return err + } + + // err = t.GetEnvoyConfigDump() + // if err != nil { + // return err + // } + + messages, err := t.RunAllTests(c.flagUpstreamEnvoyID, c.flagUpstreamIP) + if err != nil { + return err + } + + c.UI.Output("Validation", terminal.WithHeaderStyle()) + for _, o := range messages { + if o.Success { + c.UI.Output(o.Message, terminal.WithSuccessStyle()) + } else { + c.UI.Output(o.Message, terminal.WithErrorStyle()) + if o.PossibleActions != "" { + c.UI.Output(fmt.Sprintf("possible actions: %v", o.PossibleActions), terminal.WithInfoStyle()) + } + } + } + + return nil +} + +// AutocompleteFlags returns a mapping of supported flags and autocomplete +// options for this command. The map key for the Flags map should be the +// complete flag such as "-foo" or "--foo". +func (c *ProxyCommand) AutocompleteFlags() complete.Flags { + return complete.Flags{ + fmt.Sprintf("-%s", flagNameNamespace): complete.PredictNothing, + fmt.Sprintf("-%s", flagNameKubeConfig): complete.PredictFiles("*"), + fmt.Sprintf("-%s", flagNameKubeContext): complete.PredictNothing, + } +} + +// AutocompleteArgs returns the argument predictor for this command. +// Since argument completion is not supported, this will return +// complete.PredictNothing. +func (c *ProxyCommand) AutocompleteArgs() complete.Predictor { + return complete.PredictNothing +} + +func (c *ProxyCommand) Synopsis() string { + return synopsis +} + +func (c *ProxyCommand) Help() string { + return help +} + +const ( + synopsis = "Troubleshoots service mesh issues." + help = ` +Usage: consul-k8s troubleshoot proxy [options] + + Connect to a pod with a proxy and troubleshoots service mesh communication issues. + + Requires a pod and upstream service SNI. + + Examples: + $ consul-k8s troubleshoot proxy -pod pod1 -upstream foo + + where 'pod1' is the pod running a consul proxy and 'foo' is the upstream envoy ID which + can be obtained by running: + $ consul-k8s troubleshoot upstreams [options] +` +) diff --git a/cli/cmd/troubleshoot/proxy/proxy_test.go b/cli/cmd/troubleshoot/proxy/proxy_test.go new file mode 100644 index 0000000000..784cc7a136 --- /dev/null +++ b/cli/cmd/troubleshoot/proxy/proxy_test.go @@ -0,0 +1,72 @@ +package proxy + +import ( + "bytes" + "context" + "io" + "os" + "testing" + + "github.com/hashicorp/consul-k8s/cli/common" + "github.com/hashicorp/consul-k8s/cli/common/terminal" + "github.com/hashicorp/go-hclog" + "github.com/stretchr/testify/require" + "k8s.io/client-go/kubernetes/fake" +) + +func TestFlagParsing(t *testing.T) { + cases := map[string]struct { + args []string + out int + }{ + "No args, should fail": { + args: []string{}, + out: 1, + }, + "Nonexistent flag passed, -foo bar, should fail": { + args: []string{"-foo", "bar"}, + out: 1, + }, + "Invalid argument passed, -namespace notaname, should fail": { + args: []string{"-namespace", "notaname"}, + out: 1, + }, + "Cannot pass both -upstream-envoy-id and -upstream-ip flags, should fail": { + args: []string{"-upstream-envoy-id", "1234", "-upstream-ip", "127.0.0.1"}, + out: 1, + }, + "Cannot pass empty -upstream-envoy-id and -upstream-ip flags, should fail": { + args: []string{"-upstream-envoy-id", "-upstream-ip"}, + out: 1, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + c := setupCommand(new(bytes.Buffer)) + c.kubernetes = fake.NewSimpleClientset() + out := c.Run(tc.args) + require.Equal(t, tc.out, out) + }) + } +} + +func setupCommand(buf io.Writer) *ProxyCommand { + // Log at a test level to standard out. + log := hclog.New(&hclog.LoggerOptions{ + Name: "test", + Level: hclog.Debug, + Output: os.Stdout, + }) + + // Setup and initialize the command struct + command := &ProxyCommand{ + BaseCommand: &common.BaseCommand{ + Log: log, + UI: terminal.NewUI(context.Background(), buf), + }, + } + command.init() + + return command +} diff --git a/cli/cmd/troubleshoot/upstreams/upstreams.go b/cli/cmd/troubleshoot/upstreams/upstreams.go new file mode 100644 index 0000000000..8b20928c7a --- /dev/null +++ b/cli/cmd/troubleshoot/upstreams/upstreams.go @@ -0,0 +1,265 @@ +package upstreams + +import ( + "fmt" + "net" + "strconv" + "strings" + "sync" + + "github.com/hashicorp/consul-k8s/cli/common" + "github.com/hashicorp/consul-k8s/cli/common/flag" + "github.com/hashicorp/consul-k8s/cli/common/terminal" + troubleshoot "github.com/hashicorp/consul/troubleshoot/proxy" + "github.com/posener/complete" + helmCLI "helm.sh/helm/v3/pkg/cli" + "k8s.io/apimachinery/pkg/api/validation" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" +) + +const ( + defaultAdminPort int = 19000 + flagNameKubeConfig = "kubeconfig" + flagNameKubeContext = "context" + flagNameNamespace = "namespace" + flagNamePod = "pod" +) + +type UpstreamsCommand struct { + *common.BaseCommand + + kubernetes kubernetes.Interface + + set *flag.Sets + + flagKubeConfig string + flagKubeContext string + flagNamespace string + + flagPod string + + restConfig *rest.Config + + once sync.Once + help string +} + +// init sets up flags and help text for the command. +func (c *UpstreamsCommand) init() { + c.set = flag.NewSets() + f := c.set.NewSet("Command Options") + + f.StringVar(&flag.StringVar{ + Name: flagNamePod, + Target: &c.flagPod, + Usage: "The pod to port-forward to.", + Aliases: []string{"p"}, + }) + + f = c.set.NewSet("Global Options") + f.StringVar(&flag.StringVar{ + Name: flagNameKubeConfig, + Aliases: []string{"c"}, + Target: &c.flagKubeConfig, + Default: "", + Usage: "Set the path to kubeconfig file.", + }) + f.StringVar(&flag.StringVar{ + Name: flagNameKubeContext, + Target: &c.flagKubeContext, + Default: "", + Usage: "Set the Kubernetes context to use.", + }) + + f.StringVar(&flag.StringVar{ + Name: flagNameNamespace, + Target: &c.flagNamespace, + Usage: "The namespace the pod is in.", + Aliases: []string{"n"}, + }) + + c.help = c.set.Help() +} + +// Run executes the list command. +func (c *UpstreamsCommand) Run(args []string) int { + c.once.Do(c.init) + c.Log.ResetNamed("list") + defer common.CloseWithError(c.BaseCommand) + + // Parse the command line flags. + if err := c.set.Parse(args); err != nil { + c.UI.Output("Error parsing arguments: %v", err.Error(), terminal.WithErrorStyle()) + return 1 + } + + // Validate the command line flags. + if err := c.validateFlags(); err != nil { + c.UI.Output("Invalid argument: %v", err.Error(), terminal.WithErrorStyle()) + return 1 + } + + if c.kubernetes == nil { + if err := c.initKubernetes(); err != nil { + c.UI.Output("Error initializing Kubernetes client: %v", err.Error(), terminal.WithErrorStyle()) + return 1 + } + } + + if err := c.Troubleshoot(); err != nil { + c.UI.Output("Error running troubleshoot: %v", err.Error(), terminal.WithErrorStyle()) + return 1 + } + + return 0 +} + +// validateFlags ensures that the flags passed in by the can be used. +func (c *UpstreamsCommand) validateFlags() error { + + if c.flagPod == "" { + return fmt.Errorf("-pod flag is required") + } + + if errs := validation.ValidateNamespaceName(c.flagNamespace, false); c.flagNamespace != "" && len(errs) > 0 { + return fmt.Errorf("invalid namespace name passed for -namespace/-n: %v", strings.Join(errs, "; ")) + } + + return nil +} + +// initKubernetes initializes the Kubernetes client. +func (c *UpstreamsCommand) initKubernetes() (err error) { + settings := helmCLI.New() + + if c.flagKubeConfig != "" { + settings.KubeConfig = c.flagKubeConfig + } + + if c.flagKubeContext != "" { + settings.KubeContext = c.flagKubeContext + } + + if c.restConfig == nil { + if c.restConfig, err = settings.RESTClientGetter().ToRESTConfig(); err != nil { + return fmt.Errorf("error creating Kubernetes REST config %v", err) + } + } + + if c.kubernetes == nil { + if c.kubernetes, err = kubernetes.NewForConfig(c.restConfig); err != nil { + return fmt.Errorf("error creating Kubernetes client %v", err) + } + } + + if c.flagNamespace == "" { + c.flagNamespace = settings.Namespace() + } + + return nil +} + +func (c *UpstreamsCommand) Troubleshoot() error { + pf := common.PortForward{ + Namespace: c.flagNamespace, + PodName: c.flagPod, + RemotePort: defaultAdminPort, + KubeClient: c.kubernetes, + RestConfig: c.restConfig, + } + + endpoint, err := pf.Open(c.Ctx) + if err != nil { + return fmt.Errorf("error opening endpoint: %v", err) + } + defer pf.Close() + + adminAddr, adminPort, err := net.SplitHostPort(endpoint) + if err != nil { + return fmt.Errorf("error splitting hostport: %v", err) + } + + adminAddrIP, err := net.ResolveIPAddr("ip", adminAddr) + if err != nil { + return fmt.Errorf("error resolving ip address: %v", err) + } + + t, err := troubleshoot.NewTroubleshoot(adminAddrIP, adminPort) + if err != nil { + return fmt.Errorf("error creating new troubleshoot: %v", err) + } + + envoyIDs, upstreamIPs, err := t.GetUpstreams() + if err != nil { + return fmt.Errorf("error getting upstreams: %v", err) + } + + c.UI.Output(fmt.Sprintf("Envoy Identifiers (explicit upstreams only) (%v)", len(envoyIDs)), terminal.WithHeaderStyle()) + for _, e := range envoyIDs { + c.UI.Output(e) + } + + c.UI.Output(fmt.Sprintf("Upstream IPs (transparent proxy only) (%v)", len(upstreamIPs)), terminal.WithHeaderStyle()) + table := terminal.NewTable("IPs ", "Virtual ", "Cluster Names") + for _, u := range upstreamIPs { + table.AddRow([]string{formatIPs(u.IPs), strconv.FormatBool(u.IsVirtual), formatClusterNames(u.ClusterNames)}, []string{}) + } + c.UI.Table(table) + + return nil +} + +// AutocompleteFlags returns a mapping of supported flags and autocomplete +// options for this command. The map key for the Flags map should be the +// complete flag such as "-foo" or "--foo". +func (c *UpstreamsCommand) AutocompleteFlags() complete.Flags { + return complete.Flags{ + fmt.Sprintf("-%s", flagNameNamespace): complete.PredictNothing, + fmt.Sprintf("-%s", flagNameKubeConfig): complete.PredictFiles("*"), + fmt.Sprintf("-%s", flagNameKubeContext): complete.PredictNothing, + } +} + +// AutocompleteArgs returns the argument predictor for this command. +// Since argument completion is not supported, this will return +// complete.PredictNothing. +func (c *UpstreamsCommand) AutocompleteArgs() complete.Predictor { + return complete.PredictNothing +} + +func (c *UpstreamsCommand) Synopsis() string { + return synopsis +} + +func (c *UpstreamsCommand) Help() string { + return help +} + +func formatIPs(ips []string) string { + return strings.Join(ips, ", ") +} + +func formatClusterNames(names map[string]struct{}) string { + var out []string + for k := range names { + out = append(out, k) + } + return strings.Join(out, ", ") +} + +const ( + synopsis = "Connect to a pod with a proxy and gather upstream services." + help = ` +Usage: consul-k8s troubleshoot upstreams [options] + + Connect to a pod with a proxy and gather upstream services. + + Requires a pod. + + Examples: + $ consul-k8s troubleshoot upstreams -pod pod1 + + where 'pod1' is the pod running a consul proxy +` +) diff --git a/cli/cmd/troubleshoot/upstreams/upstreams_test.go b/cli/cmd/troubleshoot/upstreams/upstreams_test.go new file mode 100644 index 0000000000..f5ddefbd28 --- /dev/null +++ b/cli/cmd/troubleshoot/upstreams/upstreams_test.go @@ -0,0 +1,127 @@ +package upstreams + +import ( + "bytes" + "context" + "io" + "os" + "testing" + + "github.com/hashicorp/consul-k8s/cli/common" + "github.com/hashicorp/consul-k8s/cli/common/terminal" + "github.com/hashicorp/go-hclog" + "github.com/stretchr/testify/require" + "k8s.io/client-go/kubernetes/fake" +) + +func TestFlagParsing(t *testing.T) { + cases := map[string]struct { + args []string + out int + }{ + "No args, should fail": { + args: []string{}, + out: 1, + }, + "Nonexistent flag passed, -foo bar, should fail": { + args: []string{"-foo", "bar"}, + out: 1, + }, + "Invalid argument passed, -namespace notaname, should fail": { + args: []string{"-namespace", "notaname"}, + out: 1, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + c := setupCommand(new(bytes.Buffer)) + c.kubernetes = fake.NewSimpleClientset() + out := c.Run(tc.args) + require.Equal(t, tc.out, out) + }) + } +} + +func TestFormatIPs(t *testing.T) { + t.Parallel() + + cases := []struct { + name string + actual []string + expected string + }{ + { + name: "single IPs", + actual: []string{"1.1.1.1"}, + expected: "1.1.1.1", + }, + + { + name: "several IPs", + actual: []string{"1.1.1.1", "2.2.2.2"}, + expected: "1.1.1.1, 2.2.2.2", + }, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + got := formatIPs(c.actual) + if c.expected != got { + t.Errorf("expected %v, got %v", c.expected, got) + } + }) + } +} + +func TestFormatClusterNames(t *testing.T) { + cases := []struct { + name string + actual map[string]struct{} + expected string + }{ + { + name: "single cluster", + actual: map[string]struct{}{ + "cluster1": {}, + }, + expected: "cluster1", + }, + { + name: "several clusters", + actual: map[string]struct{}{ + "cluster1": {}, + "cluster2": {}, + "cluster3": {}, + }, + expected: "cluster1, cluster2, cluster3", + }, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + got := formatClusterNames(c.actual) + if c.expected != got { + t.Errorf("expected %v, got %v", c.expected, got) + } + }) + } +} + +func setupCommand(buf io.Writer) *UpstreamsCommand { + // Log at a test level to standard out. + log := hclog.New(&hclog.LoggerOptions{ + Name: "test", + Level: hclog.Debug, + Output: os.Stdout, + }) + + // Setup and initialize the command struct + command := &UpstreamsCommand{ + BaseCommand: &common.BaseCommand{ + Log: log, + UI: terminal.NewUI(context.Background(), buf), + }, + } + command.init() + + return command +} diff --git a/cli/commands.go b/cli/commands.go index 2946873e51..fe4c47400e 100644 --- a/cli/commands.go +++ b/cli/commands.go @@ -9,6 +9,9 @@ import ( "github.com/hashicorp/consul-k8s/cli/cmd/proxy/loglevel" "github.com/hashicorp/consul-k8s/cli/cmd/proxy/read" "github.com/hashicorp/consul-k8s/cli/cmd/status" + "github.com/hashicorp/consul-k8s/cli/cmd/troubleshoot" + troubleshoot_proxy "github.com/hashicorp/consul-k8s/cli/cmd/troubleshoot/proxy" + "github.com/hashicorp/consul-k8s/cli/cmd/troubleshoot/upstreams" "github.com/hashicorp/consul-k8s/cli/cmd/uninstall" "github.com/hashicorp/consul-k8s/cli/cmd/upgrade" cmdversion "github.com/hashicorp/consul-k8s/cli/cmd/version" @@ -63,13 +66,28 @@ func initializeCommands(ctx context.Context, log hclog.Logger) (*common.BaseComm BaseCommand: baseCommand, }, nil }, + "proxy log": func() (cli.Command, error) { + return &loglevel.LogLevelCommand{ + BaseCommand: baseCommand, + }, nil + }, "proxy read": func() (cli.Command, error) { return &read.ReadCommand{ BaseCommand: baseCommand, }, nil }, - "proxy log": func() (cli.Command, error) { - return &loglevel.LogLevelCommand{ + "troubleshoot": func() (cli.Command, error) { + return &troubleshoot.TroubleshootCommand{ + BaseCommand: baseCommand, + }, nil + }, + "troubleshoot proxy": func() (cli.Command, error) { + return &troubleshoot_proxy.ProxyCommand{ + BaseCommand: baseCommand, + }, nil + }, + "troubleshoot upstreams": func() (cli.Command, error) { + return &upstreams.UpstreamsCommand{ BaseCommand: baseCommand, }, nil }, diff --git a/cli/go.mod b/cli/go.mod index 2fbe57f6c5..7c1a5af4b8 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -8,15 +8,16 @@ require ( github.com/fatih/color v1.13.0 github.com/google/go-cmp v0.5.8 github.com/hashicorp/consul-k8s/charts v0.0.0-00010101000000-000000000000 - github.com/hashicorp/go-hclog v0.16.2 + github.com/hashicorp/consul/troubleshoot v0.0.0-20230210154717-4f2ce606547b + github.com/hashicorp/go-hclog v1.2.1 github.com/hashicorp/hcp-sdk-go v0.23.1-0.20220921131124-49168300a7dc github.com/kr/text v0.2.0 - github.com/mattn/go-isatty v0.0.14 + github.com/mattn/go-isatty v0.0.16 github.com/mitchellh/cli v1.1.2 github.com/olekukonko/tablewriter v0.0.5 github.com/posener/complete v1.2.3 - github.com/stretchr/testify v1.7.2 - golang.org/x/text v0.3.7 + github.com/stretchr/testify v1.8.0 + golang.org/x/text v0.5.0 helm.sh/helm/v3 v3.9.4 k8s.io/api v0.25.0 k8s.io/apiextensions-apiserver v0.25.0 @@ -27,6 +28,8 @@ require ( sigs.k8s.io/yaml v1.3.0 ) +require go.opentelemetry.io/proto/otlp v0.11.0 // indirect + require ( cloud.google.com/go v0.99.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect @@ -46,14 +49,17 @@ require ( github.com/Masterminds/squirrel v1.5.3 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/armon/go-metrics v0.3.10 // indirect github.com/armon/go-radix v1.0.0 // indirect - github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect + github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect + github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc // indirect github.com/containerd/containerd v1.6.6 // indirect github.com/cyphar/filepath-securejoin v0.2.3 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/docker/cli v20.10.17+incompatible // indirect github.com/docker/distribution v2.8.1+incompatible // indirect github.com/docker/docker v20.10.17+incompatible // indirect @@ -62,24 +68,25 @@ require ( github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-units v0.4.0 // indirect github.com/emicklei/go-restful/v3 v3.8.0 // indirect + github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1 // indirect + github.com/envoyproxy/protoc-gen-validate v0.9.1 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/go-errors/errors v1.0.1 // indirect - github.com/go-gorp/gorp/v3 v3.0.2 // indirect + github.com/go-gorp/gorp/v3 v3.1.0 // indirect github.com/go-logr/logr v1.2.3 // indirect - github.com/go-openapi/analysis v0.20.0 // indirect + github.com/go-openapi/analysis v0.21.2 // indirect github.com/go-openapi/errors v0.20.2 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.5 // indirect - github.com/go-openapi/loads v0.20.2 // indirect - github.com/go-openapi/runtime v0.19.24 // indirect - github.com/go-openapi/spec v0.20.3 // indirect - github.com/go-openapi/strfmt v0.20.0 // indirect - github.com/go-openapi/swag v0.19.14 // indirect - github.com/go-openapi/validate v0.20.2 // indirect + github.com/go-openapi/jsonreference v0.19.6 // indirect + github.com/go-openapi/loads v0.21.1 // indirect + github.com/go-openapi/runtime v0.24.1 // indirect + github.com/go-openapi/spec v0.20.4 // indirect + github.com/go-openapi/strfmt v0.21.3 // indirect + github.com/go-openapi/swag v0.21.1 // indirect + github.com/go-openapi/validate v0.21.0 // indirect github.com/go-ozzo/ozzo-validation v3.6.0+incompatible // indirect - github.com/go-stack/stack v1.8.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.2.0 // indirect @@ -88,15 +95,22 @@ require ( github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.2.0 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gosuri/uitable v0.0.4 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect + github.com/hashicorp/consul/api v1.10.1-0.20230209203402-db2bd404bf72 // indirect + github.com/hashicorp/consul/envoyextensions v0.0.0-20230210154717-4f2ce606547b // indirect github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-cleanhttp v0.5.1 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/go-version v1.2.1 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/serf v0.10.1 // indirect github.com/huandu/xstrings v1.3.2 // indirect - github.com/imdario/mergo v0.3.12 // indirect + github.com/imdario/mergo v0.3.13 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jmoiron/sqlx v1.3.5 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -104,16 +118,17 @@ require ( github.com/klauspost/compress v1.13.6 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect - github.com/lib/pq v1.10.6 // indirect + github.com/lib/pq v1.10.7 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect - github.com/mailru/easyjson v0.7.6 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect - github.com/mitchellh/mapstructure v1.4.1 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/locker v1.0.1 // indirect github.com/moby/spdystream v0.2.0 // indirect @@ -123,12 +138,14 @@ require ( github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/oklog/ulid v1.3.1 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.12.2 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect @@ -145,19 +162,19 @@ require ( github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect - go.mongodb.org/mongo-driver v1.4.6 // indirect - go.starlark.net v0.0.0-20200707032745-474f21a9602d // indirect - golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect - golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect - golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect + go.mongodb.org/mongo-driver v1.11.1 // indirect + go.starlark.net v0.0.0-20230128213706-3f75dec8e403 // indirect + golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect + golang.org/x/net v0.4.0 // indirect + golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 // indirect golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect - golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect + golang.org/x/sys v0.3.0 // indirect + golang.org/x/term v0.3.0 // indirect golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect - google.golang.org/grpc v1.47.0 // indirect - google.golang.org/protobuf v1.28.0 // indirect + google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737 // indirect + google.golang.org/grpc v1.49.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/cli/go.sum b/cli/go.sum index bc940a4844..d736c0176a 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -71,6 +71,7 @@ github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= @@ -90,34 +91,30 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6 github.com/Microsoft/hcsshim v0.9.3 h1:k371PzBuRrz2b+ebGuI2nVgVhgsVX60jMfSw80NECxo= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= -github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.3.10 h1:FR+drcQStOe+32sYyJYyZ7FIdgoGGBnwLl+flodp8Uo= +github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= -github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= -github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef h1:46PFijGLmAjMPwCCCo7Jf0W6f9slllCkkv7vyc1yOSg= github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= +github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= +github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -135,6 +132,8 @@ github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXe github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.3.0 h1:t/LhUZLVitR1Ow2YOnduCsavhwFUklBMoGVYUCqmCqk= +github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -144,15 +143,20 @@ github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc h1:PYXxkRUBGUMa5xgMVMDl62vEklZvKpVaxQeN9ie7Hfk= +github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= github.com/containerd/containerd v1.6.6 h1:xJNPhbrmz8xAMDNoVjHy9YHtWwEQNS+CDkcIRh7t8Y0= github.com/containerd/containerd v1.6.6/go.mod h1:ZoP1geJldzCVY3Tonoz7b1IXk8rIX0Nltt5QE4OMNk0= @@ -167,8 +171,9 @@ github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= 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/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/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/distribution/distribution/v3 v3.0.0-20220526142353-ffbd94cbe269 h1:hbCT8ZPPMqefiAWD2ZKjn7ypokIGViTvBBg/ExLSdCk= @@ -185,7 +190,6 @@ github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5Xh github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= -github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= @@ -203,8 +207,12 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1 h1:xvqufLtNVwAhN8NMyWklVgxnWohi+wtMGQMhtxexlm0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v0.9.1 h1:PS7VIOgmSVhWUEeZwTe7z7zouA22Cr590PzXKbZHOVY= +github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -212,6 +220,7 @@ github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwC github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= @@ -225,15 +234,14 @@ github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmV github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gorp/gorp/v3 v3.0.2 h1:ULqJXIekoqMx29FI5ekXXFoH1dT2Vc8UhnRzBg+Emz4= github.com/go-gorp/gorp/v3 v3.0.2/go.mod h1:BJ3q1ejpV8cVALtcXvXaXyTOlMmJhWDxTmncaR6rwBY= +github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= +github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= @@ -246,104 +254,45 @@ github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= -github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= -github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= -github.com/go-openapi/analysis v0.19.4/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= -github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= -github.com/go-openapi/analysis v0.19.10/go.mod h1:qmhS3VNFxBlquFJ0RGoDtylO9y4pgTAUNE9AEEMdlJQ= -github.com/go-openapi/analysis v0.19.16/go.mod h1:GLInF007N83Ad3m8a/CbQ5TPzdnGT7workfHwuVjNVk= -github.com/go-openapi/analysis v0.20.0 h1:UN09o0kNhleunxW7LR+KnltD0YrJ8FF03pSqvAN3Vro= -github.com/go-openapi/analysis v0.20.0/go.mod h1:BMchjvaHDykmRMsK40iPtvyOfFdMMxlOmQr9FBZk+Og= -github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= -github.com/go-openapi/errors v0.19.3/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= -github.com/go-openapi/errors v0.19.6/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.19.7/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/analysis v0.21.2 h1:hXFrOYFHUAMQdu6zwAiKKJHJQ8kqZs1ux/ru1P1wLJU= +github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/errors v0.20.2 h1:dxy7PGTqEh94zj2E3h1cUmQQWiM1+aeCROfAr02EmK8= github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= -github.com/go-openapi/loads v0.19.3/go.mod h1:YVfqhUCdahYwR3f3iiwQLhicVRvLlU/WO5WPaZvcvSI= -github.com/go-openapi/loads v0.19.5/go.mod h1:dswLCAdonkRufe/gSUC3gN8nTSaB9uaS2es0x5/IbjY= -github.com/go-openapi/loads v0.19.6/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc= -github.com/go-openapi/loads v0.19.7/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc= -github.com/go-openapi/loads v0.20.0/go.mod h1:2LhKquiE513rN5xC6Aan6lYOSddlL8Mp20AW9kpviM4= -github.com/go-openapi/loads v0.20.2 h1:z5p5Xf5wujMxS1y8aP+vxwW5qYT2zdJBbXKmQUG3lcc= -github.com/go-openapi/loads v0.20.2/go.mod h1:hTVUotJ+UonAMMZsvakEgmWKgtulweO9vYP2bQYKA/o= -github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= -github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= -github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= -github.com/go-openapi/runtime v0.19.15/go.mod h1:dhGWCTKRXlAfGnQG0ONViOZpjfg0m2gUt9nTQPQZuoo= -github.com/go-openapi/runtime v0.19.16/go.mod h1:5P9104EJgYcizotuXhEuUrzVc+j1RiSjahULvYmlv98= -github.com/go-openapi/runtime v0.19.24 h1:TqagMVlRAOTwllE/7hNKx6rQ10O6T8ZzeJdMjSTKaD4= -github.com/go-openapi/runtime v0.19.24/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk= -github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= -github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/spec v0.19.6/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= -github.com/go-openapi/spec v0.19.8/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= -github.com/go-openapi/spec v0.19.15/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= -github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= -github.com/go-openapi/spec v0.20.1/go.mod h1:93x7oh+d+FQsmsieroS4cmR3u0p/ywH649a3qwC9OsQ= -github.com/go-openapi/spec v0.20.3 h1:uH9RQ6vdyPSs2pSy9fL8QPspDF2AMIMPtmK5coSSjtQ= -github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg= -github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= -github.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= -github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= -github.com/go-openapi/strfmt v0.19.4/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= -github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= -github.com/go-openapi/strfmt v0.19.11/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc= -github.com/go-openapi/strfmt v0.20.0 h1:l2omNtmNbMc39IGptl9BuXBEKcZfS8zjrTsPKTiJiDM= -github.com/go-openapi/strfmt v0.20.0/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc= -github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/loads v0.21.1 h1:Wb3nVZpdEzDTcly8S4HMkey6fjARRzb7iEaySimlDW0= +github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g= +github.com/go-openapi/runtime v0.24.1 h1:Sml5cgQKGYQHF+M7yYSHaH1eOjvTykrddTE/KtQVjqo= +github.com/go-openapi/runtime v0.24.1/go.mod h1:AKurw9fNre+h3ELZfk6ILsfvPN+bvvlaU/M9q/r9hpk= +github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= +github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= +github.com/go-openapi/strfmt v0.21.2/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= +github.com/go-openapi/strfmt v0.21.3 h1:xwhj5X6CjXEZZHMWy1zKJxvW9AfHC9pkyUjLvHtKG7o= +github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= -github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= -github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M= -github.com/go-openapi/swag v0.19.13/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= -github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= -github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo= -github.com/go-openapi/validate v0.19.10/go.mod h1:RKEZTUWDkxKQxN2jDT7ZnZi2bhZlbNMAuKvKB+IaGx8= -github.com/go-openapi/validate v0.19.12/go.mod h1:Rzou8hA/CBw8donlS6WNEUQupNvUZ0waH08tGe6kAQ4= -github.com/go-openapi/validate v0.19.15/go.mod h1:tbn/fdOwYHgrhPBzidZfJC2MIVvs9GA7monOmWBbeCI= -github.com/go-openapi/validate v0.20.1/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE9E4k54HpKcJ0= -github.com/go-openapi/validate v0.20.2 h1:AhqDegYV3J3iQkMPJSXkvzymHKMTw0BST3RK3hTT4ts= -github.com/go-openapi/validate v0.20.2/go.mod h1:e7OJoKNgd0twXZwIn0A43tHbvIcr/rZIVCbJBpTUoY0= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/validate v0.21.0 h1:+Wqk39yKOhfpLqNLEC0/eViCkzM5FVXVqrvt526+wcI= +github.com/go-openapi/validate v0.21.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= github.com/go-ozzo/ozzo-validation v3.6.0+incompatible h1:msy24VGS42fKO9K1vLz82/GeYW1cILu7Nuuj1N3BBkE= github.com/go-ozzo/ozzo-validation v3.6.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= @@ -468,11 +417,10 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLe github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -489,34 +437,61 @@ github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWet github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/api v1.10.1-0.20230209203402-db2bd404bf72 h1:O+z5m5kNtu6NHBMwMsRb1S0P7giqNu5vBBeCzgiAesg= +github.com/hashicorp/consul/api v1.10.1-0.20230209203402-db2bd404bf72/go.mod h1:c1u8FzGHcavbEtRW/p1YditvfMgn4QsKNgz2rnCDF7c= +github.com/hashicorp/consul/envoyextensions v0.0.0-20230210154717-4f2ce606547b h1:T+El0UxZP7h2mGL+EPBJejS4gKM/w0KAYOSpTs7hrbY= +github.com/hashicorp/consul/envoyextensions v0.0.0-20230210154717-4f2ce606547b/go.mod h1:oJKG0zAMtq6ZmZNYQyeKh6kIJmi01rZSZDSgnjzZ15w= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.13.0 h1:lce3nFlpv8humJL8rNrrGHYSKc3q+Kxfeg3Ii1m6ZWU= +github.com/hashicorp/consul/troubleshoot v0.0.0-20230210154717-4f2ce606547b h1:I5zDW3o7KwW4cX5kkerhm7bZOEknlSjdnIgtxnhBxOk= +github.com/hashicorp/consul/troubleshoot v0.0.0-20230210154717-4f2ce606547b/go.mod h1:rskvju2tK8XvHYTAILHjO7lpV1/uViHs3Q3mg9Rkwlg= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs= -github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v1.2.1 h1:YQsLlGDJgwhXFpucSPyVbCBviQtjlHv3jLTlp8YmtEw= +github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcp-sdk-go v0.23.1-0.20220921131124-49168300a7dc h1:on26TCKYnX7JzZCtwkR/LWHSqMu40PoZ6h/0e6Pq8ug= github.com/hashicorp/hcp-sdk-go v0.23.1-0.20220921131124-49168300a7dc/go.mod h1:/9UoDY2FYYA8lFaKBb2HmM/jKYZGANmf65q9QRc/cVw= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= +github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= +github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= @@ -525,12 +500,10 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= @@ -540,6 +513,7 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -553,10 +527,8 @@ github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaR github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -567,10 +539,9 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= @@ -580,20 +551,18 @@ github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhR github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs= -github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= +github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= @@ -603,27 +572,34 @@ github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/cli v1.1.2 h1:PvH+lL2B7IQ101xQL63Of8yFS2y+aDlsFcsqNc+u/Kw= github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= @@ -632,6 +608,8 @@ github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFW github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= @@ -640,11 +618,11 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= @@ -677,6 +655,8 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= @@ -695,9 +675,11 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec= github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= @@ -709,16 +691,18 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1 h1:oL4IBbcqwhhNWh31bjOX8C/OCy0zs9906d/VUru+bqg= github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU= +github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 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.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= @@ -731,6 +715,7 @@ github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2 github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 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.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= @@ -738,6 +723,7 @@ github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= @@ -746,6 +732,7 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rubenv/sql-migrate v1.1.1 h1:haR5Hn8hbW9/SpAICrXoZqXnywS7Q5WijwkQENPeNWY= github.com/rubenv/sql-migrate v1.1.1/go.mod h1:/7TZymwxN8VWumcIxw1jjHEcR1djpdkMHQPT4FWdnbQ= @@ -754,8 +741,8 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= @@ -791,22 +778,28 @@ github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= -github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= -github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= +github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= @@ -815,6 +808,7 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17 github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI= github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -825,19 +819,16 @@ github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= -github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= -go.mongodb.org/mongo-driver v1.3.4/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= -go.mongodb.org/mongo-driver v1.4.3/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= -go.mongodb.org/mongo-driver v1.4.4/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= -go.mongodb.org/mongo-driver v1.4.6 h1:rh7GdYmDrb8AQSkF8yteAus8qYOgOASWDOv1BWqBXkU= -go.mongodb.org/mongo-driver v1.4.6/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= +go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= +go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= +go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= +go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= +go.mongodb.org/mongo-driver v1.11.1 h1:QP0znIRTuL0jf1oBQoAoM0C6ZJfBK4kx0Uumtv1A7w8= +go.mongodb.org/mongo-driver v1.11.1/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -857,9 +848,11 @@ go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v0.11.0 h1:cLDgIBTf4lLOlztkhzAEdQsJ4Lj+i5Wc9k6Nn0K1VyU= +go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= -go.starlark.net v0.0.0-20200707032745-474f21a9602d h1:uFqwFYlX7d5ZSp+IqhXxct0SybXrTzEBDvb2CkEhPBs= -go.starlark.net v0.0.0-20200707032745-474f21a9602d/go.mod h1:f0znQkUKRrkk36XxWbGjMqQM8wGv/xHBVE2qc3B5oFU= +go.starlark.net v0.0.0-20230128213706-3f75dec8e403 h1:jPeC7Exc+m8OBJUlWbBLh0O5UZPM7yU5W4adnhhbG4U= +go.starlark.net v0.0.0-20230128213706-3f75dec8e403/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= @@ -868,25 +861,24 @@ go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -926,14 +918,12 @@ golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -943,6 +933,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -955,28 +946,27 @@ golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= 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= @@ -993,8 +983,9 @@ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 h1:lxqLZaMad/dJHMFZH0NiNpiEZI/nhgWhe4wgzpE+MuA= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= 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= @@ -1018,7 +1009,6 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1027,15 +1017,15 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1067,11 +1057,13 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1091,11 +1083,18 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1104,8 +1103,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1113,9 +1113,7 @@ golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U= golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -1129,12 +1127,11 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1284,8 +1281,8 @@ google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 h1:hrbNEivu7Zn1pxvHk6MBrq9iE22woVILTHqexqBxe6I= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737 h1:K1zaaMdYBXRyX+cwFnxj7M6zwDyumLQMZ5xqwGvjreQ= +google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737/go.mod h1:2r/26NEF3bFmT3eC3aZreahSal0C3Shl8Gi6vyDYqOQ= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1311,9 +1308,9 @@ google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 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= @@ -1328,8 +1325,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1357,6 +1354,7 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= From de88a4186d2b307605f1e1549aeef297b678ff37 Mon Sep 17 00:00:00 2001 From: jm96441n Date: Mon, 23 Jan 2023 15:09:22 -0500 Subject: [PATCH 069/340] Added set command for log levels Move format parsing into envoy package Move enovy to common package, move param parsing to calling package use a LoggerParams struct for handling a format for log changes to envoy refactor to use logger params and methods to set and validate logger and log levels before calling envoy linting changes clean up from rebase Improve comment on envoy logging endpoint function, switched to using '-update-level' for updating envoy log level flag for better usability --- cli/cmd/proxy/loglevel/command.go | 116 ++++++------ cli/cmd/proxy/loglevel/command_test.go | 141 ++++++++------ cli/cmd/proxy/read/command.go | 42 +++-- cli/cmd/proxy/read/command_test.go | 114 ++++++++++-- cli/cmd/proxy/read/filters.go | 14 +- cli/cmd/proxy/read/filters_test.go | 46 ++--- cli/cmd/proxy/read/format.go | 17 +- cli/cmd/proxy/read/format_test.go | 18 +- .../read/config.go => common/envoy/http.go} | 60 +++++- .../envoy/http_test.go} | 57 ++++-- cli/common/envoy/logger_params.go | 166 +++++++++++++++++ cli/common/envoy/logger_params_test.go | 172 ++++++++++++++++++ .../envoy}/testdata/fetch_debug_levels.txt | 0 .../envoy/testdata}/test_clusters.json | 0 .../envoy/testdata}/test_config_dump.json | 0 .../envoy_types.go => common/envoy/types.go} | 2 +- 16 files changed, 753 insertions(+), 212 deletions(-) rename cli/{cmd/proxy/read/config.go => common/envoy/http.go} (91%) rename cli/{cmd/proxy/read/config_test.go => common/envoy/http_test.go} (93%) create mode 100644 cli/common/envoy/logger_params.go create mode 100644 cli/common/envoy/logger_params_test.go rename cli/{cmd/proxy/loglevel => common/envoy}/testdata/fetch_debug_levels.txt (100%) rename cli/{cmd/proxy/read => common/envoy/testdata}/test_clusters.json (100%) rename cli/{cmd/proxy/read => common/envoy/testdata}/test_config_dump.json (100%) rename cli/{cmd/proxy/read/envoy_types.go => common/envoy/types.go} (99%) diff --git a/cli/cmd/proxy/loglevel/command.go b/cli/cmd/proxy/loglevel/command.go index 80444d7339..85dc91bfa2 100644 --- a/cli/cmd/proxy/loglevel/command.go +++ b/cli/cmd/proxy/loglevel/command.go @@ -1,39 +1,36 @@ package loglevel import ( - "bytes" "context" "errors" "fmt" - "io" - "net/http" "strings" "sync" - "github.com/hashicorp/consul-k8s/cli/common" - "github.com/hashicorp/consul-k8s/cli/common/flag" - "github.com/hashicorp/consul-k8s/cli/common/terminal" "github.com/posener/complete" helmCLI "helm.sh/helm/v3/pkg/cli" "k8s.io/apimachinery/pkg/api/validation" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" + + "github.com/hashicorp/consul-k8s/cli/common" + "github.com/hashicorp/consul-k8s/cli/common/envoy" + "github.com/hashicorp/consul-k8s/cli/common/flag" + "github.com/hashicorp/consul-k8s/cli/common/terminal" ) const ( defaultAdminPort = 19000 flagNameNamespace = "namespace" + flagNameUpdateLevel = "update-level" flagNameKubeConfig = "kubeconfig" flagNameKubeContext = "context" ) -type LoggerConfig map[string]string +var ErrIncorrectArgFormat = errors.New("Exactly one positional argument is required: ") -var ( - ErrIncorrectArgFormat = errors.New("Exactly one positional argument is required: ") - ErrNoLoggersReturned = errors.New("No loggers were returned from Envoy") -) +type LoggerConfig map[string]string var levelToColor = map[string]string{ "trace": terminal.Green, @@ -54,13 +51,14 @@ type LogLevelCommand struct { // Command Flags podName string namespace string + level string kubeConfig string kubeContext string - once sync.Once - help string - restConfig *rest.Config - logLevelFetcher func(context.Context, common.PortForwarder) (LoggerConfig, error) + once sync.Once + help string + restConfig *rest.Config + envoyLoggingCaller func(context.Context, common.PortForwarder, *envoy.LoggerParams) (map[string]string, error) } func (l *LogLevelCommand) init() { @@ -74,6 +72,13 @@ func (l *LogLevelCommand) init() { Aliases: []string{"n"}, }) + f.StringVar(&flag.StringVar{ + Name: flagNameUpdateLevel, + Target: &l.level, + Usage: "Update the level for the logger. Can be either `-update-level warning` to change all loggers to warning, or a comma delineated list of loggers with level can be passed like `-update-level grpc:warning,http:info` to only modify specific loggers.", + Aliases: []string{"u"}, + }) + f = l.set.NewSet("Global Options") f.StringVar(&flag.StringVar{ Name: flagNameKubeConfig, @@ -103,8 +108,8 @@ func (l *LogLevelCommand) Run(args []string) int { return l.logOutputAndDie(err) } - if l.logLevelFetcher == nil { - l.logLevelFetcher = FetchLogLevel + if l.envoyLoggingCaller == nil { + l.envoyLoggingCaller = envoy.CallLoggingEndpoint } err = l.initKubernetes() @@ -117,11 +122,11 @@ func (l *LogLevelCommand) Run(args []string) int { return l.logOutputAndDie(err) } - logLevels, err := l.fetchLogLevels(adminPorts) + err = l.fetchOrSetLogLevels(adminPorts) if err != nil { return l.logOutputAndDie(err) } - l.outputLevels(logLevels) + return 0 } @@ -150,6 +155,7 @@ func (l *LogLevelCommand) parseFlags(args []string) error { if err != nil { return err } + return nil } @@ -222,7 +228,7 @@ func (l *LogLevelCommand) fetchAdminPorts() (map[string]int, error) { return adminPorts, nil } -func (l *LogLevelCommand) fetchLogLevels(adminPorts map[string]int) (map[string]LoggerConfig, error) { +func (l *LogLevelCommand) fetchOrSetLogLevels(adminPorts map[string]int) error { loggers := make(map[string]LoggerConfig, 0) for name, port := range adminPorts { @@ -233,61 +239,47 @@ func (l *LogLevelCommand) fetchLogLevels(adminPorts map[string]int) (map[string] KubeClient: l.kubernetes, RestConfig: l.restConfig, } - - logLevels, err := l.logLevelFetcher(l.Ctx, &pf) + params, err := parseParams(l.level) + if err != nil { + return err + } + logLevels, err := l.envoyLoggingCaller(l.Ctx, &pf, params) if err != nil { - return loggers, err + return err } loggers[name] = logLevels } - return loggers, nil -} - -// FetchLogLevel requests the logging endpoint from Envoy Admin Interface for a given port -// more can be read about that endpoint https://www.envoyproxy.io/docs/envoy/latest/operations/admin#post--logging -func FetchLogLevel(ctx context.Context, portForward common.PortForwarder) (LoggerConfig, error) { - endpoint, err := portForward.Open(ctx) - if err != nil { - return nil, err - } - - defer portForward.Close() - - // this endpoint does not support returning json, so we've gotta parse the plain text - response, err := http.Post(fmt.Sprintf("http://%s/logging", endpoint), "text/plain", bytes.NewBuffer([]byte{})) - if err != nil { - return nil, err - } - body, err := io.ReadAll(response.Body) - if err != nil { - return nil, fmt.Errorf("failed to reach envoy: %v", err) - } + l.outputLevels(loggers) + return nil +} - if response.StatusCode >= 400 { - return nil, fmt.Errorf("call to envoy failed with status code: %d, and message: %s", response.StatusCode, body) +func parseParams(params string) (*envoy.LoggerParams, error) { + loggerParams := envoy.NewLoggerParams() + if len(params) == 0 { + return loggerParams, nil } - loggers := strings.Split(string(body), "\n") - if len(loggers) == 0 { - return nil, ErrNoLoggersReturned + // contains global log level change + if !strings.Contains(params, ":") { + err := loggerParams.SetGlobalLoggerLevel(params) + if err != nil { + return nil, err + } + return loggerParams, nil } - logLevels := make(map[string]string) - var name string - var level string + // contains changes to at least 1 specific log level + loggerChanges := strings.Split(params, ",") - // the first line here is just a header - for _, logger := range loggers[1:] { - if len(logger) == 0 { - continue + for _, logger := range loggerChanges { + levelValues := strings.Split(logger, ":") + err := loggerParams.SetLoggerLevel(levelValues[0], levelValues[1]) + if err != nil { + return nil, err } - fmt.Sscanf(logger, "%s %s", &name, &level) - name = strings.TrimRight(name, ":") - logLevels[name] = level } - - return logLevels, nil + return loggerParams, nil } func (l *LogLevelCommand) outputLevels(logLevels map[string]LoggerConfig) { diff --git a/cli/cmd/proxy/loglevel/command_test.go b/cli/cmd/proxy/loglevel/command_test.go index 6207270c20..1c5387fd5f 100644 --- a/cli/cmd/proxy/loglevel/command_test.go +++ b/cli/cmd/proxy/loglevel/command_test.go @@ -5,20 +5,19 @@ import ( "context" "fmt" "io" - "net/http" - "net/http/httptest" "os" "regexp" - "strings" "testing" - "github.com/hashicorp/consul-k8s/cli/common" - "github.com/hashicorp/consul-k8s/cli/common/terminal" - "github.com/hashicorp/go-hclog" "github.com/stretchr/testify/require" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/fake" + + "github.com/hashicorp/consul-k8s/cli/common" + "github.com/hashicorp/consul-k8s/cli/common/envoy" + "github.com/hashicorp/consul-k8s/cli/common/terminal" + "github.com/hashicorp/go-hclog" ) func TestFlagParsingFails(t *testing.T) { @@ -56,7 +55,7 @@ func TestFlagParsingFails(t *testing.T) { t.Run(name, func(t *testing.T) { c := setupCommand(bytes.NewBuffer([]byte{})) c.kubernetes = fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{fakePod}}) - c.logLevelFetcher = func(context.Context, common.PortForwarder) (LoggerConfig, error) { + c.envoyLoggingCaller = func(context.Context, common.PortForwarder, *envoy.LoggerParams) (map[string]string, error) { return testLogConfig, nil } @@ -84,6 +83,36 @@ func TestFlagParsingSucceeds(t *testing.T) { podNamespace: "another", out: 0, }, + "With single pod name and blanket level": { + args: []string{podName, "-u", "warning"}, + podNamespace: "default", + out: 0, + }, + "With single pod name and single level": { + args: []string{podName, "-u", "grpc:warning"}, + podNamespace: "default", + out: 0, + }, + "With single pod name and multiple levels": { + args: []string{podName, "-u", "grpc:warning,http:info"}, + podNamespace: "default", + out: 0, + }, + "With single pod name and blanket level full flag": { + args: []string{podName, "-update-level", "warning"}, + podNamespace: "default", + out: 0, + }, + "With single pod name and single level full flag": { + args: []string{podName, "-update-level", "grpc:warning"}, + podNamespace: "default", + out: 0, + }, + "With single pod name and multiple levels full flag": { + args: []string{podName, "-update-level", "grpc:warning,http:info"}, + podNamespace: "default", + out: 0, + }, } for name, tc := range testCases { @@ -97,7 +126,7 @@ func TestFlagParsingSucceeds(t *testing.T) { c := setupCommand(bytes.NewBuffer([]byte{})) c.kubernetes = fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{fakePod}}) - c.logLevelFetcher = func(context.Context, common.PortForwarder) (LoggerConfig, error) { + c.envoyLoggingCaller = func(context.Context, common.PortForwarder, *envoy.LoggerParams) (map[string]string, error) { return testLogConfig, nil } @@ -107,7 +136,7 @@ func TestFlagParsingSucceeds(t *testing.T) { } } -func TestOutputForGettingLogLevel(t *testing.T) { +func TestOutputForGettingLogLevels(t *testing.T) { t.Parallel() podName := "now-this-is-pod-racing" expectedHeader := fmt.Sprintf("Envoy log configuration for %s in namespace default:", podName) @@ -120,12 +149,49 @@ func TestOutputForGettingLogLevel(t *testing.T) { buf := bytes.NewBuffer([]byte{}) c := setupCommand(buf) - c.logLevelFetcher = func(context.Context, common.PortForwarder) (LoggerConfig, error) { + newLogLevel := "warning" + config := make(map[string]string, len(testLogConfig)) + for logger := range testLogConfig { + config[logger] = newLogLevel + } + + c.envoyLoggingCaller = func(context.Context, common.PortForwarder, *envoy.LoggerParams) (map[string]string, error) { + return config, nil + } + c.kubernetes = fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{fakePod}}) + + args := []string{podName, "-u", newLogLevel} + out := c.Run(args) + require.Equal(t, 0, out) + + actual := buf.String() + + require.Regexp(t, expectedHeader, actual) + require.Regexp(t, "Log Levels for now-this-is-pod-racing", actual) + for logger, level := range config { + require.Regexp(t, regexp.MustCompile(logger+`.*`+level), actual) + } +} + +func TestOutputForSettingLogLevels(t *testing.T) { + t.Parallel() + podName := "now-this-is-pod-racing" + expectedHeader := fmt.Sprintf("Envoy log configuration for %s in namespace default:", podName) + fakePod := v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: podName, + Namespace: "default", + }, + } + + buf := bytes.NewBuffer([]byte{}) + c := setupCommand(buf) + c.envoyLoggingCaller = func(context.Context, common.PortForwarder, *envoy.LoggerParams) (map[string]string, error) { return testLogConfig, nil } c.kubernetes = fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{fakePod}}) - args := []string{podName} + args := []string{podName, "-u", "warning"} out := c.Run(args) require.Equal(t, 0, out) @@ -149,34 +215,24 @@ func TestHelp(t *testing.T) { require.Regexp(t, expectedUsage, actual) } -func TestFetchLogLevel(t *testing.T) { - t.Parallel() - rawLogLevels, err := os.ReadFile("testdata/fetch_debug_levels.txt") - require.NoError(t, err) - mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write(rawLogLevels) - })) - - defer mockServer.Close() +func setupCommand(buf io.Writer) *LogLevelCommand { + log := hclog.New(&hclog.LoggerOptions{ + Name: "test", + Level: hclog.Debug, + Output: os.Stdout, + }) - mpf := &mockPortForwarder{ - openBehavior: func(ctx context.Context) (string, error) { - return strings.Replace(mockServer.URL, "http://", "", 1), nil + command := &LogLevelCommand{ + BaseCommand: &common.BaseCommand{ + Log: log, + UI: terminal.NewUI(context.Background(), buf), }, } - logLevels, err := FetchLogLevel(context.Background(), mpf) - require.NoError(t, err) - require.Equal(t, testLogConfig, logLevels) -} - -type mockPortForwarder struct { - openBehavior func(context.Context) (string, error) + command.init() + return command } -func (m *mockPortForwarder) Open(ctx context.Context) (string, error) { return m.openBehavior(ctx) } -func (m *mockPortForwarder) Close() {} - -var testLogConfig = LoggerConfig{ +var testLogConfig = map[string]string{ "admin": "debug", "alternate_protocols_cache": "debug", "aws": "debug", @@ -235,20 +291,3 @@ var testLogConfig = LoggerConfig{ "wasm": "debug", "websocket": "debug", } - -func setupCommand(buf io.Writer) *LogLevelCommand { - log := hclog.New(&hclog.LoggerOptions{ - Name: "test", - Level: hclog.Debug, - Output: os.Stdout, - }) - - command := &LogLevelCommand{ - BaseCommand: &common.BaseCommand{ - Log: log, - UI: terminal.NewUI(context.Background(), buf), - }, - } - command.init() - return command -} diff --git a/cli/cmd/proxy/read/command.go b/cli/cmd/proxy/read/command.go index ad2bb96303..e85d7166e9 100644 --- a/cli/cmd/proxy/read/command.go +++ b/cli/cmd/proxy/read/command.go @@ -7,9 +7,6 @@ import ( "strings" "sync" - "github.com/hashicorp/consul-k8s/cli/common" - "github.com/hashicorp/consul-k8s/cli/common/flag" - "github.com/hashicorp/consul-k8s/cli/common/terminal" "github.com/posener/complete" helmCLI "helm.sh/helm/v3/pkg/cli" "k8s.io/apimachinery/pkg/api/validation" @@ -17,6 +14,11 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/utils/strings/slices" + + "github.com/hashicorp/consul-k8s/cli/common" + "github.com/hashicorp/consul-k8s/cli/common/envoy" + "github.com/hashicorp/consul-k8s/cli/common/flag" + "github.com/hashicorp/consul-k8s/cli/common/terminal" ) // defaultAdminPort is the port where the Envoy admin API is exposed. @@ -67,7 +69,7 @@ type ReadCommand struct { flagKubeConfig string flagKubeContext string - fetchConfig func(context.Context, common.PortForwarder) (*EnvoyConfig, error) + fetchConfig func(context.Context, common.PortForwarder) (*envoy.EnvoyConfig, error) restConfig *rest.Config @@ -77,7 +79,7 @@ type ReadCommand struct { func (c *ReadCommand) init() { if c.fetchConfig == nil { - c.fetchConfig = FetchConfig + c.fetchConfig = envoy.FetchConfig } c.set = flag.NewSets() @@ -320,8 +322,8 @@ func (c *ReadCommand) fetchAdminPorts() (map[string]int, error) { return adminPorts, nil } -func (c *ReadCommand) fetchConfigs(adminPorts map[string]int) (map[string]*EnvoyConfig, error) { - configs := make(map[string]*EnvoyConfig, 0) +func (c *ReadCommand) fetchConfigs(adminPorts map[string]int) (map[string]*envoy.EnvoyConfig, error) { + configs := make(map[string]*envoy.EnvoyConfig, 0) for name, adminPort := range adminPorts { pf := common.PortForward{ @@ -343,7 +345,7 @@ func (c *ReadCommand) fetchConfigs(adminPorts map[string]int) (map[string]*Envoy return configs, nil } -func (c *ReadCommand) outputConfigs(configs map[string]*EnvoyConfig) error { +func (c *ReadCommand) outputConfigs(configs map[string]*envoy.EnvoyConfig) error { switch c.flagOutput { case Table: return c.outputTables(configs) @@ -396,7 +398,7 @@ func (c *ReadCommand) filterWarnings() []string { return warnings } -func (c *ReadCommand) outputTables(configs map[string]*EnvoyConfig) error { +func (c *ReadCommand) outputTables(configs map[string]*envoy.EnvoyConfig) error { if c.flagFQDN != "" || c.flagAddress != "" || c.flagPort != -1 { c.UI.Output("Filters applied", terminal.WithHeaderStyle()) @@ -431,7 +433,7 @@ func (c *ReadCommand) outputTables(configs map[string]*EnvoyConfig) error { return nil } -func (c *ReadCommand) outputJSON(configs map[string]*EnvoyConfig) error { +func (c *ReadCommand) outputJSON(configs map[string]*envoy.EnvoyConfig) error { cfgs := make(map[string]interface{}) for name, config := range configs { cfg := make(map[string]interface{}) @@ -467,11 +469,11 @@ func (c *ReadCommand) outputJSON(configs map[string]*EnvoyConfig) error { return nil } -func (c *ReadCommand) outputRaw(configs map[string]*EnvoyConfig) error { +func (c *ReadCommand) outputRaw(configs map[string]*envoy.EnvoyConfig) error { cfgs := make(map[string]interface{}, 0) for name, config := range configs { var cfg interface{} - if err := json.Unmarshal(config.rawCfg, &cfg); err != nil { + if err := json.Unmarshal(config.RawCfg, &cfg); err != nil { return err } @@ -488,7 +490,7 @@ func (c *ReadCommand) outputRaw(configs map[string]*EnvoyConfig) error { return nil } -func (c *ReadCommand) outputClustersTable(clusters []Cluster) { +func (c *ReadCommand) outputClustersTable(clusters []envoy.Cluster) { if !c.shouldPrintTable(c.flagClusters) { return } @@ -496,14 +498,16 @@ func (c *ReadCommand) outputClustersTable(clusters []Cluster) { c.UI.Output(fmt.Sprintf("Clusters (%d)", len(clusters)), terminal.WithHeaderStyle()) table := terminal.NewTable("Name", "FQDN", "Endpoints", "Type", "Last Updated") for _, cluster := range clusters { - table.AddRow([]string{cluster.Name, cluster.FullyQualifiedDomainName, strings.Join(cluster.Endpoints, ", "), - cluster.Type, cluster.LastUpdated}, []string{}) + table.AddRow([]string{ + cluster.Name, cluster.FullyQualifiedDomainName, strings.Join(cluster.Endpoints, ", "), + cluster.Type, cluster.LastUpdated, + }, []string{}) } c.UI.Table(table) c.UI.Output("") } -func (c *ReadCommand) outputEndpointsTable(endpoints []Endpoint) { +func (c *ReadCommand) outputEndpointsTable(endpoints []envoy.Endpoint) { if !c.shouldPrintTable(c.flagEndpoints) { return } @@ -512,7 +516,7 @@ func (c *ReadCommand) outputEndpointsTable(endpoints []Endpoint) { c.UI.Table(formatEndpoints(endpoints)) } -func (c *ReadCommand) outputListenersTable(listeners []Listener) { +func (c *ReadCommand) outputListenersTable(listeners []envoy.Listener) { if !c.shouldPrintTable(c.flagListeners) { return } @@ -521,7 +525,7 @@ func (c *ReadCommand) outputListenersTable(listeners []Listener) { c.UI.Table(formatListeners(listeners)) } -func (c *ReadCommand) outputRoutesTable(routes []Route) { +func (c *ReadCommand) outputRoutesTable(routes []envoy.Route) { if !c.shouldPrintTable(c.flagRoutes) { return } @@ -530,7 +534,7 @@ func (c *ReadCommand) outputRoutesTable(routes []Route) { c.UI.Table(formatRoutes(routes)) } -func (c *ReadCommand) outputSecretsTable(secrets []Secret) { +func (c *ReadCommand) outputSecretsTable(secrets []envoy.Secret) { if !c.shouldPrintTable(c.flagSecrets) { return } diff --git a/cli/cmd/proxy/read/command_test.go b/cli/cmd/proxy/read/command_test.go index 27f19e7370..1e439bce91 100644 --- a/cli/cmd/proxy/read/command_test.go +++ b/cli/cmd/proxy/read/command_test.go @@ -9,16 +9,18 @@ import ( "os" "testing" - "github.com/hashicorp/consul-k8s/cli/common" - cmnFlag "github.com/hashicorp/consul-k8s/cli/common/flag" - "github.com/hashicorp/consul-k8s/cli/common/terminal" - "github.com/hashicorp/go-hclog" "github.com/posener/complete" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/fake" + + "github.com/hashicorp/consul-k8s/cli/common" + "github.com/hashicorp/consul-k8s/cli/common/envoy" + cmnFlag "github.com/hashicorp/consul-k8s/cli/common/flag" + "github.com/hashicorp/consul-k8s/cli/common/terminal" + "github.com/hashicorp/go-hclog" ) func TestFlagParsing(t *testing.T) { @@ -65,38 +67,48 @@ func TestReadCommandOutput(t *testing.T) { // These regular expressions must be present in the output. expectedHeader := fmt.Sprintf("Envoy configuration for %s in namespace default:", podName) expected := map[string][]string{ - "-clusters": {"==> Clusters \\(5\\)", + "-clusters": { + "==> Clusters \\(5\\)", "Name.*FQDN.*Endpoints.*Type.*Last Updated", "local_agent.*192\\.168\\.79\\.187:8502.*STATIC.*2022-05-13T04:22:39\\.553Z", "local_app.*127\\.0\\.0\\.1:8080.*STATIC.*2022-05-13T04:22:39\\.655Z", "client.*client\\.default\\.dc1\\.internal\\.bc3815c2-1a0f-f3ff-a2e9-20d791f08d00\\.consul.*EDS", "frontend.*frontend\\.default\\.dc1\\.internal\\.bc3815c2-1a0f-f3ff-a2e9-20d791f08d00\\.consul", - "original-destination.*ORIGINAL_DST"}, + "original-destination.*ORIGINAL_DST", + }, - "-endpoints": {"==> Endpoints \\(6\\)", + "-endpoints": { + "==> Endpoints \\(6\\)", "Address:Port.*Cluster.*Weight.*Status", "192.168.79.187:8502.*local_agent.*1.00.*HEALTHY", "127.0.0.1:8080.*local_app.*1.00.*HEALTHY", "192.168.18.110:20000.*client.*1.00.*HEALTHY", "192.168.52.101:20000.*client.*1.00.*HEALTHY", "192.168.65.131:20000.*client.*1.00.*HEALTHY", - "192.168.63.120:20000.*frontend.*1.00.*HEALTHY"}, + "192.168.63.120:20000.*frontend.*1.00.*HEALTHY", + }, - "-listeners": {"==> Listeners \\(2\\)", + "-listeners": { + "==> Listeners \\(2\\)", "Name.*Address:Port.*Direction.*Filter Chain Match.*Filters.*Last Updated", "public_listener.*192\\.168\\.69\\.179:20000.*INBOUND.*Any.*\\* -> local_app/", "outbound_listener.*127.0.0.1:15001.*OUTBOUND.*10\\.100\\.134\\.173/32, 240\\.0\\.0\\.3/32.*TCP: -> client", "10\\.100\\.31\\.2/32, 240\\.0\\.0\\.5/32.*TCP: -> frontend", - "Any.*TCP: -> original-destination"}, + "Any.*TCP: -> original-destination", + }, - "-routes": {"==> Routes \\(1\\)", + "-routes": { + "==> Routes \\(1\\)", "Name.*Destination Cluster.*Last Updated", - "public_listener.*local_app/"}, + "public_listener.*local_app/", + }, - "-secrets": {"==> Secrets \\(2\\)", + "-secrets": { + "==> Secrets \\(2\\)", "Name.*Type.*Last Updated", "default.*Dynamic Active", - "ROOTCA.*Dynamic Warming"}, + "ROOTCA.*Dynamic Warming", + }, } cases := map[string][]string{ @@ -122,7 +134,7 @@ func TestReadCommandOutput(t *testing.T) { c.kubernetes = fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{fakePod}}) // A fetchConfig function that just returns the test Envoy config. - c.fetchConfig = func(context.Context, common.PortForwarder) (*EnvoyConfig, error) { + c.fetchConfig = func(context.Context, common.PortForwarder) (*envoy.EnvoyConfig, error) { return testEnvoyConfig, nil } @@ -230,7 +242,7 @@ func TestFilterWarnings(t *testing.T) { buf := new(bytes.Buffer) c := setupCommand(buf) c.kubernetes = fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{fakePod}}) - c.fetchConfig = func(context.Context, common.PortForwarder) (*EnvoyConfig, error) { + c.fetchConfig = func(context.Context, common.PortForwarder) (*envoy.EnvoyConfig, error) { return testEnvoyConfig, nil } @@ -295,3 +307,73 @@ func TestTaskCreateCommand_AutocompleteArgs(t *testing.T) { c := cmd.AutocompleteArgs() assert.Equal(t, complete.PredictNothing, c) } + +// testEnvoyConfig is what we expect the config at `test_config_dump.json` to be. + +var testEnvoyConfig = &envoy.EnvoyConfig{ + Clusters: []envoy.Cluster{ + {Name: "local_agent", FullyQualifiedDomainName: "local_agent", Endpoints: []string{"192.168.79.187:8502"}, Type: "STATIC", LastUpdated: "2022-05-13T04:22:39.553Z"}, + + {Name: "client", FullyQualifiedDomainName: "client.default.dc1.internal.bc3815c2-1a0f-f3ff-a2e9-20d791f08d00.consul", Endpoints: []string{"192.168.18.110:20000", "192.168.52.101:20000", "192.168.65.131:20000"}, Type: "EDS", LastUpdated: "2022-08-10T12:30:32.326Z"}, + + {Name: "frontend", FullyQualifiedDomainName: "frontend.default.dc1.internal.bc3815c2-1a0f-f3ff-a2e9-20d791f08d00.consul", Endpoints: []string{"192.168.63.120:20000"}, Type: "EDS", LastUpdated: "2022-08-10T12:30:32.233Z"}, + + {Name: "local_app", FullyQualifiedDomainName: "local_app", Endpoints: []string{"127.0.0.1:8080"}, Type: "STATIC", LastUpdated: "2022-05-13T04:22:39.655Z"}, + + {Name: "original-destination", FullyQualifiedDomainName: "original-destination", Endpoints: []string{}, Type: "ORIGINAL_DST", LastUpdated: "2022-05-13T04:22:39.743Z"}, + }, + + Endpoints: []envoy.Endpoint{ + {Address: "192.168.79.187:8502", Cluster: "local_agent", Weight: 1, Status: "HEALTHY"}, + + {Address: "192.168.18.110:20000", Cluster: "client", Weight: 1, Status: "HEALTHY"}, + + {Address: "192.168.52.101:20000", Cluster: "client", Weight: 1, Status: "HEALTHY"}, + + {Address: "192.168.65.131:20000", Cluster: "client", Weight: 1, Status: "HEALTHY"}, + + {Address: "192.168.63.120:20000", Cluster: "frontend", Weight: 1, Status: "HEALTHY"}, + + {Address: "127.0.0.1:8080", Cluster: "local_app", Weight: 1, Status: "HEALTHY"}, + }, + + Listeners: []envoy.Listener{ + {Name: "public_listener", Address: "192.168.69.179:20000", FilterChain: []envoy.FilterChain{{Filters: []string{"HTTP: * -> local_app/"}, FilterChainMatch: "Any"}}, Direction: "INBOUND", LastUpdated: "2022-08-10T12:30:47.142Z"}, + + {Name: "outbound_listener", Address: "127.0.0.1:15001", FilterChain: []envoy.FilterChain{ + {Filters: []string{"TCP: -> client"}, FilterChainMatch: "10.100.134.173/32, 240.0.0.3/32"}, + + {Filters: []string{"TCP: -> frontend"}, FilterChainMatch: "10.100.31.2/32, 240.0.0.5/32"}, + + {Filters: []string{"TCP: -> original-destination"}, FilterChainMatch: "Any"}, + }, Direction: "OUTBOUND", LastUpdated: "2022-07-18T15:31:03.246Z"}, + }, + + Routes: []envoy.Route{ + { + Name: "public_listener", + + DestinationCluster: "local_app/", + + LastUpdated: "2022-08-10T12:30:47.141Z", + }, + }, + + Secrets: []envoy.Secret{ + { + Name: "default", + + Type: "Dynamic Active", + + LastUpdated: "2022-05-24T17:41:59.078Z", + }, + + { + Name: "ROOTCA", + + Type: "Dynamic Warming", + + LastUpdated: "2022-03-15T05:14:22.868Z", + }, + }, +} diff --git a/cli/cmd/proxy/read/filters.go b/cli/cmd/proxy/read/filters.go index dc65172f32..ab3e21b4d8 100644 --- a/cli/cmd/proxy/read/filters.go +++ b/cli/cmd/proxy/read/filters.go @@ -3,6 +3,8 @@ package read import ( "strconv" "strings" + + "github.com/hashicorp/consul-k8s/cli/common/envoy" ) // FilterClusters takes a slice of clusters along with parameters for filtering @@ -17,7 +19,7 @@ import ( // // The filters are applied in combination such that a cluster must adhere to // all of the filtering values which are passed in. -func FilterClusters(clusters []Cluster, fqdn, address string, port int) []Cluster { +func FilterClusters(clusters []envoy.Cluster, fqdn, address string, port int) []envoy.Cluster { // No filtering no-op. if fqdn == "" && address == "" && port == -1 { return clusters @@ -25,7 +27,7 @@ func FilterClusters(clusters []Cluster, fqdn, address string, port int) []Cluste portStr := ":" + strconv.Itoa(port) - filtered := make([]Cluster, 0) + filtered := make([]envoy.Cluster, 0) for _, cluster := range clusters { if !strings.Contains(cluster.FullyQualifiedDomainName, fqdn) { continue @@ -58,14 +60,14 @@ func FilterClusters(clusters []Cluster, fqdn, address string, port int) []Cluste // // The filters are applied in combination such that an endpoint must adhere to // all of the filtering values which are passed in. -func FilterEndpoints(endpoints []Endpoint, address string, port int) []Endpoint { +func FilterEndpoints(endpoints []envoy.Endpoint, address string, port int) []envoy.Endpoint { if address == "" && port == -1 { return endpoints } portStr := ":" + strconv.Itoa(port) - filtered := make([]Endpoint, 0) + filtered := make([]envoy.Endpoint, 0) for _, endpoint := range endpoints { if strings.Contains(endpoint.Address, address) && (port == -1 || strings.Contains(endpoint.Address, portStr)) { filtered = append(filtered, endpoint) @@ -85,14 +87,14 @@ func FilterEndpoints(endpoints []Endpoint, address string, port int) []Endpoint // // The filters are applied in combination such that an listener must adhere to // all of the filtering values which are passed in. -func FilterListeners(listeners []Listener, address string, port int) []Listener { +func FilterListeners(listeners []envoy.Listener, address string, port int) []envoy.Listener { if address == "" && port == -1 { return listeners } portStr := ":" + strconv.Itoa(port) - filtered := make([]Listener, 0) + filtered := make([]envoy.Listener, 0) for _, listener := range listeners { if strings.Contains(listener.Address, address) && (port == -1 || strings.Contains(listener.Address, portStr)) { filtered = append(filtered, listener) diff --git a/cli/cmd/proxy/read/filters_test.go b/cli/cmd/proxy/read/filters_test.go index 48ff3a97da..7340b6ae0d 100644 --- a/cli/cmd/proxy/read/filters_test.go +++ b/cli/cmd/proxy/read/filters_test.go @@ -4,10 +4,12 @@ import ( "testing" "github.com/stretchr/testify/require" + + "github.com/hashicorp/consul-k8s/cli/common/envoy" ) func TestFilterClusters(t *testing.T) { - given := []Cluster{ + given := []envoy.Cluster{ { FullyQualifiedDomainName: "local_agent", Endpoints: []string{"192.168.79.187:8502"}, @@ -42,13 +44,13 @@ func TestFilterClusters(t *testing.T) { fqdn string address string port int - expected []Cluster + expected []envoy.Cluster }{ "No filter": { fqdn: "", address: "", port: -1, - expected: []Cluster{ + expected: []envoy.Cluster{ { FullyQualifiedDomainName: "local_agent", Endpoints: []string{"192.168.79.187:8502"}, @@ -83,7 +85,7 @@ func TestFilterClusters(t *testing.T) { fqdn: "default", address: "", port: -1, - expected: []Cluster{ + expected: []envoy.Cluster{ { FullyQualifiedDomainName: "client.default.dc1.internal.bc3815c2-1a0f-f3ff-a2e9-20d791f08d00.consul", Endpoints: []string{}, @@ -102,7 +104,7 @@ func TestFilterClusters(t *testing.T) { fqdn: "", address: "127.0.", port: -1, - expected: []Cluster{ + expected: []envoy.Cluster{ { FullyQualifiedDomainName: "local_app", Endpoints: []string{"127.0.0.1:8080"}, @@ -117,7 +119,7 @@ func TestFilterClusters(t *testing.T) { fqdn: "", address: "", port: 8080, - expected: []Cluster{ + expected: []envoy.Cluster{ { FullyQualifiedDomainName: "local_app", Endpoints: []string{"127.0.0.1:8080"}, @@ -132,7 +134,7 @@ func TestFilterClusters(t *testing.T) { fqdn: "local", address: "127.0.0.1", port: -1, - expected: []Cluster{ + expected: []envoy.Cluster{ { FullyQualifiedDomainName: "local_app", Endpoints: []string{"127.0.0.1:8080"}, @@ -147,7 +149,7 @@ func TestFilterClusters(t *testing.T) { fqdn: "local", address: "", port: 8080, - expected: []Cluster{ + expected: []envoy.Cluster{ { FullyQualifiedDomainName: "local_app", Endpoints: []string{"127.0.0.1:8080"}, @@ -158,7 +160,7 @@ func TestFilterClusters(t *testing.T) { fqdn: "", address: "127.0.0.1", port: 8080, - expected: []Cluster{ + expected: []envoy.Cluster{ { FullyQualifiedDomainName: "local_app", Endpoints: []string{"127.0.0.1:8080"}, @@ -169,7 +171,7 @@ func TestFilterClusters(t *testing.T) { fqdn: "local", address: "192.168.79.187", port: 8502, - expected: []Cluster{ + expected: []envoy.Cluster{ { FullyQualifiedDomainName: "local_agent", Endpoints: []string{"192.168.79.187:8502"}, @@ -187,7 +189,7 @@ func TestFilterClusters(t *testing.T) { } func TestFilterEndpoints(t *testing.T) { - given := []Endpoint{ + given := []envoy.Endpoint{ { Address: "192.168.79.187:8502", }, @@ -208,12 +210,12 @@ func TestFilterEndpoints(t *testing.T) { cases := map[string]struct { address string port int - expected []Endpoint + expected []envoy.Endpoint }{ "No filter": { address: "", port: -1, - expected: []Endpoint{ + expected: []envoy.Endpoint{ { Address: "192.168.79.187:8502", }, @@ -234,7 +236,7 @@ func TestFilterEndpoints(t *testing.T) { "Filter address": { address: "127.0.0.1", port: -1, - expected: []Endpoint{ + expected: []envoy.Endpoint{ { Address: "127.0.0.1:8080", }, @@ -243,7 +245,7 @@ func TestFilterEndpoints(t *testing.T) { "Filter port": { address: "", port: 20000, - expected: []Endpoint{ + expected: []envoy.Endpoint{ { Address: "192.168.31.201:20000", }, @@ -258,7 +260,7 @@ func TestFilterEndpoints(t *testing.T) { "Filter address and port": { address: "235", port: 20000, - expected: []Endpoint{ + expected: []envoy.Endpoint{ { Address: "192.168.47.235:20000", }, @@ -275,7 +277,7 @@ func TestFilterEndpoints(t *testing.T) { } func TestFilterListeners(t *testing.T) { - given := []Listener{ + given := []envoy.Listener{ { Address: "192.168.69.179:20000", }, @@ -287,12 +289,12 @@ func TestFilterListeners(t *testing.T) { cases := map[string]struct { address string port int - expected []Listener + expected []envoy.Listener }{ "No filter": { address: "", port: -1, - expected: []Listener{ + expected: []envoy.Listener{ { Address: "192.168.69.179:20000", }, @@ -304,7 +306,7 @@ func TestFilterListeners(t *testing.T) { "Filter address": { address: "127.0.0.1", port: -1, - expected: []Listener{ + expected: []envoy.Listener{ { Address: "127.0.0.1:15001", }, @@ -313,7 +315,7 @@ func TestFilterListeners(t *testing.T) { "Filter port": { address: "", port: 20000, - expected: []Listener{ + expected: []envoy.Listener{ { Address: "192.168.69.179:20000", }, @@ -322,7 +324,7 @@ func TestFilterListeners(t *testing.T) { "Filter address and port": { address: "192.168.69.179", port: 20000, - expected: []Listener{ + expected: []envoy.Listener{ { Address: "192.168.69.179:20000", }, diff --git a/cli/cmd/proxy/read/format.go b/cli/cmd/proxy/read/format.go index 97d5ada86a..90137db6a5 100644 --- a/cli/cmd/proxy/read/format.go +++ b/cli/cmd/proxy/read/format.go @@ -4,20 +4,23 @@ import ( "fmt" "strings" + "github.com/hashicorp/consul-k8s/cli/common/envoy" "github.com/hashicorp/consul-k8s/cli/common/terminal" ) -func formatClusters(clusters []Cluster) *terminal.Table { +func formatClusters(clusters []envoy.Cluster) *terminal.Table { table := terminal.NewTable("Name", "FQDN", "Endpoints", "Type", "Last Updated") for _, cluster := range clusters { - table.AddRow([]string{cluster.Name, cluster.FullyQualifiedDomainName, strings.Join(cluster.Endpoints, ", "), - cluster.Type, cluster.LastUpdated}, []string{}) + table.AddRow([]string{ + cluster.Name, cluster.FullyQualifiedDomainName, strings.Join(cluster.Endpoints, ", "), + cluster.Type, cluster.LastUpdated, + }, []string{}) } return table } -func formatEndpoints(endpoints []Endpoint) *terminal.Table { +func formatEndpoints(endpoints []envoy.Endpoint) *terminal.Table { table := terminal.NewTable("Address:Port", "Cluster", "Weight", "Status") for _, endpoint := range endpoints { var statusColor string @@ -35,7 +38,7 @@ func formatEndpoints(endpoints []Endpoint) *terminal.Table { return table } -func formatListeners(listeners []Listener) *terminal.Table { +func formatListeners(listeners []envoy.Listener) *terminal.Table { table := terminal.NewTable("Name", "Address:Port", "Direction", "Filter Chain Match", "Filters", "Last Updated") for _, listener := range listeners { for index, filter := range listener.FilterChain { @@ -57,7 +60,7 @@ func formatListeners(listeners []Listener) *terminal.Table { return table } -func formatRoutes(routes []Route) *terminal.Table { +func formatRoutes(routes []envoy.Route) *terminal.Table { table := terminal.NewTable("Name", "Destination Cluster", "Last Updated") for _, route := range routes { table.AddRow([]string{route.Name, route.DestinationCluster, route.LastUpdated}, []string{}) @@ -66,7 +69,7 @@ func formatRoutes(routes []Route) *terminal.Table { return table } -func formatSecrets(secrets []Secret) *terminal.Table { +func formatSecrets(secrets []envoy.Secret) *terminal.Table { table := terminal.NewTable("Name", "Type", "Last Updated") for _, secret := range secrets { table.AddRow([]string{secret.Name, secret.Type, secret.LastUpdated}, []string{}) diff --git a/cli/cmd/proxy/read/format_test.go b/cli/cmd/proxy/read/format_test.go index 7d6f975d39..95c562a0dd 100644 --- a/cli/cmd/proxy/read/format_test.go +++ b/cli/cmd/proxy/read/format_test.go @@ -5,8 +5,10 @@ import ( "context" "testing" - "github.com/hashicorp/consul-k8s/cli/common/terminal" "github.com/stretchr/testify/require" + + "github.com/hashicorp/consul-k8s/cli/common/envoy" + "github.com/hashicorp/consul-k8s/cli/common/terminal" ) func TestFormatClusters(t *testing.T) { @@ -21,7 +23,7 @@ func TestFormatClusters(t *testing.T) { "server.*server.default.dc1.internal.bc3815c2-1a0f-f3ff-a2e9-20d791f08d00.consul.*EDS.*2022-06-09T00:39:12\\.754Z", } - given := []Cluster{ + given := []envoy.Cluster{ { Name: "local_agent", FullyQualifiedDomainName: "local_agent", @@ -97,7 +99,7 @@ func TestFormatEndpoints(t *testing.T) { "192.168.65.131:20000.*1.00.*HEALTHY", } - given := []Endpoint{ + given := []envoy.Endpoint{ { Address: "192.168.79.187:8502", Cluster: "local_agent", @@ -174,11 +176,11 @@ func TestFormatListeners(t *testing.T) { "Any.*-> original-destination", } - given := []Listener{ + given := []envoy.Listener{ { Name: "public_listener", Address: "192.168.69.179:20000", - FilterChain: []FilterChain{ + FilterChain: []envoy.FilterChain{ { FilterChainMatch: "Any", Filters: []string{"* -> local_app/"}, @@ -190,7 +192,7 @@ func TestFormatListeners(t *testing.T) { { Name: "outbound_listener", Address: "127.0.0.1:15001", - FilterChain: []FilterChain{ + FilterChain: []envoy.FilterChain{ { FilterChainMatch: "10.100.134.173/32, 240.0.0.3/32", Filters: []string{"-> client.default.dc1.internal.bc3815c2-1a0f-f3ff-a2e9-20d791f08d00.consul"}, @@ -245,7 +247,7 @@ func TestFormatRoutes(t *testing.T) { "server.*server\\.default\\.dc1\\.internal\\.bc3815c2-1a0f-f3ff-a2e9-20d791f08d00\\.consul/.*2022-05-24T17:41:59\\.078Z", } - given := []Route{ + given := []envoy.Route{ { Name: "public_listener", DestinationCluster: "local_app/", @@ -282,7 +284,7 @@ func TestFormatSecrets(t *testing.T) { "ROOTCA.*Dynamic Warming.*2022-03-15T05:14:22.868Z", } - given := []Secret{ + given := []envoy.Secret{ { Name: "default", Type: "Dynamic Active", diff --git a/cli/cmd/proxy/read/config.go b/cli/common/envoy/http.go similarity index 91% rename from cli/cmd/proxy/read/config.go rename to cli/common/envoy/http.go index e7e6bcad34..88299d6724 100644 --- a/cli/cmd/proxy/read/config.go +++ b/cli/common/envoy/http.go @@ -1,8 +1,10 @@ -package read +package envoy import ( + "bytes" "context" "encoding/json" + "errors" "fmt" "io" "net" @@ -12,11 +14,13 @@ import ( "github.com/hashicorp/consul-k8s/cli/common" ) +var ErrNoLoggersReturned = errors.New("No loggers were returned from Envoy") + // EnvoyConfig represents the configuration retrieved from a config dump at the // admin endpoint. It wraps the Envoy ConfigDump struct to give us convenient // access to the different sections of the config. type EnvoyConfig struct { - rawCfg []byte + RawCfg []byte Clusters []Cluster Endpoints []Endpoint Listeners []Listener @@ -69,6 +73,54 @@ type Secret struct { LastUpdated string } +// CallLoggingEndpoint requests the logging endpoint from Envoy Admin Interface for a given port +// This is used to both read and update the logging levels (the envoy admin interface uses the same endpoint for both) +// more can be read about that endpoint https://www.envoyproxy.io/docs/envoy/latest/operations/admin#post--logging +func CallLoggingEndpoint(ctx context.Context, portForward common.PortForwarder, params *LoggerParams) (map[string]string, error) { + endpoint, err := portForward.Open(ctx) + if err != nil { + return nil, err + } + + defer portForward.Close() + + // this endpoint does not support returning json, so we've gotta parse the plain text + response, err := http.Post(fmt.Sprintf("http://%s/logging%s", endpoint, params), "text/plain", bytes.NewBuffer([]byte{})) + if err != nil { + return nil, err + } + + body, err := io.ReadAll(response.Body) + if err != nil { + return nil, fmt.Errorf("failed to reach envoy: %v", err) + } + + if response.StatusCode >= 400 { + return nil, fmt.Errorf("call to envoy failed with status code: %d, and message: %s", response.StatusCode, body) + } + + loggers := strings.Split(string(body), "\n") + if len(loggers) == 0 { + return nil, ErrNoLoggersReturned + } + + logLevels := make(map[string]string) + var name string + var level string + + // the first line here is just a header + for _, logger := range loggers[1:] { + if len(logger) == 0 { + continue + } + fmt.Sscanf(logger, "%s %s", &name, &level) + name = strings.TrimRight(name, ":") + logLevels[name] = level + } + + return logLevels, nil +} + // FetchConfig opens a port forward to the Envoy admin API and fetches the // configuration from the config dump endpoint. func FetchConfig(ctx context.Context, portForward common.PortForwarder) (*EnvoyConfig, error) { @@ -117,7 +169,7 @@ func FetchConfig(ctx context.Context, portForward common.PortForwarder) (*EnvoyC // JSON returns the original JSON Envoy config dump data which was used to create // the Config object. func (c *EnvoyConfig) JSON() []byte { - return c.rawCfg + return c.RawCfg } // UnmarshalJSON implements the json.Unmarshaler interface to unmarshal the raw @@ -126,7 +178,7 @@ func (c *EnvoyConfig) JSON() []byte { func (c *EnvoyConfig) UnmarshalJSON(b []byte) error { // Save the original config dump bytes for marshalling. We should treat this // struct as immutable so this should be safe. - c.rawCfg = b + c.RawCfg = b var root root err := json.Unmarshal(b, &root) diff --git a/cli/cmd/proxy/read/config_test.go b/cli/common/envoy/http_test.go similarity index 93% rename from cli/cmd/proxy/read/config_test.go rename to cli/common/envoy/http_test.go index 6b0e425794..2f10e10a2f 100644 --- a/cli/cmd/proxy/read/config_test.go +++ b/cli/common/envoy/http_test.go @@ -1,21 +1,38 @@ -package read +package envoy import ( "bytes" "context" - "embed" "encoding/json" "fmt" "net/http" "net/http/httptest" + "os" "strings" "testing" "github.com/stretchr/testify/require" ) -//go:embed test_config_dump.json test_clusters.json -var fs embed.FS +func TestCallLoggingEndpoint(t *testing.T) { + t.Parallel() + rawLogLevels, err := os.ReadFile("testdata/fetch_debug_levels.txt") + require.NoError(t, err) + mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write(rawLogLevels) + })) + + defer mockServer.Close() + + mpf := &mockPortForwarder{ + openBehavior: func(ctx context.Context) (string, error) { + return strings.Replace(mockServer.URL, "http://", "", 1), nil + }, + } + logLevels, err := CallLoggingEndpoint(context.Background(), mpf, NewLoggerParams()) + require.NoError(t, err) + require.Equal(t, testLogConfig(), logLevels) +} const ( testConfigDump = "test_config_dump.json" @@ -35,7 +52,7 @@ func TestUnmarshaling(t *testing.T) { } func TestJSON(t *testing.T) { - raw, err := fs.ReadFile(testConfigDump) + raw, err := os.ReadFile(fmt.Sprintf("testdata/%s", testConfigDump)) require.NoError(t, err) expected := bytes.TrimSpace(raw) @@ -49,10 +66,10 @@ func TestJSON(t *testing.T) { } func TestFetchConfig(t *testing.T) { - configDump, err := fs.ReadFile(testConfigDump) + configDump, err := os.ReadFile(fmt.Sprintf("testdata/%s", testConfigDump)) require.NoError(t, err) - clusters, err := fs.ReadFile(testClusters) + clusters, err := os.ReadFile(fmt.Sprintf("testdata/%s", testClusters)) require.NoError(t, err) mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -446,18 +463,11 @@ func TestClusterParsingEndpoints(t *testing.T) { require.Equal(t, expected, actual) } -type mockPortForwarder struct { - openBehavior func(context.Context) (string, error) -} - -func (m *mockPortForwarder) Open(ctx context.Context) (string, error) { return m.openBehavior(ctx) } -func (m *mockPortForwarder) Close() {} - func rawEnvoyConfig(t *testing.T) []byte { - configDump, err := fs.ReadFile(testConfigDump) + configDump, err := os.ReadFile(fmt.Sprintf("testdata/%s", testConfigDump)) require.NoError(t, err) - clusters, err := fs.ReadFile(testClusters) + clusters, err := os.ReadFile(fmt.Sprintf("testdata/%s", testClusters)) require.NoError(t, err) return []byte(fmt.Sprintf("{\n\"config_dump\":%s,\n\"clusters\":%s}", string(configDump), string(clusters))) @@ -508,3 +518,18 @@ var testEnvoyConfig = &EnvoyConfig{ }, }, } + +type mockPortForwarder struct { + openBehavior func(context.Context) (string, error) +} + +func (m *mockPortForwarder) Open(ctx context.Context) (string, error) { return m.openBehavior(ctx) } +func (m *mockPortForwarder) Close() {} + +func testLogConfig() map[string]string { + cfg := make(map[string]string, len(envoyLoggers)) + for k := range envoyLoggers { + cfg[k] = "debug" + } + return cfg +} diff --git a/cli/common/envoy/logger_params.go b/cli/common/envoy/logger_params.go new file mode 100644 index 0000000000..346ac19478 --- /dev/null +++ b/cli/common/envoy/logger_params.go @@ -0,0 +1,166 @@ +package envoy + +import ( + "fmt" + "strings" +) + +type logLevel struct { + name string + level string +} + +type LoggerParams struct { + globalLevel string + individualLevels []logLevel +} + +func NewLoggerParams() *LoggerParams { + return &LoggerParams{ + individualLevels: make([]logLevel, 0), + } +} + +func (l *LoggerParams) SetLoggerLevel(name, level string) error { + err := validateLoggerName(name) + if err != nil { + return err + } + err = validateLogLevel(level) + if err != nil { + return err + } + + l.individualLevels = append(l.individualLevels, logLevel{name: name, level: level}) + return nil +} + +func (l *LoggerParams) SetGlobalLoggerLevel(level string) error { + err := validateLogLevel(level) + if err != nil { + return err + } + l.globalLevel = level + return nil +} + +func validateLogLevel(level string) error { + if _, ok := envoyLevels[level]; !ok { + logLevels := []string{} + for levelName := range envoyLevels { + logLevels = append(logLevels, levelName) + } + return fmt.Errorf("Unknown log level %s, available log levels are %q", level, strings.Join(logLevels, ", ")) + } + return nil +} + +func validateLoggerName(name string) error { + if _, ok := envoyLoggers[name]; !ok { + loggers := []string{} + for loggerName := range envoyLevels { + loggers = append(loggers, loggerName) + } + return fmt.Errorf("Unknown logger %s, available loggers are %q", name, strings.Join(loggers, ", ")) + + } + return nil +} + +func (l *LoggerParams) String() string { + switch { + // Global log level change is set + case l.globalLevel != "": + return fmt.Sprintf("?level=%s", l.globalLevel) + + // only one specific logger is changed + case len(l.individualLevels) == 1: + params := fmt.Sprintf("?%s=%s", l.individualLevels[0].name, l.individualLevels[0].level) + return params + + // multiple specific loggers are changed + case len(l.individualLevels) > 1: + logParams := make([]string, 0, len(l.individualLevels)) + for _, logger := range l.individualLevels { + logParams = append(logParams, fmt.Sprintf("%s:%s", logger.name, logger.level)) + } + + params := fmt.Sprintf("?paths=%s", strings.Join(logParams, ",")) + return params + default: + + // default path, this is hit if there are no params + return "" + } +} + +// trace debug info warning error critical off. +var envoyLevels = map[string]struct{}{ + "trace": {}, + "debug": {}, + "info": {}, + "warning": {}, + "error": {}, + "critical": {}, + "off": {}, +} + +var envoyLoggers = map[string]struct{}{ + "admin": {}, + "alternate_protocols_cache": {}, + "aws": {}, + "assert": {}, + "backtrace": {}, + "cache_filter": {}, + "client": {}, + "config": {}, + "connection": {}, + "conn_handler": {}, + "decompression": {}, + "dns": {}, + "dubbo": {}, + "envoy_bug": {}, + "ext_authz": {}, + "ext_proc": {}, + "rocketmq": {}, + "file": {}, + "filter": {}, + "forward_proxy": {}, + "grpc": {}, + "happy_eyeballs": {}, + "hc": {}, + "health_checker": {}, + "http": {}, + "http2": {}, + "hystrix": {}, + "init": {}, + "io": {}, + "jwt": {}, + "kafka": {}, + "key_value_store": {}, + "lua": {}, + "main": {}, + "matcher": {}, + "misc": {}, + "mongo": {}, + "multi_connection": {}, + "oauth2": {}, + "quic": {}, + "quic_stream": {}, + "pool": {}, + "rbac": {}, + "rds": {}, + "redis": {}, + "router": {}, + "runtime": {}, + "stats": {}, + "secret": {}, + "tap": {}, + "testing": {}, + "thrift": {}, + "tracing": {}, + "upstream": {}, + "udp": {}, + "wasm": {}, + "websocket": {}, +} diff --git a/cli/common/envoy/logger_params_test.go b/cli/common/envoy/logger_params_test.go new file mode 100644 index 0000000000..26be9f69a6 --- /dev/null +++ b/cli/common/envoy/logger_params_test.go @@ -0,0 +1,172 @@ +package envoy + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSetLoggerLevelSucceeds(t *testing.T) { + t.Parallel() + testCases := map[string]struct { + levelsToSet [][]string + expectedIndividualLevels []logLevel + }{ + "single log level change trace": { + levelsToSet: [][]string{ + {"admin", "trace"}, + }, + expectedIndividualLevels: []logLevel{ + {name: "admin", level: "trace"}, + }, + }, + "single log level change debug": { + levelsToSet: [][]string{ + {"admin", "debug"}, + }, + expectedIndividualLevels: []logLevel{ + {name: "admin", level: "debug"}, + }, + }, + "single log level change info": { + levelsToSet: [][]string{ + {"admin", "info"}, + }, + expectedIndividualLevels: []logLevel{ + {name: "admin", level: "info"}, + }, + }, + "single log level change warning": { + levelsToSet: [][]string{ + {"admin", "warning"}, + }, + expectedIndividualLevels: []logLevel{ + {name: "admin", level: "warning"}, + }, + }, + "single log level change error": { + levelsToSet: [][]string{ + {"admin", "error"}, + }, + expectedIndividualLevels: []logLevel{ + {name: "admin", level: "error"}, + }, + }, + "single log level change critical": { + levelsToSet: [][]string{ + {"admin", "critical"}, + }, + expectedIndividualLevels: []logLevel{ + {name: "admin", level: "critical"}, + }, + }, + "single log level change off": { + levelsToSet: [][]string{ + {"admin", "off"}, + }, + expectedIndividualLevels: []logLevel{ + {name: "admin", level: "off"}, + }, + }, + "multiple log level change": { + levelsToSet: [][]string{ + {"admin", "info"}, + {"grpc", "debug"}, + }, + expectedIndividualLevels: []logLevel{ + {name: "admin", level: "info"}, + {name: "grpc", level: "debug"}, + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + loggerParams := NewLoggerParams() + for _, loggerLevel := range tc.levelsToSet { + logger, level := loggerLevel[0], loggerLevel[1] + err := loggerParams.SetLoggerLevel(logger, level) + require.NoError(t, err) + } + require.Equal(t, loggerParams.individualLevels, tc.expectedIndividualLevels) + }) + } +} + +func TestSetLoggerLevelFails(t *testing.T) { + t.Parallel() + testCases := map[string]struct { + loggerName string + loggerLevel string + }{ + "invalid logger name": { + loggerName: "this is not the logger you're looking for", + loggerLevel: "info", + }, + "invalid logger level": { + loggerName: "grpc", + loggerLevel: "this is also incorrect", + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + loggerParams := NewLoggerParams() + err := loggerParams.SetLoggerLevel(tc.loggerName, tc.loggerLevel) + require.Error(t, err) + }) + } +} + +func TestSetGlobalLoggerLevel(t *testing.T) { + t.Parallel() + for level := range envoyLevels { + loggerParams := NewLoggerParams() + err := loggerParams.SetGlobalLoggerLevel(level) + require.NoError(t, err) + } +} + +func TestSetGlobalLoggerLevelFails(t *testing.T) { + t.Parallel() + loggerParams := NewLoggerParams() + err := loggerParams.SetGlobalLoggerLevel("not a valid level") + require.Error(t, err) +} + +func TestString(t *testing.T) { + t.Parallel() + testCases := map[string]struct { + subject *LoggerParams + expectedOutput string + }{ + "when global level is set": { + subject: &LoggerParams{globalLevel: "warn"}, + expectedOutput: "?level=warn", + }, + "when one specific log level is set": { + subject: &LoggerParams{ + individualLevels: []logLevel{ + {name: "grpc", level: "warn"}, + }, + }, + expectedOutput: "?grpc=warn", + }, + "when multiple specific log levels are set": { + subject: &LoggerParams{ + individualLevels: []logLevel{ + {name: "grpc", level: "warn"}, + {name: "http", level: "info"}, + }, + }, + expectedOutput: "?paths=grpc:warn,http:info", + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + actual := tc.subject.String() + require.Equal(t, actual, tc.expectedOutput) + }) + } +} diff --git a/cli/cmd/proxy/loglevel/testdata/fetch_debug_levels.txt b/cli/common/envoy/testdata/fetch_debug_levels.txt similarity index 100% rename from cli/cmd/proxy/loglevel/testdata/fetch_debug_levels.txt rename to cli/common/envoy/testdata/fetch_debug_levels.txt diff --git a/cli/cmd/proxy/read/test_clusters.json b/cli/common/envoy/testdata/test_clusters.json similarity index 100% rename from cli/cmd/proxy/read/test_clusters.json rename to cli/common/envoy/testdata/test_clusters.json diff --git a/cli/cmd/proxy/read/test_config_dump.json b/cli/common/envoy/testdata/test_config_dump.json similarity index 100% rename from cli/cmd/proxy/read/test_config_dump.json rename to cli/common/envoy/testdata/test_config_dump.json diff --git a/cli/cmd/proxy/read/envoy_types.go b/cli/common/envoy/types.go similarity index 99% rename from cli/cmd/proxy/read/envoy_types.go rename to cli/common/envoy/types.go index cc1ffcf7e2..505248e983 100644 --- a/cli/cmd/proxy/read/envoy_types.go +++ b/cli/common/envoy/types.go @@ -1,4 +1,4 @@ -package read +package envoy /* Envoy Types These types are based on the JSON returned from the Envoy Config Dump API on the From a7f4f914eba5094085a406b663c61859b211c8b4 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Mon, 13 Feb 2023 16:40:20 -0800 Subject: [PATCH 070/340] pinned consul test image to latest dev nightly (#1901) * pinned consul test image to latest dev nightly - created a new variable for setting the consul test image used for the majority of tests - fixed some incorrect spacing * updated builtin lambda envoy extensions in tests --- .circleci/config.yml | 45 ++++++++++++------- .../bases/crds-oss/proxydefaults.yaml | 4 +- .../bases/crds-oss/servicedefaults.yaml | 4 +- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f55e540f13..c371b63bdf 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -23,6 +23,7 @@ aks-terraform-path: &aks-terraform-path charts/consul/test/terraform/aks openshift-terraform-path: &openshift-terraform-path charts/consul/test/terraform/openshift # This image is built from test/docker/Test.dockerfile consul-helm-test-image: &consul-helm-test-image docker.mirror.hashicorp.services/hashicorpdev/consul-helm-test:0.15.0 +consul-test-image: &consul-test-image hashicorppreview/consul-enterprise:1.15-dev ######################## # COMMANDS @@ -564,7 +565,8 @@ jobs: ########################### acceptance: environment: - - TEST_RESULTS: /tmp/test-results + - TEST_RESULTS: /tmp/test-results] + - CONSUL_TEST_IMAGE: *consul-test-image machine: image: ubuntu-2004:202010-01 resource_class: xlarge @@ -589,7 +591,7 @@ jobs: - run: mkdir -p $TEST_RESULTS - run-acceptance-tests: failfast: true - additional-flags: -use-kind -kubecontext="kind-dc1" -secondary-kubecontext="kind-dc2" -consul-image=docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.15-dev-23aaa4f83845d0e2eced9ea69f731d7eedf840d1 + additional-flags: -use-kind -kubecontext="kind-dc1" -secondary-kubecontext="kind-dc2" -consul-image=$CONSUL_TEST_IMAGE - store_test_results: path: /tmp/test-results - store_artifacts: @@ -598,6 +600,7 @@ jobs: acceptance-tproxy: environment: - TEST_RESULTS: /tmp/test-results + - CONSUL_TEST_IMAGE: *consul-test-image machine: image: ubuntu-2004:202010-01 resource_class: xlarge @@ -622,7 +625,7 @@ jobs: - run: mkdir -p $TEST_RESULTS - run-acceptance-tests: failfast: true - additional-flags: -use-kind -kubecontext="kind-dc1" -secondary-kubecontext="kind-dc2" -enable-transparent-proxy -consul-image=docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.15-dev-23aaa4f83845d0e2eced9ea69f731d7eedf840d1 + additional-flags: -use-kind -kubecontext="kind-dc1" -secondary-kubecontext="kind-dc2" -enable-transparent-proxy -consul-image=$CONSUL_TEST_IMAGE - store_test_results: path: /tmp/test-results - store_artifacts: @@ -631,6 +634,7 @@ jobs: acceptance-tproxy-cni: environment: - TEST_RESULTS: /tmp/test-results + - CONSUL_TEST_IMAGE: *consul-test-image machine: image: ubuntu-2004:202010-01 resource_class: xlarge @@ -655,7 +659,7 @@ jobs: - run: mkdir -p $TEST_RESULTS - run-acceptance-tests: failfast: true - additional-flags: -use-kind -kubecontext="kind-dc1" -secondary-kubecontext="kind-dc2" -enable-transparent-proxy -enable-cni -consul-image=docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.15-dev-23aaa4f83845d0e2eced9ea69f731d7eedf840d1 + additional-flags: -use-kind -kubecontext="kind-dc1" -secondary-kubecontext="kind-dc2" -enable-transparent-proxy -enable-cni -consul-image=$CONSUL_TEST_IMAGE - store_test_results: path: /tmp/test-results - store_artifacts: @@ -728,6 +732,7 @@ jobs: environment: - TEST_RESULTS: /tmp/test-results - USE_GKE_GCLOUD_AUTH_PLUGIN: true + - CONSUL_TEST_IMAGE: *consul-test-image docker: - image: *consul-helm-test-image @@ -773,7 +778,7 @@ jobs: - run: mkdir -p $TEST_RESULTS - run-acceptance-tests: - additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -consul-image=hashicorppreview/consul-enterprise:1.15-dev-23aaa4f83845d0e2eced9ea69f731d7eedf840d1 + additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -consul-image=$CONSUL_TEST_IMAGE - store_test_results: path: /tmp/test-results @@ -797,6 +802,7 @@ jobs: environment: - TEST_RESULTS: /tmp/test-results - USE_GKE_GCLOUD_AUTH_PLUGIN: true + - CONSUL_TEST_IMAGE: *consul-test-image docker: - image: *consul-helm-test-image @@ -842,7 +848,7 @@ jobs: - run: mkdir -p $TEST_RESULTS - run-acceptance-tests: - additional-flags: -use-gke -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -enable-cni -consul-image=hashicorppreview/consul-enterprise:1.15-dev-23aaa4f83845d0e2eced9ea69f731d7eedf840d1 + additional-flags: -use-gke -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -enable-cni -consul-image=$CONSUL_TEST_IMAGE - store_test_results: path: /tmp/test-results @@ -865,6 +871,7 @@ jobs: parallelism: 3 environment: - TEST_RESULTS: /tmp/test-results + - CONSUL_TEST_IMAGE: *consul-test-image docker: - image: *consul-helm-test-image @@ -899,7 +906,7 @@ jobs: - run: mkdir -p $TEST_RESULTS - run-acceptance-tests: - additional-flags: -use-aks -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -consul-image=hashicorppreview/consul-enterprise:1.15-dev-23aaa4f83845d0e2eced9ea69f731d7eedf840d1 + additional-flags: -use-aks -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -consul-image=$CONSUL_TEST_IMAGE - store_test_results: path: /tmp/test-results @@ -922,6 +929,7 @@ jobs: parallelism: 3 environment: - TEST_RESULTS: /tmp/test-results + - CONSUL_TEST_IMAGE: *consul-test-image docker: - image: *consul-helm-test-image @@ -956,7 +964,7 @@ jobs: - run: mkdir -p $TEST_RESULTS - run-acceptance-tests: - additional-flags: -use-aks -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -enable-cni -consul-image=hashicorppreview/consul-enterprise:1.15-dev-23aaa4f83845d0e2eced9ea69f731d7eedf840d1 + additional-flags: -use-aks -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -enable-cni -consul-image=$CONSUL_TEST_IMAGE - store_test_results: path: /tmp/test-results @@ -978,6 +986,7 @@ jobs: parallelism: 3 environment: - TEST_RESULTS: /tmp/test-results + - CONSUL_TEST_IMAGE: *consul-test-image docker: - image: *consul-helm-test-image @@ -1018,7 +1027,7 @@ jobs: - run: mkdir -p $TEST_RESULTS - run-acceptance-tests: - additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -consul-image=hashicorppreview/consul-enterprise:1.15-dev-23aaa4f83845d0e2eced9ea69f731d7eedf840d1 + additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -consul-image=$CONSUL_TEST_IMAGE - store_test_results: path: /tmp/test-results @@ -1041,6 +1050,7 @@ jobs: parallelism: 3 environment: - TEST_RESULTS: /tmp/test-results + - CONSUL_TEST_IMAGE: *consul-test-image docker: - image: *consul-helm-test-image @@ -1081,7 +1091,7 @@ jobs: - run: mkdir -p $TEST_RESULTS - run-acceptance-tests: - additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -enable-cni -consul-image=hashicorppreview/consul-enterprise:1.15-dev-23aaa4f83845d0e2eced9ea69f731d7eedf840d1 + additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -enable-cni -consul-image=$CONSUL_TEST_IMAGE - store_test_results: path: /tmp/test-results @@ -1103,6 +1113,7 @@ jobs: acceptance-openshift: environment: TEST_RESULTS: /tmp/test-results + CONSUL_TEST_IMAGE: *consul-test-image parallelism: 1 docker: - image: *consul-helm-test-image @@ -1135,7 +1146,7 @@ jobs: - run: mkdir -p $TEST_RESULTS - run-acceptance-tests: - additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-openshift -enable-transparent-proxy -consul-image=hashicorppreview/consul-enterprise:1.15-dev-23aaa4f83845d0e2eced9ea69f731d7eedf840d1 + additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-openshift -enable-transparent-proxy -consul-image=$CONSUL_TEST_IMAGE - store_test_results: path: /tmp/test-results @@ -1264,15 +1275,15 @@ workflows: - acceptance: context: consul-ci requires: - - dev-upload-docker + - dev-upload-docker - acceptance-tproxy-cni: context: consul-ci requires: - - dev-upload-docker + - dev-upload-docker - acceptance-tproxy: context: consul-ci requires: - - dev-upload-docker + - dev-upload-docker nightly-cleanup: @@ -1313,13 +1324,13 @@ workflows: # - acceptance-openshift - acceptance-gke-1-25: requires: - - dev-upload-docker + - dev-upload-docker - acceptance-gke-cni-1-25: requires: - - acceptance-gke-1-25 + - acceptance-gke-1-25 - acceptance-tproxy: requires: - - dev-upload-docker + - dev-upload-docker nightly-acceptance-tests-main: description: | diff --git a/acceptance/tests/fixtures/bases/crds-oss/proxydefaults.yaml b/acceptance/tests/fixtures/bases/crds-oss/proxydefaults.yaml index 878b4c37e8..6a90f29506 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/proxydefaults.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/proxydefaults.yaml @@ -24,9 +24,9 @@ spec: required: false arguments: payloadPassthrough: false - region: us-west-2 + arn: arn:aws:lambda:us-west-2:111111111111:function:lambda-1234 - name: builtin/aws/lambda required: false arguments: payloadPassthrough: false - region: us-east-1 + arn: arn:aws:lambda:us-east-1:111111111111:function:lambda-1234 diff --git a/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml b/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml index 33bc0e5096..413eb68d22 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml @@ -31,9 +31,9 @@ spec: required: false arguments: payloadPassthrough: false - region: us-west-2 + arn: arn:aws:lambda:us-west-2:111111111111:function:lambda-1234 - name: builtin/aws/lambda required: false arguments: payloadPassthrough: false - region: us-east-1 \ No newline at end of file + arn: arn:aws:lambda:us-east-1:111111111111:function:lambda-1234 \ No newline at end of file From 22bb4e25994da2b3b8ac01bfef15181d0ee7f43a Mon Sep 17 00:00:00 2001 From: Kyle Schochenmaier Date: Tue, 14 Feb 2023 10:21:10 -0600 Subject: [PATCH 071/340] Update server-acl-init to always check for the deployed serviceAccountToken secret (#1770) --- CHANGELOG.md | 2 ++ .../server-acl-init/connect_inject.go | 17 +++++++---------- .../server-acl-init/connect_inject_test.go | 18 ++++++++++++++++-- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4aecdf96d0..b704a1ec09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,8 @@ BUG FIXES: IMPROVEMENTS: * Helm: * CNI: Add `connectInject.cni.namespace` stanza which allows the CNI plugin resources to be deployed in a namespace other than the namespace that Consul is installed. [[GH-1756](https://github.com/hashicorp/consul-k8s/pull/1756)] +* Control Plane: + * Server ACL Init always appends both, the secrets from the serviceAccount's secretRefs and the one created by the Helm chart, to support Openshift secret handling. [[GH-1770](https://github.com/hashicorp/consul-k8s/pull/1770)] BUG FIXES: * Helm: diff --git a/control-plane/subcommand/server-acl-init/connect_inject.go b/control-plane/subcommand/server-acl-init/connect_inject.go index 0160efd0a1..e732dae452 100644 --- a/control-plane/subcommand/server-acl-init/connect_inject.go +++ b/control-plane/subcommand/server-acl-init/connect_inject.go @@ -96,16 +96,13 @@ func (c *Command) createAuthMethodTmpl(authMethodName string, useNS bool) (api.A var saSecret *apiv1.Secret var secretNames []string - if len(authMethodServiceAccount.Secrets) == 0 { - // In Kube 1.24+ there is no automatically generated long term JWT token for a ServiceAccount. - // Furthermore, there is no reference to a Secret in the ServiceAccount. Instead we have deployed - // a Secret in Helm which references the ServiceAccount and contains a permanent JWT token. - secretNames = append(secretNames, c.withPrefix("auth-method")) - } else { - // ServiceAccounts always have a SecretRef in Kubernetes < 1.24. The Secret contains the JWT token. - for _, secretRef := range authMethodServiceAccount.Secrets { - secretNames = append(secretNames, secretRef.Name) - } + // In Kube 1.24+ there is no automatically generated long term JWT token for a ServiceAccount. + // Furthermore, there is no reference to a Secret in the ServiceAccount. Instead we have deployed + // a Secret in Helm which references the ServiceAccount and contains a permanent JWT token. + secretNames = append(secretNames, c.withPrefix("auth-method")) + // ServiceAccounts always have a SecretRef in Kubernetes < 1.24. The Secret contains the JWT token. + for _, secretRef := range authMethodServiceAccount.Secrets { + secretNames = append(secretNames, secretRef.Name) } // Because there could be multiple secrets attached to the service account, // we need pick the first one of type corev1.SecretTypeServiceAccountToken. diff --git a/control-plane/subcommand/server-acl-init/connect_inject_test.go b/control-plane/subcommand/server-acl-init/connect_inject_test.go index e3166442af..e7144146b7 100644 --- a/control-plane/subcommand/server-acl-init/connect_inject_test.go +++ b/control-plane/subcommand/server-acl-init/connect_inject_test.go @@ -30,6 +30,20 @@ func TestCommand_createAuthMethodTmpl_SecretNotFound(t *testing.T) { ctx: ctx, } + // create the auth method secret since it is always deployed by helm chart. + authMethodSecretName := resourcePrefix + "-auth-method" + secret := &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: authMethodSecretName, + Labels: map[string]string{common.CLILabelKey: common.CLILabelValue}, + }, + Data: map[string][]byte{}, + // Make it not a service-account-token so the test can pass through to checking the other secrets. + Type: v1.SecretTypeOpaque, + } + _, err := k8s.CoreV1().Secrets(ns).Create(ctx, secret, metav1.CreateOptions{}) + require.NoError(t, err) + serviceAccountName := resourcePrefix + "-auth-method" secretName := resourcePrefix + "-connect-injector" @@ -53,7 +67,7 @@ func TestCommand_createAuthMethodTmpl_SecretNotFound(t *testing.T) { } // Create a secret of non service-account-token type (we're using the opaque type). - secret := &v1.Secret{ + secret = &v1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: secretName, Labels: map[string]string{common.CLILabelKey: common.CLILabelValue}, @@ -61,7 +75,7 @@ func TestCommand_createAuthMethodTmpl_SecretNotFound(t *testing.T) { Data: map[string][]byte{}, Type: v1.SecretTypeOpaque, } - _, err := k8s.CoreV1().Secrets(ns).Create(ctx, secret, metav1.CreateOptions{}) + _, err = k8s.CoreV1().Secrets(ns).Create(ctx, secret, metav1.CreateOptions{}) require.NoError(t, err) _, err = cmd.createAuthMethodTmpl("test", true) From 3dc73a6c9898683f4150fb0417b0e217035463a2 Mon Sep 17 00:00:00 2001 From: jm96441n Date: Mon, 23 Jan 2023 15:24:33 -0500 Subject: [PATCH 072/340] Add reset command for envoy proxy log command Move format parsing into envoy package Move enovy to common package, move param parsing to calling package Added changelog for proxy log command Cleaning up usage message and error message from pr review clean up issue with exported constant used in test, fmt'd/linted export loggers undo autoformatting last fix to changelog to clean up from auto format set level on reset for envoy log level to info Fix spelling mistake in changelog --- CHANGELOG.md | 3 +++ cli/cmd/proxy/loglevel/command.go | 17 +++++++++++++++++ cli/common/envoy/http_test.go | 4 ++-- cli/common/envoy/logger_params.go | 4 ++-- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4aecdf96d0..31014df601 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,9 @@ IMPROVEMENTS: * Add support for the annotation `consul.hashicorp.com/use-proxy-health-check`. When this annotation is used by a service, it configures a readiness endpoint on Consul Dataplane and queries it instead of the proxy's inbound port which forwards requests to the application. [[GH-1824](https://github.com/hashicorp/consul-k8s/pull/1824)], [[GH-1841](https://github.com/hashicorp/consul-k8s/pull/1841)] * Add health check for synced services based on the status of the Kubernetes readiness probe on synced pod. [[GH-1821](https://github.com/hashicorp/consul-k8s/pull/1821)] * Remove extraneous `gnupg` dependency from `consul-k8s-control-plane` since it is no longer needed for validating binary artifacts prior to release. [[GH-1882](https://github.com/hashicorp/consul-k8s/pull/1882)] +* CLI: + * Add `consul-k8s proxy log podname` command for displaying and modifying Envoy log levels for a given Pod. [GH-1844](https://github.com/hashicorp/consul-k8s/pull/1844), [GH-1849](https://github.com/hashicorp/consul-k8s/pull/1849), [GH-1864](https://github.com/hashicorp/consul-k8s/pull/1864) + BUG FIXES: * Control Plane diff --git a/cli/cmd/proxy/loglevel/command.go b/cli/cmd/proxy/loglevel/command.go index 85dc91bfa2..4355997ed3 100644 --- a/cli/cmd/proxy/loglevel/command.go +++ b/cli/cmd/proxy/loglevel/command.go @@ -24,6 +24,7 @@ const ( defaultAdminPort = 19000 flagNameNamespace = "namespace" flagNameUpdateLevel = "update-level" + flagNameReset = "reset" flagNameKubeConfig = "kubeconfig" flagNameKubeContext = "context" ) @@ -52,6 +53,7 @@ type LogLevelCommand struct { podName string namespace string level string + reset bool kubeConfig string kubeContext string @@ -79,6 +81,13 @@ func (l *LogLevelCommand) init() { Aliases: []string{"u"}, }) + f.BoolVar(&flag.BoolVar{ + Name: flagNameReset, + Target: &l.reset, + Usage: "Reset the log level for all loggers in a pod to the Envoy default (info).", + Aliases: []string{"r"}, + }) + f = l.set.NewSet("Global Options") f.StringVar(&flag.StringVar{ Name: flagNameKubeConfig, @@ -108,6 +117,11 @@ func (l *LogLevelCommand) Run(args []string) int { return l.logOutputAndDie(err) } + // if we're resetting the default log level for envoy is info: https://www.envoyproxy.io/docs/envoy/latest/start/quick-start/run-envoy#debugging-envoy + if l.reset { + l.level = "info" + } + if l.envoyLoggingCaller == nil { l.envoyLoggingCaller = envoy.CallLoggingEndpoint } @@ -160,6 +174,9 @@ func (l *LogLevelCommand) parseFlags(args []string) error { } func (l *LogLevelCommand) validateFlags() error { + if l.level != "" && l.reset { + return fmt.Errorf("cannot set log level to %q and reset to 'info' at the same time", l.level) + } if l.namespace == "" { return nil } diff --git a/cli/common/envoy/http_test.go b/cli/common/envoy/http_test.go index 2f10e10a2f..291eb2c22b 100644 --- a/cli/common/envoy/http_test.go +++ b/cli/common/envoy/http_test.go @@ -527,8 +527,8 @@ func (m *mockPortForwarder) Open(ctx context.Context) (string, error) { return m func (m *mockPortForwarder) Close() {} func testLogConfig() map[string]string { - cfg := make(map[string]string, len(envoyLoggers)) - for k := range envoyLoggers { + cfg := make(map[string]string, len(EnvoyLoggers)) + for k := range EnvoyLoggers { cfg[k] = "debug" } return cfg diff --git a/cli/common/envoy/logger_params.go b/cli/common/envoy/logger_params.go index 346ac19478..b2eff623c0 100644 --- a/cli/common/envoy/logger_params.go +++ b/cli/common/envoy/logger_params.go @@ -56,7 +56,7 @@ func validateLogLevel(level string) error { } func validateLoggerName(name string) error { - if _, ok := envoyLoggers[name]; !ok { + if _, ok := EnvoyLoggers[name]; !ok { loggers := []string{} for loggerName := range envoyLevels { loggers = append(loggers, loggerName) @@ -105,7 +105,7 @@ var envoyLevels = map[string]struct{}{ "off": {}, } -var envoyLoggers = map[string]struct{}{ +var EnvoyLoggers = map[string]struct{}{ "admin": {}, "alternate_protocols_cache": {}, "aws": {}, From ad8b239ea2139217e60531b39ba37d8e397bf598 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Tue, 14 Feb 2023 14:55:32 -0800 Subject: [PATCH 073/340] updated golangci-lint-action to latest (#1912) --- .github/workflows/reusable-golangci-lint.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/reusable-golangci-lint.yml b/.github/workflows/reusable-golangci-lint.yml index c8c3793b03..30b6a0a3b3 100644 --- a/.github/workflows/reusable-golangci-lint.yml +++ b/.github/workflows/reusable-golangci-lint.yml @@ -22,13 +22,13 @@ jobs: uses: actions/checkout@v2 - name: Setup go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: ${{ inputs.go-version }} - name: golangci-lint-${{inputs.directory}} - uses: golangci/golangci-lint-action@v3.2.0 + uses: golangci/golangci-lint-action@v3.4.0 with: - version: v1.46.2 + version: v1.51 working-directory: ${{inputs.directory}} args: ${{inputs.args}} From 6655508821ff18b5d176037bcbab8a7fe86eaeb6 Mon Sep 17 00:00:00 2001 From: David Yu Date: Tue, 14 Feb 2023 16:01:19 -0800 Subject: [PATCH 074/340] move GH-1770 to unreleased (#1909) --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbbb5b86ce..6a625430c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ IMPROVEMENTS: * Add support for the annotation `consul.hashicorp.com/use-proxy-health-check`. When this annotation is used by a service, it configures a readiness endpoint on Consul Dataplane and queries it instead of the proxy's inbound port which forwards requests to the application. [[GH-1824](https://github.com/hashicorp/consul-k8s/pull/1824)], [[GH-1841](https://github.com/hashicorp/consul-k8s/pull/1841)] * Add health check for synced services based on the status of the Kubernetes readiness probe on synced pod. [[GH-1821](https://github.com/hashicorp/consul-k8s/pull/1821)] * Remove extraneous `gnupg` dependency from `consul-k8s-control-plane` since it is no longer needed for validating binary artifacts prior to release. [[GH-1882](https://github.com/hashicorp/consul-k8s/pull/1882)] + * Server ACL Init always appends both, the secrets from the serviceAccount's secretRefs and the one created by the Helm chart, to support Openshift secret handling. [[GH-1770](https://github.com/hashicorp/consul-k8s/pull/1770)] * CLI: * Add `consul-k8s proxy log podname` command for displaying and modifying Envoy log levels for a given Pod. [GH-1844](https://github.com/hashicorp/consul-k8s/pull/1844), [GH-1849](https://github.com/hashicorp/consul-k8s/pull/1849), [GH-1864](https://github.com/hashicorp/consul-k8s/pull/1864) @@ -67,8 +68,6 @@ BUG FIXES: IMPROVEMENTS: * Helm: * CNI: Add `connectInject.cni.namespace` stanza which allows the CNI plugin resources to be deployed in a namespace other than the namespace that Consul is installed. [[GH-1756](https://github.com/hashicorp/consul-k8s/pull/1756)] -* Control Plane: - * Server ACL Init always appends both, the secrets from the serviceAccount's secretRefs and the one created by the Helm chart, to support Openshift secret handling. [[GH-1770](https://github.com/hashicorp/consul-k8s/pull/1770)] BUG FIXES: * Helm: From 77658e3e70f820e3f335deb6a1c2555f804d439e Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Thu, 16 Feb 2023 00:30:31 -0800 Subject: [PATCH 075/340] GitHub actions now uses branch local reusable tests (#1915) --- .github/workflows/test.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3967d1c816..79eefd17f3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -82,7 +82,7 @@ jobs: golangci-lint-helm-gen: needs: - get-go-version - uses: hashicorp/consul-k8s/.github/workflows/reusable-golangci-lint.yml@main + uses: ./.github/workflows/reusable-golangci-lint.yml with: directory: hack/helm-reference-gen go-version: ${{ needs.get-go-version.outputs.go-version }} @@ -91,7 +91,7 @@ jobs: unit-helm-gen: needs: [get-go-version, golangci-lint-helm-gen, validate-helm-gen] - uses: hashicorp/consul-k8s/.github/workflows/reusable-unit.yml@main + uses: ./.github/workflows/reusable-unit.yml with: directory: hack/helm-reference-gen go-version: ${{ needs.get-go-version.outputs.go-version }} @@ -133,7 +133,7 @@ jobs: golangci-lint-control-plane: needs: - get-go-version - uses: hashicorp/consul-k8s/.github/workflows/reusable-golangci-lint.yml@main + uses: ./.github/workflows/reusable-golangci-lint.yml with: directory: control-plane go-version: ${{ needs.get-go-version.outputs.go-version }} @@ -280,14 +280,14 @@ jobs: golangci-lint-acceptance: needs: - get-go-version - uses: hashicorp/consul-k8s/.github/workflows/reusable-golangci-lint.yml@main + uses: ./.github/workflows/reusable-golangci-lint.yml with: directory: acceptance go-version: ${{ needs.get-go-version.outputs.go-version }} unit-acceptance-framework: needs: [get-go-version, golangci-lint-acceptance] - uses: hashicorp/consul-k8s/.github/workflows/reusable-unit.yml@main + uses: ./.github/workflows/reusable-unit.yml with: directory: acceptance/framework go-version: ${{ needs.get-go-version.outputs.go-version }} @@ -295,14 +295,14 @@ jobs: golangci-lint-cli: needs: - get-go-version - uses: hashicorp/consul-k8s/.github/workflows/reusable-golangci-lint.yml@main + uses: ./.github/workflows/reusable-golangci-lint.yml with: directory: cli go-version: ${{ needs.get-go-version.outputs.go-version }} unit-cli: needs: [get-go-version, golangci-lint-cli] - uses: hashicorp/consul-k8s/.github/workflows/reusable-unit.yml@main + uses: ./.github/workflows/reusable-unit.yml with: directory: cli go-version: ${{ needs.get-go-version.outputs.go-version }} @@ -359,7 +359,7 @@ jobs: # Disable GHA acceptance tests until GHA formally supported # acceptance: # needs: [ get-product-version, dev-upload-docker, get-go-version ] -# uses: hashicorp/consul-k8s/.github/workflows/reusable-acceptance.yml@main +# uses: ./.github/workflows/reusable-acceptance.yml # with: # name: acceptance # directory: acceptance/tests @@ -372,7 +372,7 @@ jobs: # # acceptance-tproxy: # needs: [ get-product-version, dev-upload-docker, get-go-version ] -# uses: hashicorp/consul-k8s/.github/workflows/reusable-acceptance.yml@main +# uses: ./.github/workflows/reusable-acceptance.yml # with: # name: acceptance-tproxy # directory: acceptance/tests @@ -385,7 +385,7 @@ jobs: # # acceptance-cni: # needs: [ get-product-version, dev-upload-docker, get-go-version ] -# uses: hashicorp/consul-k8s/.github/workflows/reusable-acceptance.yml@main +# uses: ./.github/workflows/reusable-acceptance.yml # with: # name: acceptance # directory: acceptance/tests From 5d263901656c138d2ac30f4ce6ef7150b95f2e11 Mon Sep 17 00:00:00 2001 From: Iryna Shustava Date: Fri, 17 Feb 2023 08:31:44 -0700 Subject: [PATCH 076/340] Fix flakey test in troubleshoot/upstreams_test.go (#1919) When using maps as input, the order is not guaranteed in go, and so our tests are flakey because we're asserting on specific order in the output. This changes the formatClusterNames function to sort the cluster names array first. --- cli/cmd/troubleshoot/upstreams/upstreams.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cli/cmd/troubleshoot/upstreams/upstreams.go b/cli/cmd/troubleshoot/upstreams/upstreams.go index 8b20928c7a..c11a82ac6e 100644 --- a/cli/cmd/troubleshoot/upstreams/upstreams.go +++ b/cli/cmd/troubleshoot/upstreams/upstreams.go @@ -3,6 +3,7 @@ package upstreams import ( "fmt" "net" + "sort" "strconv" "strings" "sync" @@ -245,6 +246,7 @@ func formatClusterNames(names map[string]struct{}) string { for k := range names { out = append(out, k) } + sort.Strings(out) return strings.Join(out, ", ") } From 89494fd35afc41833c80f685428c1c7b2bfa3552 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Fri, 17 Feb 2023 11:29:46 -0800 Subject: [PATCH 077/340] Pipeline Updates (#1908) * updated to go 1.20 - ran go mod tidy on all go mod files * updated go version to 1.20.1 - removed compatibility tests, those won't be needed now that we have paired Consul/Consul-K8s versions * addressed linter error - updated tests to remove require.New(t) and instead pass t directly when required * update to use gotestsum testname for better debugging * add 1.1.x to the nightly release testing * added changelog updates --- .circleci/config.yml | 41 ++++++------------- .github/workflows/reusable-acceptance.yml | 4 +- .github/workflows/reusable-unit.yml | 2 +- .github/workflows/test.yml | 4 +- .go-version | 2 +- CHANGELOG.md | 5 ++- acceptance/go.mod | 2 +- charts/go.mod | 2 +- cli/go.mod | 2 +- control-plane/cni/go.mod | 2 +- control-plane/go.mod | 2 +- control-plane/go.sum | 4 -- .../subcommand/common/common_test.go | 19 +++------ hack/aws-acceptance-test-cleanup/go.mod | 2 +- hack/copy-crds-to-chart/go.mod | 2 +- hack/helm-reference-gen/go.mod | 2 +- 16 files changed, 37 insertions(+), 60 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c371b63bdf..7ab9c55f01 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,7 +6,7 @@ orbs: executors: go: docker: - - image: docker.mirror.hashicorp.services/cimg/go:1.19.2 + - image: docker.mirror.hashicorp.services/cimg/go:1.20.1 environment: TEST_RESULTS: /tmp/test-results # path to where test results are saved @@ -35,9 +35,9 @@ commands: - run: name: Install go, gotestsum, kind, kubectl, and helm command: | - wget https://golang.org/dl/go1.19.2.linux-amd64.tar.gz - sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.19.2.linux-amd64.tar.gz - rm go1.19.2.linux-amd64.tar.gz + wget https://golang.org/dl/go1.20.1.linux-amd64.tar.gz + sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.20.1.linux-amd64.tar.gz + rm go1.20.1.linux-amd64.tar.gz echo 'export PATH=$PATH:/usr/local/go/bin' >> $BASH_ENV wget https://github.com/gotestyourself/gotestsum/releases/download/v1.8.2/gotestsum_1.8.2_linux_amd64.tar.gz @@ -169,7 +169,7 @@ commands: echo $pkgs for pkg in $pkgs do - if ! gotestsum --no-summary=all --jsonfile=jsonfile-${pkg////-} -- $pkg -p 1 -timeout 2h -failfast \ + if ! gotestsum --format=testname --no-summary=all --jsonfile=jsonfile-${pkg////-} -- $pkg -p 1 -timeout 2h -failfast \ << parameters.additional-flags >> \ -enable-multi-cluster \ ${ENABLE_ENTERPRISE:+-enable-enterprise} \ @@ -181,7 +181,7 @@ commands: break fi done - gotestsum --raw-command --junitfile "$TEST_RESULTS/gotestsum-report.xml" -- cat jsonfile* + gotestsum --format=testname --raw-command --junitfile "$TEST_RESULTS/gotestsum-report.xml" -- cat jsonfile* exit $exit_code - unless: @@ -200,7 +200,7 @@ commands: pkgs=$(go list ./... | circleci tests split --split-by=timings --timings-type=classname) echo "Running $pkgs" - gotestsum --junitfile "$TEST_RESULTS/gotestsum-report.xml" -- $pkgs -p 1 -timeout 2h -failfast \ + gotestsum --format testname --junitfile "$TEST_RESULTS/gotestsum-report.xml" -- $pkgs -p 1 -timeout 2h -failfast \ << parameters.additional-flags >> \ ${ENABLE_ENTERPRISE:+-enable-enterprise} \ -enable-multi-cluster \ @@ -281,7 +281,7 @@ jobs: unzip consul_"${CONSUL_VERSION}"_linux_amd64.zip -d /home/circleci/bin && rm consul_"${CONSUL_VERSION}"_linux_amd64.zip PACKAGE_NAMES=$(go list ./...) - gotestsum --junitfile $TEST_RESULTS/gotestsum-report.xml -- -p 4 $PACKAGE_NAMES + gotestsum --format testname --junitfile $TEST_RESULTS/gotestsum-report.xml -- -p 4 $PACKAGE_NAMES - store_test_results: path: /tmp/test-results @@ -312,7 +312,7 @@ jobs: unzip consul_"${CONSUL_ENT_VERSION}"_linux_amd64.zip -d /home/circleci/bin && rm consul_"${CONSUL_ENT_VERSION}"_linux_amd64.zip PACKAGE_NAMES=$(go list ./...) - gotestsum --junitfile $TEST_RESULTS/gotestsum-report.xml -- -tags=enterprise -p 4 $PACKAGE_NAMES + gotestsum --format testname --junitfile $TEST_RESULTS/gotestsum-report.xml -- -tags=enterprise -p 4 $PACKAGE_NAMES - store_test_results: path: /tmp/test-results @@ -401,7 +401,7 @@ jobs: name: Run tests working_directory: *cli-path command: | - gotestsum --junitfile $TEST_RESULTS/gotestsum-report.xml ./... -- -p 4 + gotestsum --format testname --junitfile $TEST_RESULTS/gotestsum-report.xml ./... -- -p 4 - store_test_results: path: /tmp/test-results @@ -500,7 +500,7 @@ jobs: name: Run tests working_directory: *acceptance-framework-path command: | - gotestsum --junitfile $TEST_RESULTS/gotestsum-report.xml ./... -- -p 4 + gotestsum --format testname --junitfile $TEST_RESULTS/gotestsum-report.xml ./... -- -p 4 - store_test_results: path: /tmp/test-results @@ -523,7 +523,7 @@ jobs: name: Run tests working_directory: *helm-gen-path command: | - gotestsum --junitfile $TEST_RESULTS/gotestsum-report.xml ./... -- -p 4 + gotestsum --format testname --junitfile $TEST_RESULTS/gotestsum-report.xml ./... -- -p 4 - store_test_results: path: /tmp/test-results @@ -1312,6 +1312,7 @@ workflows: only: - release/0.49.x - release/1.0.x + - release/1.1.x jobs: - build-distro: OS: "linux" @@ -1375,19 +1376,3 @@ workflows: - acceptance-tproxy: requires: - dev-upload-docker - - nightly-kind-acceptance-tests-consul-compatability: - description: | - Acceptance tests which run nightly to verify the compatibility between - a consul-k8s binary and it's consul version pair. Tests will be conducted - for up to n-2 previous Consul-k8s releases. - triggers: - - schedule: - cron: "0 0 * * *" # Run at 12 am UTC (5 pm PST) - filters: - branches: - only: - - main - jobs: - - acceptance-kind-1-23-consul-compat-nightly-1-12 - - acceptance-kind-1-23-consul-compat-nightly-1-13 diff --git a/.github/workflows/reusable-acceptance.yml b/.github/workflows/reusable-acceptance.yml index 142754e41a..e4401932d3 100644 --- a/.github/workflows/reusable-acceptance.yml +++ b/.github/workflows/reusable-acceptance.yml @@ -127,7 +127,7 @@ jobs: do fullpkg="github.com/hashicorp/consul-k8s/${{ inputs.directory }}/${pkg}" echo "Testing package: ${fullpkg}" - if ! gotestsum --jsonfile=jsonfile-${pkg////-} -- ${fullpkg} -p 1 -timeout 2h -failfast \ + if ! gotestsum --format=testname --jsonfile=jsonfile-${pkg////-} -- ${fullpkg} -p 1 -timeout 2h -failfast \ ${{ inputs.additional-flags }} \ -enable-enterprise \ -enable-multi-cluster \ @@ -139,7 +139,7 @@ jobs: break fi done - gotestsum --raw-command --junitfile "${{ env.TEST_RESULTS }}/gotestsum-report.xml" -- cat jsonfile* + gotestsum --format=testname --raw-command --junitfile "${{ env.TEST_RESULTS }}/gotestsum-report.xml" -- cat jsonfile* exit $exit_code - name: Upload tests diff --git a/.github/workflows/reusable-unit.yml b/.github/workflows/reusable-unit.yml index fa2b4130f5..8ba09377cc 100644 --- a/.github/workflows/reusable-unit.yml +++ b/.github/workflows/reusable-unit.yml @@ -53,5 +53,5 @@ jobs: - name: Run tests working-directory: ${{inputs.directory}} run: | - gotestsum --junitfile ${{env.TEST_RESULTS}}/gotestsum-report.xml ./... -- -p 4 + gotestsum --format=testname --junitfile ${{env.TEST_RESULTS}}/gotestsum-report.xml ./... -- -p 4 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 79eefd17f3..803aed69a4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -180,7 +180,7 @@ jobs: working-directory: control-plane run: | PACKAGE_NAMES=$(go list ./...) - gotestsum --junitfile ${{env.TEST_RESULTS}}/gotestsum-report.xml -- -p 4 $PACKAGE_NAMES + gotestsum --format=testname --junitfile ${{env.TEST_RESULTS}}/gotestsum-report.xml -- -p 4 $PACKAGE_NAMES test-enterprise-control-plane: if: github.repository_owner == 'hashicorp' # Do not run on forks as this requires secrets @@ -228,7 +228,7 @@ jobs: working-directory: control-plane run: | PACKAGE_NAMES=$(go list ./...) - gotestsum --junitfile ${{env.TEST_RESULTS}}/gotestsum-report.xml -- -tags=enterprise -p 4 $PACKAGE_NAMES + gotestsum --format=testname --junitfile ${{env.TEST_RESULTS}}/gotestsum-report.xml -- -tags=enterprise -p 4 $PACKAGE_NAMES build-distros: needs: [get-go-version, get-product-version] diff --git a/.go-version b/.go-version index 836ae4eda2..0044d6cb96 100644 --- a/.go-version +++ b/.go-version @@ -1 +1 @@ -1.19.2 +1.20.1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a625430c0..dac76400c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,17 +24,20 @@ IMPROVEMENTS: * Add the `balanceInboundConnections` field to the `ServiceDefaults` CRD. [[GH-1823]](https://github.com/hashicorp/consul-k8s/pull/1823) * Add the `upstreamConfig.overrides[].peer` field to the `ServiceDefaults` CRD. [[GH-1853]](https://github.com/hashicorp/consul-k8s/pull/1853) * Control-Plane + * Update minimum go version for project to 1.20 [[GH-1908](https://github.com/hashicorp/consul-k8s/pull/1908)] * Add support for the annotation `consul.hashicorp.com/use-proxy-health-check`. When this annotation is used by a service, it configures a readiness endpoint on Consul Dataplane and queries it instead of the proxy's inbound port which forwards requests to the application. [[GH-1824](https://github.com/hashicorp/consul-k8s/pull/1824)], [[GH-1841](https://github.com/hashicorp/consul-k8s/pull/1841)] * Add health check for synced services based on the status of the Kubernetes readiness probe on synced pod. [[GH-1821](https://github.com/hashicorp/consul-k8s/pull/1821)] * Remove extraneous `gnupg` dependency from `consul-k8s-control-plane` since it is no longer needed for validating binary artifacts prior to release. [[GH-1882](https://github.com/hashicorp/consul-k8s/pull/1882)] * Server ACL Init always appends both, the secrets from the serviceAccount's secretRefs and the one created by the Helm chart, to support Openshift secret handling. [[GH-1770](https://github.com/hashicorp/consul-k8s/pull/1770)] * CLI: + * Update minimum go version for project to 1.20 [[GH-1908](https://github.com/hashicorp/consul-k8s/pull/1908)] * Add `consul-k8s proxy log podname` command for displaying and modifying Envoy log levels for a given Pod. [GH-1844](https://github.com/hashicorp/consul-k8s/pull/1844), [GH-1849](https://github.com/hashicorp/consul-k8s/pull/1849), [GH-1864](https://github.com/hashicorp/consul-k8s/pull/1864) - BUG FIXES: * Control Plane * Don't incorrectly diff intention config entries when upgrading from Consul pre-1.12 to 1.12+ [[GH-1804](https://github.com/hashicorp/consul-k8s/pull/1804)] +* Security: + * Upgrade to use Go 1.20.1 This resolves vulnerabilities [CVE-2022-41724](https://go.dev/issue/58001) in `crypto/tls` and [CVE-2022-41723](https://go.dev/issue/57855) in `net/http`. [[GH-1908](https://github.com/hashicorp/consul-k8s/pull/1908)] ## 1.0.3 (January 30, 2023) diff --git a/acceptance/go.mod b/acceptance/go.mod index 55acec04ba..f81eff4066 100644 --- a/acceptance/go.mod +++ b/acceptance/go.mod @@ -1,6 +1,6 @@ module github.com/hashicorp/consul-k8s/acceptance -go 1.19 +go 1.20 require ( github.com/gruntwork-io/terratest v0.31.2 diff --git a/charts/go.mod b/charts/go.mod index cdb23e46b0..f76282d756 100644 --- a/charts/go.mod +++ b/charts/go.mod @@ -1,3 +1,3 @@ module github.com/hashicorp/consul-k8s/charts -go 1.19 +go 1.20 diff --git a/cli/go.mod b/cli/go.mod index 7c1a5af4b8..4563450755 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -1,6 +1,6 @@ module github.com/hashicorp/consul-k8s/cli -go 1.19 +go 1.20 require ( github.com/bgentry/speakeasy v0.1.0 diff --git a/control-plane/cni/go.mod b/control-plane/cni/go.mod index 2b43b784fd..c7820438cb 100644 --- a/control-plane/cni/go.mod +++ b/control-plane/cni/go.mod @@ -50,4 +50,4 @@ require ( replace github.com/hashicorp/consul/sdk => github.com/hashicorp/consul/sdk v0.4.1-0.20221021205723-cc843c4be892 -go 1.19 +go 1.20 diff --git a/control-plane/go.mod b/control-plane/go.mod index d5805fe558..51e4ad39a0 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -136,4 +136,4 @@ require ( replace github.com/hashicorp/consul/sdk => github.com/hashicorp/consul/sdk v0.4.1-0.20221021205723-cc843c4be892 -go 1.19 +go 1.20 diff --git a/control-plane/go.sum b/control-plane/go.sum index d1473ae24f..1fbb30b7ff 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -346,10 +346,6 @@ github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af6526 github.com/hashicorp/consul-server-connection-manager v0.1.0 h1:XCweGvMHzra88rYv2zxwwuUOjBUdcQmNKVrnQmt/muo= github.com/hashicorp/consul-server-connection-manager v0.1.0/go.mod h1:XVVlO+Yk7aiRpspiHZkrrFVn9BJIiOPnQIzqytPxGaU= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.10.1-0.20230106171340-8d923c178919 h1:8aVegJMSv7PIAAa1zqQQ0CT4TKv+Nf7I4rhE6+uDa1U= -github.com/hashicorp/consul/api v1.10.1-0.20230106171340-8d923c178919/go.mod h1:c1u8FzGHcavbEtRW/p1YditvfMgn4QsKNgz2rnCDF7c= -github.com/hashicorp/consul/api v1.10.1-0.20230126204442-a43eadd1225b h1:dNIQYhru10Hg+E1oEL8f9CX6MC+8CW5JuQ4jk3g70LA= -github.com/hashicorp/consul/api v1.10.1-0.20230126204442-a43eadd1225b/go.mod h1:c1u8FzGHcavbEtRW/p1YditvfMgn4QsKNgz2rnCDF7c= github.com/hashicorp/consul/api v1.10.1-0.20230203155153-2f149d60ccbf h1:vvsHghmX3LyNUaDe7onYKHyDiny+ystdHKIEujbNj4Q= github.com/hashicorp/consul/api v1.10.1-0.20230203155153-2f149d60ccbf/go.mod h1:c1u8FzGHcavbEtRW/p1YditvfMgn4QsKNgz2rnCDF7c= github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3usQMXZF6ziK3SzXU= diff --git a/control-plane/subcommand/common/common_test.go b/control-plane/subcommand/common/common_test.go index 521831473a..6021bd0b49 100644 --- a/control-plane/subcommand/common/common_test.go +++ b/control-plane/subcommand/common/common_test.go @@ -8,7 +8,6 @@ import ( "net/url" "os" "testing" - "time" "github.com/hashicorp/consul-k8s/control-plane/helper/go-discover/mocks" "github.com/hashicorp/consul/api" @@ -163,36 +162,33 @@ func TestConsulLogin_TokenNotReplicated(t *testing.T) { func TestConsulLogin_EmptyBearerTokenFile(t *testing.T) { t.Parallel() - require := require.New(t) bearerTokenFile := WriteTempFile(t, "") params := LoginParams{ BearerTokenFile: bearerTokenFile, } _, err := ConsulLogin(nil, params, hclog.NewNullLogger()) - require.EqualError(err, fmt.Sprintf("no bearer token found in %q", bearerTokenFile)) + require.EqualError(t, err, fmt.Sprintf("no bearer token found in %q", bearerTokenFile)) } func TestConsulLogin_BearerTokenFileDoesNotExist(t *testing.T) { t.Parallel() - require := require.New(t) randFileName := fmt.Sprintf("/foo/%d/%d", rand.Int(), rand.Int()) params := LoginParams{ BearerTokenFile: randFileName, } _, err := ConsulLogin(nil, params, hclog.NewNullLogger()) - require.Error(err) - require.Contains(err.Error(), "unable to read bearer token file") + require.Error(t, err) + require.Contains(t, err.Error(), "unable to read bearer token file") } func TestConsulLogin_TokenFileUnwritable(t *testing.T) { t.Parallel() - require := require.New(t) bearerTokenFile := WriteTempFile(t, "foo") client := startMockServer(t) // This is a common.Logger. log, err := Logger("INFO", false) - require.NoError(err) + require.NoError(t, err) randFileName := fmt.Sprintf("/foo/%d/%d", rand.Int(), rand.Int()) params := LoginParams{ AuthMethod: testAuthMethod, @@ -201,13 +197,12 @@ func TestConsulLogin_TokenFileUnwritable(t *testing.T) { NumRetries: 2, } _, err = ConsulLogin(client, params, log) - require.Error(err) - require.Contains(err.Error(), "error writing token to file sink") + require.Error(t, err) + require.Contains(t, err.Error(), "error writing token to file sink") } func TestWriteFileWithPerms_InvalidOutputFile(t *testing.T) { t.Parallel() - rand.Seed(time.Now().UnixNano()) randFileName := fmt.Sprintf("/tmp/tmp/tmp/%d", rand.Int()) t.Cleanup(func() { os.RemoveAll(randFileName) @@ -218,7 +213,6 @@ func TestWriteFileWithPerms_InvalidOutputFile(t *testing.T) { func TestWriteFileWithPerms_OutputFileExists(t *testing.T) { t.Parallel() - rand.Seed(time.Now().UnixNano()) randFileName := fmt.Sprintf("/tmp/%d", rand.Int()) err := os.WriteFile(randFileName, []byte("foo"), os.FileMode(0444)) require.NoError(t, err) @@ -236,7 +230,6 @@ func TestWriteFileWithPerms_OutputFileExists(t *testing.T) { func TestWriteFileWithPerms(t *testing.T) { t.Parallel() payload := "foo-foo-foo-foo" - rand.Seed(time.Now().UnixNano()) randFileName := fmt.Sprintf("/tmp/%d", rand.Int()) t.Cleanup(func() { os.RemoveAll(randFileName) diff --git a/hack/aws-acceptance-test-cleanup/go.mod b/hack/aws-acceptance-test-cleanup/go.mod index 13e8f48909..ac4f7b0d1a 100644 --- a/hack/aws-acceptance-test-cleanup/go.mod +++ b/hack/aws-acceptance-test-cleanup/go.mod @@ -1,6 +1,6 @@ module github.com/hashicorp/consul-helm/hack/aws-acceptance-test-cleanup -go 1.19 +go 1.20 require ( github.com/aws/aws-sdk-go v1.38.63 diff --git a/hack/copy-crds-to-chart/go.mod b/hack/copy-crds-to-chart/go.mod index 73b1f10306..c224f8f244 100644 --- a/hack/copy-crds-to-chart/go.mod +++ b/hack/copy-crds-to-chart/go.mod @@ -1,3 +1,3 @@ module github.com/hashicorp/consul-k8s/hack/copy-crds-to-chart -go 1.19 +go 1.20 diff --git a/hack/helm-reference-gen/go.mod b/hack/helm-reference-gen/go.mod index 7e41675f18..36e1ff3a8d 100644 --- a/hack/helm-reference-gen/go.mod +++ b/hack/helm-reference-gen/go.mod @@ -1,6 +1,6 @@ module github.com/hashicorp/consul-k8s/hack/helm-reference-gen -go 1.19 +go 1.20 require ( github.com/stretchr/testify v1.6.1 From 60d1023bc4610e5f09d66ff368dfe22af4fb8263 Mon Sep 17 00:00:00 2001 From: Nitya Dhanushkodi Date: Tue, 21 Feb 2023 08:44:09 -0600 Subject: [PATCH 078/340] update troubleshoot module and some messages for consistency across consul and consul-k8s (#1922) - update troubleshoot module and some messages for consistency across consul and consul-k8s --- acceptance/tests/cli/cli_install_test.go | 4 ++-- cli/cmd/troubleshoot/proxy/proxy.go | 4 ++-- cli/cmd/troubleshoot/upstreams/upstreams.go | 9 ++++++++- cli/go.mod | 2 +- cli/go.sum | 4 ++-- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/acceptance/tests/cli/cli_install_test.go b/acceptance/tests/cli/cli_install_test.go index d45093dd59..b6c52e9fc4 100644 --- a/acceptance/tests/cli/cli_install_test.go +++ b/acceptance/tests/cli/cli_install_test.go @@ -109,7 +109,7 @@ func TestInstall(t *testing.T) { proxyOut, err := cli.Run(t, ctx.KubectlOptions(t), "troubleshoot", "proxy", "-pod", clientPodName, "-upstream-ip", serverIP) require.NoError(t, err) - require.Regexp(t, "upstream resources are valid", string(proxyOut)) + require.Regexp(t, "Upstream resources are valid", string(proxyOut)) logger.Log(t, string(proxyOut)) } else { // With tproxy disabled and explicit upstreams we need the envoy-id of the server @@ -117,7 +117,7 @@ func TestInstall(t *testing.T) { proxyOut, err := cli.Run(t, ctx.KubectlOptions(t), "troubleshoot", "proxy", "-pod", clientPodName, "-upstream-envoy-id", "static-server") require.NoError(t, err) - require.Regexp(t, "upstream resources are valid", string(proxyOut)) + require.Regexp(t, "Upstream resources are valid", string(proxyOut)) logger.Log(t, string(proxyOut)) } diff --git a/cli/cmd/troubleshoot/proxy/proxy.go b/cli/cmd/troubleshoot/proxy/proxy.go index d17c491f5d..624757fad3 100644 --- a/cli/cmd/troubleshoot/proxy/proxy.go +++ b/cli/cmd/troubleshoot/proxy/proxy.go @@ -228,8 +228,8 @@ func (c *ProxyCommand) Troubleshoot() error { c.UI.Output(o.Message, terminal.WithSuccessStyle()) } else { c.UI.Output(o.Message, terminal.WithErrorStyle()) - if o.PossibleActions != "" { - c.UI.Output(fmt.Sprintf("possible actions: %v", o.PossibleActions), terminal.WithInfoStyle()) + for _, action := range o.PossibleActions { + c.UI.Output(fmt.Sprintf("-> %s", action), terminal.WithInfoStyle()) } } } diff --git a/cli/cmd/troubleshoot/upstreams/upstreams.go b/cli/cmd/troubleshoot/upstreams/upstreams.go index c11a82ac6e..7765b170a7 100644 --- a/cli/cmd/troubleshoot/upstreams/upstreams.go +++ b/cli/cmd/troubleshoot/upstreams/upstreams.go @@ -196,7 +196,7 @@ func (c *UpstreamsCommand) Troubleshoot() error { return fmt.Errorf("error getting upstreams: %v", err) } - c.UI.Output(fmt.Sprintf("Envoy Identifiers (explicit upstreams only) (%v)", len(envoyIDs)), terminal.WithHeaderStyle()) + c.UI.Output(fmt.Sprintf("Upstreams (explicit upstreams only) (%v)", len(envoyIDs)), terminal.WithHeaderStyle()) for _, e := range envoyIDs { c.UI.Output(e) } @@ -208,6 +208,13 @@ func (c *UpstreamsCommand) Troubleshoot() error { } c.UI.Table(table) + c.UI.Output("\nIf you cannot find the upstream address or cluster for a transparent proxy upstream:", terminal.WithInfoStyle()) + c.UI.Output("-> Check intentions: Transparent proxy upstreams are configured based on intentions. Make sure you "+ + "have configured intentions to allow traffic to your upstream.", terminal.WithInfoStyle()) + c.UI.Output("-> To check that the right cluster is being dialed, run a DNS lookup "+ + "for the upstream you are dialing. For example, run `dig backend.svc.consul` to return the IP address for the `backend` service. If the address you get from that is missing "+ + "from the upstream IPs, it means that your proxy may be misconfigured.", terminal.WithInfoStyle()) + return nil } diff --git a/cli/go.mod b/cli/go.mod index 4563450755..ae69aa5a64 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -8,7 +8,7 @@ require ( github.com/fatih/color v1.13.0 github.com/google/go-cmp v0.5.8 github.com/hashicorp/consul-k8s/charts v0.0.0-00010101000000-000000000000 - github.com/hashicorp/consul/troubleshoot v0.0.0-20230210154717-4f2ce606547b + github.com/hashicorp/consul/troubleshoot v0.0.0-20230217154305-8dab825c3640 github.com/hashicorp/go-hclog v1.2.1 github.com/hashicorp/hcp-sdk-go v0.23.1-0.20220921131124-49168300a7dc github.com/kr/text v0.2.0 diff --git a/cli/go.sum b/cli/go.sum index d736c0176a..6582dc76ac 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -443,8 +443,8 @@ github.com/hashicorp/consul/envoyextensions v0.0.0-20230210154717-4f2ce606547b h github.com/hashicorp/consul/envoyextensions v0.0.0-20230210154717-4f2ce606547b/go.mod h1:oJKG0zAMtq6ZmZNYQyeKh6kIJmi01rZSZDSgnjzZ15w= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.13.0 h1:lce3nFlpv8humJL8rNrrGHYSKc3q+Kxfeg3Ii1m6ZWU= -github.com/hashicorp/consul/troubleshoot v0.0.0-20230210154717-4f2ce606547b h1:I5zDW3o7KwW4cX5kkerhm7bZOEknlSjdnIgtxnhBxOk= -github.com/hashicorp/consul/troubleshoot v0.0.0-20230210154717-4f2ce606547b/go.mod h1:rskvju2tK8XvHYTAILHjO7lpV1/uViHs3Q3mg9Rkwlg= +github.com/hashicorp/consul/troubleshoot v0.0.0-20230217154305-8dab825c3640 h1:P81kThpSzUW2oERDMrLsiZE3OuilLo3/EQhtVQW5M+8= +github.com/hashicorp/consul/troubleshoot v0.0.0-20230217154305-8dab825c3640/go.mod h1:rskvju2tK8XvHYTAILHjO7lpV1/uViHs3Q3mg9Rkwlg= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= From 74af05612c0f53c292d3aa1727106d4e8ab2cce3 Mon Sep 17 00:00:00 2001 From: Iryna Shustava Date: Tue, 21 Feb 2023 12:31:46 -0700 Subject: [PATCH 079/340] Fix a typo in circleci config (#1931) --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7ab9c55f01..9c84f112cc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -565,7 +565,7 @@ jobs: ########################### acceptance: environment: - - TEST_RESULTS: /tmp/test-results] + - TEST_RESULTS: /tmp/test-results - CONSUL_TEST_IMAGE: *consul-test-image machine: image: ubuntu-2004:202010-01 From 0cb71f2890a97bf559585ccdbbef32aa6c588a68 Mon Sep 17 00:00:00 2001 From: David Yu Date: Wed, 22 Feb 2023 08:25:59 -0800 Subject: [PATCH 080/340] Dockerfile: bump Alpine base image to 3.17 (#1934) * Update Dockerfile --- CHANGELOG.md | 1 + control-plane/Dockerfile | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dac76400c8..222af34026 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ IMPROVEMENTS: * Add health check for synced services based on the status of the Kubernetes readiness probe on synced pod. [[GH-1821](https://github.com/hashicorp/consul-k8s/pull/1821)] * Remove extraneous `gnupg` dependency from `consul-k8s-control-plane` since it is no longer needed for validating binary artifacts prior to release. [[GH-1882](https://github.com/hashicorp/consul-k8s/pull/1882)] * Server ACL Init always appends both, the secrets from the serviceAccount's secretRefs and the one created by the Helm chart, to support Openshift secret handling. [[GH-1770](https://github.com/hashicorp/consul-k8s/pull/1770)] + * Update alpine to 3.17 in the Docker image. [[GH-1934](https://github.com/hashicorp/consul-k8s/pull/1934)] * CLI: * Update minimum go version for project to 1.20 [[GH-1908](https://github.com/hashicorp/consul-k8s/pull/1908)] * Add `consul-k8s proxy log podname` command for displaying and modifying Envoy log levels for a given Pod. [GH-1844](https://github.com/hashicorp/consul-k8s/pull/1844), [GH-1849](https://github.com/hashicorp/consul-k8s/pull/1849), [GH-1864](https://github.com/hashicorp/consul-k8s/pull/1864) diff --git a/control-plane/Dockerfile b/control-plane/Dockerfile index 2989712a5f..850cdd42c6 100644 --- a/control-plane/Dockerfile +++ b/control-plane/Dockerfile @@ -19,7 +19,7 @@ RUN CGO_ENABLED=0 go install github.com/hashicorp/go-discover/cmd/discover@49f60 # dev copies the binary from a local build # ----------------------------------- # BIN_NAME is a requirement in the hashicorp docker github action -FROM alpine:3.16 AS dev +FROM alpine:3.17 AS dev # NAME and VERSION are the name of the software in releases.hashicorp.com # and the version to download. Example: NAME=consul VERSION=1.2.3. @@ -71,7 +71,7 @@ CMD /bin/${BIN_NAME} # We don't rebuild the software because we want the exact checksums and # binary signatures to match the software and our builds aren't fully # reproducible currently. -FROM alpine:3.16 AS release-default +FROM alpine:3.17 AS release-default ARG BIN_NAME=consul-k8s-control-plane ARG CNI_BIN_NAME=consul-cni From bd7a7526c8ee9266883c8acca8afeefa815e1d05 Mon Sep 17 00:00:00 2001 From: Luke Kysow <1034429+lkysow@users.noreply.github.com> Date: Wed, 22 Feb 2023 08:54:03 -0800 Subject: [PATCH 081/340] Include allowed presets in err msg (#1933) * Include allowed presets in err msg --- cli/cmd/install/install.go | 2 +- cli/cmd/install/install_test.go | 14 ++++++++++---- cli/cmd/upgrade/upgrade.go | 3 +-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/cli/cmd/install/install.go b/cli/cmd/install/install.go index 7b5d5bb31c..cdfef4f670 100644 --- a/cli/cmd/install/install.go +++ b/cli/cmd/install/install.go @@ -589,7 +589,7 @@ func (c *Command) validateFlags(args []string) error { return fmt.Errorf("cannot set both -%s and -%s", flagNameConfigFile, flagNamePreset) } if ok := slices.Contains(preset.Presets, c.flagPreset); c.flagPreset != defaultPreset && !ok { - return fmt.Errorf("'%s' is not a valid preset", c.flagPreset) + return fmt.Errorf("'%s' is not a valid preset (valid presets: %s)", c.flagPreset, strings.Join(preset.Presets, ", ")) } if !common.IsValidLabel(c.flagNamespace) { return fmt.Errorf("'%s' is an invalid namespace. Namespaces follow the RFC 1123 label convention and must "+ diff --git a/cli/cmd/install/install_test.go b/cli/cmd/install/install_test.go index 07c04defef..da77b740f8 100644 --- a/cli/cmd/install/install_test.go +++ b/cli/cmd/install/install_test.go @@ -162,39 +162,45 @@ func TestValidateFlags(t *testing.T) { testCases := []struct { description string input []string + expErr string }{ { "Should disallow non-flag arguments.", []string{"foo", "-auto-approve"}, + "should have no non-flag arguments", }, { "Should disallow specifying both values file AND presets.", []string{"-f='f.txt'", "-preset=demo"}, + "cannot set both -config-file and -preset", }, { "Should error on invalid presets.", []string{"-preset=foo"}, + "'foo' is not a valid preset (valid presets: cloud, quickstart, secure)", }, { "Should error on invalid timeout.", []string{"-timeout=invalid-timeout"}, + "unable to parse -timeout: time: invalid duration \"invalid-timeout\"", }, { "Should error on an invalid namespace. If this failed, TestValidLabel() probably did too.", []string{"-namespace=\" nsWithSpace\""}, + "'\" nsWithSpace\"' is an invalid namespace. Namespaces follow the RFC 1123 label convention and must consist of a lower case alphanumeric character or '-' and must start/end with an alphanumeric character", }, { - "Should have errored on a non-existant file.", + "Should have errored on a non-existent file.", []string{"-f=\"does_not_exist.txt\""}, + "file '\"does_not_exist.txt\"' does not exist", }, } for _, testCase := range testCases { c := getInitializedCommand(t, nil) t.Run(testCase.description, func(t *testing.T) { - if err := c.validateFlags(testCase.input); err == nil { - t.Errorf("Test case should have failed.") - } + err := c.validateFlags(testCase.input) + require.EqualError(t, err, testCase.expErr) }) } } diff --git a/cli/cmd/upgrade/upgrade.go b/cli/cmd/upgrade/upgrade.go index 4c962c47b5..2672a9f989 100644 --- a/cli/cmd/upgrade/upgrade.go +++ b/cli/cmd/upgrade/upgrade.go @@ -17,7 +17,6 @@ import ( "github.com/hashicorp/consul-k8s/cli/helm" "github.com/hashicorp/consul-k8s/cli/preset" "github.com/posener/complete" - helmCLI "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/cli/values" "helm.sh/helm/v3/pkg/getter" @@ -425,7 +424,7 @@ func (c *Command) validateFlags(args []string) error { return fmt.Errorf("cannot set both -%s and -%s", flagNameConfigFile, flagNamePreset) } if ok := slices.Contains(preset.Presets, c.flagPreset); c.flagPreset != defaultPreset && !ok { - return fmt.Errorf("'%s' is not a valid preset", c.flagPreset) + return fmt.Errorf("'%s' is not a valid preset (valid presets: %s)", c.flagPreset, strings.Join(preset.Presets, ", ")) } if _, err := time.ParseDuration(c.flagTimeout); err != nil { return fmt.Errorf("unable to parse -%s: %s", flagNameTimeout, err) From 3550b61f63557aed7e8d30a833c632a943ba0a04 Mon Sep 17 00:00:00 2001 From: Luke Kysow <1034429+lkysow@users.noreply.github.com> Date: Wed, 22 Feb 2023 10:17:01 -0800 Subject: [PATCH 082/340] Update docs for global.tls.caKey (#1900) It's no longer required since https://github.com/hashicorp/consul-helm/pull/1046 --- charts/consul/values.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 63c2a63cbf..dfe98f6bab 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -364,8 +364,9 @@ global: # # Note that we need the CA key so that we can generate server and client certificates. # It is particularly important for the client certificates since they need to have host IPs - # as Subject Alternative Names. In the future, we may support bringing your own server - # certificates. + # as Subject Alternative Names. If you are setting server certs yourself via `server.serverCert` + # and you are not enabling clients (or clients are enabled with autoEncrypt) then you do not + # need to provide the CA key. caKey: # The name of the Kubernetes or Vault secret that holds the CA key. # @type: string From a263119c2287ea487197cbd711e2195ab499f5df Mon Sep 17 00:00:00 2001 From: David Yu Date: Wed, 22 Feb 2023 10:26:17 -0800 Subject: [PATCH 083/340] Removing docs directory since no longer relevant (#1942) --- docs/admin-partitions-with-acls.md | 98 ------------------------------ 1 file changed, 98 deletions(-) delete mode 100644 docs/admin-partitions-with-acls.md diff --git a/docs/admin-partitions-with-acls.md b/docs/admin-partitions-with-acls.md deleted file mode 100644 index fb282fa38d..0000000000 --- a/docs/admin-partitions-with-acls.md +++ /dev/null @@ -1,98 +0,0 @@ -## Installing Admin Partitions with ACLs enabled - -To enable ACLs on the server cluster use the following config: -```yaml -global: - enableConsulNamespaces: true - tls: - enabled: true - image: hashicorp/consul-enterprise:1.11.1 - adminPartitions: - enabled: true - acls: - manageSystemACLs: true -server: - exposeGossipAndRPCPorts: true - enterpriseLicense: - secretName: license - secretKey: key - replicas: 1 -connectInject: - enabled: true - transparentProxy: - defaultEnabled: false - consulNamespaces: - mirroringK8S: true -controller: - enabled: true -meshGateway: - enabled: true -``` - -Identify the LoadBalancer External IP of the `partition-service` -```bash -kubectl get svc consul-consul-partition-service -o json | jq -r '.status.loadBalancer.ingress[0].ip' -``` - -Migrate the TLS CA credentials from the server cluster to the workload clusters -```bash -kubectl get secret consul-consul-ca-key --context "server-context" -o json | kubectl apply --context "workload-context" -f - -kubectl get secret consul-consul-ca-cert --context "server-context" -o json | kubectl apply --context "workload-context" -f - -``` - -Migrate the Partition token from the server cluster to the workload clusters -```bash -kubectl get secret consul-consul-partitions-acl-token --context "server-context" -o json | kubectl apply --context "workload-context" -f - -``` - -Identify the Kubernetes AuthMethod URL of the workload cluster to use as the `k8sAuthMethodHost`: -```bash -kubectl config view -o "jsonpath={.clusters[?(@.name=='workload-cluster-name')].cluster.server}" -``` - -Configure the workload cluster using the following: - -```yaml -global: - enabled: false - enableConsulNamespaces: true - image: hashicorp/consul-enterprise:1.11.1 - adminPartitions: - enabled: true - name: "partition-name" - tls: - enabled: true - caCert: - secretName: consul-consul-ca-cert - secretKey: tls.crt - caKey: - secretName: consul-consul-ca-key - secretKey: tls.key - acls: - manageSystemACLs: true - bootstrapToken: - secretName: consul-consul-partitions-acl-token - secretKey: token -server: - enterpriseLicense: - secretName: license - secretKey: key -externalServers: - enabled: true - hosts: [ "loadbalancer IP" ] - tlsServerName: server.dc1.consul - k8sAuthMethodHost: "authmethod-host IP" -client: - enabled: true - exposeGossipPorts: true - join: [ "loadbalancer IP" ] -connectInject: - enabled: true - consulNamespaces: - mirroringK8S: true -controller: - enabled: true -meshGateway: - enabled: true -``` -This should create clusters that have Admin Partitions deployed on them with ACLs enabled. From d6789a395dbb454d5ea1caeceb4e74e2acd78156 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Tue, 28 Feb 2023 14:22:01 -0500 Subject: [PATCH 084/340] Update main to the latest versions post 1.1.0 (#1954) * update to the latest versions --- CHANGELOG.md | 9 ++++++++- charts/consul/Chart.yaml | 14 +++++++------- charts/consul/values.yaml | 8 ++++---- cli/version/version.go | 2 +- control-plane/version/version.go | 2 +- 5 files changed, 21 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 222af34026..81edcde7c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## UNRELEASED +## 1.1.0 (February 27, 2023) + BREAKING CHANGES: * Helm: * Change defaults to exclude the `openebs` namespace from sidecar injection. If you previously had pods in that namespace @@ -17,6 +19,7 @@ BREAKING CHANGES: IMPROVEMENTS: * Helm: + * CNI: Add `connectInject.cni.namespace` stanza which allows the CNI plugin resources to be deployed in a namespace other than the namespace that Consul is installed. [[GH-1756](https://github.com/hashicorp/consul-k8s/pull/1756)] * Kubernetes v1.26 is now supported. Minimum tested version of Kubernetes is now v1.23. [[GH-1852](https://github.com/hashicorp/consul-k8s/pull/1852)] * Add a `global.extraLabels` stanza to allow setting global Kubernetes labels for all components deployed by the `consul-k8s` Helm chart. [[GH-1778](https://github.com/hashicorp/consul-k8s/pull/1778)] * Add the `accessLogs` field to the `ProxyDefaults` CRD. [[GH-1816](https://github.com/hashicorp/consul-k8s/pull/1816)] @@ -36,7 +39,11 @@ IMPROVEMENTS: BUG FIXES: * Control Plane - * Don't incorrectly diff intention config entries when upgrading from Consul pre-1.12 to 1.12+ [[GH-1804](https://github.com/hashicorp/consul-k8s/pull/1804)] + * Don't incorrectly diff intention config entries when upgrading from Consul pre-1.12 to 1.12+ [[GH-1804](https://github.com/hashicorp/consul-k8s/pull/1804)] + * Add discover binary to control-plane image [[GH-1749](https://github.com/hashicorp/consul-k8s/pull/1749)] +* Helm: + * Don't pass in a CA file to the API Gateway controller when `externalServers.useSystemRoots` is `true`. [[GH-1743](https://github.com/hashicorp/consul-k8s/pull/1743)] + * Use the correct autogenerated cert for the API Gateway Controller when connecting to servers versus clients. [[GH-1753](https://github.com/hashicorp/consul-k8s/pull/1753)] * Security: * Upgrade to use Go 1.20.1 This resolves vulnerabilities [CVE-2022-41724](https://go.dev/issue/58001) in `crypto/tls` and [CVE-2022-41723](https://go.dev/issue/57855) in `net/http`. [[GH-1908](https://github.com/hashicorp/consul-k8s/pull/1908)] diff --git a/charts/consul/Chart.yaml b/charts/consul/Chart.yaml index 41b95611e6..58ca134137 100644 --- a/charts/consul/Chart.yaml +++ b/charts/consul/Chart.yaml @@ -1,8 +1,8 @@ apiVersion: v2 name: consul -version: 1.1.0-dev -appVersion: 1.14.4 -kubeVersion: ">=1.21.0-0" +version: 1.2.0-dev +appVersion: 1.15.0 +kubeVersion: ">=1.22.0-0" description: Official HashiCorp Consul Chart home: https://www.consul.io icon: https://raw.githubusercontent.com/hashicorp/consul-k8s/main/assets/icon.png @@ -13,13 +13,13 @@ annotations: artifacthub.io/prerelease: true artifacthub.io/images: | - name: consul - image: hashicorp/consul:1.14.4 + image: hashicorp/consul:1.15.0 - name: consul-k8s-control-plane - image: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.1.0-dev + image: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.2.0-dev - name: consul-dataplane - image: hashicorp/consul-dataplane:1.0.1 + image: hashicorp/consul-dataplane:1.1.0 - name: envoy - image: envoyproxy/envoy:v1.23.1 + image: envoyproxy/envoy:v1.25.1 artifacthub.io/license: MPL-2.0 artifacthub.io/links: | - name: Documentation diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 8b5055572a..dbc5935211 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -63,7 +63,7 @@ global: # image: "hashicorp/consul-enterprise:1.10.0-ent" # ``` # @default: hashicorp/consul: - image: "hashicorp/consul:1.14.4" + image: "hashicorp/consul:1.15.0" # Array of objects containing image pull secret names that will be applied to each service account. # This can be used to reference image pull secrets if using a custom consul or consul-k8s-control-plane Docker image. @@ -83,7 +83,7 @@ global: # image that is used for functionality such as catalog sync. # This can be overridden per component. # @default: hashicorp/consul-k8s-control-plane: - imageK8S: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.1.0-dev + imageK8S: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.2.0-dev # The name of the datacenter that the agents should # register as. This can't be changed once the Consul cluster is up and running @@ -544,7 +544,7 @@ global: # The name (and tag) of the consul-dataplane Docker image used for the # connect-injected sidecar proxies and mesh, terminating, and ingress gateways. # @default: hashicorp/consul-dataplane: - imageConsulDataplane: "hashicorp/consul-dataplane:1.0.1" + imageConsulDataplane: "hashicorp/consul-dataplane:1.1.0" # Configuration for running this Helm chart on the Red Hat OpenShift platform. # This Helm chart currently supports OpenShift v4.x+. @@ -2858,7 +2858,7 @@ apiGateway: # The name (and tag) of the Envoy Docker image used for the # apiGateway. For other Consul compoenents, imageEnvoy has been replaced with Consul Dataplane. # @default: envoyproxy/envoy: - imageEnvoy: "envoyproxy/envoy:v1.23.1" + imageEnvoy: "envoyproxy/envoy:v1.25.1" # Override global log verbosity level for api-gateway-controller pods. One of "debug", "info", "warn", or "error". # @type: string diff --git a/cli/version/version.go b/cli/version/version.go index 933f072f35..8ae06829bb 100644 --- a/cli/version/version.go +++ b/cli/version/version.go @@ -14,7 +14,7 @@ var ( // // Version must conform to the format expected by // github.com/hashicorp/go-version for tests to work. - Version = "1.1.0" + Version = "1.2.0" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release diff --git a/control-plane/version/version.go b/control-plane/version/version.go index 933f072f35..8ae06829bb 100644 --- a/control-plane/version/version.go +++ b/control-plane/version/version.go @@ -14,7 +14,7 @@ var ( // // Version must conform to the format expected by // github.com/hashicorp/go-version for tests to work. - Version = "1.1.0" + Version = "1.2.0" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release From 1ab2cacc52851c9a7bc5e71a824367de9d541b53 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Tue, 28 Feb 2023 11:26:36 -0800 Subject: [PATCH 085/340] Added support for go-changelog automations (#1947) * Added some go-changelog automations - added templates for go-changelog, pretty much copied from Consul - added a checker for missing changelog entries * added contributing doc information on new changelog * update prepare release to update the changelog - Prepare release now requires an additional LAST_RELEASE_GIT_TAG environment variable required by go-changelog - removed adding the unreleased tag to the Changelog as we will no longer be doing that. All changelog entries will be added at the time of release by the go-changelog tool --- .changelog/changelog.tmpl | 57 ++++++ .changelog/note.tmpl | 3 + .github/workflows/changelog-checker.yml | 46 +++++ CONTRIBUTING.md | 41 ++++ Makefile | 4 +- .../build-support/functions/10-util.sh | 177 +++++------------- 6 files changed, 200 insertions(+), 128 deletions(-) create mode 100644 .changelog/changelog.tmpl create mode 100644 .changelog/note.tmpl create mode 100644 .github/workflows/changelog-checker.yml diff --git a/.changelog/changelog.tmpl b/.changelog/changelog.tmpl new file mode 100644 index 0000000000..c1de4293b9 --- /dev/null +++ b/.changelog/changelog.tmpl @@ -0,0 +1,57 @@ +{{- if index .NotesByType "breaking-change" -}} +BREAKING CHANGES: + +{{range index .NotesByType "breaking-change" -}} +* {{ template "note" . }} +{{ end -}} +{{- end -}} + +{{- if .NotesByType.security }} +SECURITY: + +{{range .NotesByType.security -}} +* {{ template "note" . }} +{{ end -}} +{{- end -}} + +{{- if .NotesByType.feature }} +FEATURES: + +{{range .NotesByType.feature -}} +* {{ template "note" . }} +{{ end -}} +{{- end -}} + +{{- $improvements := combineTypes .NotesByType.improvement .NotesByType.enhancement -}} +{{- if $improvements }} +IMPROVEMENTS: + +{{range $improvements | sort -}} +* {{ template "note" . }} +{{ end -}} +{{- end -}} + +{{- if .NotesByType.deprecation }} +DEPRECATIONS: + +{{range .NotesByType.deprecation -}} +* {{ template "note" . }} +{{ end -}} +{{- end -}} + +{{- if .NotesByType.bug }} +BUG FIXES: + +{{range .NotesByType.bug -}} +* {{ template "note" . }} +{{ end -}} +{{- end -}} + +{{- if .NotesByType.note }} +NOTES: + +{{range .NotesByType.note -}} +* {{ template "note" . }} +{{ end -}} +{{- end -}} + diff --git a/.changelog/note.tmpl b/.changelog/note.tmpl new file mode 100644 index 0000000000..7588c65fd4 --- /dev/null +++ b/.changelog/note.tmpl @@ -0,0 +1,3 @@ +{{- define "note" -}} +{{.Body}}{{if not (stringHasPrefix .Issue "_")}} [[GH-{{- .Issue -}}](https://github.com/hashicorp/consul-k8s/issues/{{- .Issue -}})]{{end}} +{{- end -}} diff --git a/.github/workflows/changelog-checker.yml b/.github/workflows/changelog-checker.yml new file mode 100644 index 0000000000..bb13255d29 --- /dev/null +++ b/.github/workflows/changelog-checker.yml @@ -0,0 +1,46 @@ +# This workflow checks that there is either a 'pr/no-changelog' label applied to a PR +# or there is a .changelog/.txt file associated with a PR for a changelog entry + +name: Changelog Checker + +on: + pull_request: + types: [opened, synchronize, labeled] + # Runs on PRs to main and all release branches + branches: + - main + - release/* + +jobs: + # checks that a .changelog entry is present for a PR + changelog-check: + # If there a `pr/no-changelog` label we ignore this check. Also, we ignore PRs created by the bot assigned to `backport-assistant` + if: "! ( contains(github.event.pull_request.labels.*.name, 'pr/no-changelog') || github.event.pull_request.user.login == 'hc-github-team-consul-core' )" + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 # by default the checkout action doesn't checkout all branches + - name: Check for changelog entry in diff + run: | + # check if there is a diff in the .changelog directory + # for PRs against the main branch, the changelog file name should match the PR number + if [ "${{ github.event.pull_request.base.ref }}" = "${{ github.event.repository.default_branch }}" ]; then + enforce_matching_pull_request_number="matching this PR number " + changelog_file_path=".changelog/(_)?${{ github.event.pull_request.number }}.txt" + else + changelog_file_path=".changelog/[_0-9]*.txt" + fi + + changelog_files=$(git --no-pager diff --name-only HEAD "$(git merge-base HEAD "origin/main")" | egrep ${changelog_file_path}) + + # If we do not find a file in .changelog/, we fail the check + if [ -z "$changelog_files" ]; then + # Fail status check when no .changelog entry was found on the PR + echo "Did not find a .changelog entry ${enforce_matching_pull_request_number}and the 'pr/no-changelog' label was not applied. Reference - https://github.com/hashicorp/consul/pull/8387" + exit 1 + else + echo "Found .changelog entry in PR!" + fi diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 510d4c3b3f..8e634e323e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,6 +24,7 @@ 1. [Writing Acceptance tests](#writing-acceptance-tests) 1. [Using the Acceptance Test Framework to Debug](#using-acceptance-test-framework-to-debug) 1. [Helm Reference Docs](#helm-reference-docs) +1. [Adding a Changelog Entry](#adding-a-changelog-entry) ## Contributing 101 @@ -1214,3 +1215,43 @@ So that the documentation can look like: ```markdown - `ports` ((#v-ingressgateways-defaults-service-ports)) (`array: [{port: 8080, port: 8443}]`) - Port docs ``` + +## Adding a Changelog Entry + +Any change that a Consul-K8s user might need to know about should have a changelog entry. + +What doesn't need a changelog entry? +- Typos/fixes, unless they are in a public-facing API +- Code changes we are certain no Consul-K8s users will need to know about + +To include a [changelog entry](../.changelog) in a PR, commit a text file +named `.changelog/.txt`, where `` is the number associated with the open +PR in GitHub. The text file should describe the changes in the following format: + +```` +```release-note: +: +``` +```` + +Valid values for `` include: +- `feature`: for the addition of a new feature +- `improvement`: for an improvement (not a bug fix) to an existing feature +- `bug`: for a bug fix +- `security`: for any Common Vulnerabilities and Exposures (CVE) resolutions +- `breaking-change`: for any change that is not fully backwards-compatible +- `deprecation`: for functionality which is now marked for removal in a future release + +`` is meant to categorize the functionality affected by the change. +Some common values are: +- `cli`: related to the command-line interface and its commands +- `control-plane`: related to control-plane functionality +- `helm`: related to the charts module and any files, yaml, go, etc. therein + +There may be cases where a `code area` doesn't make sense (i.e. addressing a Go CVE). In these +cases it is okay not to provide a `code area`. + +For more examples, look in the [`.changelog/`](../.changelog) folder for existing changelog entries. + +If a PR deserves multiple changelog entries, just add multiple entries separated by a newline +in the format described above to the `.changelog/.txt` file. diff --git a/Makefile b/Makefile index 6ad59a2a91..daee6b693b 100644 --- a/Makefile +++ b/Makefile @@ -156,7 +156,7 @@ endif ifndef RELEASE_DATE $(error RELEASE_DATE is required, use format , (ex. October 4, 2022)) endif - source $(CURDIR)/control-plane/build-support/scripts/functions.sh; prepare_release $(CURDIR) $(RELEASE_VERSION) "$(RELEASE_DATE)" $(PRERELEASE_VERSION) + source $(CURDIR)/control-plane/build-support/scripts/functions.sh; prepare_release $(CURDIR) $(RELEASE_VERSION) "$(RELEASE_DATE)" $(LAST_RELEASE_GIT_TAG) $(PRERELEASE_VERSION) prepare-dev: ifndef RELEASE_VERSION @@ -168,7 +168,7 @@ endif ifndef NEXT_RELEASE_VERSION $(error NEXT_RELEASE_VERSION is required) endif - source $(CURDIR)/control-plane/build-support/scripts/functions.sh; prepare_dev $(CURDIR) $(RELEASE_VERSION) "$(RELEASE_DATE)" $(NEXT_RELEASE_VERSION) $(PRERELEASE_VERSION) + source $(CURDIR)/control-plane/build-support/scripts/functions.sh; prepare_dev $(CURDIR) $(RELEASE_VERSION) "$(RELEASE_DATE)" $(NEXT_RELEASE_VERSION) # ===========> Makefile config diff --git a/control-plane/build-support/functions/10-util.sh b/control-plane/build-support/functions/10-util.sh index b807d35397..8367c22743 100644 --- a/control-plane/build-support/functions/10-util.sh +++ b/control-plane/build-support/functions/10-util.sh @@ -684,94 +684,6 @@ function update_version_helm { return $? } -function set_changelog_version { - # Arguments: - # $1 - Path to top level Consul source - # $2 - Version to put into the Changelog - # $3 - Release Date - # - # Returns: - # 0 - success - # * - error - - local changelog="${1}/CHANGELOG.md" - local version="$2" - local rel_date="$3" - - if ! test -f "${changelog}" - then - err "ERROR: File not found: ${changelog}" - return 1 - fi - - if test -z "${version}" - then - err "ERROR: Must specify a version to put into the changelog" - return 1 - fi - - if test -z "${rel_date}" - then - rel_date=$(date +"%B %d, %Y") - fi - - sed_i ${SED_EXT} -e "s/## UNRELEASED/## ${version} (${rel_date})/" "${changelog}" - return $? -} - -function unset_changelog_version { - # Arguments: - # $1 - Path to top level Consul source - # - # Returns: - # 0 - success - # * - error - - local changelog="${1}/CHANGELOG.md" - - if ! test -f "${changelog}" - then - err "ERROR: File not found: ${changelog}" - return 1 - fi - - sed_i ${SED_EXT} -e "1 s/^## [0-9]+\.[0-9]+\.[0-9]+ \([^)]*\)/## UNRELEASED/" "${changelog}" - return $? -} - -function add_unreleased_to_changelog { - # Arguments: - # $1 - Path to top level Consul source - # - # Returns: - # 0 - success - # * - error - - local changelog="${1}/CHANGELOG.md" - - if ! test -f "${changelog}" - then - err "ERROR: File not found: ${changelog}" - return 1 - fi - - # Check if we are already in unreleased mode - if head -n 1 "${changelog}" | grep -q -c UNRELEASED - then - return 0 - fi - - local tfile="$(mktemp) -t "CHANGELOG.md_")" - ( - echo -e "## UNRELEASED\n" > "${tfile}" && - cat "${changelog}" >> "${tfile}" && - cp "${tfile}" "${changelog}" - ) - local ret=$? - rm "${tfile}" - return $ret -} - function set_version { # Arguments: # $1 - Path to top level Consul source @@ -803,21 +715,18 @@ function set_version { status_stage "==> Updating control-plane version/version.go with version info: ${vers} "$4"" if ! update_version "${sdir}/control-plane/version/version.go" "${vers}" "$4" then - unset_changelog_version "${sdir}" return 1 fi status_stage "==> Updating cli version/version.go with version info: ${vers} "$4"" if ! update_version "${sdir}/cli/version/version.go" "${vers}" "$4" then - unset_changelog_version "${sdir}" return 1 fi status_stage "==> Updating Helm chart versions with version info: ${vers} "$4"" if ! update_version_helm "${sdir}/charts/consul" "${vers}" "$4" "$5" then - unset_changelog_version "${sdir}" return 1 fi @@ -825,31 +734,52 @@ function set_version { } function set_changelog { - # Arguments: - # $1 - Path to top level Consul source - # $2 - The version of the release - # $3 - The release date - # $4 - The pre-release version - # - # - # Returns: - # 0 - success - # * - error - local sdir="$1" - local vers="$2" - local rel_date="$(date +"%B %d, %Y")" - if test -n "$3" - then - rel_date="$3" - fi + # Arguments: + # $1 - Path to top level Consul source + # $2 - Version + # $3 - Release Date + # $4 - The last git release tag + # + # + # Returns: + # 0 - success + # * - error - local changelog_vers="${vers}" - if test -n "$4" - then - changelog_vers="${vers}-$4" - fi - status_stage "==> Updating CHANGELOG.md with release info: ${changelog_vers} (${rel_date})" - set_changelog_version "${sdir}" "${changelog_vers}" "${rel_date}" || return 1 + # Check if changelog-build is installed + if ! command -v changelog-build &> /dev/null; then + echo "Error: changelog-build is not installed. Please install it and try again." + exit 1 + fi + + local curdir="$1" + local version="$2" + local rel_date="$(date +"%B %d, %Y")" + if test -n "$3" + then + rel_date="$3" + fi + local last_release_date_git_tag=$4 + + if test -z "${version}" + then + err "ERROR: Must specify a version to put into the changelog" + return 1 + fi + + if [ -z "$LAST_RELEASE_GIT_TAG" ]; then + echo "Error: LAST_RELEASE_GIT_TAG not specified." + exit 1 + fi + +cat < tmp && mv tmp "${curdir}"/CHANGELOG.MD +## ${version} (${rel_date}) +$(changelog-build -last-release ${LAST_RELEASE_GIT_TAG} \ + -entries-dir .changelog/ \ + -changelog-template .changelog/changelog.tmpl \ + -note-template .changelog/note.tmpl \ + -this-release $(git rev-parse HEAD)) + +EOT } function prepare_release { @@ -857,14 +787,16 @@ function prepare_release { # $1 - Path to top level Consul source # $2 - The version of the release # $3 - The release date - # $4 - The pre-release version + # $4 - The last release git tag for this branch (eg. v1.1.0) + # $5 - The pre-release version # # # Returns: # 0 - success # * - error - echo "release version: " $1 $2 $3 $4 - set_version "$1" "$2" "$3" "$4" "hashicorp\/consul-k8s-control-plane:" + + echo "release version: " "$1" "$2" "$3" "$4" + set_version "$1" "$2" "$3" "$5" "hashicorp\/consul-k8s-control-plane:" set_changelog "$1" "$2" "$3" "$4" } @@ -874,22 +806,15 @@ function prepare_dev { # $2 - The version of the release # $3 - The release date # $4 - The version of the next release - # $5 - The pre-release version (for setting beta in changelog) + # $5 - The last release git tag for this branch (eg. v1.1.0) # # Returns: # 0 - success # * - error echo "dev version: " $1 $4 $3 "dev" - - local sdir="$1" - - set_changelog "$1" "$2" "$3" "$5" set_version "$1" "$4" "$3" "dev" "docker.mirror.hashicorp.services\/hashicorppreview\/consul-k8s-control-plane:" - status_stage "==> Adding new UNRELEASED label in CHANGELOG.md" - add_unreleased_to_changelog "${sdir}" || return 1 - return 0 } From 2e8eeb2f58793a2fb99f82335dd4fe3577af7f3a Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Wed, 1 Mar 2023 15:23:58 -0800 Subject: [PATCH 086/340] updated security scan to have parity with consul-dataplane (#1965) --- .release/security-scan.hcl | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.release/security-scan.hcl b/.release/security-scan.hcl index 994e169dd9..516ae4f5e6 100644 --- a/.release/security-scan.hcl +++ b/.release/security-scan.hcl @@ -1,14 +1,13 @@ container { - dependencies = false - alpine_secdb = false - secrets = false + dependencies = true + alpine_secdb = true + secrets = true } binary { - secrets = false - go_modules = false - osv = false + secrets = true + go_modules = true + osv = true oss_index = false nvd = false -} - +} \ No newline at end of file From 968eb429c993b099cb28c652703ab22a9f32c415 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Thu, 2 Mar 2023 08:29:28 -0800 Subject: [PATCH 087/340] add 365 days instead of 1 year to account for leap years (#1969) --- control-plane/subcommand/tls-init/command_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/control-plane/subcommand/tls-init/command_test.go b/control-plane/subcommand/tls-init/command_test.go index 33493bba16..ae3cbd8982 100644 --- a/control-plane/subcommand/tls-init/command_test.go +++ b/control-plane/subcommand/tls-init/command_test.go @@ -395,7 +395,9 @@ func TestRun_CreatesServerCertificatesWithExpiryWithinSpecifiedDays(t *testing.T certBlock, _ := pem.Decode(newServerCert) certificate, err := x509.ParseCertificate(certBlock.Bytes) require.NoError(t, err) - require.Equal(t, time.Now().AddDate(1, 0, 0).Unix(), certificate.NotAfter.Unix()) + + // Add 365 days instead of 1 year to account for leap years + require.Equal(t, time.Now().AddDate(0, 0, 365).Unix(), certificate.NotAfter.Unix()) } func TestRun_CreatesServerCertificatesWithProvidedHosts(t *testing.T) { From 1ea918e0965f73c4560764bc4dbcea3ebf7e80c8 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Thu, 2 Mar 2023 12:02:57 -0800 Subject: [PATCH 088/340] disable go mod scanning (#1974) --- .release/security-scan.hcl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.release/security-scan.hcl b/.release/security-scan.hcl index 516ae4f5e6..42576d29b2 100644 --- a/.release/security-scan.hcl +++ b/.release/security-scan.hcl @@ -6,7 +6,7 @@ container { binary { secrets = true - go_modules = true + go_modules = false osv = true oss_index = false nvd = false From c0e5c691082126160ce4b41bd55d49f0af8568c8 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Fri, 3 Mar 2023 13:02:59 -0800 Subject: [PATCH 089/340] Mw/add backport checker (#1982) * fix grammar in changelog checker * add backport checker --- .github/workflows/backport-checker.yml | 32 +++++++++++++++++++++++++ .github/workflows/changelog-checker.yml | 4 ++-- 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/backport-checker.yml diff --git a/.github/workflows/backport-checker.yml b/.github/workflows/backport-checker.yml new file mode 100644 index 0000000000..5bcac5a38e --- /dev/null +++ b/.github/workflows/backport-checker.yml @@ -0,0 +1,32 @@ +# This workflow checks that there is either a 'pr/no-backport' label applied to a PR +# or there is a backport/.txt file associated with a PR for a backport label + +name: Backport Checker + +on: + pull_request: + types: [opened, synchronize, labeled] + # Runs on PRs to main and all release branches + branches: + - main + - release/* + +jobs: + # checks that a backport label is present for a PR + backport-check: + # If there's a `pr/no-backport` label we ignore this check. Also, we ignore PRs created by the bot assigned to `backport-assistant` + if: "! ( contains(github.event.pull_request.labels.*.name, 'pr/no-backport') || github.event.pull_request.user.login == 'hc-github-team-consul-core' )" + runs-on: ubuntu-latest + + steps: + - name: Check for Backport Label + run: | + labels="${{join(github.event.pull_request.labels.*.name, ', ') }}" + if [[ "$labels" =~ .*"backport/".* ]]; then + echo "Found backport label!" + exit 0 + fi + # Fail status check when no backport label was found on the PR + echo "Did not find a backport label matching the pattern 'backport/*' and the 'pr/no-backport' label was not applied. Reference - https://github.com/hashicorp/consul-k8s/pull/1982" + exit 1 + diff --git a/.github/workflows/changelog-checker.yml b/.github/workflows/changelog-checker.yml index bb13255d29..ae2e88170b 100644 --- a/.github/workflows/changelog-checker.yml +++ b/.github/workflows/changelog-checker.yml @@ -14,7 +14,7 @@ on: jobs: # checks that a .changelog entry is present for a PR changelog-check: - # If there a `pr/no-changelog` label we ignore this check. Also, we ignore PRs created by the bot assigned to `backport-assistant` + # If there's a `pr/no-changelog` label we ignore this check. Also, we ignore PRs created by the bot assigned to `backport-assistant` if: "! ( contains(github.event.pull_request.labels.*.name, 'pr/no-changelog') || github.event.pull_request.user.login == 'hc-github-team-consul-core' )" runs-on: ubuntu-latest @@ -39,7 +39,7 @@ jobs: # If we do not find a file in .changelog/, we fail the check if [ -z "$changelog_files" ]; then # Fail status check when no .changelog entry was found on the PR - echo "Did not find a .changelog entry ${enforce_matching_pull_request_number}and the 'pr/no-changelog' label was not applied. Reference - https://github.com/hashicorp/consul/pull/8387" + echo "Did not find a .changelog entry ${enforce_matching_pull_request_number}and the 'pr/no-changelog' label was not applied. Reference - https://github.com/hashicorp/consul-k8s/pull/1947" exit 1 else echo "Found .changelog entry in PR!" From 7079fc8ad02f48bb7ca571233546e26864c76d1e Mon Sep 17 00:00:00 2001 From: Paul Glass Date: Mon, 6 Mar 2023 13:11:11 -0600 Subject: [PATCH 090/340] Automatic ACL bootstrap with Vault secrets backend (#1920) Support automatic ACL bootstrapping with the Vault secrets backend With the Vault secrets backend, server-acl-init now: * Runs the Vault agent as a sidecar * Bootstraps ACLs if the Vault bootstrap token is empty or not found, and writes the bootstrap token back to Vault via the Vault agent The Kubernetes backend will write the bootstrap token to the user-provided secret if that secret is empty. The Vault behavior is the same. The Vault backend writes to a default secret name if the secretName and secretKey are not set in the helm chart values. server-acl-init reads the secret directly from k8s or Vault. * Remove -bootstrap-token-file flag from server-acl-init and remove the * Remove the volume/mount for bootstrap token --------- Co-authored-by: Chris Thain <32781396+cthain@users.noreply.github.com> --- .changelog/1920.txt | 3 + acceptance/framework/vault/helpers.go | 14 + acceptance/tests/vault/vault_test.go | 60 ++- .../consul/templates/server-acl-init-job.yaml | 55 ++- .../consul/test/unit/server-acl-init-job.bats | 148 +++--- charts/consul/values.yaml | 14 +- control-plane/go.mod | 29 +- control-plane/go.sum | 64 ++- .../subcommand/server-acl-init/command.go | 150 ++++-- .../server-acl-init/command_ent_test.go | 3 +- .../server-acl-init/command_test.go | 457 ++++++++---------- .../server-acl-init/k8s_secrets_backend.go | 62 +++ .../server-acl-init/secrets_backend.go | 18 + .../subcommand/server-acl-init/servers.go | 54 +-- .../test_fake_secrets_backend.go | 20 + .../server-acl-init/vault_secrets_backend.go | 66 +++ 16 files changed, 751 insertions(+), 466 deletions(-) create mode 100644 .changelog/1920.txt create mode 100644 control-plane/subcommand/server-acl-init/k8s_secrets_backend.go create mode 100644 control-plane/subcommand/server-acl-init/secrets_backend.go create mode 100644 control-plane/subcommand/server-acl-init/test_fake_secrets_backend.go create mode 100644 control-plane/subcommand/server-acl-init/vault_secrets_backend.go diff --git a/.changelog/1920.txt b/.changelog/1920.txt new file mode 100644 index 0000000000..4b1f151fe4 --- /dev/null +++ b/.changelog/1920.txt @@ -0,0 +1,3 @@ +```release-note:improvement +helm: When the `global.acls.bootstrapToken` field is set and the content of the secret is empty, the bootstrap ACL token is written to that secret after bootstrapping ACLs. This applies to both the Vault and Consul secrets backends. +``` diff --git a/acceptance/framework/vault/helpers.go b/acceptance/framework/vault/helpers.go index 4726e246ae..2280aee8b2 100644 --- a/acceptance/framework/vault/helpers.go +++ b/acceptance/framework/vault/helpers.go @@ -167,6 +167,20 @@ func (config *KV2Secret) SaveSecretAndAddReadPolicy(t *testing.T, vaultClient *v path "%s" { capabilities = ["read"] }`, config.Path) + config.saveSecretAndAddPolicy(t, vaultClient, policy) +} + +// SaveSecretAndAddUpdatePolicy will create an update policy for the PolicyName +// on the KV2Secret and then will save the secret in the KV2 store. +func (config *KV2Secret) SaveSecretAndAddUpdatePolicy(t *testing.T, vaultClient *vapi.Client) { + policy := fmt.Sprintf(` + path "%s" { + capabilities = ["read", "update"] + }`, config.Path) + config.saveSecretAndAddPolicy(t, vaultClient, policy) +} + +func (config *KV2Secret) saveSecretAndAddPolicy(t *testing.T, vaultClient *vapi.Client, policy string) { // Create the Vault Policy for the secret. logger.Log(t, "Creating policy") err := vaultClient.Sys().PutPolicy(config.PolicyName, policy) diff --git a/acceptance/tests/vault/vault_test.go b/acceptance/tests/vault/vault_test.go index cf0c926b22..4d43d8bb5b 100644 --- a/acceptance/tests/vault/vault_test.go +++ b/acceptance/tests/vault/vault_test.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/consul-k8s/acceptance/framework/logger" "github.com/hashicorp/consul-k8s/acceptance/framework/portforward" "github.com/hashicorp/consul-k8s/acceptance/framework/vault" + "github.com/hashicorp/consul/sdk/testutil/retry" "github.com/hashicorp/go-uuid" "github.com/hashicorp/go-version" "github.com/stretchr/testify/require" @@ -31,6 +32,29 @@ const ( // TestVault installs Vault, bootstraps it with secrets, policies, and Kube Auth Method. // It then configures Consul to use vault as the backend and checks that it works. func TestVault(t *testing.T) { + cases := map[string]struct { + autoBootstrap bool + }{ + "manual ACL bootstrap": {}, + "automatic ACL bootstrap": { + autoBootstrap: true, + }, + } + for name, c := range cases { + c := c + t.Run(name, func(t *testing.T) { + testVault(t, c.autoBootstrap) + }) + } +} + +// testVault is the implementation for TestVault: +// +// - testAutoBootstrap = false. Test when ACL bootstrapping has already occurred. +// The test pre-populates a Vault secret with the bootstrap token. +// - testAutoBootstrap = true. Test that server-acl-init automatically ACL bootstraps +// consul and writes the bootstrap token to Vault. +func testVault(t *testing.T, testAutoBootstrap bool) { cfg := suite.Config() ctx := suite.Environment().DefaultContext(t) kubectlOptions := ctx.KubectlOptions(t) @@ -123,16 +147,22 @@ func TestVault(t *testing.T) { licenseSecret.SaveSecretAndAddReadPolicy(t, vaultClient) } - // Bootstrap Token - bootstrapToken, err := uuid.GenerateUUID() - require.NoError(t, err) bootstrapTokenSecret := &vault.KV2Secret{ Path: "consul/data/secret/bootstrap", Key: "token", - Value: bootstrapToken, + Value: "", PolicyName: "bootstrap", } - bootstrapTokenSecret.SaveSecretAndAddReadPolicy(t, vaultClient) + if testAutoBootstrap { + bootstrapTokenSecret.SaveSecretAndAddUpdatePolicy(t, vaultClient) + } else { + id, err := uuid.GenerateUUID() + require.NoError(t, err) + bootstrapTokenSecret.Value = id + bootstrapTokenSecret.SaveSecretAndAddReadPolicy(t, vaultClient) + } + + bootstrapToken := bootstrapTokenSecret.Value // ------------------------- // Additional Auth Roles @@ -265,6 +295,26 @@ func TestVault(t *testing.T) { logger.Logf(t, "Wait %d seconds for certificates to rotate....", expirationInSeconds) time.Sleep(time.Duration(expirationInSeconds) * time.Second) + if testAutoBootstrap { + logger.Logf(t, "Validating the ACL bootstrap token was stored in Vault.") + timer := &retry.Timer{Timeout: 10 * time.Second, Wait: 1 * time.Second} + retry.RunWith(timer, t, func(r *retry.R) { + secret, err := vaultClient.Logical().Read("consul/data/secret/bootstrap") + require.NoError(r, err) + + data, ok := secret.Data["data"].(map[string]interface{}) + require.True(r, ok) + require.NotNil(r, data) + + tok, ok := data["token"].(string) + require.True(r, ok) + require.NotEmpty(r, tok) + + // Set bootstrapToken for subsequent validations. + bootstrapToken = tok + }) + } + // Validate that the gossip encryption key is set correctly. logger.Log(t, "Validating the gossip key has been set correctly.") consulCluster.ACLToken = bootstrapToken diff --git a/charts/consul/templates/server-acl-init-job.yaml b/charts/consul/templates/server-acl-init-job.yaml index 440ab8bee0..e62db41ec2 100644 --- a/charts/consul/templates/server-acl-init-job.yaml +++ b/charts/consul/templates/server-acl-init-job.yaml @@ -47,14 +47,23 @@ spec: annotations: "consul.hashicorp.com/connect-inject": "false" {{- if .Values.global.secretsBackend.vault.enabled }} - "vault.hashicorp.com/agent-pre-populate-only": "true" + + {{- /* Run the Vault agent as both an init container and sidecar. + The Vault agent sidecar is needed when server-acl-init bootstraps ACLs + and writes the bootstrap token back to Vault. + * agent-pre-populate: true - Run the Vault agent init container. + * agent-pre-populate-only: false - Also, run the Vault agent sidecar. + * agent-cache-enable: true - Enable the Agent cache listener. + * agent-cache-listener-port: 8200 - (optional) Listen on 127.0.0.1:8200. + * agent-enable-quit: true - Enable a "quit" endpoint. server-acl-init + tells the Vault agent to stop (without this the Job will not complete). + */}} + "vault.hashicorp.com/agent-pre-populate": "true" + "vault.hashicorp.com/agent-pre-populate-only": "false" + "vault.hashicorp.com/agent-cache-enable": "true" + "vault.hashicorp.com/agent-cache-listener-port": "8200" + "vault.hashicorp.com/agent-enable-quit": "true" "vault.hashicorp.com/agent-inject": "true" - {{- if .Values.global.acls.bootstrapToken.secretName }} - {{- with .Values.global.acls.bootstrapToken }} - "vault.hashicorp.com/agent-inject-secret-bootstrap-token": "{{ .secretName }}" - "vault.hashicorp.com/agent-inject-template-bootstrap-token": {{ template "consul.vaultSecretTemplate" . }} - {{- end }} - {{- end }} {{- if .Values.global.acls.partitionToken.secretName }} {{- with .Values.global.acls.partitionToken }} "vault.hashicorp.com/agent-inject-secret-partition-token": "{{ .secretName }}" @@ -101,14 +110,7 @@ spec: path: tls.crt {{- end }} {{- end }} - {{- if (and .Values.global.acls.bootstrapToken.secretName (not .Values.global.secretsBackend.vault.enabled)) }} - - name: bootstrap-token - secret: - secretName: {{ .Values.global.acls.bootstrapToken.secretName }} - items: - - key: {{ .Values.global.acls.bootstrapToken.secretKey }} - path: bootstrap-token - {{- else if and .Values.global.acls.replicationToken.secretName (not .Values.global.secretsBackend.vault.enabled) }} + {{- if and .Values.global.acls.replicationToken.secretName (not .Values.global.secretsBackend.vault.enabled) }} - name: acl-replication-token secret: secretName: {{ .Values.global.acls.replicationToken.secretName }} @@ -129,6 +131,13 @@ spec: valueFrom: fieldRef: fieldPath: metadata.name + # Extract the Vault namespace from the Vault agent annotations. + {{- if .Values.global.secretsBackend.vault.enabled }} + {{- if .Values.global.secretsBackend.vault.agentAnnotations }} + - name: VAULT_NAMESPACE + value: {{ get (tpl .Values.global.secretsBackend.vault.agentAnnotations . | fromYaml) "vault.hashicorp.com/namespace" }} + {{- end }} + {{- end }} {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 8 }} {{- if (or .Values.global.tls.enabled .Values.global.acls.replicationToken.secretName .Values.global.acls.bootstrapToken.secretName) }} volumeMounts: @@ -139,11 +148,7 @@ spec: readOnly: true {{- end }} {{- end }} - {{- if (and .Values.global.acls.bootstrapToken.secretName (not .Values.global.secretsBackend.vault.enabled)) }} - - name: bootstrap-token - mountPath: /consul/acl/tokens - readOnly: true - {{- else if and .Values.global.acls.replicationToken.secretName (not .Values.global.secretsBackend.vault.enabled) }} + {{- if and .Values.global.acls.replicationToken.secretName (not .Values.global.secretsBackend.vault.enabled) }} - name: acl-replication-token mountPath: /consul/acl/tokens readOnly: true @@ -161,13 +166,15 @@ spec: -resource-prefix=${CONSUL_FULLNAME} \ -k8s-namespace={{ .Release.Namespace }} \ -set-server-tokens={{ $serverEnabled }} \ - - {{- if .Values.global.acls.bootstrapToken.secretName }} {{- if .Values.global.secretsBackend.vault.enabled }} - -bootstrap-token-file=/vault/secrets/bootstrap-token \ + -secrets-backend=vault \ {{- else }} - -bootstrap-token-file=/consul/acl/tokens/bootstrap-token \ + -secrets-backend=kubernetes \ {{- end }} + + {{- if .Values.global.acls.bootstrapToken.secretName }} + -bootstrap-token-secret-name={{ .Values.global.acls.bootstrapToken.secretName }} \ + -bootstrap-token-secret-key={{ .Values.global.acls.bootstrapToken.secretKey }} \ {{- end }} {{- if .Values.syncCatalog.enabled }} diff --git a/charts/consul/test/unit/server-acl-init-job.bats b/charts/consul/test/unit/server-acl-init-job.bats index 63450aa4c2..81064c95eb 100644 --- a/charts/consul/test/unit/server-acl-init-job.bats +++ b/charts/consul/test/unit/server-acl-init-job.bats @@ -634,7 +634,19 @@ load _helpers yq -r '.spec.template' | tee /dev/stderr) # Check annotations + actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-pre-populate"]' | tee /dev/stderr) + [ "${actual}" = "true" ] + actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-pre-populate-only"]' | tee /dev/stderr) + [ "${actual}" = "false" ] + + actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-cache-enable"]' | tee /dev/stderr) + [ "${actual}" = "true" ] + + actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-cache-listener-port"]' | tee /dev/stderr) + [ "${actual}" = "8200" ] + + actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-enable-quit"]' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject"]' | tee /dev/stderr) @@ -643,15 +655,13 @@ load _helpers local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/role"]' | tee /dev/stderr) [ "${actual}" = "aclrole" ] - local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject-secret-bootstrap-token"]' | tee /dev/stderr) - [ "${actual}" = "foo" ] + local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-secrets-backend=vault"))') + [ "${actual}" = "true" ] - local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject-template-bootstrap-token"]' | tee /dev/stderr) - local expected=$'{{- with secret \"foo\" -}}\n{{- .Data.data.bar -}}\n{{- end -}}' - [ "${actual}" = "${expected}" ] + local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-secret-name=foo"))') + [ "${actual}" = "true" ] - # Check that the bootstrap token flag is set to the path of the Vault secret. - local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-file=/vault/secrets/bootstrap-token"))') + local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-secret-key=bar"))') [ "${actual}" = "true" ] # Check that no (secret) volumes are not attached @@ -682,20 +692,31 @@ load _helpers yq -r '.spec.template' | tee /dev/stderr) # Check annotations - local actual - actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-pre-populate-only"]' | tee /dev/stderr) + local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-pre-populate"]' | tee /dev/stderr) [ "${actual}" = "true" ] - local actual - actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject"]' | tee /dev/stderr) + + local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-pre-populate-only"]' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-cache-enable"]' | tee /dev/stderr) [ "${actual}" = "true" ] - local actual - actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/role"]' | tee /dev/stderr) + + local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-cache-listener-port"]' | tee /dev/stderr) + [ "${actual}" = "8200" ] + + local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-enable-quit"]' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject"]' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/role"]' | tee /dev/stderr) [ "${actual}" = "aclrole" ] - local actual - actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject-secret-serverca.crt"]' | tee /dev/stderr) + + local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject-secret-serverca.crt"]' | tee /dev/stderr) [ "${actual}" = "foo" ] - local actual - actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject-template-serverca.crt"]' | tee /dev/stderr) + + local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject-template-serverca.crt"]' | tee /dev/stderr) [ "${actual}" = $'{{- with secret \"foo\" -}}\n{{- .Data.certificate -}}\n{{- end -}}' ] # Check that the consul-ca-cert volume is not attached @@ -881,12 +902,14 @@ load _helpers local expected=$'{{- with secret \"/vault/replication\" -}}\n{{- .Data.data.token -}}\n{{- end -}}' [ "${actual}" = "${expected}" ] - local actual=$(echo $object | yq -r '.metadata.annotations."vault.hashicorp.com/agent-inject-secret-bootstrap-token"') - [ "${actual}" = "/vault/bootstrap" ] + local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-secrets-backend=vault"))') + [ "${actual}" = "true" ] - local actual=$(echo $object | yq -r '.metadata.annotations."vault.hashicorp.com/agent-inject-template-bootstrap-token"') - local expected=$'{{- with secret \"/vault/bootstrap\" -}}\n{{- .Data.data.token -}}\n{{- end -}}' - [ "${actual}" = "${expected}" ] + local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-secret-name=/vault/bootstrap"))') + [ "${actual}" = "true" ] + + local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-secret-key=token"))') + [ "${actual}" = "true" ] # Check that replication token Kubernetes secret volumes and volumeMounts are not attached. local actual=$(echo $object | jq -r '.spec.volumes') @@ -895,12 +918,9 @@ load _helpers local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").volumeMounts') [ "${actual}" = "null" ] - # Check that the replication and bootstrap token flags are set to the path of the Vault secret. + # Replication token file is passed. local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-acl-replication-token-file=/vault/secrets/replication-token"))') [ "${actual}" = "true" ] - - local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-file=/vault/secrets/bootstrap-token"))') - [ "${actual}" = "true" ] } #-------------------------------------------------------------------- @@ -953,7 +973,7 @@ load _helpers #-------------------------------------------------------------------- # Vault agent annotations -@test "serverACLInit/Job: no vault agent annotations defined by default" { +@test "serverACLInit/Job: default vault agent annotations" { cd `chart_dir` local actual=$(helm template \ -s templates/server-acl-init-job.yaml \ @@ -967,8 +987,21 @@ load _helpers --set 'global.secretsBackend.vault.consulCARole=carole' \ --set 'global.secretsBackend.vault.manageSystemACLsRole=aclrole' \ . | tee /dev/stderr | - yq -r '.spec.template.metadata.annotations | del(."consul.hashicorp.com/connect-inject") | del(."vault.hashicorp.com/agent-inject") | del(."vault.hashicorp.com/agent-pre-populate-only") | del(."vault.hashicorp.com/role") | del(."vault.hashicorp.com/agent-inject-secret-bootstrap-token") | del(."vault.hashicorp.com/agent-inject-template-bootstrap-token")' | tee /dev/stderr) - [ "${actual}" = "{}" ] + yq -r .spec.template.metadata.annotations | tee /dev/stderr) + + local expected=$(echo '{ + "consul.hashicorp.com/connect-inject": "false", + "vault.hashicorp.com/agent-inject": "true", + "vault.hashicorp.com/agent-pre-populate": "true", + "vault.hashicorp.com/agent-pre-populate-only": "false", + "vault.hashicorp.com/agent-cache-enable": "true", + "vault.hashicorp.com/agent-cache-listener-port": "8200", + "vault.hashicorp.com/agent-enable-quit": "true", + "vault.hashicorp.com/role": "aclrole" + }' | tee /dev/stderr) + + local equal=$(jq -n --argjson a "$actual" --argjson b "$expected" '$a == $b') + [ "$equal" = "true" ] } @test "serverACLInit/Job: vault agent annotations can be set" { @@ -1807,55 +1840,23 @@ load _helpers [[ "$output" =~ "both global.acls.bootstrapToken.secretKey and global.acls.bootstrapToken.secretName must be set if one of them is provided" ]] } -@test "serverACLInit/Job: -bootstrap-token-file is not set by default" { - cd `chart_dir` - local object=$(helm template \ - -s templates/server-acl-init-job.yaml \ - --set 'global.acls.manageSystemACLs=true' \ - . | tee /dev/stderr) - - # Test the flag is not set. - local actual=$(echo "$object" | - yq '.spec.template.spec.containers[0].command | any(contains("-bootstrap-token-file"))' | tee /dev/stderr) - [ "${actual}" = "false" ] - - # Test the volume doesn't exist - local actual=$(echo "$object" | - yq '.spec.template.spec.volumes | length == 0' | tee /dev/stderr) - [ "${actual}" = "true" ] - - # Test the volume mount doesn't exist - local actual=$(echo "$object" | - yq '.spec.template.spec.containers[0].volumeMounts | length == 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "serverACLInit/Job: -bootstrap-token-file is set when acls.bootstrapToken.secretKey and secretName are set" { +@test "serverACLInit/Job: bootstrap token secret is passed when acls.bootstrapToken.secretKey and secretName are set" { cd `chart_dir` local object=$(helm template \ -s templates/server-acl-init-job.yaml \ --set 'global.acls.manageSystemACLs=true' \ --set 'global.acls.bootstrapToken.secretName=name' \ --set 'global.acls.bootstrapToken.secretKey=key' \ - . | tee /dev/stderr) + . | yq .spec.template | tee /dev/stderr) - # Test the -bootstrap-token-file flag is set. - local actual=$(echo "$object" | - yq '.spec.template.spec.containers[0].command | any(contains("-bootstrap-token-file=/consul/acl/tokens/bootstrap-token"))' | tee /dev/stderr) + local actual=$(echo "$object" | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-secret-name=name"))') [ "${actual}" = "true" ] - # Test the volume exists - local actual=$(echo "$object" | - yq '.spec.template.spec.volumes | map(select(.name == "bootstrap-token")) | length == 1' | tee /dev/stderr) - [ "${actual}" = "true" ] - - # Test the volume mount exists - local actual=$(echo "$object" | - yq '.spec.template.spec.containers[0].volumeMounts | map(select(.name == "bootstrap-token")) | length == 1' | tee /dev/stderr) + local actual=$(echo "$object" | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-secret-key=key"))') [ "${actual}" = "true" ] } -@test "serverACLInit/Job: -bootstrap-token-file is preferred when both acls.bootstrapToken and acls.replicationToken are set" { +@test "serverACLInit/Job: bootstrap token secret is passed when both acl.bootstrapToken and acls.replicationToken are set" { cd `chart_dir` local object=$(helm template \ -s templates/server-acl-init-job.yaml \ @@ -1864,21 +1865,12 @@ load _helpers --set 'global.acls.bootstrapToken.secretKey=key' \ --set 'global.acls.replicationToken.secretName=replication' \ --set 'global.acls.replicationToken.secretKey=token' \ - . | tee /dev/stderr) + . | yq .spec.template | tee /dev/stderr) - # Test the -bootstrap-token-file flag is set. - local actual=$(echo "$object" | - yq '.spec.template.spec.containers[0].command | any(contains("-bootstrap-token-file=/consul/acl/tokens/bootstrap-token"))' | tee /dev/stderr) + local actual=$(echo "$object" | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-secret-name=name"))') [ "${actual}" = "true" ] - # Test the volume exists - local actual=$(echo "$object" | - yq '.spec.template.spec.volumes | map(select(.name == "bootstrap-token")) | length == 1' | tee /dev/stderr) - [ "${actual}" = "true" ] - - # Test the volume mount exists - local actual=$(echo "$object" | - yq '.spec.template.spec.containers[0].volumeMounts | map(select(.name == "bootstrap-token")) | length == 1' | tee /dev/stderr) + local actual=$(echo "$object" | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-secret-key=key"))') [ "${actual}" = "true" ] } diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index dbc5935211..009e851e1b 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -392,14 +392,20 @@ global: # This requires Consul >= 1.4. manageSystemACLs: false - # A Kubernetes or Vault secret containing the bootstrap token to use for - # creating policies and tokens for all Consul and consul-k8s-control-plane components. - # If set, we will skip ACL bootstrapping of the servers and will only - # initialize ACLs for the Consul clients and consul-k8s-control-plane system components. + # A Kubernetes or Vault secret containing the bootstrap token to use for creating policies and + # tokens for all Consul and consul-k8s-control-plane components. If `secretName` and `secretKey` + # are unset, a default secret name and secret key are used. If the secret is populated, then + # we will skip ACL bootstrapping of the servers and will only initialize ACLs for the Consul + # clients and consul-k8s-control-plane system components. + # If the secret is empty, then we will bootstrap ACLs on the Consul servers, and write the + # bootstrap token to this secret. If ACLs are already bootstrapped on the servers, then the + # secret must contain the bootstrap token. bootstrapToken: # The name of the Kubernetes or Vault secret that holds the bootstrap token. + # If unset, this defaults to `{{ global.name }}-bootstrap-acl-token`. secretName: null # The key within the Kubernetes or Vault secret that holds the bootstrap token. + # If unset, this defaults to `token`. secretKey: null # If true, an ACL token will be created that can be used in secondary diff --git a/control-plane/go.mod b/control-plane/go.mod index 51e4ad39a0..a60079cc53 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -19,14 +19,15 @@ require ( github.com/hashicorp/go-rootcerts v1.0.2 github.com/hashicorp/go-version v1.6.0 github.com/hashicorp/serf v0.10.1 + github.com/hashicorp/vault/api v1.8.3 github.com/kr/text v0.2.0 github.com/miekg/dns v1.1.41 github.com/mitchellh/cli v1.1.0 github.com/mitchellh/go-homedir v1.1.0 - github.com/mitchellh/mapstructure v1.4.1 + github.com/mitchellh/mapstructure v1.5.0 github.com/stretchr/testify v1.7.2 go.uber.org/zap v1.19.0 - golang.org/x/text v0.3.7 + golang.org/x/text v0.3.8 golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 gomodules.xyz/jsonpatch/v2 v2.2.0 k8s.io/api v0.22.2 @@ -55,6 +56,7 @@ require ( github.com/aws/aws-sdk-go v1.25.41 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect + github.com/cenkalti/backoff/v3 v3.0.0 // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash/v2 v2.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -68,6 +70,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/snappy v0.0.4 // indirect github.com/google/go-querystring v1.0.0 // indirect github.com/google/gofuzz v1.1.0 // indirect github.com/google/uuid v1.1.2 // indirect @@ -75,13 +78,22 @@ require ( github.com/googleapis/gnostic v0.5.5 // indirect github.com/gophercloud/gophercloud v0.1.0 // indirect github.com/hashicorp/consul/proto-public v0.1.0 // indirect - github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-immutable-radix v1.3.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-plugin v1.4.5 // indirect + github.com/hashicorp/go-retryablehttp v0.6.6 // indirect + github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 // indirect + github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect + github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect + github.com/hashicorp/go-sockaddr v1.0.2 // indirect github.com/hashicorp/go-uuid v1.0.2 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/mdns v1.0.4 // indirect + github.com/hashicorp/vault/sdk v0.7.0 // indirect github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443 // indirect + github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f // indirect @@ -90,10 +102,15 @@ require ( github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/mitchellh/copystructure v1.0.0 // indirect + github.com/mitchellh/go-testing-interface v1.0.0 // indirect + github.com/mitchellh/reflectwalk v1.0.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 // indirect + github.com/oklog/run v1.0.0 // indirect github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c // indirect + github.com/pierrec/lz4 v2.5.2+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/posener/complete v1.2.3 // indirect @@ -102,6 +119,7 @@ require ( github.com/prometheus/common v0.26.0 // indirect github.com/prometheus/procfs v0.6.0 // indirect github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 // indirect + github.com/ryanuber/go-glob v1.0.0 // indirect github.com/sirupsen/logrus v1.8.1 // indirect github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d // indirect github.com/spf13/pflag v1.0.5 // indirect @@ -109,7 +127,7 @@ require ( github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible // indirect github.com/vmware/govmomi v0.18.0 // indirect go.opencensus.io v0.23.0 // indirect - go.uber.org/atomic v1.7.0 // indirect + go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.6.0 // indirect golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect @@ -125,6 +143,7 @@ require ( google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/resty.v1 v1.12.0 // indirect + gopkg.in/square/go-jose.v2 v2.5.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.22.2 // indirect diff --git a/control-plane/go.sum b/control-plane/go.sum index 1fbb30b7ff..b4ed16e150 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -117,6 +117,8 @@ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJm github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c= +github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -197,10 +199,12 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/frankban/quicktest v1.13.0 h1:yNZif1OkDfNoDfb9zZa9aXIpejNR4F23Wely0c+Qdqk= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= @@ -235,6 +239,7 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= @@ -276,6 +281,8 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= @@ -352,20 +359,22 @@ github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3us github.com/hashicorp/consul/proto-public v0.1.0/go.mod h1:vs2KkuWwtjkIgA5ezp4YKPzQp4GitV+q/+PvksrA92k= github.com/hashicorp/consul/sdk v0.4.1-0.20221021205723-cc843c4be892 h1:jw0NwPmNPr5CxAU04hACdj61JSaJBKZ0FdBo+kwfNp4= github.com/hashicorp/consul/sdk v0.4.1-0.20221021205723-cc843c4be892/go.mod h1:yPkX5Q6CsxTFMjQQDJwzeNmUUF5NUGGbrDsv9wTb8cw= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f h1:7WFMVeuJQp6BkzuTv9O52pzwtEFVUJubKYN+zez8eTI= github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f/go.mod h1:D4eo8/CN92vm9/9UDG+ldX1/fMFa4kpl8qzyTolus8o= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.2.2 h1:ihRI7YFwcZdiSD7SIenIhHfQH3OuDvWerAUBZbeQS3M= github.com/hashicorp/go-hclog v1.2.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.3.0 h1:8exGP7ego3OmkfksihtSouGMZ+hQrhxx+FVELeXpVPE= -github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= @@ -374,12 +383,24 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-netaddrs v0.1.0 h1:TnlYvODD4C/wO+j7cX1z69kV5gOzI87u3OcUinANaW8= github.com/hashicorp/go-netaddrs v0.1.0/go.mod h1:33+a/emi5R5dqRspOuZKO0E+Tuz5WV1F84eRWALkedA= +github.com/hashicorp/go-plugin v1.4.5 h1:oTE/oQR4eghggRg8VY7PAz3dr++VwDNBGCcOfIvHpBo= +github.com/hashicorp/go-plugin v1.4.5/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= +github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 h1:cCRo8gK7oq6A2L6LICkUZ+/a5rLiRXFMf1Qd4xSwxTc= +github.com/hashicorp/go-secure-stdlib/mlock v0.1.1/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -392,6 +413,7 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= @@ -404,8 +426,14 @@ github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4 github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= +github.com/hashicorp/vault/api v1.8.3 h1:cHQOLcMhBR+aVI0HzhPxO62w2+gJhIrKguQNONPzu6o= +github.com/hashicorp/vault/api v1.8.3/go.mod h1:4g/9lj9lmuJQMtT6CmVMHC5FW1yENaVv+Nv4ZfG8fAg= +github.com/hashicorp/vault/sdk v0.7.0 h1:2pQRO40R1etpKkia5fb4kjrdYMx3BHklPxl1pxpxDHg= +github.com/hashicorp/vault/sdk v0.7.0/go.mod h1:KyfArJkhooyba7gYCKSq8v66QdqJmnbAxtV/OX1+JTs= github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443 h1:O/pT5C1Q3mVXMyuqg7yuAWUg/jMZR1/0QTzTRdNR6Uw= github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443/go.mod h1:bEpDU35nTu0ey1EXjwNwPjI9xErAsoOCmcMb9GKvyxo= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -419,6 +447,7 @@ github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGk github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da h1:FjHUJJ7oBW4G/9j1KzlHaXL09LyMVM9rupS39lncbXk= github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -449,6 +478,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -486,15 +516,23 @@ github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJys github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0 h1:tEElEatulEHDeedTxwckzyYMA5c86fbmNIUL1hBIiTg= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -516,6 +554,8 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20180130162743-b8a9be070da4/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -542,6 +582,8 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= +github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -588,6 +630,9 @@ github.com/rs/zerolog v1.4.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKk github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= +github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/conswriter v0.0.0-20180208195008-f5ae3917a627/go.mod h1:7zjs06qF79/FKAJpBvFx3P8Ww4UTIMAe+lpNXDHziac= github.com/sean-/pager v0.0.0-20180208200047-666be9bf53b5/go.mod h1:BeybITEsBEg6qbIiqJ6/Bqeq25bCLbL7YFmpaFfJDuM= @@ -683,8 +728,9 @@ go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4 go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= @@ -915,8 +961,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -982,8 +1028,8 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= 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= @@ -1121,6 +1167,8 @@ gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24 gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= diff --git a/control-plane/subcommand/server-acl-init/command.go b/control-plane/subcommand/server-acl-init/command.go index 698da2a25c..a444f65aaa 100644 --- a/control-plane/subcommand/server-acl-init/command.go +++ b/control-plane/subcommand/server-acl-init/command.go @@ -22,12 +22,11 @@ import ( "github.com/hashicorp/consul/api" "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-netaddrs" + vaultApi "github.com/hashicorp/vault/api" "github.com/mitchellh/cli" "github.com/mitchellh/mapstructure" "golang.org/x/text/cases" "golang.org/x/text/language" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" ) @@ -86,8 +85,10 @@ type Command struct { flagEnableInjectK8SNSMirroring bool // Enables mirroring of k8s namespaces into Consul for Connect inject flagInjectK8SNSMirroringPrefix string // Prefix added to Consul namespaces created when mirroring injected services - // Flag to support a custom bootstrap token. - flagBootstrapTokenFile string + // Flags for the secrets backend. + flagSecretsBackend SecretsBackendType + flagBootstrapTokenSecretName string + flagBootstrapTokenSecretKey string flagLogLevel string flagLogJSON bool @@ -98,7 +99,9 @@ type Command struct { // flagFederation indicates if federation has been enabled in the cluster. flagFederation bool - clientset kubernetes.Interface + backend SecretsBackend // for unit testing. + clientset kubernetes.Interface + vaultClient *vaultApi.Client watcher consul.ServerConnectionManager @@ -194,9 +197,14 @@ func (c *Command) init() { c.flags.BoolVar(&c.flagFederation, "federation", false, "Toggle for when federation has been enabled.") - c.flags.StringVar(&c.flagBootstrapTokenFile, "bootstrap-token-file", "", - "Path to file containing ACL token for creating policies and tokens. This token must have 'acl:write' permissions."+ - "When provided, servers will not be bootstrapped and their policies and tokens will not be updated.") + c.flags.StringVar((*string)(&c.flagSecretsBackend), "secrets-backend", "kubernetes", + `The secrets backend to use. Either "vault" or "kubernetes". Defaults to "kubernetes"`) + c.flags.StringVar(&c.flagBootstrapTokenSecretName, "bootstrap-token-secret-name", "", + "The name of the Vault or Kuberenetes secret for the bootstrap token. This token must have `ac::write` permission "+ + "in order to create policies and tokens. If not provided or if the secret is empty, then this command will "+ + "bootstrap ACLs and write the bootstrap token to this secret.") + c.flags.StringVar(&c.flagBootstrapTokenSecretKey, "bootstrap-token-secret-key", "", + "The key within the Vault or Kuberenetes secret containing the bootstrap token.") c.flags.DurationVar(&c.flagTimeout, "timeout", 10*time.Minute, "How long we'll try to bootstrap ACLs for before timing out, e.g. 1ms, 2s, 3m") @@ -231,6 +239,7 @@ func (c *Command) Help() string { // The function will retry its tasks indefinitely until they are complete. func (c *Command) Run(args []string) int { c.once.Do(c.init) + defer c.quitVaultAgent() if err := c.flags.Parse(args); err != nil { return 1 } @@ -264,16 +273,6 @@ func (c *Command) Run(args []string) int { } } - var providedBootstrapToken string - if c.flagBootstrapTokenFile != "" { - var err error - providedBootstrapToken, err = loadTokenFromFile(c.flagBootstrapTokenFile) - if err != nil { - c.UI.Error(err.Error()) - return 1 - } - } - var cancel context.CancelFunc c.ctx, cancel = context.WithTimeout(context.Background(), c.flagTimeout) // The context will only ever be intentionally ended by the timeout. @@ -306,8 +305,12 @@ func (c *Command) Run(args []string) int { c.UI.Error(err.Error()) } - var bootstrapToken string + if err := c.configureSecretsBackend(); err != nil { + c.log.Error(err.Error()) + return 1 + } + var bootstrapToken string if c.flagACLReplicationTokenFile != "" && !c.flagCreateACLReplicationToken { // If ACL replication is enabled, we don't need to ACL bootstrap the servers // since they will be performing replication. @@ -316,21 +319,7 @@ func (c *Command) Run(args []string) int { c.log.Info("ACL replication is enabled so skipping Consul server ACL bootstrapping") bootstrapToken = aclReplicationToken } else { - // Check if we've already been bootstrapped. - var bootTokenSecretName string - if providedBootstrapToken != "" { - c.log.Info("Using provided bootstrap token") - bootstrapToken = providedBootstrapToken - } else { - bootTokenSecretName = c.withPrefix("bootstrap-acl-token") - bootstrapToken, err = c.getBootstrapToken(bootTokenSecretName) - if err != nil { - c.log.Error(fmt.Sprintf("Unexpected error looking for preexisting bootstrap Secret: %s", err)) - return 1 - } - } - - bootstrapToken, err = c.bootstrapServers(ipAddrs, bootstrapToken, bootTokenSecretName) + bootstrapToken, err = c.bootstrapServers(ipAddrs, c.backend) if err != nil { c.log.Error(err.Error()) return 1 @@ -806,24 +795,6 @@ func (c *Command) configureGateway(gatewayParams ConfigureGatewayParams, consulC return nil } -// getBootstrapToken returns the existing bootstrap token if there is one by -// reading the Kubernetes Secret with name secretName. -// If there is no bootstrap token yet, then it returns an empty string (not an error). -func (c *Command) getBootstrapToken(secretName string) (string, error) { - secret, err := c.clientset.CoreV1().Secrets(c.flagK8sNamespace).Get(c.ctx, secretName, metav1.GetOptions{}) - if err != nil { - if k8serrors.IsNotFound(err) { - return "", nil - } - return "", err - } - token, ok := secret.Data[common.ACLTokenSecretKey] - if !ok { - return "", fmt.Errorf("secret %q does not have data key 'token'", secretName) - } - return string(token), nil -} - func (c *Command) configureKubeClient() error { config, err := subcommand.K8SConfig(c.k8s.KubeConfig()) if err != nil { @@ -836,6 +807,55 @@ func (c *Command) configureKubeClient() error { return nil } +// configureSecretsBackend configures either the Kubernetes or Vault +// secrets backend based on flags. +func (c *Command) configureSecretsBackend() error { + if c.backend != nil { + // support a fake backend in unit tests + return nil + } + secretName := c.flagBootstrapTokenSecretName + if secretName == "" { + secretName = c.withPrefix("bootstrap-acl-token") + } + + secretKey := c.flagBootstrapTokenSecretKey + if secretKey == "" { + secretKey = common.ACLTokenSecretKey + } + + switch c.flagSecretsBackend { + case SecretsBackendTypeKubernetes: + c.backend = &KubernetesSecretsBackend{ + ctx: c.ctx, + clientset: c.clientset, + k8sNamespace: c.flagK8sNamespace, + secretName: secretName, + secretKey: secretKey, + } + return nil + case SecretsBackendTypeVault: + cfg := vaultApi.DefaultConfig() + cfg.Address = "" + cfg.AgentAddress = "http://127.0.0.1:8200" + vaultClient, err := vaultApi.NewClient(cfg) + if err != nil { + return fmt.Errorf("Error initializing Vault client: %w", err) + } + + c.vaultClient = vaultClient // must set this for c.quitVaultAgent. + c.backend = &VaultSecretsBackend{ + vaultClient: c.vaultClient, + secretName: secretName, + secretKey: secretKey, + } + return nil + default: + validValues := []SecretsBackendType{SecretsBackendTypeKubernetes, SecretsBackendTypeVault} + return fmt.Errorf("Invalid value for -secrets-backend: %q. Valid values are %v.", c.flagSecretsBackend, validValues) + } +} + // untilSucceeds runs op until it returns a nil error. // If c.cmdTimeout is cancelled it will exit. func (c *Command) untilSucceeds(opName string, op func() error) error { @@ -962,6 +982,10 @@ func (c *Command) validateFlags() error { return errors.New("-consul-api-timeout must be set to a value greater than 0") } + //if c.flagVaultNamespace != "" && c.flagSecretsBackend != SecretsBackendTypeVault { + // return fmt.Errorf("-vault-namespace not supported for -secrets-backend=%q", c.flagSecretsBackend) + //} + return nil } @@ -977,6 +1001,28 @@ func loadTokenFromFile(tokenFile string) (string, error) { return strings.TrimSpace(string(tokenBytes)), nil } +func (c *Command) quitVaultAgent() { + if c.vaultClient == nil { + return + } + + // Tell the Vault agent sidecar to quit. Without this, the Job does not + // complete because the Vault agent does not stop. This retries because it + // does not know exactly when the Vault agent sidecar will start. + err := c.untilSucceeds("tell Vault agent to quit", func() error { + // TODO: RawRequest is deprecated, but there is also not a high level + // method for this in the Vault client. + // nolint:staticcheck // SA1004 ignore + _, err := c.vaultClient.RawRequest( + c.vaultClient.NewRequest("POST", "/agent/v1/quit"), + ) + return err + }) + if err != nil { + c.log.Error("Error telling Vault agent to quit", "error", err) + } +} + const ( consulDefaultNamespace = "default" consulDefaultPartition = "default" diff --git a/control-plane/subcommand/server-acl-init/command_ent_test.go b/control-plane/subcommand/server-acl-init/command_ent_test.go index e31b787e4b..fa56f3bb3a 100644 --- a/control-plane/subcommand/server-acl-init/command_ent_test.go +++ b/control-plane/subcommand/server-acl-init/command_ent_test.go @@ -222,7 +222,6 @@ func TestRun_ConnectInject_NamespaceMirroring(t *testing.T) { // a non-default partition. func TestRun_AnonymousToken_CreatedFromNonDefaultPartition(t *testing.T) { bootToken := "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" - tokenFile := common.WriteTempFile(t, bootToken) server := partitionedSetup(t, bootToken, "test") k8s := fake.NewSimpleClientset() setUpK8sServiceAccount(t, k8s, ns) @@ -231,6 +230,7 @@ func TestRun_AnonymousToken_CreatedFromNonDefaultPartition(t *testing.T) { cmd := Command{ UI: ui, clientset: k8s, + backend: &FakeSecretsBackend{bootstrapToken: bootToken}, } cmd.init() args := []string{ @@ -239,7 +239,6 @@ func TestRun_AnonymousToken_CreatedFromNonDefaultPartition(t *testing.T) { "-grpc-port=" + strings.Split(server.GRPCAddr, ":")[1], "-resource-prefix=" + resourcePrefix, "-k8s-namespace=" + ns, - "-bootstrap-token-file", tokenFile, "-allow-dns", "-partition=test", "-enable-namespaces", diff --git a/control-plane/subcommand/server-acl-init/command_test.go b/control-plane/subcommand/server-acl-init/command_test.go index 3111f58820..ffe60af593 100644 --- a/control-plane/subcommand/server-acl-init/command_test.go +++ b/control-plane/subcommand/server-acl-init/command_test.go @@ -60,13 +60,6 @@ func TestRun_FlagValidation(t *testing.T) { "-resource-prefix=prefix"}, ExpErr: "unable to read token from file \"/notexist\": open /notexist: no such file or directory", }, - { - Flags: []string{ - "-bootstrap-token-file=/notexist", - "-addresses=localhost", - "-resource-prefix=prefix"}, - ExpErr: "unable to read token from file \"/notexist\": open /notexist: no such file or directory", - }, { Flags: []string{ "-addresses=localhost", @@ -407,7 +400,6 @@ func TestRun_TokensWithProvidedBootstrapToken(t *testing.T) { for _, c := range cases { t.Run(c.TestName, func(t *testing.T) { bootToken := "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" - tokenFile := common.WriteTempFile(t, bootToken) k8s, testAgent := completeBootstrappedSetup(t, bootToken) setUpK8sServiceAccount(t, k8s, ns) @@ -417,11 +409,11 @@ func TestRun_TokensWithProvidedBootstrapToken(t *testing.T) { cmd := Command{ UI: ui, clientset: k8s, + backend: &FakeSecretsBackend{bootstrapToken: bootToken}, } cmdArgs := append([]string{ "-timeout=1m", "-k8s-namespace", ns, - "-bootstrap-token-file", tokenFile, "-addresses", strings.Split(testAgent.TestServer.HTTPAddr, ":")[0], "-http-port", strings.Split(testAgent.TestServer.HTTPAddr, ":")[1], "-grpc-port", strings.Split(testAgent.TestServer.GRPCAddr, ":")[1], @@ -915,7 +907,6 @@ func TestRun_ErrorsOnDuplicateACLPolicy(t *testing.T) { // Create Consul with ACLs already bootstrapped so that we can // then seed it with our manually created policy. bootToken := "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" - tokenFile := common.WriteTempFile(t, bootToken) k8s, testAgent := completeBootstrappedSetup(t, bootToken) setUpK8sServiceAccount(t, k8s, ns) @@ -938,11 +929,11 @@ func TestRun_ErrorsOnDuplicateACLPolicy(t *testing.T) { cmd := Command{ UI: ui, clientset: k8s, + backend: &FakeSecretsBackend{bootstrapToken: bootToken}, } cmdArgs := []string{ "-timeout=1s", "-k8s-namespace", ns, - "-bootstrap-token-file", tokenFile, "-resource-prefix=" + resourcePrefix, "-k8s-namespace=" + ns, "-addresses", strings.Split(testAgent.TestServer.HTTPAddr, ":")[0], @@ -1453,272 +1444,223 @@ func TestRun_ClientPolicyAndBindingRuleRetry(t *testing.T) { // Test if there is an old bootstrap Secret we still try to create and set // server tokens. func TestRun_AlreadyBootstrapped(t *testing.T) { - t.Parallel() - cases := map[string]bool{ - "token saved in k8s secret": true, - "token provided via file": false, - } - - for name, tokenFromK8sSecret := range cases { - t.Run(name, func(t *testing.T) { - k8s := fake.NewSimpleClientset() - - type APICall struct { - Method string - Path string - } - var consulAPICalls []APICall - - // Start the Consul server. - consulServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Record all the API calls made. - consulAPICalls = append(consulAPICalls, APICall{ - Method: r.Method, - Path: r.URL.Path, - }) - switch r.URL.Path { - case "/v1/agent/self": - fmt.Fprintln(w, `{"Config": {"Datacenter": "dc1", "PrimaryDatacenter": "dc1"}}`) - case "/v1/acl/tokens": - fmt.Fprintln(w, `[]`) - case "/v1/acl/token": - fmt.Fprintln(w, `{}`) - case "/v1/acl/policy": - fmt.Fprintln(w, `{}`) - case "/v1/agent/token/acl_agent_token": - fmt.Fprintln(w, `{}`) - case "/v1/acl/auth-method": - fmt.Fprintln(w, `{}`) - case "/v1/acl/role/name/release-name-consul-client-acl-role": - w.WriteHeader(404) - case "/v1/acl/role": - fmt.Fprintln(w, `{}`) - case "/v1/acl/binding-rules": - fmt.Fprintln(w, `[]`) - case "/v1/acl/binding-rule": - fmt.Fprintln(w, `{}`) - default: - w.WriteHeader(500) - fmt.Fprintln(w, "Mock Server not configured for this route: "+r.URL.Path) - } - })) - defer consulServer.Close() + k8s := fake.NewSimpleClientset() - serverURL, err := url.Parse(consulServer.URL) - require.NoError(t, err) - port, err := strconv.Atoi(serverURL.Port()) - require.NoError(t, err) - setUpK8sServiceAccount(t, k8s, ns) + type APICall struct { + Method string + Path string + } + var consulAPICalls []APICall - cmdArgs := []string{ - "-timeout=500ms", - "-resource-prefix=" + resourcePrefix, - "-k8s-namespace=" + ns, - "-addresses=" + serverURL.Hostname(), - "-http-port=" + serverURL.Port(), - } + // Start the Consul server. + consulServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Record all the API calls made. + consulAPICalls = append(consulAPICalls, APICall{ + Method: r.Method, + Path: r.URL.Path, + }) + switch r.URL.Path { + case "/v1/agent/self": + fmt.Fprintln(w, `{"Config": {"Datacenter": "dc1", "PrimaryDatacenter": "dc1"}}`) + case "/v1/acl/tokens": + fmt.Fprintln(w, `[]`) + case "/v1/acl/token": + fmt.Fprintln(w, `{}`) + case "/v1/acl/policy": + fmt.Fprintln(w, `{}`) + case "/v1/agent/token/acl_agent_token": + fmt.Fprintln(w, `{}`) + case "/v1/acl/auth-method": + fmt.Fprintln(w, `{}`) + case "/v1/acl/role/name/release-name-consul-client-acl-role": + w.WriteHeader(404) + case "/v1/acl/role": + fmt.Fprintln(w, `{}`) + case "/v1/acl/binding-rules": + fmt.Fprintln(w, `[]`) + case "/v1/acl/binding-rule": + fmt.Fprintln(w, `{}`) + default: + w.WriteHeader(500) + fmt.Fprintln(w, "Mock Server not configured for this route: "+r.URL.Path) + } + })) + defer consulServer.Close() - // Create the bootstrap secret. - if tokenFromK8sSecret { - _, err = k8s.CoreV1().Secrets(ns).Create( - context.Background(), - &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: resourcePrefix + "-bootstrap-acl-token", - Labels: map[string]string{common.CLILabelKey: common.CLILabelValue}, - }, - Data: map[string][]byte{ - "token": []byte("old-token"), - }, - }, - metav1.CreateOptions{}) - require.NoError(t, err) - } else { - // Write token to a file. - bootTokenFile, err := os.CreateTemp("", "") - require.NoError(t, err) - defer os.RemoveAll(bootTokenFile.Name()) + serverURL, err := url.Parse(consulServer.URL) + require.NoError(t, err) + port, err := strconv.Atoi(serverURL.Port()) + require.NoError(t, err) + setUpK8sServiceAccount(t, k8s, ns) - _, err = bootTokenFile.WriteString("old-token") - require.NoError(t, err) + cmdArgs := []string{ + "-timeout=500ms", + "-resource-prefix=" + resourcePrefix, + "-k8s-namespace=" + ns, + "-addresses=" + serverURL.Hostname(), + "-http-port=" + serverURL.Port(), + } - require.NoError(t, err) - cmdArgs = append(cmdArgs, "-bootstrap-token-file", bootTokenFile.Name()) - } + // Create the bootstrap secret. + _, err = k8s.CoreV1().Secrets(ns).Create( + context.Background(), + &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourcePrefix + "-bootstrap-acl-token", + Labels: map[string]string{common.CLILabelKey: common.CLILabelValue}, + }, + Data: map[string][]byte{ + "token": []byte("old-token"), + }, + }, + metav1.CreateOptions{}) + require.NoError(t, err) - // Run the command. - ui := cli.NewMockUi() - cmd := Command{ - UI: ui, - clientset: k8s, - watcher: test.MockConnMgrForIPAndPort(serverURL.Hostname(), port), - } + // Run the command. + ui := cli.NewMockUi() + cmd := Command{ + UI: ui, + clientset: k8s, + watcher: test.MockConnMgrForIPAndPort(serverURL.Hostname(), port), + } - responseCode := cmd.Run(cmdArgs) - require.Equal(t, 0, responseCode, ui.ErrorWriter.String()) + responseCode := cmd.Run(cmdArgs) + require.Equal(t, 0, responseCode, ui.ErrorWriter.String()) - // Test that the Secret is the same. - if tokenFromK8sSecret { - secret, err := k8s.CoreV1().Secrets(ns).Get(context.Background(), resourcePrefix+"-bootstrap-acl-token", metav1.GetOptions{}) - require.NoError(t, err) - require.Contains(t, secret.Data, "token") - require.Equal(t, "old-token", string(secret.Data["token"])) - } + // Test that the Secret is the same. + secret, err := k8s.CoreV1().Secrets(ns).Get(context.Background(), resourcePrefix+"-bootstrap-acl-token", metav1.GetOptions{}) + require.NoError(t, err) + require.Contains(t, secret.Data, "token") + require.Equal(t, "old-token", string(secret.Data["token"])) - // Test that the expected API calls were made. - require.Equal(t, []APICall{ - // We expect calls for updating the server policy, setting server tokens, - // and updating client policy. - { - "PUT", - "/v1/acl/policy", - }, - { - "GET", - "/v1/acl/tokens", - }, - { - "PUT", - "/v1/acl/token", - }, - { - "PUT", - "/v1/agent/token/agent", - }, - { - "PUT", - "/v1/agent/token/acl_agent_token", - }, - { - "GET", - "/v1/agent/self", - }, - { - "PUT", - "/v1/acl/auth-method", - }, - { - "PUT", - "/v1/acl/policy", - }, - { - "GET", - "/v1/acl/role/name/release-name-consul-client-acl-role", - }, - { - "PUT", - "/v1/acl/role", - }, - { - "GET", - "/v1/acl/binding-rules", - }, - { - "PUT", - "/v1/acl/binding-rule", - }, - }, consulAPICalls) - }) - } + // Test that the expected API calls were made. + require.Equal(t, []APICall{ + // We expect calls for updating the server policy, setting server tokens, + // and updating client policy. + { + "PUT", + "/v1/acl/policy", + }, + { + "GET", + "/v1/acl/tokens", + }, + { + "PUT", + "/v1/acl/token", + }, + { + "PUT", + "/v1/agent/token/agent", + }, + { + "PUT", + "/v1/agent/token/acl_agent_token", + }, + { + "GET", + "/v1/agent/self", + }, + { + "PUT", + "/v1/acl/auth-method", + }, + { + "PUT", + "/v1/acl/policy", + }, + { + "GET", + "/v1/acl/role/name/release-name-consul-client-acl-role", + }, + { + "PUT", + "/v1/acl/role", + }, + { + "GET", + "/v1/acl/binding-rules", + }, + { + "PUT", + "/v1/acl/binding-rule", + }, + }, consulAPICalls) } // Test if there is an old bootstrap Secret and the server token exists // that we don't try and recreate the token. func TestRun_AlreadyBootstrapped_ServerTokenExists(t *testing.T) { - t.Parallel() - cases := map[string]bool{ - "token saved in k8s secret": true, - "token provided via file": false, - } - - for name, tokenInK8sSecret := range cases { - t.Run(name, func(t *testing.T) { - - // First set everything up with ACLs bootstrapped. - bootToken := "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" - k8s, testAgent := completeBootstrappedSetup(t, bootToken) - setUpK8sServiceAccount(t, k8s, ns) - - cmdArgs := []string{ - "-timeout=1m", - "-k8s-namespace", ns, - "-addresses", strings.Split(testAgent.TestServer.HTTPAddr, ":")[0], - "-http-port", strings.Split(testAgent.TestServer.HTTPAddr, ":")[1], - "-grpc-port", strings.Split(testAgent.TestServer.GRPCAddr, ":")[1], - "-resource-prefix", resourcePrefix, - } - - if tokenInK8sSecret { - _, err := k8s.CoreV1().Secrets(ns).Create(context.Background(), &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: resourcePrefix + "-bootstrap-acl-token", - }, - Data: map[string][]byte{ - "token": []byte(bootToken), - }, - }, metav1.CreateOptions{}) - require.NoError(t, err) - } else { - // Write token to a file. - bootTokenFile, err := os.CreateTemp("", "") - require.NoError(t, err) - defer os.RemoveAll(bootTokenFile.Name()) + // First set everything up with ACLs bootstrapped. + bootToken := "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" + k8s, testAgent := completeBootstrappedSetup(t, bootToken) + setUpK8sServiceAccount(t, k8s, ns) - _, err = bootTokenFile.WriteString(bootToken) - require.NoError(t, err) + cmdArgs := []string{ + "-timeout=1m", + "-k8s-namespace", ns, + "-addresses", strings.Split(testAgent.TestServer.HTTPAddr, ":")[0], + "-http-port", strings.Split(testAgent.TestServer.HTTPAddr, ":")[1], + "-grpc-port", strings.Split(testAgent.TestServer.GRPCAddr, ":")[1], + "-resource-prefix", resourcePrefix, + } - require.NoError(t, err) - cmdArgs = append(cmdArgs, "-bootstrap-token-file", bootTokenFile.Name()) - } + _, err := k8s.CoreV1().Secrets(ns).Create(context.Background(), &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourcePrefix + "-bootstrap-acl-token", + }, + Data: map[string][]byte{ + "token": []byte(bootToken), + }, + }, metav1.CreateOptions{}) + require.NoError(t, err) - consulClient, err := api.NewClient(&api.Config{ - Address: testAgent.TestServer.HTTPAddr, - Token: bootToken, - }) - require.NoError(t, err) - ui := cli.NewMockUi() - cmd := Command{ - UI: ui, - clientset: k8s, - } + consulClient, err := api.NewClient(&api.Config{ + Address: testAgent.TestServer.HTTPAddr, + Token: bootToken, + }) + require.NoError(t, err) + ui := cli.NewMockUi() + cmd := Command{ + UI: ui, + clientset: k8s, + } - cmd.init() - // Create the server policy and token _before_ we run the command. - agentPolicyRules, err := cmd.agentRules() - require.NoError(t, err) - policy, _, err := consulClient.ACL().PolicyCreate(&api.ACLPolicy{ - Name: "agent-token", - Description: "Agent Token Policy", - Rules: agentPolicyRules, - }, nil) - require.NoError(t, err) - _, _, err = consulClient.ACL().TokenCreate(&api.ACLToken{ - Description: fmt.Sprintf("Server Token for %s", strings.Split(testAgent.TestServer.HTTPAddr, ":")[0]), - Policies: []*api.ACLTokenPolicyLink{ - { - Name: policy.Name, - }, - }, - }, nil) - require.NoError(t, err) + cmd.init() + // Create the server policy and token _before_ we run the command. + agentPolicyRules, err := cmd.agentRules() + require.NoError(t, err) + policy, _, err := consulClient.ACL().PolicyCreate(&api.ACLPolicy{ + Name: "agent-token", + Description: "Agent Token Policy", + Rules: agentPolicyRules, + }, nil) + require.NoError(t, err) + _, _, err = consulClient.ACL().TokenCreate(&api.ACLToken{ + Description: fmt.Sprintf("Server Token for %s", strings.Split(testAgent.TestServer.HTTPAddr, ":")[0]), + Policies: []*api.ACLTokenPolicyLink{ + { + Name: policy.Name, + }, + }, + }, nil) + require.NoError(t, err) - // Run the command. - responseCode := cmd.Run(cmdArgs) - require.Equal(t, 0, responseCode, ui.ErrorWriter.String()) + // Run the command. + responseCode := cmd.Run(cmdArgs) + require.Equal(t, 0, responseCode, ui.ErrorWriter.String()) - // Check that only one server token exists, i.e. it didn't create an - // extra token. - tokens, _, err := consulClient.ACL().TokenList(nil) - require.NoError(t, err) - count := 0 - for _, token := range tokens { - if len(token.Policies) == 1 && token.Policies[0].Name == policy.Name { - count++ - } - } - require.Equal(t, 1, count) - }) + // Check that only one server token exists, i.e. it didn't create an + // extra token. + tokens, _, err := consulClient.ACL().TokenList(nil) + require.NoError(t, err) + count := 0 + for _, token := range tokens { + if len(token.Policies) == 1 && token.Policies[0].Name == policy.Name { + count++ + } } + require.Equal(t, 1, count) } // Test if -set-server-tokens is false (i.e. servers are disabled), we skip bootstrapping of the servers @@ -1728,7 +1670,6 @@ func TestRun_SkipBootstrapping_WhenServersAreDisabled(t *testing.T) { k8s := fake.NewSimpleClientset() bootToken := "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" - tokenFile := common.WriteTempFile(t, bootToken) type APICall struct { Method string @@ -1766,6 +1707,7 @@ func TestRun_SkipBootstrapping_WhenServersAreDisabled(t *testing.T) { UI: ui, clientset: k8s, watcher: test.MockConnMgrForIPAndPort(serverURL.Hostname(), port), + backend: &FakeSecretsBackend{bootstrapToken: bootToken}, } responseCode := cmd.Run([]string{ "-timeout=500ms", @@ -1773,7 +1715,6 @@ func TestRun_SkipBootstrapping_WhenServersAreDisabled(t *testing.T) { "-k8s-namespace=" + ns, "-addresses=" + serverURL.Hostname(), "-http-port=" + serverURL.Port(), - "-bootstrap-token-file=" + tokenFile, "-set-server-tokens=false", "-client=false", // disable client token, so there are fewer calls }) diff --git a/control-plane/subcommand/server-acl-init/k8s_secrets_backend.go b/control-plane/subcommand/server-acl-init/k8s_secrets_backend.go new file mode 100644 index 0000000000..4e3efcf65b --- /dev/null +++ b/control-plane/subcommand/server-acl-init/k8s_secrets_backend.go @@ -0,0 +1,62 @@ +package serveraclinit + +import ( + "context" + "fmt" + + "github.com/hashicorp/consul-k8s/control-plane/subcommand/common" + apiv1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" +) + +const SecretsBackendTypeKubernetes SecretsBackendType = "kubernetes" + +type KubernetesSecretsBackend struct { + ctx context.Context + clientset kubernetes.Interface + k8sNamespace string + secretName string + secretKey string +} + +var _ SecretsBackend = (*KubernetesSecretsBackend)(nil) + +// BootstrapToken returns the existing bootstrap token if there is one by +// reading the Kubernetes Secret. If there is no bootstrap token yet, then +// it returns an empty string (not an error). +func (b *KubernetesSecretsBackend) BootstrapToken() (string, error) { + secret, err := b.clientset.CoreV1().Secrets(b.k8sNamespace).Get(b.ctx, b.secretName, metav1.GetOptions{}) + if err != nil { + if k8serrors.IsNotFound(err) { + return "", nil + } + return "", err + } + token, ok := secret.Data[b.secretKey] + if !ok { + return "", fmt.Errorf("secret %q does not have data key %q", b.secretName, b.secretKey) + } + return string(token), nil + +} + +// WriteBootstrapToken writes the given bootstrap token to the Kubernetes Secret. +func (b *KubernetesSecretsBackend) WriteBootstrapToken(bootstrapToken string) error { + secret := &apiv1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: b.secretName, + Labels: map[string]string{common.CLILabelKey: common.CLILabelValue}, + }, + Data: map[string][]byte{ + b.secretKey: []byte(bootstrapToken), + }, + } + _, err := b.clientset.CoreV1().Secrets(b.k8sNamespace).Create(b.ctx, secret, metav1.CreateOptions{}) + return err +} + +func (b *KubernetesSecretsBackend) BootstrapTokenSecretName() string { + return b.secretName +} diff --git a/control-plane/subcommand/server-acl-init/secrets_backend.go b/control-plane/subcommand/server-acl-init/secrets_backend.go new file mode 100644 index 0000000000..4b4d5c2fc4 --- /dev/null +++ b/control-plane/subcommand/server-acl-init/secrets_backend.go @@ -0,0 +1,18 @@ +package serveraclinit + +type SecretsBackendType string + +type SecretsBackend interface { + // BootstrapToken fetches the bootstrap token from the backend. If the + // token is not found or empty, implementations should return an empty + // string (not an error). + BootstrapToken() (string, error) + + // WriteBootstrapToken writes the given bootstrap token to the backend. + // Implementations of this method do not need to retry the write until + // successful. + WriteBootstrapToken(string) error + + // BootstrapTokenSecretName returns the name of the bootstrap token secret. + BootstrapTokenSecretName() string +} diff --git a/control-plane/subcommand/server-acl-init/servers.go b/control-plane/subcommand/server-acl-init/servers.go index 2dc8f8ab67..01e9a58145 100644 --- a/control-plane/subcommand/server-acl-init/servers.go +++ b/control-plane/subcommand/server-acl-init/servers.go @@ -1,7 +1,6 @@ package serveraclinit import ( - "errors" "fmt" "net" "net/http" @@ -9,29 +8,30 @@ import ( "time" "github.com/hashicorp/consul/api" - apiv1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/hashicorp/consul-k8s/control-plane/consul" - "github.com/hashicorp/consul-k8s/control-plane/subcommand/common" ) // bootstrapServers bootstraps ACLs and ensures each server has an ACL token. -// If bootstrapToken is not empty then ACLs are already bootstrapped. -func (c *Command) bootstrapServers(serverAddresses []net.IPAddr, bootstrapToken, bootTokenSecretName string) (string, error) { +// If a bootstrap is found in the secrets backend, then skip ACL bootstrapping. +// Otherwise, bootstrap ACLs and write the bootstrap token to the secrets backend. +func (c *Command) bootstrapServers(serverAddresses []net.IPAddr, backend SecretsBackend) (string, error) { // Pick the first server address to connect to for bootstrapping and set up connection. firstServerAddr := fmt.Sprintf("%s:%d", serverAddresses[0].IP.String(), c.consulFlags.HTTPPort) - if bootstrapToken == "" { - c.log.Info("No bootstrap token from previous installation found, continuing on to bootstrapping") + bootstrapToken, err := backend.BootstrapToken() + if err != nil { + return "", fmt.Errorf("Unexpected error fetching bootstrap token secret: %w", err) + } - var err error - bootstrapToken, err = c.bootstrapACLs(firstServerAddr, bootTokenSecretName) + if bootstrapToken != "" { + c.log.Info("Found bootstrap token in secrets backend", "secret", backend.BootstrapTokenSecretName()) + } else { + c.log.Info("No bootstrap token found in secrets backend, continuing to ACL bootstrapping", "secret", backend.BootstrapTokenSecretName()) + bootstrapToken, err = c.bootstrapACLs(firstServerAddr, backend) if err != nil { return "", err } - } else { - c.log.Info(fmt.Sprintf("ACLs already bootstrapped - retrieved bootstrap token from Secret %q", bootTokenSecretName)) } // We should only create and set server tokens when servers are running within this cluster. @@ -47,7 +47,7 @@ func (c *Command) bootstrapServers(serverAddresses []net.IPAddr, bootstrapToken, // bootstrapACLs makes the ACL bootstrap API call and writes the bootstrap token // to a kube secret. -func (c *Command) bootstrapACLs(firstServerAddr, bootTokenSecretName string) (string, error) { +func (c *Command) bootstrapACLs(firstServerAddr string, backend SecretsBackend) (string, error) { config := c.consulFlags.ConsulClientConfig().APIClientConfig config.Address = firstServerAddr // Exempting this particular use of the http client from using global.consulAPITimeout @@ -78,9 +78,12 @@ func (c *Command) bootstrapACLs(firstServerAddr, bootTokenSecretName string) (st // Check if already bootstrapped. if strings.Contains(err.Error(), "Unexpected response code: 403") { - unrecoverableErr = errors.New("ACLs already bootstrapped but the ACL token was not written to a Kubernetes secret." + - " We can't proceed because the bootstrap token is lost." + - " You must reset ACLs.") + unrecoverableErr = fmt.Errorf( + "ACLs already bootstrapped but unable to find the bootstrap token in the secrets backend."+ + " We can't proceed without a bootstrap token."+ + " Store a token with `acl:write` permission in the secret %q.", + backend.BootstrapTokenSecretName(), + ) return nil } @@ -98,21 +101,12 @@ func (c *Command) bootstrapACLs(firstServerAddr, bootTokenSecretName string) (st return "", err } - // Write bootstrap token to a Kubernetes secret. - err = c.untilSucceeds(fmt.Sprintf("writing bootstrap Secret %q", bootTokenSecretName), + // Write bootstrap token to the secrets backend. + err = c.untilSucceeds(fmt.Sprintf("writing bootstrap Secret %q", backend.BootstrapTokenSecretName()), func() error { - secret := &apiv1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: bootTokenSecretName, - Labels: map[string]string{common.CLILabelKey: common.CLILabelValue}, - }, - Data: map[string][]byte{ - common.ACLTokenSecretKey: []byte(bootstrapToken), - }, - } - _, err := c.clientset.CoreV1().Secrets(c.flagK8sNamespace).Create(c.ctx, secret, metav1.CreateOptions{}) - return err - }) + return backend.WriteBootstrapToken(bootstrapToken) + }, + ) return bootstrapToken, err } diff --git a/control-plane/subcommand/server-acl-init/test_fake_secrets_backend.go b/control-plane/subcommand/server-acl-init/test_fake_secrets_backend.go new file mode 100644 index 0000000000..5c9a63f1a5 --- /dev/null +++ b/control-plane/subcommand/server-acl-init/test_fake_secrets_backend.go @@ -0,0 +1,20 @@ +package serveraclinit + +type FakeSecretsBackend struct { + bootstrapToken string +} + +func (b *FakeSecretsBackend) BootstrapToken() (string, error) { + return b.bootstrapToken, nil +} + +func (*FakeSecretsBackend) BootstrapTokenSecretName() string { + return "fake-bootstrap-token" +} + +func (b *FakeSecretsBackend) WriteBootstrapToken(token string) error { + b.bootstrapToken = token + return nil +} + +var _ SecretsBackend = (*FakeSecretsBackend)(nil) diff --git a/control-plane/subcommand/server-acl-init/vault_secrets_backend.go b/control-plane/subcommand/server-acl-init/vault_secrets_backend.go new file mode 100644 index 0000000000..2706567878 --- /dev/null +++ b/control-plane/subcommand/server-acl-init/vault_secrets_backend.go @@ -0,0 +1,66 @@ +package serveraclinit + +import ( + "fmt" + + "github.com/hashicorp/vault/api" +) + +const SecretsBackendTypeVault SecretsBackendType = "vault" + +type VaultSecretsBackend struct { + vaultClient *api.Client + secretName string + secretKey string +} + +var _ SecretsBackend = (*VaultSecretsBackend)(nil) + +// BootstrapToken returns the bootstrap token stored in Vault. +// If not found this returns an empty string (not an error). +func (b *VaultSecretsBackend) BootstrapToken() (string, error) { + secret, err := b.vaultClient.Logical().Read(b.secretName) + if err != nil { + return "", err + } + if secret == nil || secret.Data == nil { + // secret not found or empty. + return "", nil + } + // Grab secret.Data["data"][secretKey]. + dataRaw, found := secret.Data["data"] + if !found { + return "", nil + } + data, ok := dataRaw.(map[string]interface{}) + if !ok { + return "", nil + } + tokRaw, found := data[b.secretKey] + if !found { + return "", nil + } + if tok, ok := tokRaw.(string); ok { + return tok, nil + } + return "", fmt.Errorf("Unexpected data. To resolve this, "+ + "`vault kv put %[1]s=` if Consul is already ACL bootstrapped. "+ + "If not ACL bootstrapped, `vault kv put %[1]s=\"\"`", b.secretKey, b.secretKey) +} + +// BootstrapTokenSecretName returns the name of the bootstrap token secret. +func (b *VaultSecretsBackend) BootstrapTokenSecretName() string { + return b.secretName +} + +// WriteBootstrapToken writes the bootstrap token to Vault. +func (b *VaultSecretsBackend) WriteBootstrapToken(bootstrapToken string) error { + _, err := b.vaultClient.Logical().Write(b.secretName, + map[string]interface{}{ + "data": map[string]interface{}{ + b.secretKey: bootstrapToken, + }, + }, + ) + return err +} From 86454d27581b16730db0f2d6a85c1b5ddbb2f95d Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Tue, 7 Mar 2023 19:43:00 -0800 Subject: [PATCH 091/340] Mw/update to 1.15.1 (#1990) * update charts to point to 1.15.1 * updated consul libraries to the latest --- acceptance/go.mod | 4 ++-- acceptance/go.sum | 10 ++++------ charts/consul/Chart.yaml | 4 ++-- charts/consul/values.yaml | 2 +- cli/go.mod | 6 +++--- cli/go.sum | 12 +++++++----- control-plane/cni/go.mod | 20 ++++++++++---------- control-plane/cni/go.sum | 37 ++++++++++++++++++++++--------------- control-plane/go.mod | 6 ++---- control-plane/go.sum | 12 +++++------- 10 files changed, 58 insertions(+), 55 deletions(-) diff --git a/acceptance/go.mod b/acceptance/go.mod index f81eff4066..66fa6c4b44 100644 --- a/acceptance/go.mod +++ b/acceptance/go.mod @@ -5,8 +5,8 @@ go 1.20 require ( github.com/gruntwork-io/terratest v0.31.2 github.com/hashicorp/consul-k8s/control-plane v0.0.0-20221117191905-0b1cc2b631e3 - github.com/hashicorp/consul/api v1.16.0 - github.com/hashicorp/consul/sdk v0.12.0 + github.com/hashicorp/consul/api v1.20.0 + github.com/hashicorp/consul/sdk v0.13.1 github.com/hashicorp/go-uuid v1.0.3 github.com/hashicorp/go-version v1.6.0 github.com/hashicorp/vault/api v1.2.0 diff --git a/acceptance/go.sum b/acceptance/go.sum index 2b154c10f7..44969f22ae 100644 --- a/acceptance/go.sum +++ b/acceptance/go.sum @@ -364,10 +364,10 @@ github.com/gruntwork-io/terratest v0.31.2 h1:xvYHA80MUq5kx670dM18HInewOrrQrAN+Xb github.com/gruntwork-io/terratest v0.31.2/go.mod h1:EEgJie28gX/4AD71IFqgMj6e99KP5mi81hEtzmDjxTo= github.com/hashicorp/consul-k8s/control-plane v0.0.0-20221117191905-0b1cc2b631e3 h1:4wROIZB8Y4cN/wPILChc2zQ/q00z1VyJitdgyLbITdU= github.com/hashicorp/consul-k8s/control-plane v0.0.0-20221117191905-0b1cc2b631e3/go.mod h1:j9Db/whkzvNC+KP2GftY0HxxleLm9swxXjlu3tYaOAw= -github.com/hashicorp/consul/api v1.16.0 h1:Vf/QVFIwz+PdHR4T4lSwYzLULtbHVq0BheXCUAKP50M= -github.com/hashicorp/consul/api v1.16.0/go.mod h1:GJI1Sif0Wc/iYyqg7EXHJV37IPush6eJTewvYdF9uO8= -github.com/hashicorp/consul/sdk v0.12.0 h1:qsNQToBEs9v5MUWOv/JhiOu4wPeq9VdK7Jcgf7shOrU= -github.com/hashicorp/consul/sdk v0.12.0/go.mod h1:yPkX5Q6CsxTFMjQQDJwzeNmUUF5NUGGbrDsv9wTb8cw= +github.com/hashicorp/consul/api v1.20.0 h1:9IHTjNVSZ7MIwjlW3N3a7iGiykCMDpxZu8jsxFJh0yc= +github.com/hashicorp/consul/api v1.20.0/go.mod h1:nR64eD44KQ59Of/ECwt2vUmIK2DKsDzAwTmwmLl8Wpo= +github.com/hashicorp/consul/sdk v0.13.1 h1:EygWVWWMczTzXGpO93awkHFzfUka6hLYJ0qhETd+6lY= +github.com/hashicorp/consul/sdk v0.13.1/go.mod h1:SW/mM4LbKfqmMvcFu8v+eiQQ7oitXEFeiBe9StxERb0= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -803,7 +803,6 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -902,7 +901,6 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/charts/consul/Chart.yaml b/charts/consul/Chart.yaml index 58ca134137..ffd7961302 100644 --- a/charts/consul/Chart.yaml +++ b/charts/consul/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 name: consul version: 1.2.0-dev -appVersion: 1.15.0 +appVersion: 1.15.1 kubeVersion: ">=1.22.0-0" description: Official HashiCorp Consul Chart home: https://www.consul.io @@ -13,7 +13,7 @@ annotations: artifacthub.io/prerelease: true artifacthub.io/images: | - name: consul - image: hashicorp/consul:1.15.0 + image: hashicorp/consul:1.15.1 - name: consul-k8s-control-plane image: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.2.0-dev - name: consul-dataplane diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 009e851e1b..6fca91244b 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -63,7 +63,7 @@ global: # image: "hashicorp/consul-enterprise:1.10.0-ent" # ``` # @default: hashicorp/consul: - image: "hashicorp/consul:1.15.0" + image: "hashicorp/consul:1.15.1" # Array of objects containing image pull secret names that will be applied to each service account. # This can be used to reference image pull secrets if using a custom consul or consul-k8s-control-plane Docker image. diff --git a/cli/go.mod b/cli/go.mod index ae69aa5a64..adc0309466 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -8,7 +8,7 @@ require ( github.com/fatih/color v1.13.0 github.com/google/go-cmp v0.5.8 github.com/hashicorp/consul-k8s/charts v0.0.0-00010101000000-000000000000 - github.com/hashicorp/consul/troubleshoot v0.0.0-20230217154305-8dab825c3640 + github.com/hashicorp/consul/troubleshoot v0.1.2 github.com/hashicorp/go-hclog v1.2.1 github.com/hashicorp/hcp-sdk-go v0.23.1-0.20220921131124-49168300a7dc github.com/kr/text v0.2.0 @@ -99,8 +99,8 @@ require ( github.com/gorilla/mux v1.8.0 // indirect github.com/gosuri/uitable v0.0.4 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect - github.com/hashicorp/consul/api v1.10.1-0.20230209203402-db2bd404bf72 // indirect - github.com/hashicorp/consul/envoyextensions v0.0.0-20230210154717-4f2ce606547b // indirect + github.com/hashicorp/consul/api v1.20.0 // indirect + github.com/hashicorp/consul/envoyextensions v0.1.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect diff --git a/cli/go.sum b/cli/go.sum index 6582dc76ac..6ba77d4281 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -437,14 +437,16 @@ github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWet github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.10.1-0.20230209203402-db2bd404bf72 h1:O+z5m5kNtu6NHBMwMsRb1S0P7giqNu5vBBeCzgiAesg= -github.com/hashicorp/consul/api v1.10.1-0.20230209203402-db2bd404bf72/go.mod h1:c1u8FzGHcavbEtRW/p1YditvfMgn4QsKNgz2rnCDF7c= -github.com/hashicorp/consul/envoyextensions v0.0.0-20230210154717-4f2ce606547b h1:T+El0UxZP7h2mGL+EPBJejS4gKM/w0KAYOSpTs7hrbY= -github.com/hashicorp/consul/envoyextensions v0.0.0-20230210154717-4f2ce606547b/go.mod h1:oJKG0zAMtq6ZmZNYQyeKh6kIJmi01rZSZDSgnjzZ15w= +github.com/hashicorp/consul/api v1.20.0 h1:9IHTjNVSZ7MIwjlW3N3a7iGiykCMDpxZu8jsxFJh0yc= +github.com/hashicorp/consul/api v1.20.0/go.mod h1:nR64eD44KQ59Of/ECwt2vUmIK2DKsDzAwTmwmLl8Wpo= +github.com/hashicorp/consul/envoyextensions v0.1.2 h1:PvPqJ/td3UpOeIKQl5ycFPUy46XZP9awfhAUCduDeI4= +github.com/hashicorp/consul/envoyextensions v0.1.2/go.mod h1:N94DQQkgITiA40zuTQ/UdPOLAAWobgHfVT5u7wxE/aU= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.13.0 h1:lce3nFlpv8humJL8rNrrGHYSKc3q+Kxfeg3Ii1m6ZWU= +github.com/hashicorp/consul/sdk v0.13.1 h1:EygWVWWMczTzXGpO93awkHFzfUka6hLYJ0qhETd+6lY= github.com/hashicorp/consul/troubleshoot v0.0.0-20230217154305-8dab825c3640 h1:P81kThpSzUW2oERDMrLsiZE3OuilLo3/EQhtVQW5M+8= github.com/hashicorp/consul/troubleshoot v0.0.0-20230217154305-8dab825c3640/go.mod h1:rskvju2tK8XvHYTAILHjO7lpV1/uViHs3Q3mg9Rkwlg= +github.com/hashicorp/consul/troubleshoot v0.1.2 h1:c6uMTSt/qTMhK3e18nl4xW4j7JcANdQNHOEYhoXH1P8= +github.com/hashicorp/consul/troubleshoot v0.1.2/go.mod h1:q35QOtN7K5kFLPm2SXHBDD+PzsuBekcqTZuuoOTzbWA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= diff --git a/control-plane/cni/go.mod b/control-plane/cni/go.mod index c7820438cb..aabc1ceaf0 100644 --- a/control-plane/cni/go.mod +++ b/control-plane/cni/go.mod @@ -3,9 +3,9 @@ module github.com/hashicorp/consul-k8s/control-plane/cni require ( github.com/containernetworking/cni v1.1.1 github.com/containernetworking/plugins v1.1.1 - github.com/hashicorp/consul/sdk v0.13.0 - github.com/hashicorp/go-hclog v0.16.1 - github.com/stretchr/testify v1.7.1 + github.com/hashicorp/consul/sdk v0.13.1 + github.com/hashicorp/go-hclog v1.2.1 + github.com/stretchr/testify v1.7.2 k8s.io/api v0.22.2 k8s.io/apimachinery v0.22.2 k8s.io/client-go v0.22.2 @@ -14,7 +14,7 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect - github.com/fatih/color v1.12.0 // indirect + github.com/fatih/color v1.13.0 // indirect github.com/go-logr/logr v0.4.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.2 // indirect @@ -23,24 +23,24 @@ require ( github.com/googleapis/gnostic v0.5.5 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/json-iterator/go v1.1.11 // indirect - github.com/mattn/go-colorable v0.1.8 // indirect - github.com/mattn/go-isatty v0.0.13 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/net v0.0.0-20211209124913-491a49abca63 // indirect + golang.org/x/net v0.0.0-20211216030914-fe4d6282115f // indirect golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect - golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect + golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.26.0 // indirect + google.golang.org/protobuf v1.27.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.9.0 // indirect k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a // indirect diff --git a/control-plane/cni/go.sum b/control-plane/cni/go.sum index 1188cc5dd4..ec1e322cf3 100644 --- a/control-plane/cni/go.sum +++ b/control-plane/cni/go.sum @@ -57,8 +57,8 @@ github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCv github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= -github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -135,8 +135,8 @@ github.com/hashicorp/consul/sdk v0.4.1-0.20221021205723-cc843c4be892 h1:jw0NwPmN github.com/hashicorp/consul/sdk v0.4.1-0.20221021205723-cc843c4be892/go.mod h1:yPkX5Q6CsxTFMjQQDJwzeNmUUF5NUGGbrDsv9wTb8cw= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v0.16.1 h1:IVQwpTGNRRIHafnTs2dQLIk4ENtneRIEEJWOVDqz99o= -github.com/hashicorp/go-hclog v0.16.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.2.1 h1:YQsLlGDJgwhXFpucSPyVbCBviQtjlHv3jLTlp8YmtEw= +github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -162,14 +162,15 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= -github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -217,8 +218,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -286,8 +287,8 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY= -golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 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= @@ -334,8 +335,12 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= @@ -445,8 +450,9 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -467,8 +473,9 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/control-plane/go.mod b/control-plane/go.mod index a60079cc53..b0d67085ec 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -10,8 +10,8 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af65262de8 github.com/hashicorp/consul-server-connection-manager v0.1.0 - github.com/hashicorp/consul/api v1.10.1-0.20230203155153-2f149d60ccbf - github.com/hashicorp/consul/sdk v0.13.0 + github.com/hashicorp/consul/api v1.20.0 + github.com/hashicorp/consul/sdk v0.13.1 github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f github.com/hashicorp/go-hclog v1.2.2 github.com/hashicorp/go-multierror v1.1.1 @@ -153,6 +153,4 @@ require ( sigs.k8s.io/yaml v1.2.0 // indirect ) -replace github.com/hashicorp/consul/sdk => github.com/hashicorp/consul/sdk v0.4.1-0.20221021205723-cc843c4be892 - go 1.20 diff --git a/control-plane/go.sum b/control-plane/go.sum index b4ed16e150..06dfbb8b10 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -353,12 +353,13 @@ github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af6526 github.com/hashicorp/consul-server-connection-manager v0.1.0 h1:XCweGvMHzra88rYv2zxwwuUOjBUdcQmNKVrnQmt/muo= github.com/hashicorp/consul-server-connection-manager v0.1.0/go.mod h1:XVVlO+Yk7aiRpspiHZkrrFVn9BJIiOPnQIzqytPxGaU= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.10.1-0.20230203155153-2f149d60ccbf h1:vvsHghmX3LyNUaDe7onYKHyDiny+ystdHKIEujbNj4Q= -github.com/hashicorp/consul/api v1.10.1-0.20230203155153-2f149d60ccbf/go.mod h1:c1u8FzGHcavbEtRW/p1YditvfMgn4QsKNgz2rnCDF7c= +github.com/hashicorp/consul/api v1.20.0 h1:9IHTjNVSZ7MIwjlW3N3a7iGiykCMDpxZu8jsxFJh0yc= +github.com/hashicorp/consul/api v1.20.0/go.mod h1:nR64eD44KQ59Of/ECwt2vUmIK2DKsDzAwTmwmLl8Wpo= github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3usQMXZF6ziK3SzXU= github.com/hashicorp/consul/proto-public v0.1.0/go.mod h1:vs2KkuWwtjkIgA5ezp4YKPzQp4GitV+q/+PvksrA92k= -github.com/hashicorp/consul/sdk v0.4.1-0.20221021205723-cc843c4be892 h1:jw0NwPmNPr5CxAU04hACdj61JSaJBKZ0FdBo+kwfNp4= -github.com/hashicorp/consul/sdk v0.4.1-0.20221021205723-cc843c4be892/go.mod h1:yPkX5Q6CsxTFMjQQDJwzeNmUUF5NUGGbrDsv9wTb8cw= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.13.1 h1:EygWVWWMczTzXGpO93awkHFzfUka6hLYJ0qhETd+6lY= +github.com/hashicorp/consul/sdk v0.13.1/go.mod h1:SW/mM4LbKfqmMvcFu8v+eiQQ7oitXEFeiBe9StxERb0= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -369,7 +370,6 @@ github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/S github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f h1:7WFMVeuJQp6BkzuTv9O52pzwtEFVUJubKYN+zez8eTI= github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f/go.mod h1:D4eo8/CN92vm9/9UDG+ldX1/fMFa4kpl8qzyTolus8o= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.2.2 h1:ihRI7YFwcZdiSD7SIenIhHfQH3OuDvWerAUBZbeQS3M= github.com/hashicorp/go-hclog v1.2.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -500,7 +500,6 @@ github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZb github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= @@ -895,7 +894,6 @@ golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= From a46657e2725977dc6ee64bb015ebc6288b1cd6cc Mon Sep 17 00:00:00 2001 From: Michael Wilkerson Date: Thu, 9 Mar 2023 11:25:38 -0800 Subject: [PATCH 092/340] added changelog files from release branches --- .changelog/1770.txt | 3 +++ .changelog/1914.txt | 3 +++ .changelog/1934.txt | 3 +++ .changelog/1953.txt | 3 +++ .changelog/1975.txt | 11 +++++++++++ .changelog/1976.txt | 3 +++ 6 files changed, 26 insertions(+) create mode 100644 .changelog/1770.txt create mode 100644 .changelog/1914.txt create mode 100644 .changelog/1934.txt create mode 100644 .changelog/1953.txt create mode 100644 .changelog/1975.txt create mode 100644 .changelog/1976.txt diff --git a/.changelog/1770.txt b/.changelog/1770.txt new file mode 100644 index 0000000000..f8b1c570da --- /dev/null +++ b/.changelog/1770.txt @@ -0,0 +1,3 @@ +```release-note:improvement +control-plane: server ACL Init always appends both, the secrets from the serviceAccount's secretRefs and the one created by the Helm chart, to support Openshift secret handling. +``` \ No newline at end of file diff --git a/.changelog/1914.txt b/.changelog/1914.txt new file mode 100644 index 0000000000..3179f3b0b3 --- /dev/null +++ b/.changelog/1914.txt @@ -0,0 +1,3 @@ +```release-note:bug +control-plane: fix issue where consul-connect-injector acl token was unintentionally being deleted and not recreated when a container was restarted due to a livenessProbe failure. +``` \ No newline at end of file diff --git a/.changelog/1934.txt b/.changelog/1934.txt new file mode 100644 index 0000000000..a8bc41fd50 --- /dev/null +++ b/.changelog/1934.txt @@ -0,0 +1,3 @@ +```release-note:improvement +control-plane: update alpine to 3.17 in the Docker image. +``` \ No newline at end of file diff --git a/.changelog/1953.txt b/.changelog/1953.txt new file mode 100644 index 0000000000..3185330864 --- /dev/null +++ b/.changelog/1953.txt @@ -0,0 +1,3 @@ +```release-note:improvement +helm: update `imageConsulDataplane` value to `hashicorp/consul-dataplane:1.1.0`. +``` \ No newline at end of file diff --git a/.changelog/1975.txt b/.changelog/1975.txt new file mode 100644 index 0000000000..ba26b1ab1e --- /dev/null +++ b/.changelog/1975.txt @@ -0,0 +1,11 @@ +```release-note:security +upgrade to use Go 1.19.6. This resolves vulnerabilities CVE-2022-41724 in crypto/tls and CVE-2022-41723 in net/http. +``` + +```release-note:improvement +cli: update minimum go version for project to 1.19. +``` + +```release-note:improvement +control-plane: update minimum go version for project to 1.19. +``` \ No newline at end of file diff --git a/.changelog/1976.txt b/.changelog/1976.txt new file mode 100644 index 0000000000..65024aa6f9 --- /dev/null +++ b/.changelog/1976.txt @@ -0,0 +1,3 @@ +```release-note:security +upgrade to use Go 1.19.6. This resolves vulnerabilities CVE-2022-41724 in crypto/tls and CVE-2022-41723 in net/http. +``` \ No newline at end of file From d13953b2a2873b38ee3f64d38387a354707e8a5a Mon Sep 17 00:00:00 2001 From: Michael Wilkerson Date: Thu, 9 Mar 2023 11:25:49 -0800 Subject: [PATCH 093/340] copied release notes from release branches --- CHANGELOG.md | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81edcde7c2..e0df700efa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,31 @@ -## UNRELEASED +## 1.0.5 (March 9, 2023) + +SECURITY: + +* upgrade to use Go 1.19.6. This resolves vulnerabilities CVE-2022-41724 in crypto/tls and CVE-2022-41723 in net/http. [[GH-1976](https://github.com/hashicorp/consul-k8s/issues/1976)] + +IMPROVEMENTS: + +* control-plane: server ACL Init always appends both, the secrets from the serviceAccount's secretRefs and the one created by the Helm chart, to support Openshift secret handling. [[GH-1770](https://github.com/hashicorp/consul-k8s/issues/1770)] +* control-plane: update alpine to 3.17 in the Docker image. [[GH-1934](https://github.com/hashicorp/consul-k8s/issues/1934)] +* helm: update `imageConsulDataplane` value to `hashicorp/consul-dataplane:1.1.0`. [[GH-1953](https://github.com/hashicorp/consul-k8s/issues/1953)] + +## 0.49.5 (March 9, 2023) + +SECURITY: + +* upgrade to use Go 1.19.6. This resolves vulnerabilities CVE-2022-41724 in crypto/tls and CVE-2022-41723 in net/http. [[GH-1975](https://github.com/hashicorp/consul-k8s/issues/1975)] + +IMPROVEMENTS: + +* cli: update minimum go version for project to 1.19. [[GH-1975](https://github.com/hashicorp/consul-k8s/issues/1975)] +* control-plane: server ACL Init always appends both, the secrets from the serviceAccount's secretRefs and the one created by the Helm chart, to support Openshift secret handling. [[GH-1770](https://github.com/hashicorp/consul-k8s/issues/1770)] +* control-plane: update alpine to 3.17 in the Docker image. [[GH-1934](https://github.com/hashicorp/consul-k8s/issues/1934)] +* control-plane: update minimum go version for project to 1.19. [[GH-1975](https://github.com/hashicorp/consul-k8s/issues/1975)] + +BUG FIXES: + +* control-plane: fix issue where consul-connect-injector acl token was unintentionally being deleted and not recreated when a container was restarted due to a livenessProbe failure. [[GH-1914](https://github.com/hashicorp/consul-k8s/issues/1914)] ## 1.1.0 (February 27, 2023) From 6fbb20ff22906cd36a158f69eb8ea7b377660707 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Mon, 13 Mar 2023 10:03:38 -0700 Subject: [PATCH 094/340] added more retries for tests (#2006) --- acceptance/framework/consul/cli_cluster.go | 2 +- acceptance/framework/consul/helm_cluster.go | 75 +++++++++++-------- acceptance/framework/helpers/helpers.go | 2 +- .../framework/portforward/port_forward.go | 2 +- 4 files changed, 45 insertions(+), 36 deletions(-) diff --git a/acceptance/framework/consul/cli_cluster.go b/acceptance/framework/consul/cli_cluster.go index 271f8f2eae..0a3e688951 100644 --- a/acceptance/framework/consul/cli_cluster.go +++ b/acceptance/framework/consul/cli_cluster.go @@ -252,7 +252,7 @@ func (c *CLICluster) SetupConsulClient(t *testing.T, secure bool) (*api.Client, c.logger) // Retry creating the port forward since it can fail occasionally. - retry.RunWith(&retry.Counter{Wait: 1 * time.Second, Count: 3}, t, func(r *retry.R) { + retry.RunWith(&retry.Counter{Wait: 3 * time.Second, Count: 60}, t, func(r *retry.R) { // NOTE: It's okay to pass in `t` to ForwardPortE despite being in a retry // because we're using ForwardPortE (not ForwardPort) so the `t` won't // get used to fail the test, just for logging. diff --git a/acceptance/framework/consul/helm_cluster.go b/acceptance/framework/consul/helm_cluster.go index eab1ba2904..3f4d31173d 100644 --- a/acceptance/framework/consul/helm_cluster.go +++ b/acceptance/framework/consul/helm_cluster.go @@ -142,8 +142,11 @@ func (h *HelmCluster) Destroy(t *testing.T) { h.helmOptions.ExtraArgs = map[string][]string{ "--wait": nil, } - err := helm.DeleteE(t, h.helmOptions, h.releaseName, false) - require.NoError(t, err) + + retry.RunWith(&retry.Counter{Wait: 1 * time.Second, Count: 15}, t, func(r *retry.R) { + err := helm.DeleteE(t, h.helmOptions, h.releaseName, false) + require.NoError(r, err) + }) // Retry because sometimes certain resources (like PVC) take time to delete // in cloud providers. @@ -324,22 +327,25 @@ func (h *HelmCluster) SetupConsulClient(t *testing.T, secure bool) (client *api. if h.ACLToken != "" { config.Token = h.ACLToken } else { - // Get the ACL token. First, attempt to read it from the bootstrap token (this will be true in primary Consul servers). - // If the bootstrap token doesn't exist, it means we are running against a secondary cluster - // and will try to read the replication token from the federation secret. - // In secondary servers, we don't create a bootstrap token since ACLs are only bootstrapped in the primary. - // Instead, we provide a replication token that serves the role of the bootstrap token. - aclSecret, err := h.kubernetesClient.CoreV1().Secrets(namespace).Get(context.Background(), h.releaseName+"-consul-bootstrap-acl-token", metav1.GetOptions{}) - if err != nil && errors.IsNotFound(err) { - federationSecret := fmt.Sprintf("%s-consul-federation", h.releaseName) - aclSecret, err = h.kubernetesClient.CoreV1().Secrets(namespace).Get(context.Background(), federationSecret, metav1.GetOptions{}) - require.NoError(t, err) - config.Token = string(aclSecret.Data["replicationToken"]) - } else if err == nil { - config.Token = string(aclSecret.Data["token"]) - } else { - require.NoError(t, err) - } + retry.RunWith(&retry.Counter{Wait: 1 * time.Second, Count: 600}, t, func(r *retry.R) { + // Get the ACL token. First, attempt to read it from the bootstrap token (this will be true in primary Consul servers). + // If the bootstrap token doesn't exist, it means we are running against a secondary cluster + // and will try to read the replication token from the federation secret. + // In secondary servers, we don't create a bootstrap token since ACLs are only bootstrapped in the primary. + // Instead, we provide a replication token that serves the role of the bootstrap token. + aclSecret, err := h.kubernetesClient.CoreV1().Secrets(namespace).Get(context.Background(), h.releaseName+"-consul-bootstrap-acl-token", metav1.GetOptions{}) + if err != nil && errors.IsNotFound(err) { + federationSecret := fmt.Sprintf("%s-consul-federation", h.releaseName) + aclSecret, err = h.kubernetesClient.CoreV1().Secrets(namespace).Get(context.Background(), federationSecret, metav1.GetOptions{}) + require.NoError(r, err) + config.Token = string(aclSecret.Data["replicationToken"]) + } else if err == nil { + config.Token = string(aclSecret.Data["token"]) + } else { + require.NoError(r, err) + } + }) + } } @@ -524,21 +530,24 @@ func defaultValues() map[string]string { } func CreateK8sSecret(t *testing.T, client kubernetes.Interface, cfg *config.TestConfig, namespace, secretName, secretKey, secret string) { - _, err := client.CoreV1().Secrets(namespace).Get(context.Background(), secretName, metav1.GetOptions{}) - if errors.IsNotFound(err) { - _, err := client.CoreV1().Secrets(namespace).Create(context.Background(), &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: secretName, - }, - StringData: map[string]string{ - secretKey: secret, - }, - Type: corev1.SecretTypeOpaque, - }, metav1.CreateOptions{}) - require.NoError(t, err) - } else { - require.NoError(t, err) - } + + retry.RunWith(&retry.Counter{Wait: 1 * time.Second, Count: 15}, t, func(r *retry.R) { + _, err := client.CoreV1().Secrets(namespace).Get(context.Background(), secretName, metav1.GetOptions{}) + if errors.IsNotFound(err) { + _, err := client.CoreV1().Secrets(namespace).Create(context.Background(), &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + }, + StringData: map[string]string{ + secretKey: secret, + }, + Type: corev1.SecretTypeOpaque, + }, metav1.CreateOptions{}) + require.NoError(r, err) + } else { + require.NoError(r, err) + } + }) helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { _ = client.CoreV1().Secrets(namespace).Delete(context.Background(), secretName, metav1.DeleteOptions{}) diff --git a/acceptance/framework/helpers/helpers.go b/acceptance/framework/helpers/helpers.go index 2c1a6ebf47..5f99a682ae 100644 --- a/acceptance/framework/helpers/helpers.go +++ b/acceptance/framework/helpers/helpers.go @@ -35,7 +35,7 @@ func CheckForPriorInstallations(t *testing.T, client kubernetes.Interface, optio // Check if there's an existing cluster and fail if there is one. // We may need to retry since this is the first command run once the Kube // cluster is created and sometimes the API server returns errors. - retry.RunWith(&retry.Counter{Wait: 1 * time.Second, Count: 3}, t, func(r *retry.R) { + retry.RunWith(&retry.Counter{Wait: 1 * time.Second, Count: 15}, t, func(r *retry.R) { var err error // NOTE: It's okay to pass in `t` to RunHelmCommandAndGetOutputE despite being in a retry // because we're using RunHelmCommandAndGetOutputE (not RunHelmCommandAndGetOutput) so the `t` won't diff --git a/acceptance/framework/portforward/port_forward.go b/acceptance/framework/portforward/port_forward.go index 97bf3f7856..30f4aed157 100644 --- a/acceptance/framework/portforward/port_forward.go +++ b/acceptance/framework/portforward/port_forward.go @@ -24,7 +24,7 @@ func CreateTunnelToResourcePort(t *testing.T, resourceName string, remotePort in logger) // Retry creating the port forward since it can fail occasionally. - retry.RunWith(&retry.Counter{Wait: 1 * time.Second, Count: 3}, t, func(r *retry.R) { + retry.RunWith(&retry.Counter{Wait: 3 * time.Second, Count: 60}, t, func(r *retry.R) { // NOTE: It's okay to pass in `t` to ForwardPortE despite being in a retry // because we're using ForwardPortE (not ForwardPort) so the `t` won't // get used to fail the test, just for logging. From e23dbb65afe8226d756de4510016ac4a7a6f469f Mon Sep 17 00:00:00 2001 From: Andrew Stucki Date: Tue, 14 Mar 2023 10:28:07 -0400 Subject: [PATCH 095/340] Add SNI skip for client node configuration --- .../api-gateway-controller-deployment.yaml | 2 +- .../api-gateway-controller-deployment.bats | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/charts/consul/templates/api-gateway-controller-deployment.yaml b/charts/consul/templates/api-gateway-controller-deployment.yaml index a9f1806cc8..86517d7140 100644 --- a/charts/consul/templates/api-gateway-controller-deployment.yaml +++ b/charts/consul/templates/api-gateway-controller-deployment.yaml @@ -112,7 +112,7 @@ spec: {{- end }} - name: CONSUL_HTTP_SSL value: "{{ .Values.global.tls.enabled }}" - {{- if and .Values.externalServers.enabled .Values.externalServers.tlsServerName }} + {{- if and (not .Values.client.enabled) .Values.externalServers.enabled .Values.externalServers.tlsServerName }} - name: CONSUL_TLS_SERVER_NAME value: {{ .Values.externalServers.tlsServerName }} {{- end }} diff --git a/charts/consul/test/unit/api-gateway-controller-deployment.bats b/charts/consul/test/unit/api-gateway-controller-deployment.bats index 2dbcb9e0f1..d4db4b373b 100755 --- a/charts/consul/test/unit/api-gateway-controller-deployment.bats +++ b/charts/consul/test/unit/api-gateway-controller-deployment.bats @@ -2,6 +2,16 @@ load _helpers +testOnly() { + if [ "$BATS_TEST_DESCRIPTION" != "$1" ]; then + skip + fi +} + +setup() { + testOnly "apiGateway/Deployment: CONSUL_TLS_SERVER_NAME will not be set for when clients are used" +} + @test "apiGateway/Deployment: disabled by default" { cd `chart_dir` assert_empty helm template \ @@ -1418,6 +1428,24 @@ load _helpers [ "${actual}" = "true" ] } +@test "apiGateway/Deployment: CONSUL_TLS_SERVER_NAME will not be set for when clients are used" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/api-gateway-controller-deployment.yaml \ + --set 'apiGateway.enabled=true' \ + --set 'apiGateway.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'externalServers.enabled=true' \ + --set 'externalServers.hosts[0]=external-consul.host' \ + --set 'externalServers.httpsPort=8501' \ + --set 'externalServers.tlsServerName=hashi' \ + --set 'client.enabled=true' \ + --set 'server.enabled=false' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[] | select (.name == "api-gateway-controller") | .env[] | select(.name == "CONSUL_TLS_SERVER_NAME")' | tee /dev/stderr) + [ "${actual}" = "" ] +} + #-------------------------------------------------------------------- # Admin Partitions From 730ab263c8d791011901208c36b8d467ce382135 Mon Sep 17 00:00:00 2001 From: Andrew Stucki Date: Tue, 14 Mar 2023 10:34:56 -0400 Subject: [PATCH 096/340] Add changelog --- .changelog/2013.txt | 3 +++ .../test/unit/api-gateway-controller-deployment.bats | 10 ---------- 2 files changed, 3 insertions(+), 10 deletions(-) create mode 100644 .changelog/2013.txt diff --git a/.changelog/2013.txt b/.changelog/2013.txt new file mode 100644 index 0000000000..056253a5d2 --- /dev/null +++ b/.changelog/2013.txt @@ -0,0 +1,3 @@ +```release-note:bug +api-gateway: fix issue where specifying an external server SNI name while using client nodes resulted in a TLS verification error. +``` \ No newline at end of file diff --git a/charts/consul/test/unit/api-gateway-controller-deployment.bats b/charts/consul/test/unit/api-gateway-controller-deployment.bats index d4db4b373b..880586ab43 100755 --- a/charts/consul/test/unit/api-gateway-controller-deployment.bats +++ b/charts/consul/test/unit/api-gateway-controller-deployment.bats @@ -2,16 +2,6 @@ load _helpers -testOnly() { - if [ "$BATS_TEST_DESCRIPTION" != "$1" ]; then - skip - fi -} - -setup() { - testOnly "apiGateway/Deployment: CONSUL_TLS_SERVER_NAME will not be set for when clients are used" -} - @test "apiGateway/Deployment: disabled by default" { cd `chart_dir` assert_empty helm template \ From 1483c1775a0dcf56f61023e14329aecf432c2686 Mon Sep 17 00:00:00 2001 From: David Yu Date: Wed, 15 Mar 2023 13:10:25 -0700 Subject: [PATCH 097/340] values.yaml - set default connect inject init cpu resource limits to `null` to increase service registration times (#2008) * Update values.yaml --- .changelog/2008.txt | 3 ++ .../test/unit/connect-inject-deployment.bats | 5 +-- charts/consul/values.yaml | 34 ++++++++++++++----- 3 files changed, 29 insertions(+), 13 deletions(-) create mode 100644 .changelog/2008.txt diff --git a/.changelog/2008.txt b/.changelog/2008.txt new file mode 100644 index 0000000000..ba8bb5fa25 --- /dev/null +++ b/.changelog/2008.txt @@ -0,0 +1,3 @@ +```release-note:improvement +helm: Set default `limits.cpu` resource setting to `null` for `consul-connect-inject-init` container to speed up registration times when onboarding services onto the mesh during the init container lifecycle. +``` diff --git a/charts/consul/test/unit/connect-inject-deployment.bats b/charts/consul/test/unit/connect-inject-deployment.bats index 9da24a7568..90166daca2 100755 --- a/charts/consul/test/unit/connect-inject-deployment.bats +++ b/charts/consul/test/unit/connect-inject-deployment.bats @@ -986,10 +986,7 @@ load _helpers local actual=$(echo "$cmd" | yq 'any(contains("-init-container-memory-limit=150Mi"))' | tee /dev/stderr) [ "${actual}" = "true" ] - - local actual=$(echo "$cmd" | - yq 'any(contains("-init-container-cpu-limit=50m"))' | tee /dev/stderr) - [ "${actual}" = "true" ] + } @test "connectInject/Deployment: can set init container resources" { diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 6fca91244b..db63e04804 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -2130,15 +2130,22 @@ connectInject: # @type: string annotations: null - # The resource settings for connect inject pods. - # @recurse: false + # The resource settings for connect inject pods. The defaults, are optimized for getting started worklows on developer deployments. The settings should be tweaked for production deployments. # @type: map resources: requests: + # Recommended production default: 500Mi + # @type: string memory: "50Mi" + # Recommended production default: 250m + # @type: string cpu: "50m" limits: + # Recommended production default: 500Mi + # @type: string memory: "50Mi" + # Recommended production default: 250m + # @type: string cpu: "50m" # Sets the failurePolicy for the mutating webhook. By default this will cause pods not part of the consul installation to fail scheduling while the webhook @@ -2306,31 +2313,40 @@ connectInject: # @type: map resources: requests: - # Recommended default: 100Mi + # Recommended production default: 100Mi # @type: string memory: null - # Recommended default: 100m + # Recommended production default: 100m # @type: string cpu: null limits: - # Recommended default: 100Mi + # Recommended production default: 100Mi # @type: string memory: null - # Recommended default: 100m + # Recommended production default: 100m # @type: string cpu: null - # The resource settings for the Connect injected init container. - # @recurse: false + # The resource settings for the Connect injected init container. If null, the resources + # won't be set for the initContainer. The defaults are optimized for developer instances of + # Kubernetes, however they should be tweaked with the recommended defaults as shown below to speed up service registration times. # @type: map initContainer: resources: requests: + # Recommended production default: 150Mi + # @type: string memory: "25Mi" + # Recommended production default: 250m + # @type: string cpu: "50m" limits: + # Recommended production default: 150Mi + # @type: string memory: "150Mi" - cpu: "50m" + # Recommended production default: 500m + # @type: string + cpu: null # [Mesh Gateways](https://developer.hashicorp.com/consul/docs/connect/gateways/mesh-gateway) enable Consul Connect to work across Consul datacenters. meshGateway: From 9de3ff9354cd7f0578554412d037dcad551732e4 Mon Sep 17 00:00:00 2001 From: Luke Kysow <1034429+lkysow@users.noreply.github.com> Date: Mon, 20 Mar 2023 14:47:44 -0700 Subject: [PATCH 098/340] Remove client.enabled requirement in docs (#2027) Clients are not required for ingress/terminating gateways. --- charts/consul/values.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index db63e04804..3ccac77c22 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -2565,8 +2565,7 @@ meshGateway: # for a specific gateway. # Requirements: consul >= 1.8.0 ingressGateways: - # Enable ingress gateway deployment. Requires `connectInject.enabled=true` - # and `client.enabled=true`. + # Enable ingress gateway deployment. Requires `connectInject.enabled=true`. enabled: false # Defaults sets default values for all gateway fields. With the exception @@ -2732,8 +2731,7 @@ ingressGateways: # for a specific gateway. # Requirements: consul >= 1.8.0 terminatingGateways: - # Enable terminating gateway deployment. Requires `connectInject.enabled=true` - # and `client.enabled=true`. + # Enable terminating gateway deployment. Requires `connectInject.enabled=true`. enabled: false # Defaults sets default values for all gateway fields. With the exception From 7d098bdd2936995be5b7920114726e5b039e6b7d Mon Sep 17 00:00:00 2001 From: Luke Kysow <1034429+lkysow@users.noreply.github.com> Date: Tue, 21 Mar 2023 11:19:39 -0700 Subject: [PATCH 099/340] Remove website prefix from generated docs (#2028) Website has linting that errors when links have the developer.hashicorp.com prefix. --- hack/helm-reference-gen/main.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hack/helm-reference-gen/main.go b/hack/helm-reference-gen/main.go index 5f23143caa..ae9b17610e 100644 --- a/hack/helm-reference-gen/main.go +++ b/hack/helm-reference-gen/main.go @@ -159,11 +159,14 @@ func GenerateDocs(yamlStr string) (string, error) { return "", err } - enterpriseSubst := strings.ReplaceAll(strings.Join(children, "\n\n"), "[Enterprise Only]", "") + docsStr := strings.Join(children, "\n\n") + docsStr = strings.ReplaceAll(docsStr, "[Enterprise Only]", "") + // Remove https://developer.hashicorp.com prefix from links because docs linting requires it. + docsStr = strings.ReplaceAll(docsStr, "https://developer.hashicorp.com/", "/") // Add table of contents. toc := generateTOC(node) - return toc + "\n\n" + enterpriseSubst + "\n", nil + return toc + "\n\n" + docsStr + "\n", nil } // Parse parses yamlStr into a tree of DocNode's. From 63440712a8c06436f4ba52fb3e5d98a693b199ab Mon Sep 17 00:00:00 2001 From: Sarah Alsmiller Date: Fri, 24 Mar 2023 11:12:15 -0500 Subject: [PATCH 100/340] update ACLs, add operator.write permission --- charts/consul/templates/api-gateway-controller-clusterrole.yaml | 1 + control-plane/subcommand/server-acl-init/rules.go | 1 + 2 files changed, 2 insertions(+) diff --git a/charts/consul/templates/api-gateway-controller-clusterrole.yaml b/charts/consul/templates/api-gateway-controller-clusterrole.yaml index eac2bd1f69..2307d6d113 100644 --- a/charts/consul/templates/api-gateway-controller-clusterrole.yaml +++ b/charts/consul/templates/api-gateway-controller-clusterrole.yaml @@ -85,6 +85,7 @@ rules: resources: - namespaces verbs: + - create - get - list - watch diff --git a/control-plane/subcommand/server-acl-init/rules.go b/control-plane/subcommand/server-acl-init/rules.go index ee6ae41e40..39efe3ee6b 100644 --- a/control-plane/subcommand/server-acl-init/rules.go +++ b/control-plane/subcommand/server-acl-init/rules.go @@ -146,6 +146,7 @@ func (c *Command) apiGatewayControllerRules() (string, error) { apiGatewayRulesTpl := `{{- if .EnablePartitions }} partition "{{ .PartitionName }}" { mesh = "write" + operator = "write" acl = "write" {{- else }} operator = "write" From 3783178a8da65b6d2686acc44bdc0921afb1d683 Mon Sep 17 00:00:00 2001 From: sarahalsmiller <100602640+sarahalsmiller@users.noreply.github.com> Date: Fri, 24 Mar 2023 18:14:27 -0500 Subject: [PATCH 101/340] Update api-gateway-controller-clusterrole.yaml --- charts/consul/templates/api-gateway-controller-clusterrole.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/charts/consul/templates/api-gateway-controller-clusterrole.yaml b/charts/consul/templates/api-gateway-controller-clusterrole.yaml index 2307d6d113..eac2bd1f69 100644 --- a/charts/consul/templates/api-gateway-controller-clusterrole.yaml +++ b/charts/consul/templates/api-gateway-controller-clusterrole.yaml @@ -85,7 +85,6 @@ rules: resources: - namespaces verbs: - - create - get - list - watch From 7ec6ec259c72bb579f2e0a6a973673d7d740573b Mon Sep 17 00:00:00 2001 From: Sarah Alsmiller Date: Sat, 25 Mar 2023 09:28:44 -0500 Subject: [PATCH 102/340] update unit tests --- .../subcommand/server-acl-init/rules.go | 6 +++-- .../subcommand/server-acl-init/rules_test.go | 27 ++++++++++++++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/control-plane/subcommand/server-acl-init/rules.go b/control-plane/subcommand/server-acl-init/rules.go index 39efe3ee6b..84e49801da 100644 --- a/control-plane/subcommand/server-acl-init/rules.go +++ b/control-plane/subcommand/server-acl-init/rules.go @@ -146,14 +146,16 @@ func (c *Command) apiGatewayControllerRules() (string, error) { apiGatewayRulesTpl := `{{- if .EnablePartitions }} partition "{{ .PartitionName }}" { mesh = "write" - operator = "write" acl = "write" + operator = "write" {{- else }} operator = "write" acl = "write" {{- end }} + {{- if .EnableNamespaces }} namespace_prefix "" { + policy = "write" {{- end }} service_prefix "" { policy = "write" @@ -168,7 +170,7 @@ namespace_prefix "" { {{- if .EnablePartitions }} } {{- end }} - ` +` return c.renderRules(apiGatewayRulesTpl) } diff --git a/control-plane/subcommand/server-acl-init/rules_test.go b/control-plane/subcommand/server-acl-init/rules_test.go index 22e63ed0ce..5452712936 100644 --- a/control-plane/subcommand/server-acl-init/rules_test.go +++ b/control-plane/subcommand/server-acl-init/rules_test.go @@ -143,6 +143,7 @@ func TestAPIGatewayControllerRules(t *testing.T) { cases := []struct { Name string EnableNamespaces bool + Partition string Expected string }{ { @@ -165,6 +166,7 @@ acl = "write" operator = "write" acl = "write" namespace_prefix "" { + policy = "write" service_prefix "" { policy = "write" intentions = "write" @@ -172,6 +174,27 @@ namespace_prefix "" { node_prefix "" { policy = "read" } +}`, + }, + { + Name: "Namespaces are enabled, partitions enabled", + EnableNamespaces: true, + Partition: "Default", + Expected: ` +partition "Default" { + mesh = "write" + acl = "write" + operator = "write" +namespace_prefix "" { + policy = "write" + service_prefix "" { + policy = "write" + intentions = "write" + } + node_prefix "" { + policy = "read" + } +} }`, }, } @@ -180,7 +203,9 @@ namespace_prefix "" { t.Run(tt.Name, func(t *testing.T) { cmd := Command{ flagEnableNamespaces: tt.EnableNamespaces, - consulFlags: &flags.ConsulFlags{}, + consulFlags: &flags.ConsulFlags{ + Partition: tt.Partition, + }, } meshGatewayRules, err := cmd.apiGatewayControllerRules() From d7188836e41c58ec7c8037f51853ced83fe56e33 Mon Sep 17 00:00:00 2001 From: Sarah Alsmiller Date: Sat, 25 Mar 2023 13:06:01 -0500 Subject: [PATCH 103/340] added changelog --- .changelog/2029.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/2029.txt diff --git a/.changelog/2029.txt b/.changelog/2029.txt new file mode 100644 index 0000000000..663b7c8142 --- /dev/null +++ b/.changelog/2029.txt @@ -0,0 +1,3 @@ +```release-note:bug +api-gateway: fix issue where when adminPartitions and ACLs are enabled, API Gateway Controller is unable to create a new namespace in Consul +``` \ No newline at end of file From 5d0a6adffff2408f2f05f0b35e0004b938df1c92 Mon Sep 17 00:00:00 2001 From: Sarah Alsmiller Date: Sat, 25 Mar 2023 13:06:19 -0500 Subject: [PATCH 104/340] add changelog --- .changelog/2029.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changelog/2029.txt b/.changelog/2029.txt index 663b7c8142..c864419eba 100644 --- a/.changelog/2029.txt +++ b/.changelog/2029.txt @@ -1,3 +1,3 @@ ```release-note:bug -api-gateway: fix issue where when adminPartitions and ACLs are enabled, API Gateway Controller is unable to create a new namespace in Consul +api-gateway: fix ACL issue where when adminPartitions and ACLs are enabled, API Gateway Controller is unable to create a new namespace in Consul ``` \ No newline at end of file From 74291afc9a3ca28533e566eb76dc003ac76ee727 Mon Sep 17 00:00:00 2001 From: Sarah Alsmiller Date: Mon, 27 Mar 2023 16:40:57 -0500 Subject: [PATCH 105/340] removed operator:write after proving namespace:write sufficently solved the bug --- control-plane/subcommand/server-acl-init/rules.go | 1 - control-plane/subcommand/server-acl-init/rules_test.go | 1 - 2 files changed, 2 deletions(-) diff --git a/control-plane/subcommand/server-acl-init/rules.go b/control-plane/subcommand/server-acl-init/rules.go index 84e49801da..02831dc4de 100644 --- a/control-plane/subcommand/server-acl-init/rules.go +++ b/control-plane/subcommand/server-acl-init/rules.go @@ -147,7 +147,6 @@ func (c *Command) apiGatewayControllerRules() (string, error) { partition "{{ .PartitionName }}" { mesh = "write" acl = "write" - operator = "write" {{- else }} operator = "write" acl = "write" diff --git a/control-plane/subcommand/server-acl-init/rules_test.go b/control-plane/subcommand/server-acl-init/rules_test.go index 5452712936..622ed341a8 100644 --- a/control-plane/subcommand/server-acl-init/rules_test.go +++ b/control-plane/subcommand/server-acl-init/rules_test.go @@ -184,7 +184,6 @@ namespace_prefix "" { partition "Default" { mesh = "write" acl = "write" - operator = "write" namespace_prefix "" { policy = "write" service_prefix "" { From f76c1dcb31031b86e5b353de87710b739c1a8dbb Mon Sep 17 00:00:00 2001 From: Ronald Ekambi Date: Wed, 15 Mar 2023 08:41:26 -0400 Subject: [PATCH 106/340] add copyright headers to file + config file --- .circleci/config.yml | 3 +++ .copywrite.hcl | 14 ++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 3 +++ .golangci.yml | 3 +++ .release/ci.hcl | 3 +++ .release/release-metadata.hcl | 3 +++ .release/security-scan.hcl | 3 +++ acceptance/framework/cli/cli.go | 3 +++ acceptance/framework/config/config.go | 3 +++ acceptance/framework/config/config_test.go | 3 +++ acceptance/framework/connhelper/connect_helper.go | 3 +++ acceptance/framework/consul/cli_cluster.go | 3 +++ acceptance/framework/consul/cluster.go | 3 +++ acceptance/framework/consul/helm_cluster.go | 3 +++ acceptance/framework/consul/helm_cluster_test.go | 3 +++ .../environment/cni-kind/custom-resources.yaml | 3 +++ .../environment/cni-kind/tigera-operator.yaml | 3 +++ acceptance/framework/environment/environment.go | 3 +++ acceptance/framework/flags/flags.go | 3 +++ acceptance/framework/flags/flags_test.go | 3 +++ acceptance/framework/helpers/helpers.go | 3 +++ acceptance/framework/helpers/helpers_test.go | 3 +++ acceptance/framework/k8s/debug.go | 3 +++ acceptance/framework/k8s/deploy.go | 3 +++ acceptance/framework/k8s/helpers.go | 3 +++ acceptance/framework/k8s/kubectl.go | 3 +++ acceptance/framework/logger/logger.go | 3 +++ acceptance/framework/portforward/port_forward.go | 3 +++ acceptance/framework/suite/suite.go | 3 +++ acceptance/framework/vault/helpers.go | 3 +++ acceptance/framework/vault/vault_cluster.go | 3 +++ acceptance/tests/basic/basic_test.go | 3 +++ acceptance/tests/basic/main_test.go | 3 +++ acceptance/tests/cli/cli_install_test.go | 3 +++ acceptance/tests/cli/cli_upgrade_test.go | 3 +++ acceptance/tests/cli/main_test.go | 3 +++ .../config_entries_namespaces_test.go | 3 +++ .../tests/config-entries/config_entries_test.go | 3 +++ acceptance/tests/config-entries/main_test.go | 3 +++ .../tests/connect/connect_external_servers_test.go | 3 +++ .../connect/connect_inject_namespaces_test.go | 3 +++ acceptance/tests/connect/connect_inject_test.go | 3 +++ acceptance/tests/connect/main_test.go | 3 +++ acceptance/tests/consul-dns/consul_dns_test.go | 3 +++ acceptance/tests/consul-dns/main_test.go | 3 +++ acceptance/tests/example/example_test.go | 3 +++ acceptance/tests/example/main_test.go | 3 +++ .../fixtures/bases/crds-oss/ingressgateway.yaml | 3 +++ .../fixtures/bases/crds-oss/kustomization.yaml | 3 +++ acceptance/tests/fixtures/bases/crds-oss/mesh.yaml | 3 +++ .../fixtures/bases/crds-oss/proxydefaults.yaml | 3 +++ .../fixtures/bases/crds-oss/servicedefaults.yaml | 3 +++ .../fixtures/bases/crds-oss/serviceexports.yaml | 3 +++ .../fixtures/bases/crds-oss/serviceintentions.yaml | 3 +++ .../fixtures/bases/crds-oss/serviceresolver.yaml | 3 +++ .../fixtures/bases/crds-oss/servicerouter.yaml | 3 +++ .../fixtures/bases/crds-oss/servicesplitter.yaml | 3 +++ .../bases/crds-oss/terminatinggateway.yaml | 3 +++ .../exportedservices-default.yaml | 3 +++ .../exportedservices-default/kustomization.yaml | 3 +++ .../exportedservices-secondary.yaml | 3 +++ .../exportedservices-secondary/kustomization.yaml | 3 +++ .../tests/fixtures/bases/intention/intention.yaml | 3 +++ .../fixtures/bases/intention/kustomization.yaml | 3 +++ .../fixtures/bases/mesh-gateway/kustomization.yaml | 3 +++ .../fixtures/bases/mesh-gateway/proxydefaults.yaml | 3 +++ .../fixtures/bases/mesh-peering/kustomization.yaml | 3 +++ .../fixtures/bases/mesh-peering/meshpeering.yaml | 3 +++ .../multiport-app/anyuid-scc-rolebinding.yaml | 3 +++ .../fixtures/bases/multiport-app/deployment.yaml | 3 +++ .../bases/multiport-app/kustomization.yaml | 3 +++ .../multiport-app/privileged-scc-rolebinding.yaml | 3 +++ .../bases/multiport-app/psp-rolebinding.yaml | 3 +++ .../tests/fixtures/bases/multiport-app/secret.yaml | 3 +++ .../fixtures/bases/multiport-app/service.yaml | 3 +++ .../bases/multiport-app/serviceaccount.yaml | 3 +++ .../fixtures/bases/peering/peering-acceptor.yaml | 3 +++ .../fixtures/bases/peering/peering-dialer.yaml | 3 +++ .../static-client/anyuid-scc-rolebinding.yaml | 3 +++ .../fixtures/bases/static-client/deployment.yaml | 3 +++ .../bases/static-client/kustomization.yaml | 3 +++ .../static-client/privileged-scc-rolebinding.yaml | 3 +++ .../bases/static-client/psp-rolebinding.yaml | 3 +++ .../fixtures/bases/static-client/service.yaml | 3 +++ .../bases/static-client/serviceaccount.yaml | 3 +++ .../bases/static-metrics-app/deployment.yaml | 3 +++ .../bases/static-metrics-app/kustomization.yaml | 3 +++ .../fixtures/bases/static-metrics-app/service.yaml | 3 +++ .../anyuid-scc-rolebinding.yaml | 3 +++ .../bases/static-server-https/configmap.yaml | 3 +++ .../bases/static-server-https/deployment.yaml | 3 +++ .../bases/static-server-https/kustomization.yaml | 3 +++ .../privileged-scc-rolebinding.yaml | 3 +++ .../bases/static-server-https/psp-rolebinding.yaml | 3 +++ .../bases/static-server-https/service.yaml | 3 +++ .../bases/static-server-https/serviceaccount.yaml | 3 +++ .../static-server/anyuid-scc-rolebinding.yaml | 3 +++ .../fixtures/bases/static-server/deployment.yaml | 3 +++ .../bases/static-server/kustomization.yaml | 3 +++ .../static-server/privileged-scc-rolebinding.yaml | 3 +++ .../bases/static-server/psp-rolebinding.yaml | 3 +++ .../fixtures/bases/static-server/service.yaml | 3 +++ .../bases/static-server/serviceaccount.yaml | 3 +++ .../default-partition-default/kustomization.yaml | 3 +++ .../default-partition-default/patch.yaml | 3 +++ .../default-partition-ns1/kustomization.yaml | 3 +++ .../default-partition-ns1/patch.yaml | 3 +++ .../secondary-partition-default/kustomization.yaml | 3 +++ .../secondary-partition-default/patch.yaml | 3 +++ .../secondary-partition-ns1/kustomization.yaml | 3 +++ .../secondary-partition-ns1/patch.yaml | 3 +++ .../crd-peers/default-namespace/kustomization.yaml | 3 +++ .../cases/crd-peers/default-namespace/patch.yaml | 3 +++ .../cases/crd-peers/default/kustomization.yaml | 3 +++ .../fixtures/cases/crd-peers/default/patch.yaml | 3 +++ .../non-default-namespace/kustomization.yaml | 3 +++ .../crd-peers/non-default-namespace/patch.yaml | 3 +++ .../fixtures/cases/crds-ent/exportedservices.yaml | 3 +++ .../fixtures/cases/crds-ent/kustomization.yaml | 3 +++ .../kustomization.yaml | 3 +++ .../static-client-inject-multiport/patch.yaml | 3 +++ .../cases/static-client-inject/kustomization.yaml | 3 +++ .../fixtures/cases/static-client-inject/patch.yaml | 3 +++ .../static-client-multi-dc/kustomization.yaml | 3 +++ .../cases/static-client-multi-dc/patch.yaml | 3 +++ .../static-client-namespaces/kustomization.yaml | 3 +++ .../cases/static-client-namespaces/patch.yaml | 3 +++ .../kustomization.yaml | 3 +++ .../default-ns-default-partition/patch.yaml | 3 +++ .../default-ns-partition/kustomization.yaml | 3 +++ .../default-ns-partition/patch.yaml | 3 +++ .../ns-default-partition/kustomization.yaml | 3 +++ .../ns-default-partition/patch.yaml | 3 +++ .../ns-partition/kustomization.yaml | 3 +++ .../ns-partition/patch.yaml | 3 +++ .../default-namespace/kustomization.yaml | 3 +++ .../default-namespace/patch.yaml | 3 +++ .../static-client-peers/default/kustomization.yaml | 3 +++ .../cases/static-client-peers/default/patch.yaml | 3 +++ .../non-default-namespace/kustomization.yaml | 3 +++ .../non-default-namespace/patch.yaml | 3 +++ .../cases/static-client-tproxy/kustomization.yaml | 3 +++ .../fixtures/cases/static-client-tproxy/patch.yaml | 3 +++ .../cases/static-server-inject/kustomization.yaml | 3 +++ .../fixtures/cases/static-server-inject/patch.yaml | 3 +++ .../ingress_gateway_namespaces_test.go | 3 +++ .../tests/ingress-gateway/ingress_gateway_test.go | 3 +++ acceptance/tests/ingress-gateway/main_test.go | 3 +++ acceptance/tests/metrics/main_test.go | 3 +++ acceptance/tests/metrics/metrics_test.go | 3 +++ acceptance/tests/partitions/main_test.go | 3 +++ .../tests/partitions/partitions_connect_test.go | 3 +++ .../tests/partitions/partitions_sync_test.go | 3 +++ acceptance/tests/peering/main_test.go | 3 +++ .../peering/peering_connect_namespaces_test.go | 3 +++ acceptance/tests/peering/peering_connect_test.go | 3 +++ acceptance/tests/snapshot-agent/main_test.go | 3 +++ .../snapshot_agent_k8s_secret_test.go | 3 +++ .../snapshot-agent/snapshot_agent_vault_test.go | 3 +++ acceptance/tests/sync/main_test.go | 3 +++ .../tests/sync/sync_catalog_namespaces_test.go | 3 +++ acceptance/tests/sync/sync_catalog_test.go | 3 +++ acceptance/tests/terminating-gateway/common.go | 3 +++ acceptance/tests/terminating-gateway/main_test.go | 3 +++ .../terminating_gateway_destinations_test.go | 3 +++ .../terminating_gateway_namespaces_test.go | 3 +++ .../terminating_gateway_test.go | 3 +++ acceptance/tests/vault/main_test.go | 3 +++ acceptance/tests/vault/vault_namespaces_test.go | 3 +++ acceptance/tests/vault/vault_partitions_test.go | 3 +++ acceptance/tests/vault/vault_test.go | 3 +++ .../tests/vault/vault_tls_auto_reload_test.go | 3 +++ acceptance/tests/vault/vault_wan_fed_test.go | 3 +++ acceptance/tests/wan-federation/main_test.go | 3 +++ .../tests/wan-federation/wan_federation_test.go | 3 +++ charts/consul/Chart.yaml | 3 +++ charts/consul/addons/gen.sh | 3 +++ charts/consul/addons/values/prometheus.yaml | 3 +++ .../api-gateway-controller-clusterrole.yaml | 3 +++ .../api-gateway-controller-clusterrolebinding.yaml | 3 +++ .../api-gateway-controller-deployment.yaml | 3 +++ .../api-gateway-controller-podsecuritypolicy.yaml | 3 +++ .../templates/api-gateway-controller-service.yaml | 3 +++ .../api-gateway-controller-serviceaccount.yaml | 3 +++ .../consul/templates/api-gateway-gatewayclass.yaml | 3 +++ .../templates/api-gateway-gatewayclassconfig.yaml | 3 +++ .../templates/api-gateway-podsecuritypolicy.yaml | 3 +++ .../consul/templates/auth-method-clusterrole.yaml | 3 +++ .../templates/auth-method-clusterrolebinding.yaml | 3 +++ charts/consul/templates/auth-method-secret.yaml | 3 +++ .../templates/auth-method-serviceaccount.yaml | 3 +++ .../consul/templates/client-config-configmap.yaml | 3 +++ charts/consul/templates/client-daemonset.yaml | 3 +++ .../consul/templates/client-podsecuritypolicy.yaml | 3 +++ charts/consul/templates/client-role.yaml | 3 +++ charts/consul/templates/client-rolebinding.yaml | 3 +++ .../client-securitycontextconstraints.yaml | 3 +++ charts/consul/templates/client-serviceaccount.yaml | 3 +++ charts/consul/templates/cni-clusterrole.yaml | 3 +++ .../consul/templates/cni-clusterrolebinding.yaml | 3 +++ charts/consul/templates/cni-daemonset.yaml | 3 +++ .../templates/cni-networkattachmentdefinition.yaml | 3 +++ charts/consul/templates/cni-podsecuritypolicy.yaml | 3 +++ charts/consul/templates/cni-resourcequota.yaml | 3 +++ .../templates/cni-securitycontextconstraints.yaml | 3 +++ charts/consul/templates/cni-serviceaccount.yaml | 3 +++ .../templates/connect-inject-clusterrole.yaml | 3 +++ .../connect-inject-clusterrolebinding.yaml | 3 +++ .../templates/connect-inject-deployment.yaml | 3 +++ .../connect-inject-leader-election-role.yaml | 3 +++ ...connect-inject-leader-election-rolebinding.yaml | 3 +++ ...onnect-inject-mutatingwebhookconfiguration.yaml | 3 +++ .../connect-inject-podsecuritypolicy.yaml | 3 +++ .../consul/templates/connect-inject-service.yaml | 3 +++ .../templates/connect-inject-serviceaccount.yaml | 3 +++ .../connect-injector-disruptionbudget.yaml | 3 +++ charts/consul/templates/crd-exportedservices.yaml | 3 +++ charts/consul/templates/crd-ingressgateways.yaml | 3 +++ charts/consul/templates/crd-meshes.yaml | 3 +++ charts/consul/templates/crd-peeringacceptors.yaml | 3 +++ charts/consul/templates/crd-peeringdialers.yaml | 3 +++ charts/consul/templates/crd-proxydefaults.yaml | 3 +++ charts/consul/templates/crd-servicedefaults.yaml | 3 +++ charts/consul/templates/crd-serviceintentions.yaml | 3 +++ charts/consul/templates/crd-serviceresolvers.yaml | 3 +++ charts/consul/templates/crd-servicerouters.yaml | 3 +++ charts/consul/templates/crd-servicesplitters.yaml | 3 +++ .../consul/templates/crd-terminatinggateways.yaml | 3 +++ .../templates/create-federation-secret-job.yaml | 3 +++ ...create-federation-secret-podsecuritypolicy.yaml | 3 +++ .../templates/create-federation-secret-role.yaml | 3 +++ .../create-federation-secret-rolebinding.yaml | 3 +++ .../create-federation-secret-serviceaccount.yaml | 3 +++ charts/consul/templates/dns-service.yaml | 3 +++ .../consul/templates/enterprise-license-job.yaml | 3 +++ .../enterprise-license-podsecuritypolicy.yaml | 3 +++ .../consul/templates/enterprise-license-role.yaml | 3 +++ .../templates/enterprise-license-rolebinding.yaml | 3 +++ .../enterprise-license-serviceaccount.yaml | 3 +++ .../consul/templates/expose-servers-service.yaml | 3 +++ .../gossip-encryption-autogenerate-job.yaml | 3 +++ ...-encryption-autogenerate-podsecuritypolicy.yaml | 3 +++ .../gossip-encryption-autogenerate-role.yaml | 3 +++ ...gossip-encryption-autogenerate-rolebinding.yaml | 3 +++ ...sip-encryption-autogenerate-serviceaccount.yaml | 3 +++ .../templates/ingress-gateways-deployment.yaml | 3 +++ .../ingress-gateways-podsecuritypolicy.yaml | 3 +++ charts/consul/templates/ingress-gateways-role.yaml | 3 +++ .../templates/ingress-gateways-rolebinding.yaml | 3 +++ .../consul/templates/ingress-gateways-service.yaml | 3 +++ .../templates/ingress-gateways-serviceaccount.yaml | 3 +++ .../consul/templates/mesh-gateway-clusterrole.yaml | 3 +++ .../templates/mesh-gateway-clusterrolebinding.yaml | 3 +++ .../consul/templates/mesh-gateway-deployment.yaml | 3 +++ .../templates/mesh-gateway-podsecuritypolicy.yaml | 3 +++ charts/consul/templates/mesh-gateway-service.yaml | 3 +++ .../templates/mesh-gateway-serviceaccount.yaml | 3 +++ charts/consul/templates/partition-init-job.yaml | 3 +++ .../partition-init-podsecuritypolicy.yaml | 3 +++ charts/consul/templates/partition-init-role.yaml | 3 +++ .../templates/partition-init-rolebinding.yaml | 3 +++ .../templates/partition-init-serviceaccount.yaml | 3 +++ .../consul/templates/partition-name-configmap.yaml | 3 +++ charts/consul/templates/prometheus.yaml | 3 +++ .../templates/server-acl-init-cleanup-job.yaml | 3 +++ .../server-acl-init-cleanup-podsecuritypolicy.yaml | 3 +++ .../templates/server-acl-init-cleanup-role.yaml | 3 +++ .../server-acl-init-cleanup-rolebinding.yaml | 3 +++ .../server-acl-init-cleanup-serviceaccount.yaml | 3 +++ charts/consul/templates/server-acl-init-job.yaml | 3 +++ .../server-acl-init-podsecuritypolicy.yaml | 3 +++ charts/consul/templates/server-acl-init-role.yaml | 3 +++ .../templates/server-acl-init-rolebinding.yaml | 3 +++ .../templates/server-acl-init-serviceaccount.yaml | 3 +++ .../consul/templates/server-config-configmap.yaml | 3 +++ .../consul/templates/server-disruptionbudget.yaml | 3 +++ .../consul/templates/server-podsecuritypolicy.yaml | 3 +++ charts/consul/templates/server-role.yaml | 3 +++ charts/consul/templates/server-rolebinding.yaml | 3 +++ .../server-securitycontextconstraints.yaml | 3 +++ charts/consul/templates/server-service.yaml | 3 +++ charts/consul/templates/server-serviceaccount.yaml | 3 +++ .../templates/server-snapshot-agent-configmap.yaml | 3 +++ charts/consul/templates/server-statefulset.yaml | 3 +++ .../consul/templates/sync-catalog-clusterrole.yaml | 3 +++ .../templates/sync-catalog-clusterrolebinding.yaml | 3 +++ .../consul/templates/sync-catalog-deployment.yaml | 3 +++ .../templates/sync-catalog-podsecuritypolicy.yaml | 3 +++ .../templates/sync-catalog-serviceaccount.yaml | 3 +++ .../templates/terminating-gateways-deployment.yaml | 3 +++ .../terminating-gateways-podsecuritypolicy.yaml | 3 +++ .../templates/terminating-gateways-role.yaml | 3 +++ .../terminating-gateways-rolebinding.yaml | 3 +++ .../templates/terminating-gateways-service.yaml | 3 +++ .../terminating-gateways-serviceaccount.yaml | 3 +++ charts/consul/templates/tests/test-runner.yaml | 3 +++ charts/consul/templates/tls-init-cleanup-job.yaml | 3 +++ .../tls-init-cleanup-podsecuritypolicy.yaml | 3 +++ charts/consul/templates/tls-init-cleanup-role.yaml | 3 +++ .../templates/tls-init-cleanup-rolebinding.yaml | 3 +++ .../templates/tls-init-cleanup-serviceaccount.yaml | 3 +++ charts/consul/templates/tls-init-job.yaml | 3 +++ .../templates/tls-init-podsecuritypolicy.yaml | 3 +++ charts/consul/templates/tls-init-role.yaml | 3 +++ charts/consul/templates/tls-init-rolebinding.yaml | 3 +++ .../consul/templates/tls-init-serviceaccount.yaml | 3 +++ charts/consul/templates/ui-ingress.yaml | 3 +++ charts/consul/templates/ui-service.yaml | 3 +++ .../webhook-cert-manager-clusterrole.yaml | 3 +++ .../webhook-cert-manager-clusterrolebinding.yaml | 3 +++ .../templates/webhook-cert-manager-configmap.yaml | 3 +++ .../templates/webhook-cert-manager-deployment.yaml | 3 +++ .../webhook-cert-manager-podsecuritypolicy.yaml | 3 +++ .../webhook-cert-manager-serviceaccount.yaml | 3 +++ charts/consul/test/docker/Test.dockerfile | 3 +++ charts/consul/test/terraform/aks/main.tf | 3 +++ charts/consul/test/terraform/aks/outputs.tf | 3 +++ charts/consul/test/terraform/aks/variables.tf | 3 +++ charts/consul/test/terraform/eks/main.tf | 3 +++ charts/consul/test/terraform/eks/outputs.tf | 3 +++ charts/consul/test/terraform/eks/variables.tf | 3 +++ charts/consul/test/terraform/gke/main.tf | 3 +++ charts/consul/test/terraform/gke/outputs.tf | 3 +++ charts/consul/test/terraform/gke/variables.tf | 3 +++ charts/consul/test/terraform/openshift/main.tf | 3 +++ charts/consul/test/terraform/openshift/oc-login.sh | 3 +++ charts/consul/test/terraform/openshift/outputs.tf | 3 +++ .../consul/test/terraform/openshift/variables.tf | 3 +++ charts/consul/test/unit/_helpers.bash | 3 +++ charts/consul/values.yaml | 3 +++ charts/demo/Chart.yaml | 3 +++ charts/demo/templates/frontend.yaml | 3 +++ charts/demo/templates/intentions.yaml | 3 +++ charts/demo/templates/nginx.yaml | 3 +++ charts/demo/templates/payments.yaml | 3 +++ charts/demo/templates/postgres.yaml | 3 +++ charts/demo/templates/products-api.yaml | 3 +++ charts/demo/templates/public-api.yaml | 3 +++ charts/demo/values.yaml | 3 +++ charts/embed_chart.go | 3 +++ cli/cmd/install/install.go | 3 +++ cli/cmd/install/install_test.go | 3 +++ cli/cmd/proxy/command.go | 3 +++ cli/cmd/proxy/list/command.go | 3 +++ cli/cmd/proxy/list/command_test.go | 3 +++ cli/cmd/proxy/loglevel/command.go | 3 +++ cli/cmd/proxy/loglevel/command_test.go | 3 +++ cli/cmd/proxy/read/command.go | 3 +++ cli/cmd/proxy/read/command_test.go | 3 +++ cli/cmd/proxy/read/filters.go | 3 +++ cli/cmd/proxy/read/filters_test.go | 3 +++ cli/cmd/proxy/read/format.go | 3 +++ cli/cmd/proxy/read/format_test.go | 3 +++ cli/cmd/status/status.go | 3 +++ cli/cmd/status/status_test.go | 3 +++ cli/cmd/troubleshoot/command.go | 3 +++ cli/cmd/troubleshoot/proxy/proxy.go | 3 +++ cli/cmd/troubleshoot/proxy/proxy_test.go | 3 +++ cli/cmd/troubleshoot/upstreams/upstreams.go | 3 +++ cli/cmd/troubleshoot/upstreams/upstreams_test.go | 3 +++ cli/cmd/uninstall/uninstall.go | 3 +++ cli/cmd/uninstall/uninstall_test.go | 3 +++ cli/cmd/upgrade/upgrade.go | 3 +++ cli/cmd/upgrade/upgrade_test.go | 3 +++ cli/cmd/version/version.go | 3 +++ cli/commands.go | 3 +++ cli/common/base.go | 3 +++ cli/common/diff.go | 3 +++ cli/common/diff_test.go | 3 +++ cli/common/envoy/http.go | 3 +++ cli/common/envoy/http_test.go | 3 +++ cli/common/envoy/logger_params.go | 3 +++ cli/common/envoy/logger_params_test.go | 3 +++ cli/common/envoy/types.go | 3 +++ cli/common/error.go | 3 +++ cli/common/flag/doc.go | 3 +++ cli/common/flag/flag.go | 3 +++ cli/common/flag/flag_bool.go | 3 +++ cli/common/flag/flag_enum.go | 3 +++ cli/common/flag/flag_enum_single.go | 3 +++ cli/common/flag/flag_float.go | 3 +++ cli/common/flag/flag_int.go | 3 +++ cli/common/flag/flag_string.go | 3 +++ cli/common/flag/flag_string_map.go | 3 +++ cli/common/flag/flag_string_slice.go | 3 +++ cli/common/flag/flag_string_slice_test.go | 3 +++ cli/common/flag/flag_time.go | 3 +++ cli/common/flag/flag_var.go | 3 +++ cli/common/flag/set.go | 3 +++ cli/common/flag/set_test.go | 3 +++ cli/common/portforward.go | 3 +++ cli/common/portforward_test.go | 3 +++ cli/common/terminal/basic.go | 3 +++ cli/common/terminal/doc.go | 3 +++ cli/common/terminal/table.go | 3 +++ cli/common/terminal/ui.go | 3 +++ cli/common/usage.go | 3 +++ cli/common/utils.go | 3 +++ cli/common/utils_test.go | 3 +++ cli/config/config.go | 3 +++ cli/helm/action.go | 3 +++ cli/helm/chart.go | 3 +++ cli/helm/chart_test.go | 3 +++ cli/helm/install.go | 3 +++ cli/helm/install_test.go | 3 +++ cli/helm/mock.go | 3 +++ cli/helm/test_fixtures/consul/Chart.yaml | 3 +++ cli/helm/test_fixtures/consul/templates/foo.yaml | 3 +++ cli/helm/test_fixtures/consul/values.yaml | 3 +++ cli/helm/upgrade.go | 3 +++ cli/helm/upgrade_test.go | 3 +++ cli/helm/values.go | 3 +++ cli/main.go | 3 +++ cli/preset/cloud_preset.go | 3 +++ cli/preset/cloud_preset_test.go | 3 +++ cli/preset/demo.go | 3 +++ cli/preset/preset.go | 3 +++ cli/preset/preset_test.go | 3 +++ cli/preset/quickstart.go | 3 +++ cli/preset/secure.go | 3 +++ cli/release/release.go | 3 +++ cli/release/release_test.go | 3 +++ cli/validation/kubernetes.go | 3 +++ cli/validation/kubernetes_test.go | 3 +++ cli/version/version.go | 3 +++ control-plane/Dockerfile | 3 +++ control-plane/api/common/common.go | 3 +++ control-plane/api/common/configentry.go | 3 +++ control-plane/api/common/configentry_webhook.go | 3 +++ .../api/common/configentry_webhook_test.go | 3 +++ .../api/v1alpha1/exportedservices_types.go | 3 +++ .../api/v1alpha1/exportedservices_types_test.go | 3 +++ .../api/v1alpha1/exportedservices_webhook.go | 3 +++ .../api/v1alpha1/exportedservices_webhook_test.go | 3 +++ control-plane/api/v1alpha1/groupversion_info.go | 3 +++ control-plane/api/v1alpha1/ingressgateway_types.go | 3 +++ .../api/v1alpha1/ingressgateway_types_test.go | 3 +++ .../api/v1alpha1/ingressgateway_webhook.go | 3 +++ control-plane/api/v1alpha1/mesh_types.go | 3 +++ control-plane/api/v1alpha1/mesh_types_test.go | 3 +++ control-plane/api/v1alpha1/mesh_webhook.go | 3 +++ control-plane/api/v1alpha1/mesh_webhook_test.go | 3 +++ .../api/v1alpha1/peeringacceptor_types.go | 3 +++ .../api/v1alpha1/peeringacceptor_types_test.go | 3 +++ .../api/v1alpha1/peeringacceptor_webhook.go | 3 +++ .../api/v1alpha1/peeringacceptor_webhook_test.go | 3 +++ control-plane/api/v1alpha1/peeringdialer_types.go | 3 +++ .../api/v1alpha1/peeringdialer_types_test.go | 3 +++ .../api/v1alpha1/peeringdialer_webhook.go | 3 +++ .../api/v1alpha1/peeringdialer_webhook_test.go | 3 +++ control-plane/api/v1alpha1/proxydefaults_types.go | 3 +++ .../api/v1alpha1/proxydefaults_types_test.go | 3 +++ .../api/v1alpha1/proxydefaults_webhook.go | 3 +++ .../api/v1alpha1/proxydefaults_webhook_test.go | 3 +++ .../api/v1alpha1/servicedefaults_types.go | 3 +++ .../api/v1alpha1/servicedefaults_types_test.go | 3 +++ .../api/v1alpha1/servicedefaults_webhook.go | 3 +++ .../api/v1alpha1/serviceintentions_types.go | 3 +++ .../api/v1alpha1/serviceintentions_types_test.go | 3 +++ .../api/v1alpha1/serviceintentions_webhook.go | 3 +++ .../api/v1alpha1/serviceintentions_webhook_test.go | 3 +++ .../api/v1alpha1/serviceresolver_types.go | 3 +++ .../api/v1alpha1/serviceresolver_types_test.go | 3 +++ .../api/v1alpha1/serviceresolver_webhook.go | 3 +++ control-plane/api/v1alpha1/servicerouter_types.go | 3 +++ .../api/v1alpha1/servicerouter_types_test.go | 3 +++ .../api/v1alpha1/servicerouter_webhook.go | 3 +++ .../api/v1alpha1/servicesplitter_types.go | 3 +++ .../api/v1alpha1/servicesplitter_types_test.go | 3 +++ .../api/v1alpha1/servicesplitter_webhook.go | 3 +++ control-plane/api/v1alpha1/shared_types.go | 3 +++ control-plane/api/v1alpha1/status.go | 3 +++ .../api/v1alpha1/terminatinggateway_types.go | 3 +++ .../api/v1alpha1/terminatinggateway_types_test.go | 3 +++ .../api/v1alpha1/terminatinggateway_webhook.go | 3 +++ control-plane/build-support/functions/00-vars.sh | 3 +++ control-plane/build-support/functions/10-util.sh | 3 +++ control-plane/build-support/functions/20-build.sh | 3 +++ .../build-support/functions/40-publish.sh | 3 +++ control-plane/build-support/scripts/build-local.sh | 3 +++ control-plane/build-support/scripts/functions.sh | 3 +++ .../build-support/scripts/terraformfmtcheck.sh | 3 +++ control-plane/build-support/scripts/version.sh | 3 +++ control-plane/catalog/to-consul/annotation.go | 3 +++ control-plane/catalog/to-consul/resource.go | 3 +++ control-plane/catalog/to-consul/resource_test.go | 3 +++ control-plane/catalog/to-consul/service_id.go | 3 +++ control-plane/catalog/to-consul/syncer.go | 3 +++ control-plane/catalog/to-consul/syncer_ent_test.go | 3 +++ control-plane/catalog/to-consul/syncer_test.go | 3 +++ control-plane/catalog/to-consul/testing.go | 3 +++ control-plane/catalog/to-k8s/sink.go | 3 +++ control-plane/catalog/to-k8s/sink_test.go | 3 +++ control-plane/catalog/to-k8s/source.go | 3 +++ control-plane/catalog/to-k8s/source_test.go | 3 +++ control-plane/catalog/to-k8s/testing.go | 3 +++ control-plane/cni/config/config.go | 3 +++ control-plane/cni/main.go | 3 +++ control-plane/cni/main_test.go | 3 +++ control-plane/commands.go | 3 +++ .../consul.hashicorp.com_exportedservices.yaml | 3 +++ .../consul.hashicorp.com_ingressgateways.yaml | 3 +++ .../crd/bases/consul.hashicorp.com_meshes.yaml | 3 +++ .../consul.hashicorp.com_peeringacceptors.yaml | 3 +++ .../bases/consul.hashicorp.com_peeringdialers.yaml | 3 +++ .../bases/consul.hashicorp.com_proxydefaults.yaml | 3 +++ .../consul.hashicorp.com_servicedefaults.yaml | 3 +++ .../consul.hashicorp.com_serviceintentions.yaml | 3 +++ .../consul.hashicorp.com_serviceresolvers.yaml | 3 +++ .../bases/consul.hashicorp.com_servicerouters.yaml | 3 +++ .../consul.hashicorp.com_servicesplitters.yaml | 3 +++ .../consul.hashicorp.com_terminatinggateways.yaml | 3 +++ control-plane/config/rbac/role.yaml | 3 +++ control-plane/config/webhook/manifests.yaml | 3 +++ control-plane/connect-inject/common/common.go | 3 +++ control-plane/connect-inject/common/common_test.go | 3 +++ .../constants/annotations_and_labels.go | 3 +++ .../connect-inject/constants/constants.go | 3 +++ .../endpoints/consul_client_health_checks.go | 3 +++ .../endpoints/consul_client_health_checks_test.go | 3 +++ .../controllers/endpoints/endpoints_controller.go | 3 +++ .../endpoints/endpoints_controller_ent_test.go | 3 +++ .../endpoints/endpoints_controller_test.go | 3 +++ .../peering/peering_acceptor_controller.go | 3 +++ .../peering/peering_acceptor_controller_test.go | 3 +++ .../peering/peering_dialer_controller.go | 3 +++ .../peering/peering_dialer_controller_test.go | 3 +++ .../metrics/metrics_configuration.go | 3 +++ .../metrics/metrics_configuration_test.go | 3 +++ .../webhook/consul_dataplane_sidecar.go | 3 +++ .../webhook/consul_dataplane_sidecar_test.go | 3 +++ .../connect-inject/webhook/container_env.go | 3 +++ .../connect-inject/webhook/container_env_test.go | 3 +++ .../connect-inject/webhook/container_init.go | 3 +++ .../connect-inject/webhook/container_init_test.go | 3 +++ .../connect-inject/webhook/container_volume.go | 3 +++ control-plane/connect-inject/webhook/dns.go | 3 +++ control-plane/connect-inject/webhook/dns_test.go | 3 +++ .../connect-inject/webhook/health_checks_test.go | 3 +++ .../connect-inject/webhook/heath_checks.go | 3 +++ .../connect-inject/webhook/mesh_webhook.go | 3 +++ .../webhook/mesh_webhook_ent_test.go | 3 +++ .../connect-inject/webhook/mesh_webhook_test.go | 3 +++ .../connect-inject/webhook/redirect_traffic.go | 3 +++ .../webhook/redirect_traffic_test.go | 3 +++ control-plane/consul/consul.go | 3 +++ control-plane/consul/consul_test.go | 3 +++ control-plane/controller/configentry_controller.go | 3 +++ .../controller/configentry_controller_ent_test.go | 3 +++ .../controller/configentry_controller_test.go | 3 +++ .../controller/exportedservices_controller.go | 3 +++ .../exportedservices_controller_ent_test.go | 3 +++ .../controller/ingressgateway_controller.go | 3 +++ control-plane/controller/mesh_controller.go | 3 +++ .../controller/proxydefaults_controller.go | 3 +++ .../controller/servicedefaults_controller.go | 3 +++ .../controller/serviceintentions_controller.go | 3 +++ .../controller/serviceresolver_controller.go | 3 +++ .../controller/servicerouter_controller.go | 3 +++ .../controller/servicesplitter_controller.go | 3 +++ .../controller/terminatinggateway_controller.go | 3 +++ control-plane/hack/lint-api-new-client/main.go | 3 +++ control-plane/helper/cert/notify.go | 3 +++ control-plane/helper/cert/notify_test.go | 3 +++ control-plane/helper/cert/source.go | 3 +++ control-plane/helper/cert/source_gen.go | 3 +++ control-plane/helper/cert/source_gen_test.go | 3 +++ control-plane/helper/cert/tls_util.go | 3 +++ control-plane/helper/coalesce/coalesce.go | 3 +++ control-plane/helper/coalesce/coalesce_test.go | 3 +++ control-plane/helper/controller/controller.go | 3 +++ control-plane/helper/controller/controller_test.go | 3 +++ control-plane/helper/controller/resource.go | 3 +++ control-plane/helper/controller/testing.go | 3 +++ control-plane/helper/go-discover/discover.go | 3 +++ control-plane/helper/go-discover/discover_test.go | 3 +++ .../helper/go-discover/mocks/mock_provider.go | 3 +++ .../mutating_webhook_configuration.go | 3 +++ .../mutating_webhook_configuration_test.go | 3 +++ control-plane/helper/parsetags/parsetags.go | 3 +++ control-plane/helper/parsetags/parsetags_test.go | 3 +++ control-plane/helper/test/test_util.go | 3 +++ control-plane/main.go | 3 +++ control-plane/namespaces/namespaces.go | 3 +++ control-plane/namespaces/namespaces_test.go | 3 +++ control-plane/subcommand/acl-init/command.go | 3 +++ control-plane/subcommand/acl-init/command_test.go | 3 +++ control-plane/subcommand/auth.go | 3 +++ control-plane/subcommand/common/common.go | 3 +++ control-plane/subcommand/common/common_test.go | 3 +++ control-plane/subcommand/common/test_util.go | 3 +++ control-plane/subcommand/connect-init/command.go | 3 +++ .../subcommand/connect-init/command_ent_test.go | 3 +++ .../subcommand/connect-init/command_test.go | 3 +++ control-plane/subcommand/consul-logout/command.go | 3 +++ .../subcommand/consul-logout/command_test.go | 3 +++ .../subcommand/create-federation-secret/command.go | 3 +++ .../create-federation-secret/command_test.go | 3 +++ .../subcommand/delete-completed-job/command.go | 3 +++ .../delete-completed-job/command_test.go | 3 +++ control-plane/subcommand/flags/consul.go | 3 +++ control-plane/subcommand/flags/consul_test.go | 3 +++ control-plane/subcommand/flags/flag_map_value.go | 3 +++ .../subcommand/flags/flag_map_value_test.go | 3 +++ control-plane/subcommand/flags/flag_slice_value.go | 3 +++ .../subcommand/flags/flag_slice_value_test.go | 3 +++ control-plane/subcommand/flags/flags.go | 3 +++ control-plane/subcommand/flags/http.go | 3 +++ control-plane/subcommand/flags/http_test.go | 3 +++ control-plane/subcommand/flags/k8s.go | 3 +++ control-plane/subcommand/flags/mapset.go | 3 +++ control-plane/subcommand/flags/usage.go | 3 +++ control-plane/subcommand/flags/usage_test.go | 3 +++ .../subcommand/get-consul-client-ca/command.go | 3 +++ .../get-consul-client-ca/command_test.go | 3 +++ .../gossip-encryption-autogenerate/command.go | 3 +++ .../gossip-encryption-autogenerate/command_test.go | 3 +++ control-plane/subcommand/inject-connect/command.go | 3 +++ .../subcommand/inject-connect/command_test.go | 3 +++ control-plane/subcommand/install-cni/binary.go | 3 +++ .../subcommand/install-cni/binary_test.go | 3 +++ control-plane/subcommand/install-cni/cniconfig.go | 3 +++ .../subcommand/install-cni/cniconfig_test.go | 3 +++ control-plane/subcommand/install-cni/command.go | 3 +++ .../subcommand/install-cni/command_test.go | 3 +++ control-plane/subcommand/install-cni/kubeconfig.go | 3 +++ .../subcommand/install-cni/kubeconfig_test.go | 3 +++ control-plane/subcommand/partition-init/command.go | 3 +++ .../subcommand/partition-init/command_ent_test.go | 3 +++ .../subcommand/server-acl-init/anonymous_token.go | 3 +++ .../subcommand/server-acl-init/command.go | 3 +++ .../subcommand/server-acl-init/command_ent_test.go | 3 +++ .../subcommand/server-acl-init/command_test.go | 3 +++ .../subcommand/server-acl-init/connect_inject.go | 3 +++ .../server-acl-init/connect_inject_test.go | 3 +++ .../subcommand/server-acl-init/create_or_update.go | 3 +++ .../server-acl-init/create_or_update_test.go | 3 +++ .../server-acl-init/k8s_secrets_backend.go | 3 +++ control-plane/subcommand/server-acl-init/rules.go | 3 +++ .../subcommand/server-acl-init/rules_test.go | 3 +++ .../subcommand/server-acl-init/secrets_backend.go | 3 +++ .../subcommand/server-acl-init/servers.go | 3 +++ .../server-acl-init/test_fake_secrets_backend.go | 3 +++ .../server-acl-init/vault_secrets_backend.go | 3 +++ control-plane/subcommand/sync-catalog/command.go | 3 +++ .../subcommand/sync-catalog/command_ent_test.go | 3 +++ .../subcommand/sync-catalog/command_test.go | 3 +++ control-plane/subcommand/tls-init/command.go | 3 +++ control-plane/subcommand/tls-init/command_test.go | 3 +++ control-plane/subcommand/version/command.go | 3 +++ .../subcommand/webhook-cert-manager/command.go | 3 +++ .../webhook-cert-manager/command_test.go | 3 +++ .../subcommand/webhook-cert-manager/mocks/mocks.go | 3 +++ control-plane/version/version.go | 3 +++ hack/aws-acceptance-test-cleanup/main.go | 3 +++ hack/copy-crds-to-chart/main.go | 3 +++ hack/helm-reference-gen/doc_node.go | 3 +++ hack/helm-reference-gen/fixtures/full-values.yaml | 3 +++ hack/helm-reference-gen/main.go | 3 +++ hack/helm-reference-gen/main_test.go | 3 +++ hack/helm-reference-gen/parse_error.go | 3 +++ 661 files changed, 1994 insertions(+) create mode 100644 .copywrite.hcl diff --git a/.circleci/config.yml b/.circleci/config.yml index 9c84f112cc..e0e59a56d9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + # Originally from consul-k8s version: 2.1 orbs: diff --git a/.copywrite.hcl b/.copywrite.hcl new file mode 100644 index 0000000000..6a47e1061d --- /dev/null +++ b/.copywrite.hcl @@ -0,0 +1,14 @@ +schema_version = 1 + +project { + license = "MPL-2.0" + copyright_year = 2018 + + # (OPTIONAL) A list of globs that should not have copyright/license headers. + # Supports doublestar glob patterns for more flexibility in defining which + # files or folders should be ignored + header_ignore = [ + # "vendors/**", + # "**autogen**", + ] +} diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index e22e28a48a..eb998e2cd9 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + blank_issues_enabled: false contact_links: - name: Consul Community Support diff --git a/.golangci.yml b/.golangci.yml index 143e0297e4..142f5c2722 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + linters: # enables all defaults + the below, `golangci-lint linters` to see the list of active linters. enable: diff --git a/.release/ci.hcl b/.release/ci.hcl index ffc2fd60d5..5c781045e7 100644 --- a/.release/ci.hcl +++ b/.release/ci.hcl @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + schema = "1" project "consul-k8s" { diff --git a/.release/release-metadata.hcl b/.release/release-metadata.hcl index c053fdac2f..10741e9c36 100644 --- a/.release/release-metadata.hcl +++ b/.release/release-metadata.hcl @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + url_docker_registry_dockerhub = "https://hub.docker.com/r/hashicorp/consul-k8s-control-plane" url_license = "https://github.com/hashicorp/consul-k8s/blob/main/LICENSE" url_project_website = "https://www.consul.io/docs/k8s" diff --git a/.release/security-scan.hcl b/.release/security-scan.hcl index 42576d29b2..692fea1578 100644 --- a/.release/security-scan.hcl +++ b/.release/security-scan.hcl @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + container { dependencies = true alpine_secdb = true diff --git a/acceptance/framework/cli/cli.go b/acceptance/framework/cli/cli.go index 5297382d94..11a158269f 100644 --- a/acceptance/framework/cli/cli.go +++ b/acceptance/framework/cli/cli.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package cli import ( diff --git a/acceptance/framework/config/config.go b/acceptance/framework/config/config.go index 8e131c9c59..310955e8f8 100644 --- a/acceptance/framework/config/config.go +++ b/acceptance/framework/config/config.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package config import ( diff --git a/acceptance/framework/config/config_test.go b/acceptance/framework/config/config_test.go index 7733d815db..f5992cdd99 100644 --- a/acceptance/framework/config/config_test.go +++ b/acceptance/framework/config/config_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package config import ( diff --git a/acceptance/framework/connhelper/connect_helper.go b/acceptance/framework/connhelper/connect_helper.go index c5d677ba6f..8a7f4d3d53 100644 --- a/acceptance/framework/connhelper/connect_helper.go +++ b/acceptance/framework/connhelper/connect_helper.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package connhelper import ( diff --git a/acceptance/framework/consul/cli_cluster.go b/acceptance/framework/consul/cli_cluster.go index 0a3e688951..a9b1dbe74f 100644 --- a/acceptance/framework/consul/cli_cluster.go +++ b/acceptance/framework/consul/cli_cluster.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package consul import ( diff --git a/acceptance/framework/consul/cluster.go b/acceptance/framework/consul/cluster.go index 41dbec86f8..734f07f36e 100644 --- a/acceptance/framework/consul/cluster.go +++ b/acceptance/framework/consul/cluster.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package consul import ( diff --git a/acceptance/framework/consul/helm_cluster.go b/acceptance/framework/consul/helm_cluster.go index 3f4d31173d..f34aab6455 100644 --- a/acceptance/framework/consul/helm_cluster.go +++ b/acceptance/framework/consul/helm_cluster.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package consul import ( diff --git a/acceptance/framework/consul/helm_cluster_test.go b/acceptance/framework/consul/helm_cluster_test.go index af70812f9a..435d9f4d7f 100644 --- a/acceptance/framework/consul/helm_cluster_test.go +++ b/acceptance/framework/consul/helm_cluster_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package consul import ( diff --git a/acceptance/framework/environment/cni-kind/custom-resources.yaml b/acceptance/framework/environment/cni-kind/custom-resources.yaml index 1d2e08da56..9a87195041 100644 --- a/acceptance/framework/environment/cni-kind/custom-resources.yaml +++ b/acceptance/framework/environment/cni-kind/custom-resources.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + # This section includes base Calico installation configuration. # For more information, see: https://projectcalico.docs.tigera.io/master/reference/installation/api#operator.tigera.io/v1.Installation apiVersion: operator.tigera.io/v1 diff --git a/acceptance/framework/environment/cni-kind/tigera-operator.yaml b/acceptance/framework/environment/cni-kind/tigera-operator.yaml index f13e65ceb0..8114b6f596 100644 --- a/acceptance/framework/environment/cni-kind/tigera-operator.yaml +++ b/acceptance/framework/environment/cni-kind/tigera-operator.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: v1 kind: Namespace metadata: diff --git a/acceptance/framework/environment/environment.go b/acceptance/framework/environment/environment.go index 15121b97e3..2c86b43707 100644 --- a/acceptance/framework/environment/environment.go +++ b/acceptance/framework/environment/environment.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package environment import ( diff --git a/acceptance/framework/flags/flags.go b/acceptance/framework/flags/flags.go index 5d90d74f9e..b8c315044b 100644 --- a/acceptance/framework/flags/flags.go +++ b/acceptance/framework/flags/flags.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package flags import ( diff --git a/acceptance/framework/flags/flags_test.go b/acceptance/framework/flags/flags_test.go index f66317a048..7546ae911c 100644 --- a/acceptance/framework/flags/flags_test.go +++ b/acceptance/framework/flags/flags_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package flags import ( diff --git a/acceptance/framework/helpers/helpers.go b/acceptance/framework/helpers/helpers.go index 5f99a682ae..544079b8fb 100644 --- a/acceptance/framework/helpers/helpers.go +++ b/acceptance/framework/helpers/helpers.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package helpers import ( diff --git a/acceptance/framework/helpers/helpers_test.go b/acceptance/framework/helpers/helpers_test.go index c1b4a916e2..2a994d7f9f 100644 --- a/acceptance/framework/helpers/helpers_test.go +++ b/acceptance/framework/helpers/helpers_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package helpers import ( diff --git a/acceptance/framework/k8s/debug.go b/acceptance/framework/k8s/debug.go index 5bf588f959..773769fced 100644 --- a/acceptance/framework/k8s/debug.go +++ b/acceptance/framework/k8s/debug.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package k8s import ( diff --git a/acceptance/framework/k8s/deploy.go b/acceptance/framework/k8s/deploy.go index 869ebdd804..771aab3fe1 100644 --- a/acceptance/framework/k8s/deploy.go +++ b/acceptance/framework/k8s/deploy.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package k8s import ( diff --git a/acceptance/framework/k8s/helpers.go b/acceptance/framework/k8s/helpers.go index 8d3da64612..efac5b35db 100644 --- a/acceptance/framework/k8s/helpers.go +++ b/acceptance/framework/k8s/helpers.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package k8s import ( diff --git a/acceptance/framework/k8s/kubectl.go b/acceptance/framework/k8s/kubectl.go index e766395f47..4416c7e6a9 100644 --- a/acceptance/framework/k8s/kubectl.go +++ b/acceptance/framework/k8s/kubectl.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package k8s import ( diff --git a/acceptance/framework/logger/logger.go b/acceptance/framework/logger/logger.go index c186e77436..26777c4e22 100644 --- a/acceptance/framework/logger/logger.go +++ b/acceptance/framework/logger/logger.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package logger import ( diff --git a/acceptance/framework/portforward/port_forward.go b/acceptance/framework/portforward/port_forward.go index 30f4aed157..0a48e8d300 100644 --- a/acceptance/framework/portforward/port_forward.go +++ b/acceptance/framework/portforward/port_forward.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package portforward import ( diff --git a/acceptance/framework/suite/suite.go b/acceptance/framework/suite/suite.go index 67dff4f587..5d93d2c23f 100644 --- a/acceptance/framework/suite/suite.go +++ b/acceptance/framework/suite/suite.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package suite import ( diff --git a/acceptance/framework/vault/helpers.go b/acceptance/framework/vault/helpers.go index 2280aee8b2..d086e10391 100644 --- a/acceptance/framework/vault/helpers.go +++ b/acceptance/framework/vault/helpers.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package vault import ( diff --git a/acceptance/framework/vault/vault_cluster.go b/acceptance/framework/vault/vault_cluster.go index c8105fa806..d09b8ed75e 100644 --- a/acceptance/framework/vault/vault_cluster.go +++ b/acceptance/framework/vault/vault_cluster.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package vault import ( diff --git a/acceptance/tests/basic/basic_test.go b/acceptance/tests/basic/basic_test.go index 91047711a1..4727b53495 100644 --- a/acceptance/tests/basic/basic_test.go +++ b/acceptance/tests/basic/basic_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package basic import ( diff --git a/acceptance/tests/basic/main_test.go b/acceptance/tests/basic/main_test.go index fa858ee4ae..0e400ef870 100644 --- a/acceptance/tests/basic/main_test.go +++ b/acceptance/tests/basic/main_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package basic import ( diff --git a/acceptance/tests/cli/cli_install_test.go b/acceptance/tests/cli/cli_install_test.go index b6c52e9fc4..009d3140fc 100644 --- a/acceptance/tests/cli/cli_install_test.go +++ b/acceptance/tests/cli/cli_install_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package cli import ( diff --git a/acceptance/tests/cli/cli_upgrade_test.go b/acceptance/tests/cli/cli_upgrade_test.go index 6fcf82f738..8c8970d88a 100644 --- a/acceptance/tests/cli/cli_upgrade_test.go +++ b/acceptance/tests/cli/cli_upgrade_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package cli import ( diff --git a/acceptance/tests/cli/main_test.go b/acceptance/tests/cli/main_test.go index 85cef25abe..2e66d89543 100644 --- a/acceptance/tests/cli/main_test.go +++ b/acceptance/tests/cli/main_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package cli import ( diff --git a/acceptance/tests/config-entries/config_entries_namespaces_test.go b/acceptance/tests/config-entries/config_entries_namespaces_test.go index a2580069a8..05ea9e5235 100644 --- a/acceptance/tests/config-entries/config_entries_namespaces_test.go +++ b/acceptance/tests/config-entries/config_entries_namespaces_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package config_entries import ( diff --git a/acceptance/tests/config-entries/config_entries_test.go b/acceptance/tests/config-entries/config_entries_test.go index 035edf6a8f..d3c0d410a0 100644 --- a/acceptance/tests/config-entries/config_entries_test.go +++ b/acceptance/tests/config-entries/config_entries_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package config_entries import ( diff --git a/acceptance/tests/config-entries/main_test.go b/acceptance/tests/config-entries/main_test.go index 64034f7663..805045dcef 100644 --- a/acceptance/tests/config-entries/main_test.go +++ b/acceptance/tests/config-entries/main_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package config_entries import ( diff --git a/acceptance/tests/connect/connect_external_servers_test.go b/acceptance/tests/connect/connect_external_servers_test.go index 5132b66e7a..a7b0f656bf 100644 --- a/acceptance/tests/connect/connect_external_servers_test.go +++ b/acceptance/tests/connect/connect_external_servers_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package connect import ( diff --git a/acceptance/tests/connect/connect_inject_namespaces_test.go b/acceptance/tests/connect/connect_inject_namespaces_test.go index db48465bda..f848594cd2 100644 --- a/acceptance/tests/connect/connect_inject_namespaces_test.go +++ b/acceptance/tests/connect/connect_inject_namespaces_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package connect import ( diff --git a/acceptance/tests/connect/connect_inject_test.go b/acceptance/tests/connect/connect_inject_test.go index 2e75846884..dd3865ae46 100644 --- a/acceptance/tests/connect/connect_inject_test.go +++ b/acceptance/tests/connect/connect_inject_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package connect import ( diff --git a/acceptance/tests/connect/main_test.go b/acceptance/tests/connect/main_test.go index a2b5925bed..e44c75e519 100644 --- a/acceptance/tests/connect/main_test.go +++ b/acceptance/tests/connect/main_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package connect import ( diff --git a/acceptance/tests/consul-dns/consul_dns_test.go b/acceptance/tests/consul-dns/consul_dns_test.go index 47cfb4af07..15b7d580be 100644 --- a/acceptance/tests/consul-dns/consul_dns_test.go +++ b/acceptance/tests/consul-dns/consul_dns_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package consuldns import ( diff --git a/acceptance/tests/consul-dns/main_test.go b/acceptance/tests/consul-dns/main_test.go index 848f30ad8f..1a17337d0a 100644 --- a/acceptance/tests/consul-dns/main_test.go +++ b/acceptance/tests/consul-dns/main_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package consuldns import ( diff --git a/acceptance/tests/example/example_test.go b/acceptance/tests/example/example_test.go index 9c6457f906..b324ac31fe 100644 --- a/acceptance/tests/example/example_test.go +++ b/acceptance/tests/example/example_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + // Rename package to your test package. package example diff --git a/acceptance/tests/example/main_test.go b/acceptance/tests/example/main_test.go index 323f421d32..f92fff8a59 100644 --- a/acceptance/tests/example/main_test.go +++ b/acceptance/tests/example/main_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + // Rename package to your test package. package example diff --git a/acceptance/tests/fixtures/bases/crds-oss/ingressgateway.yaml b/acceptance/tests/fixtures/bases/crds-oss/ingressgateway.yaml index d613dc217c..79b5b194bc 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/ingressgateway.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/ingressgateway.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: IngressGateway metadata: diff --git a/acceptance/tests/fixtures/bases/crds-oss/kustomization.yaml b/acceptance/tests/fixtures/bases/crds-oss/kustomization.yaml index 040b64f155..1217d857c8 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - ingressgateway.yaml - mesh.yaml diff --git a/acceptance/tests/fixtures/bases/crds-oss/mesh.yaml b/acceptance/tests/fixtures/bases/crds-oss/mesh.yaml index 85474c1db6..9af8106b27 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/mesh.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/mesh.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: Mesh metadata: diff --git a/acceptance/tests/fixtures/bases/crds-oss/proxydefaults.yaml b/acceptance/tests/fixtures/bases/crds-oss/proxydefaults.yaml index 6a90f29506..af4d7c30c2 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/proxydefaults.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/proxydefaults.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ProxyDefaults metadata: diff --git a/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml b/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml index 413eb68d22..d930439c67 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceDefaults metadata: diff --git a/acceptance/tests/fixtures/bases/crds-oss/serviceexports.yaml b/acceptance/tests/fixtures/bases/crds-oss/serviceexports.yaml index 8ae095cd8e..704ea9ee19 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/serviceexports.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/serviceexports.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceExports metadata: diff --git a/acceptance/tests/fixtures/bases/crds-oss/serviceintentions.yaml b/acceptance/tests/fixtures/bases/crds-oss/serviceintentions.yaml index 6b86312a78..fe3408cbd5 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/serviceintentions.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/serviceintentions.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceDefaults metadata: diff --git a/acceptance/tests/fixtures/bases/crds-oss/serviceresolver.yaml b/acceptance/tests/fixtures/bases/crds-oss/serviceresolver.yaml index 05dee04184..a71da92e35 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/serviceresolver.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/serviceresolver.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceResolver metadata: diff --git a/acceptance/tests/fixtures/bases/crds-oss/servicerouter.yaml b/acceptance/tests/fixtures/bases/crds-oss/servicerouter.yaml index 8526f1202d..0f04cd4cd4 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/servicerouter.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/servicerouter.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceDefaults metadata: diff --git a/acceptance/tests/fixtures/bases/crds-oss/servicesplitter.yaml b/acceptance/tests/fixtures/bases/crds-oss/servicesplitter.yaml index f0e8d74fe4..2eb9c3fccc 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/servicesplitter.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/servicesplitter.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceDefaults metadata: diff --git a/acceptance/tests/fixtures/bases/crds-oss/terminatinggateway.yaml b/acceptance/tests/fixtures/bases/crds-oss/terminatinggateway.yaml index 77333b2011..23daa6da20 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/terminatinggateway.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/terminatinggateway.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: TerminatingGateway metadata: diff --git a/acceptance/tests/fixtures/bases/exportedservices-default/exportedservices-default.yaml b/acceptance/tests/fixtures/bases/exportedservices-default/exportedservices-default.yaml index a260602109..0e523bdd7e 100644 --- a/acceptance/tests/fixtures/bases/exportedservices-default/exportedservices-default.yaml +++ b/acceptance/tests/fixtures/bases/exportedservices-default/exportedservices-default.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ExportedServices metadata: diff --git a/acceptance/tests/fixtures/bases/exportedservices-default/kustomization.yaml b/acceptance/tests/fixtures/bases/exportedservices-default/kustomization.yaml index e540a4def1..59527a69fb 100644 --- a/acceptance/tests/fixtures/bases/exportedservices-default/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/exportedservices-default/kustomization.yaml @@ -1,2 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - exportedservices-default.yaml diff --git a/acceptance/tests/fixtures/bases/exportedservices-secondary/exportedservices-secondary.yaml b/acceptance/tests/fixtures/bases/exportedservices-secondary/exportedservices-secondary.yaml index a514ed50d9..69151577f9 100644 --- a/acceptance/tests/fixtures/bases/exportedservices-secondary/exportedservices-secondary.yaml +++ b/acceptance/tests/fixtures/bases/exportedservices-secondary/exportedservices-secondary.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ExportedServices metadata: diff --git a/acceptance/tests/fixtures/bases/exportedservices-secondary/kustomization.yaml b/acceptance/tests/fixtures/bases/exportedservices-secondary/kustomization.yaml index 10af8e20c5..e1781d47c1 100644 --- a/acceptance/tests/fixtures/bases/exportedservices-secondary/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/exportedservices-secondary/kustomization.yaml @@ -1,2 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - exportedservices-secondary.yaml diff --git a/acceptance/tests/fixtures/bases/intention/intention.yaml b/acceptance/tests/fixtures/bases/intention/intention.yaml index c7bf26dac2..973866be03 100644 --- a/acceptance/tests/fixtures/bases/intention/intention.yaml +++ b/acceptance/tests/fixtures/bases/intention/intention.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceIntentions metadata: diff --git a/acceptance/tests/fixtures/bases/intention/kustomization.yaml b/acceptance/tests/fixtures/bases/intention/kustomization.yaml index 8d15c05511..572e7727a4 100644 --- a/acceptance/tests/fixtures/bases/intention/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/intention/kustomization.yaml @@ -1,2 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - intention.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/mesh-gateway/kustomization.yaml b/acceptance/tests/fixtures/bases/mesh-gateway/kustomization.yaml index 6a913f2c44..c271e6af8b 100644 --- a/acceptance/tests/fixtures/bases/mesh-gateway/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/mesh-gateway/kustomization.yaml @@ -1,2 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - proxydefaults.yaml diff --git a/acceptance/tests/fixtures/bases/mesh-gateway/proxydefaults.yaml b/acceptance/tests/fixtures/bases/mesh-gateway/proxydefaults.yaml index 2d28036fe5..1560f6c640 100644 --- a/acceptance/tests/fixtures/bases/mesh-gateway/proxydefaults.yaml +++ b/acceptance/tests/fixtures/bases/mesh-gateway/proxydefaults.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ProxyDefaults metadata: diff --git a/acceptance/tests/fixtures/bases/mesh-peering/kustomization.yaml b/acceptance/tests/fixtures/bases/mesh-peering/kustomization.yaml index b48237763e..e964c5ab05 100644 --- a/acceptance/tests/fixtures/bases/mesh-peering/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/mesh-peering/kustomization.yaml @@ -1,2 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - meshpeering.yaml diff --git a/acceptance/tests/fixtures/bases/mesh-peering/meshpeering.yaml b/acceptance/tests/fixtures/bases/mesh-peering/meshpeering.yaml index de84382d3e..2fb6a04bb6 100644 --- a/acceptance/tests/fixtures/bases/mesh-peering/meshpeering.yaml +++ b/acceptance/tests/fixtures/bases/mesh-peering/meshpeering.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: Mesh metadata: diff --git a/acceptance/tests/fixtures/bases/multiport-app/anyuid-scc-rolebinding.yaml b/acceptance/tests/fixtures/bases/multiport-app/anyuid-scc-rolebinding.yaml index f80bd41d81..5c2e0dcfa2 100644 --- a/acceptance/tests/fixtures/bases/multiport-app/anyuid-scc-rolebinding.yaml +++ b/acceptance/tests/fixtures/bases/multiport-app/anyuid-scc-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/acceptance/tests/fixtures/bases/multiport-app/deployment.yaml b/acceptance/tests/fixtures/bases/multiport-app/deployment.yaml index a99d415d80..f345b27c5e 100644 --- a/acceptance/tests/fixtures/bases/multiport-app/deployment.yaml +++ b/acceptance/tests/fixtures/bases/multiport-app/deployment.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/bases/multiport-app/kustomization.yaml b/acceptance/tests/fixtures/bases/multiport-app/kustomization.yaml index ead8b3afe5..fb792d63a7 100644 --- a/acceptance/tests/fixtures/bases/multiport-app/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/multiport-app/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - deployment.yaml - service.yaml diff --git a/acceptance/tests/fixtures/bases/multiport-app/privileged-scc-rolebinding.yaml b/acceptance/tests/fixtures/bases/multiport-app/privileged-scc-rolebinding.yaml index f909785b36..d0910816ed 100644 --- a/acceptance/tests/fixtures/bases/multiport-app/privileged-scc-rolebinding.yaml +++ b/acceptance/tests/fixtures/bases/multiport-app/privileged-scc-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/acceptance/tests/fixtures/bases/multiport-app/psp-rolebinding.yaml b/acceptance/tests/fixtures/bases/multiport-app/psp-rolebinding.yaml index fce63f0076..f321858164 100644 --- a/acceptance/tests/fixtures/bases/multiport-app/psp-rolebinding.yaml +++ b/acceptance/tests/fixtures/bases/multiport-app/psp-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/acceptance/tests/fixtures/bases/multiport-app/secret.yaml b/acceptance/tests/fixtures/bases/multiport-app/secret.yaml index cb3fa957e2..bc8d931f1b 100644 --- a/acceptance/tests/fixtures/bases/multiport-app/secret.yaml +++ b/acceptance/tests/fixtures/bases/multiport-app/secret.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: v1 kind: Secret metadata: diff --git a/acceptance/tests/fixtures/bases/multiport-app/service.yaml b/acceptance/tests/fixtures/bases/multiport-app/service.yaml index d18da258a3..8684c75f21 100644 --- a/acceptance/tests/fixtures/bases/multiport-app/service.yaml +++ b/acceptance/tests/fixtures/bases/multiport-app/service.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: v1 kind: Service metadata: diff --git a/acceptance/tests/fixtures/bases/multiport-app/serviceaccount.yaml b/acceptance/tests/fixtures/bases/multiport-app/serviceaccount.yaml index 2293c2e173..87b33476f4 100644 --- a/acceptance/tests/fixtures/bases/multiport-app/serviceaccount.yaml +++ b/acceptance/tests/fixtures/bases/multiport-app/serviceaccount.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: v1 kind: ServiceAccount metadata: diff --git a/acceptance/tests/fixtures/bases/peering/peering-acceptor.yaml b/acceptance/tests/fixtures/bases/peering/peering-acceptor.yaml index 3eff952833..1b3ea3956b 100644 --- a/acceptance/tests/fixtures/bases/peering/peering-acceptor.yaml +++ b/acceptance/tests/fixtures/bases/peering/peering-acceptor.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: PeeringAcceptor metadata: diff --git a/acceptance/tests/fixtures/bases/peering/peering-dialer.yaml b/acceptance/tests/fixtures/bases/peering/peering-dialer.yaml index ec125d7bb6..9bf9f03819 100644 --- a/acceptance/tests/fixtures/bases/peering/peering-dialer.yaml +++ b/acceptance/tests/fixtures/bases/peering/peering-dialer.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: PeeringDialer metadata: diff --git a/acceptance/tests/fixtures/bases/static-client/anyuid-scc-rolebinding.yaml b/acceptance/tests/fixtures/bases/static-client/anyuid-scc-rolebinding.yaml index be44c13e0a..b80bc5c562 100644 --- a/acceptance/tests/fixtures/bases/static-client/anyuid-scc-rolebinding.yaml +++ b/acceptance/tests/fixtures/bases/static-client/anyuid-scc-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/acceptance/tests/fixtures/bases/static-client/deployment.yaml b/acceptance/tests/fixtures/bases/static-client/deployment.yaml index 66bb771f6f..ff29f1b03a 100644 --- a/acceptance/tests/fixtures/bases/static-client/deployment.yaml +++ b/acceptance/tests/fixtures/bases/static-client/deployment.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/bases/static-client/kustomization.yaml b/acceptance/tests/fixtures/bases/static-client/kustomization.yaml index b9d8e11f73..9aa0009dc4 100644 --- a/acceptance/tests/fixtures/bases/static-client/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/static-client/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - deployment.yaml - service.yaml diff --git a/acceptance/tests/fixtures/bases/static-client/privileged-scc-rolebinding.yaml b/acceptance/tests/fixtures/bases/static-client/privileged-scc-rolebinding.yaml index b8e097c3d8..36f6652e65 100644 --- a/acceptance/tests/fixtures/bases/static-client/privileged-scc-rolebinding.yaml +++ b/acceptance/tests/fixtures/bases/static-client/privileged-scc-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/acceptance/tests/fixtures/bases/static-client/psp-rolebinding.yaml b/acceptance/tests/fixtures/bases/static-client/psp-rolebinding.yaml index a377c8038d..e516e57f7c 100644 --- a/acceptance/tests/fixtures/bases/static-client/psp-rolebinding.yaml +++ b/acceptance/tests/fixtures/bases/static-client/psp-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/acceptance/tests/fixtures/bases/static-client/service.yaml b/acceptance/tests/fixtures/bases/static-client/service.yaml index 16fbee0463..abd6ee4faf 100644 --- a/acceptance/tests/fixtures/bases/static-client/service.yaml +++ b/acceptance/tests/fixtures/bases/static-client/service.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: v1 kind: Service metadata: diff --git a/acceptance/tests/fixtures/bases/static-client/serviceaccount.yaml b/acceptance/tests/fixtures/bases/static-client/serviceaccount.yaml index b9bf136862..23578e5ead 100644 --- a/acceptance/tests/fixtures/bases/static-client/serviceaccount.yaml +++ b/acceptance/tests/fixtures/bases/static-client/serviceaccount.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: v1 kind: ServiceAccount metadata: diff --git a/acceptance/tests/fixtures/bases/static-metrics-app/deployment.yaml b/acceptance/tests/fixtures/bases/static-metrics-app/deployment.yaml index a3020ddb47..21852487e3 100644 --- a/acceptance/tests/fixtures/bases/static-metrics-app/deployment.yaml +++ b/acceptance/tests/fixtures/bases/static-metrics-app/deployment.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/bases/static-metrics-app/kustomization.yaml b/acceptance/tests/fixtures/bases/static-metrics-app/kustomization.yaml index 6d1374a18e..ccc066e0a8 100644 --- a/acceptance/tests/fixtures/bases/static-metrics-app/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/static-metrics-app/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - deployment.yaml - service.yaml diff --git a/acceptance/tests/fixtures/bases/static-metrics-app/service.yaml b/acceptance/tests/fixtures/bases/static-metrics-app/service.yaml index a37db26875..2e553f7e83 100644 --- a/acceptance/tests/fixtures/bases/static-metrics-app/service.yaml +++ b/acceptance/tests/fixtures/bases/static-metrics-app/service.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: v1 kind: Service metadata: diff --git a/acceptance/tests/fixtures/bases/static-server-https/anyuid-scc-rolebinding.yaml b/acceptance/tests/fixtures/bases/static-server-https/anyuid-scc-rolebinding.yaml index d224a082da..2be7cf13db 100644 --- a/acceptance/tests/fixtures/bases/static-server-https/anyuid-scc-rolebinding.yaml +++ b/acceptance/tests/fixtures/bases/static-server-https/anyuid-scc-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/acceptance/tests/fixtures/bases/static-server-https/configmap.yaml b/acceptance/tests/fixtures/bases/static-server-https/configmap.yaml index dcb975978e..5a037ecdc6 100644 --- a/acceptance/tests/fixtures/bases/static-server-https/configmap.yaml +++ b/acceptance/tests/fixtures/bases/static-server-https/configmap.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: v1 kind: ConfigMap metadata: diff --git a/acceptance/tests/fixtures/bases/static-server-https/deployment.yaml b/acceptance/tests/fixtures/bases/static-server-https/deployment.yaml index 3071162c15..c95bdb4289 100644 --- a/acceptance/tests/fixtures/bases/static-server-https/deployment.yaml +++ b/acceptance/tests/fixtures/bases/static-server-https/deployment.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/bases/static-server-https/kustomization.yaml b/acceptance/tests/fixtures/bases/static-server-https/kustomization.yaml index 78508b7938..da166af201 100644 --- a/acceptance/tests/fixtures/bases/static-server-https/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/static-server-https/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - deployment.yaml - configmap.yaml diff --git a/acceptance/tests/fixtures/bases/static-server-https/privileged-scc-rolebinding.yaml b/acceptance/tests/fixtures/bases/static-server-https/privileged-scc-rolebinding.yaml index 1b9fe13789..bed477ed26 100644 --- a/acceptance/tests/fixtures/bases/static-server-https/privileged-scc-rolebinding.yaml +++ b/acceptance/tests/fixtures/bases/static-server-https/privileged-scc-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/acceptance/tests/fixtures/bases/static-server-https/psp-rolebinding.yaml b/acceptance/tests/fixtures/bases/static-server-https/psp-rolebinding.yaml index acccd2fcec..3c6cfad8f1 100644 --- a/acceptance/tests/fixtures/bases/static-server-https/psp-rolebinding.yaml +++ b/acceptance/tests/fixtures/bases/static-server-https/psp-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/acceptance/tests/fixtures/bases/static-server-https/service.yaml b/acceptance/tests/fixtures/bases/static-server-https/service.yaml index 168c90d3d0..7f4f930c0a 100644 --- a/acceptance/tests/fixtures/bases/static-server-https/service.yaml +++ b/acceptance/tests/fixtures/bases/static-server-https/service.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: v1 kind: Service metadata: diff --git a/acceptance/tests/fixtures/bases/static-server-https/serviceaccount.yaml b/acceptance/tests/fixtures/bases/static-server-https/serviceaccount.yaml index f4267a573e..ced9002d6b 100644 --- a/acceptance/tests/fixtures/bases/static-server-https/serviceaccount.yaml +++ b/acceptance/tests/fixtures/bases/static-server-https/serviceaccount.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: v1 kind: ServiceAccount metadata: diff --git a/acceptance/tests/fixtures/bases/static-server/anyuid-scc-rolebinding.yaml b/acceptance/tests/fixtures/bases/static-server/anyuid-scc-rolebinding.yaml index d224a082da..2be7cf13db 100644 --- a/acceptance/tests/fixtures/bases/static-server/anyuid-scc-rolebinding.yaml +++ b/acceptance/tests/fixtures/bases/static-server/anyuid-scc-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/acceptance/tests/fixtures/bases/static-server/deployment.yaml b/acceptance/tests/fixtures/bases/static-server/deployment.yaml index 1c724b0d37..f61371a880 100644 --- a/acceptance/tests/fixtures/bases/static-server/deployment.yaml +++ b/acceptance/tests/fixtures/bases/static-server/deployment.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/bases/static-server/kustomization.yaml b/acceptance/tests/fixtures/bases/static-server/kustomization.yaml index b9d8e11f73..9aa0009dc4 100644 --- a/acceptance/tests/fixtures/bases/static-server/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/static-server/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - deployment.yaml - service.yaml diff --git a/acceptance/tests/fixtures/bases/static-server/privileged-scc-rolebinding.yaml b/acceptance/tests/fixtures/bases/static-server/privileged-scc-rolebinding.yaml index 1b9fe13789..bed477ed26 100644 --- a/acceptance/tests/fixtures/bases/static-server/privileged-scc-rolebinding.yaml +++ b/acceptance/tests/fixtures/bases/static-server/privileged-scc-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/acceptance/tests/fixtures/bases/static-server/psp-rolebinding.yaml b/acceptance/tests/fixtures/bases/static-server/psp-rolebinding.yaml index acccd2fcec..3c6cfad8f1 100644 --- a/acceptance/tests/fixtures/bases/static-server/psp-rolebinding.yaml +++ b/acceptance/tests/fixtures/bases/static-server/psp-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/acceptance/tests/fixtures/bases/static-server/service.yaml b/acceptance/tests/fixtures/bases/static-server/service.yaml index 4776c07239..6d8d54acc2 100644 --- a/acceptance/tests/fixtures/bases/static-server/service.yaml +++ b/acceptance/tests/fixtures/bases/static-server/service.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: v1 kind: Service metadata: diff --git a/acceptance/tests/fixtures/bases/static-server/serviceaccount.yaml b/acceptance/tests/fixtures/bases/static-server/serviceaccount.yaml index f4267a573e..ced9002d6b 100644 --- a/acceptance/tests/fixtures/bases/static-server/serviceaccount.yaml +++ b/acceptance/tests/fixtures/bases/static-server/serviceaccount.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: v1 kind: ServiceAccount metadata: diff --git a/acceptance/tests/fixtures/cases/crd-partitions/default-partition-default/kustomization.yaml b/acceptance/tests/fixtures/cases/crd-partitions/default-partition-default/kustomization.yaml index 499fdc5bc1..a175d8ece0 100644 --- a/acceptance/tests/fixtures/cases/crd-partitions/default-partition-default/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crd-partitions/default-partition-default/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - ../../../bases/exportedservices-default diff --git a/acceptance/tests/fixtures/cases/crd-partitions/default-partition-default/patch.yaml b/acceptance/tests/fixtures/cases/crd-partitions/default-partition-default/patch.yaml index c98ecb6f48..a06855e317 100644 --- a/acceptance/tests/fixtures/cases/crd-partitions/default-partition-default/patch.yaml +++ b/acceptance/tests/fixtures/cases/crd-partitions/default-partition-default/patch.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ExportedServices metadata: diff --git a/acceptance/tests/fixtures/cases/crd-partitions/default-partition-ns1/kustomization.yaml b/acceptance/tests/fixtures/cases/crd-partitions/default-partition-ns1/kustomization.yaml index 499fdc5bc1..a175d8ece0 100644 --- a/acceptance/tests/fixtures/cases/crd-partitions/default-partition-ns1/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crd-partitions/default-partition-ns1/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - ../../../bases/exportedservices-default diff --git a/acceptance/tests/fixtures/cases/crd-partitions/default-partition-ns1/patch.yaml b/acceptance/tests/fixtures/cases/crd-partitions/default-partition-ns1/patch.yaml index f826174aec..3e86df911d 100644 --- a/acceptance/tests/fixtures/cases/crd-partitions/default-partition-ns1/patch.yaml +++ b/acceptance/tests/fixtures/cases/crd-partitions/default-partition-ns1/patch.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ExportedServices metadata: diff --git a/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-default/kustomization.yaml b/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-default/kustomization.yaml index 5a9c8412aa..bb16f51e64 100644 --- a/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-default/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-default/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - ../../../bases/exportedservices-secondary diff --git a/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-default/patch.yaml b/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-default/patch.yaml index d2fc1ab914..141a080903 100644 --- a/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-default/patch.yaml +++ b/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-default/patch.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ExportedServices metadata: diff --git a/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-ns1/kustomization.yaml b/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-ns1/kustomization.yaml index 5a9c8412aa..bb16f51e64 100644 --- a/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-ns1/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-ns1/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - ../../../bases/exportedservices-secondary diff --git a/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-ns1/patch.yaml b/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-ns1/patch.yaml index 4165f2d21a..3b8c2a4068 100644 --- a/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-ns1/patch.yaml +++ b/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-ns1/patch.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ExportedServices metadata: diff --git a/acceptance/tests/fixtures/cases/crd-peers/default-namespace/kustomization.yaml b/acceptance/tests/fixtures/cases/crd-peers/default-namespace/kustomization.yaml index 499fdc5bc1..a175d8ece0 100644 --- a/acceptance/tests/fixtures/cases/crd-peers/default-namespace/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crd-peers/default-namespace/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - ../../../bases/exportedservices-default diff --git a/acceptance/tests/fixtures/cases/crd-peers/default-namespace/patch.yaml b/acceptance/tests/fixtures/cases/crd-peers/default-namespace/patch.yaml index eed6bee4cc..0031c7f601 100644 --- a/acceptance/tests/fixtures/cases/crd-peers/default-namespace/patch.yaml +++ b/acceptance/tests/fixtures/cases/crd-peers/default-namespace/patch.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ExportedServices metadata: diff --git a/acceptance/tests/fixtures/cases/crd-peers/default/kustomization.yaml b/acceptance/tests/fixtures/cases/crd-peers/default/kustomization.yaml index 499fdc5bc1..a175d8ece0 100644 --- a/acceptance/tests/fixtures/cases/crd-peers/default/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crd-peers/default/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - ../../../bases/exportedservices-default diff --git a/acceptance/tests/fixtures/cases/crd-peers/default/patch.yaml b/acceptance/tests/fixtures/cases/crd-peers/default/patch.yaml index b0dcd89ebb..e632512ff1 100644 --- a/acceptance/tests/fixtures/cases/crd-peers/default/patch.yaml +++ b/acceptance/tests/fixtures/cases/crd-peers/default/patch.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ExportedServices metadata: diff --git a/acceptance/tests/fixtures/cases/crd-peers/non-default-namespace/kustomization.yaml b/acceptance/tests/fixtures/cases/crd-peers/non-default-namespace/kustomization.yaml index 499fdc5bc1..a175d8ece0 100644 --- a/acceptance/tests/fixtures/cases/crd-peers/non-default-namespace/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crd-peers/non-default-namespace/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - ../../../bases/exportedservices-default diff --git a/acceptance/tests/fixtures/cases/crd-peers/non-default-namespace/patch.yaml b/acceptance/tests/fixtures/cases/crd-peers/non-default-namespace/patch.yaml index 4162a0f27b..300fbcf3d6 100644 --- a/acceptance/tests/fixtures/cases/crd-peers/non-default-namespace/patch.yaml +++ b/acceptance/tests/fixtures/cases/crd-peers/non-default-namespace/patch.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ExportedServices metadata: diff --git a/acceptance/tests/fixtures/cases/crds-ent/exportedservices.yaml b/acceptance/tests/fixtures/cases/crds-ent/exportedservices.yaml index 4703d23493..f9f8aad4bf 100644 --- a/acceptance/tests/fixtures/cases/crds-ent/exportedservices.yaml +++ b/acceptance/tests/fixtures/cases/crds-ent/exportedservices.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ExportedServices metadata: diff --git a/acceptance/tests/fixtures/cases/crds-ent/kustomization.yaml b/acceptance/tests/fixtures/cases/crds-ent/kustomization.yaml index cdc3e60cb1..14f6c765d8 100644 --- a/acceptance/tests/fixtures/cases/crds-ent/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crds-ent/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - ../../bases/crds-oss - exportedservices.yaml diff --git a/acceptance/tests/fixtures/cases/static-client-inject-multiport/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-inject-multiport/kustomization.yaml index 9834f91903..4d4a53b87f 100644 --- a/acceptance/tests/fixtures/cases/static-client-inject-multiport/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-inject-multiport/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - ../../bases/static-client diff --git a/acceptance/tests/fixtures/cases/static-client-inject-multiport/patch.yaml b/acceptance/tests/fixtures/cases/static-client-inject-multiport/patch.yaml index c38ce8e448..c8b0ae8412 100644 --- a/acceptance/tests/fixtures/cases/static-client-inject-multiport/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-client-inject-multiport/patch.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-client-inject/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-inject/kustomization.yaml index 9834f91903..4d4a53b87f 100644 --- a/acceptance/tests/fixtures/cases/static-client-inject/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-inject/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - ../../bases/static-client diff --git a/acceptance/tests/fixtures/cases/static-client-inject/patch.yaml b/acceptance/tests/fixtures/cases/static-client-inject/patch.yaml index 2f8b32ec0a..aea6e8c778 100644 --- a/acceptance/tests/fixtures/cases/static-client-inject/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-client-inject/patch.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-client-multi-dc/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-multi-dc/kustomization.yaml index 9834f91903..4d4a53b87f 100644 --- a/acceptance/tests/fixtures/cases/static-client-multi-dc/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-multi-dc/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - ../../bases/static-client diff --git a/acceptance/tests/fixtures/cases/static-client-multi-dc/patch.yaml b/acceptance/tests/fixtures/cases/static-client-multi-dc/patch.yaml index 7b9b095073..17e635b1d5 100644 --- a/acceptance/tests/fixtures/cases/static-client-multi-dc/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-client-multi-dc/patch.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-client-namespaces/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-namespaces/kustomization.yaml index 4c5d895a98..97a00c6466 100644 --- a/acceptance/tests/fixtures/cases/static-client-namespaces/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-namespaces/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - ../../bases/static-client diff --git a/acceptance/tests/fixtures/cases/static-client-namespaces/patch.yaml b/acceptance/tests/fixtures/cases/static-client-namespaces/patch.yaml index b41a27df3f..2ebdbf6d9e 100644 --- a/acceptance/tests/fixtures/cases/static-client-namespaces/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-client-namespaces/patch.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-default-partition/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-default-partition/kustomization.yaml index 7191edfb80..38bc36bffd 100644 --- a/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-default-partition/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-default-partition/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - ../../../bases/static-client diff --git a/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-default-partition/patch.yaml b/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-default-partition/patch.yaml index 43507364f8..4044d78bdf 100644 --- a/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-default-partition/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-default-partition/patch.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-partition/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-partition/kustomization.yaml index 7191edfb80..38bc36bffd 100644 --- a/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-partition/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-partition/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - ../../../bases/static-client diff --git a/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-partition/patch.yaml b/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-partition/patch.yaml index 42857c3d2b..f6a3c50f74 100644 --- a/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-partition/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-partition/patch.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-client-partitions/ns-default-partition/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-partitions/ns-default-partition/kustomization.yaml index 7191edfb80..38bc36bffd 100644 --- a/acceptance/tests/fixtures/cases/static-client-partitions/ns-default-partition/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-partitions/ns-default-partition/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - ../../../bases/static-client diff --git a/acceptance/tests/fixtures/cases/static-client-partitions/ns-default-partition/patch.yaml b/acceptance/tests/fixtures/cases/static-client-partitions/ns-default-partition/patch.yaml index 2fa8ec7e27..720252e833 100644 --- a/acceptance/tests/fixtures/cases/static-client-partitions/ns-default-partition/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-client-partitions/ns-default-partition/patch.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-client-partitions/ns-partition/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-partitions/ns-partition/kustomization.yaml index 7191edfb80..38bc36bffd 100644 --- a/acceptance/tests/fixtures/cases/static-client-partitions/ns-partition/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-partitions/ns-partition/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - ../../../bases/static-client diff --git a/acceptance/tests/fixtures/cases/static-client-partitions/ns-partition/patch.yaml b/acceptance/tests/fixtures/cases/static-client-partitions/ns-partition/patch.yaml index f0ceb634bd..df04ceccda 100644 --- a/acceptance/tests/fixtures/cases/static-client-partitions/ns-partition/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-client-partitions/ns-partition/patch.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-client-peers/default-namespace/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-peers/default-namespace/kustomization.yaml index 7191edfb80..38bc36bffd 100644 --- a/acceptance/tests/fixtures/cases/static-client-peers/default-namespace/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-peers/default-namespace/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - ../../../bases/static-client diff --git a/acceptance/tests/fixtures/cases/static-client-peers/default-namespace/patch.yaml b/acceptance/tests/fixtures/cases/static-client-peers/default-namespace/patch.yaml index 02ea8993ec..075cb6ecd4 100644 --- a/acceptance/tests/fixtures/cases/static-client-peers/default-namespace/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-client-peers/default-namespace/patch.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-client-peers/default/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-peers/default/kustomization.yaml index 7191edfb80..38bc36bffd 100644 --- a/acceptance/tests/fixtures/cases/static-client-peers/default/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-peers/default/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - ../../../bases/static-client diff --git a/acceptance/tests/fixtures/cases/static-client-peers/default/patch.yaml b/acceptance/tests/fixtures/cases/static-client-peers/default/patch.yaml index 715485e0f8..a6a2f72b37 100644 --- a/acceptance/tests/fixtures/cases/static-client-peers/default/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-client-peers/default/patch.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-client-peers/non-default-namespace/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-peers/non-default-namespace/kustomization.yaml index 7191edfb80..38bc36bffd 100644 --- a/acceptance/tests/fixtures/cases/static-client-peers/non-default-namespace/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-peers/non-default-namespace/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - ../../../bases/static-client diff --git a/acceptance/tests/fixtures/cases/static-client-peers/non-default-namespace/patch.yaml b/acceptance/tests/fixtures/cases/static-client-peers/non-default-namespace/patch.yaml index fd622759a4..5a7ce34e1b 100644 --- a/acceptance/tests/fixtures/cases/static-client-peers/non-default-namespace/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-client-peers/non-default-namespace/patch.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-client-tproxy/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-tproxy/kustomization.yaml index 9834f91903..4d4a53b87f 100644 --- a/acceptance/tests/fixtures/cases/static-client-tproxy/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-tproxy/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - ../../bases/static-client diff --git a/acceptance/tests/fixtures/cases/static-client-tproxy/patch.yaml b/acceptance/tests/fixtures/cases/static-client-tproxy/patch.yaml index 573bf93b01..8540fc4721 100644 --- a/acceptance/tests/fixtures/cases/static-client-tproxy/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-client-tproxy/patch.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-server-inject/kustomization.yaml b/acceptance/tests/fixtures/cases/static-server-inject/kustomization.yaml index bac892baa0..bc50c78adf 100644 --- a/acceptance/tests/fixtures/cases/static-server-inject/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-server-inject/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + resources: - ../../bases/static-server diff --git a/acceptance/tests/fixtures/cases/static-server-inject/patch.yaml b/acceptance/tests/fixtures/cases/static-server-inject/patch.yaml index f2bf0d6539..56ef015d8f 100644 --- a/acceptance/tests/fixtures/cases/static-server-inject/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-server-inject/patch.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/ingress-gateway/ingress_gateway_namespaces_test.go b/acceptance/tests/ingress-gateway/ingress_gateway_namespaces_test.go index b713620f1e..ec4878df04 100644 --- a/acceptance/tests/ingress-gateway/ingress_gateway_namespaces_test.go +++ b/acceptance/tests/ingress-gateway/ingress_gateway_namespaces_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package ingressgateway import ( diff --git a/acceptance/tests/ingress-gateway/ingress_gateway_test.go b/acceptance/tests/ingress-gateway/ingress_gateway_test.go index b6535439c1..b5df6287b6 100644 --- a/acceptance/tests/ingress-gateway/ingress_gateway_test.go +++ b/acceptance/tests/ingress-gateway/ingress_gateway_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package ingressgateway import ( diff --git a/acceptance/tests/ingress-gateway/main_test.go b/acceptance/tests/ingress-gateway/main_test.go index bd927f96cb..4a74d2f361 100644 --- a/acceptance/tests/ingress-gateway/main_test.go +++ b/acceptance/tests/ingress-gateway/main_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package ingressgateway import ( diff --git a/acceptance/tests/metrics/main_test.go b/acceptance/tests/metrics/main_test.go index 8717c7c4b5..d3c15b811b 100644 --- a/acceptance/tests/metrics/main_test.go +++ b/acceptance/tests/metrics/main_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package metrics import ( diff --git a/acceptance/tests/metrics/metrics_test.go b/acceptance/tests/metrics/metrics_test.go index 5d05e45aa4..acfe465b00 100644 --- a/acceptance/tests/metrics/metrics_test.go +++ b/acceptance/tests/metrics/metrics_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package metrics import ( diff --git a/acceptance/tests/partitions/main_test.go b/acceptance/tests/partitions/main_test.go index b2758a572c..9368c12f00 100644 --- a/acceptance/tests/partitions/main_test.go +++ b/acceptance/tests/partitions/main_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package partitions import ( diff --git a/acceptance/tests/partitions/partitions_connect_test.go b/acceptance/tests/partitions/partitions_connect_test.go index f205ac67e2..b14f079a68 100644 --- a/acceptance/tests/partitions/partitions_connect_test.go +++ b/acceptance/tests/partitions/partitions_connect_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package partitions import ( diff --git a/acceptance/tests/partitions/partitions_sync_test.go b/acceptance/tests/partitions/partitions_sync_test.go index e29ef18c78..344d64a780 100644 --- a/acceptance/tests/partitions/partitions_sync_test.go +++ b/acceptance/tests/partitions/partitions_sync_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package partitions import ( diff --git a/acceptance/tests/peering/main_test.go b/acceptance/tests/peering/main_test.go index 12bb35afd5..64c5f8ed03 100644 --- a/acceptance/tests/peering/main_test.go +++ b/acceptance/tests/peering/main_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package peering import ( diff --git a/acceptance/tests/peering/peering_connect_namespaces_test.go b/acceptance/tests/peering/peering_connect_namespaces_test.go index 3e243b4781..9276582db3 100644 --- a/acceptance/tests/peering/peering_connect_namespaces_test.go +++ b/acceptance/tests/peering/peering_connect_namespaces_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package peering import ( diff --git a/acceptance/tests/peering/peering_connect_test.go b/acceptance/tests/peering/peering_connect_test.go index 0761854497..ad62ca6926 100644 --- a/acceptance/tests/peering/peering_connect_test.go +++ b/acceptance/tests/peering/peering_connect_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package peering import ( diff --git a/acceptance/tests/snapshot-agent/main_test.go b/acceptance/tests/snapshot-agent/main_test.go index daa389d4c4..3d70823059 100644 --- a/acceptance/tests/snapshot-agent/main_test.go +++ b/acceptance/tests/snapshot-agent/main_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package snapshotagent import ( diff --git a/acceptance/tests/snapshot-agent/snapshot_agent_k8s_secret_test.go b/acceptance/tests/snapshot-agent/snapshot_agent_k8s_secret_test.go index a4c1ef7494..b5613fe76d 100644 --- a/acceptance/tests/snapshot-agent/snapshot_agent_k8s_secret_test.go +++ b/acceptance/tests/snapshot-agent/snapshot_agent_k8s_secret_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package snapshotagent import ( diff --git a/acceptance/tests/snapshot-agent/snapshot_agent_vault_test.go b/acceptance/tests/snapshot-agent/snapshot_agent_vault_test.go index ee20ffd9d7..10cceb5952 100644 --- a/acceptance/tests/snapshot-agent/snapshot_agent_vault_test.go +++ b/acceptance/tests/snapshot-agent/snapshot_agent_vault_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package snapshotagent import ( diff --git a/acceptance/tests/sync/main_test.go b/acceptance/tests/sync/main_test.go index 80c394e561..a26ecc9670 100644 --- a/acceptance/tests/sync/main_test.go +++ b/acceptance/tests/sync/main_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package sync import ( diff --git a/acceptance/tests/sync/sync_catalog_namespaces_test.go b/acceptance/tests/sync/sync_catalog_namespaces_test.go index 79f592033c..7634220b6b 100644 --- a/acceptance/tests/sync/sync_catalog_namespaces_test.go +++ b/acceptance/tests/sync/sync_catalog_namespaces_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package sync import ( diff --git a/acceptance/tests/sync/sync_catalog_test.go b/acceptance/tests/sync/sync_catalog_test.go index c4f873fcbd..7407126580 100644 --- a/acceptance/tests/sync/sync_catalog_test.go +++ b/acceptance/tests/sync/sync_catalog_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package sync import ( diff --git a/acceptance/tests/terminating-gateway/common.go b/acceptance/tests/terminating-gateway/common.go index b94dd8a897..908e6cbec1 100644 --- a/acceptance/tests/terminating-gateway/common.go +++ b/acceptance/tests/terminating-gateway/common.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package terminatinggateway import ( diff --git a/acceptance/tests/terminating-gateway/main_test.go b/acceptance/tests/terminating-gateway/main_test.go index 477e125f94..0c8127623d 100644 --- a/acceptance/tests/terminating-gateway/main_test.go +++ b/acceptance/tests/terminating-gateway/main_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package terminatinggateway import ( diff --git a/acceptance/tests/terminating-gateway/terminating_gateway_destinations_test.go b/acceptance/tests/terminating-gateway/terminating_gateway_destinations_test.go index 4ff4ae7bd4..109975d731 100644 --- a/acceptance/tests/terminating-gateway/terminating_gateway_destinations_test.go +++ b/acceptance/tests/terminating-gateway/terminating_gateway_destinations_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package terminatinggateway import ( diff --git a/acceptance/tests/terminating-gateway/terminating_gateway_namespaces_test.go b/acceptance/tests/terminating-gateway/terminating_gateway_namespaces_test.go index 8c4435ae75..7ad9f7da10 100644 --- a/acceptance/tests/terminating-gateway/terminating_gateway_namespaces_test.go +++ b/acceptance/tests/terminating-gateway/terminating_gateway_namespaces_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package terminatinggateway import ( diff --git a/acceptance/tests/terminating-gateway/terminating_gateway_test.go b/acceptance/tests/terminating-gateway/terminating_gateway_test.go index 16809de5e2..25792e9abb 100644 --- a/acceptance/tests/terminating-gateway/terminating_gateway_test.go +++ b/acceptance/tests/terminating-gateway/terminating_gateway_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package terminatinggateway import ( diff --git a/acceptance/tests/vault/main_test.go b/acceptance/tests/vault/main_test.go index 1d3a5a5842..e20892bf1c 100644 --- a/acceptance/tests/vault/main_test.go +++ b/acceptance/tests/vault/main_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package vault import ( diff --git a/acceptance/tests/vault/vault_namespaces_test.go b/acceptance/tests/vault/vault_namespaces_test.go index 8d0beefdc0..68fa819a84 100644 --- a/acceptance/tests/vault/vault_namespaces_test.go +++ b/acceptance/tests/vault/vault_namespaces_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package vault import ( diff --git a/acceptance/tests/vault/vault_partitions_test.go b/acceptance/tests/vault/vault_partitions_test.go index 0fff6726dc..53bdc23e97 100644 --- a/acceptance/tests/vault/vault_partitions_test.go +++ b/acceptance/tests/vault/vault_partitions_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package vault import ( diff --git a/acceptance/tests/vault/vault_test.go b/acceptance/tests/vault/vault_test.go index 4d43d8bb5b..47d58b68c5 100644 --- a/acceptance/tests/vault/vault_test.go +++ b/acceptance/tests/vault/vault_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package vault import ( diff --git a/acceptance/tests/vault/vault_tls_auto_reload_test.go b/acceptance/tests/vault/vault_tls_auto_reload_test.go index f079ee7492..c3a3ae4034 100644 --- a/acceptance/tests/vault/vault_tls_auto_reload_test.go +++ b/acceptance/tests/vault/vault_tls_auto_reload_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package vault import ( diff --git a/acceptance/tests/vault/vault_wan_fed_test.go b/acceptance/tests/vault/vault_wan_fed_test.go index 0a08810463..21a86937f4 100644 --- a/acceptance/tests/vault/vault_wan_fed_test.go +++ b/acceptance/tests/vault/vault_wan_fed_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package vault import ( diff --git a/acceptance/tests/wan-federation/main_test.go b/acceptance/tests/wan-federation/main_test.go index 197a3181e8..ced18d5cc7 100644 --- a/acceptance/tests/wan-federation/main_test.go +++ b/acceptance/tests/wan-federation/main_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package wanfederation import ( diff --git a/acceptance/tests/wan-federation/wan_federation_test.go b/acceptance/tests/wan-federation/wan_federation_test.go index 1b23633077..ced126af42 100644 --- a/acceptance/tests/wan-federation/wan_federation_test.go +++ b/acceptance/tests/wan-federation/wan_federation_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package wanfederation import ( diff --git a/charts/consul/Chart.yaml b/charts/consul/Chart.yaml index ffd7961302..d0216aa36e 100644 --- a/charts/consul/Chart.yaml +++ b/charts/consul/Chart.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: v2 name: consul version: 1.2.0-dev diff --git a/charts/consul/addons/gen.sh b/charts/consul/addons/gen.sh index 967b368c63..1d03390bed 100755 --- a/charts/consul/addons/gen.sh +++ b/charts/consul/addons/gen.sh @@ -1,4 +1,7 @@ #!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + WD=$(dirname "$0") WD=$(cd "$WD"; pwd) diff --git a/charts/consul/addons/values/prometheus.yaml b/charts/consul/addons/values/prometheus.yaml index 9ffe9af5ae..1f90636f2e 100644 --- a/charts/consul/addons/values/prometheus.yaml +++ b/charts/consul/addons/values/prometheus.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + # Disable non-essential components alertmanager: enabled: false diff --git a/charts/consul/templates/api-gateway-controller-clusterrole.yaml b/charts/consul/templates/api-gateway-controller-clusterrole.yaml index eac2bd1f69..5d2d45987b 100644 --- a/charts/consul/templates/api-gateway-controller-clusterrole.yaml +++ b/charts/consul/templates/api-gateway-controller-clusterrole.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.apiGateway.enabled }} # The ClusterRole to enable the API Gateway controller to access required api endpoints. apiVersion: rbac.authorization.k8s.io/v1 diff --git a/charts/consul/templates/api-gateway-controller-clusterrolebinding.yaml b/charts/consul/templates/api-gateway-controller-clusterrolebinding.yaml index d083a08129..c874f5c7af 100644 --- a/charts/consul/templates/api-gateway-controller-clusterrolebinding.yaml +++ b/charts/consul/templates/api-gateway-controller-clusterrolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.apiGateway.enabled }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/charts/consul/templates/api-gateway-controller-deployment.yaml b/charts/consul/templates/api-gateway-controller-deployment.yaml index 86517d7140..4d757cfa2c 100644 --- a/charts/consul/templates/api-gateway-controller-deployment.yaml +++ b/charts/consul/templates/api-gateway-controller-deployment.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.apiGateway.enabled }} {{- if not .Values.client.grpc }}{{ fail "client.grpc must be true for api gateway" }}{{ end }} {{- if not .Values.apiGateway.image}}{{ fail "apiGateway.image must be set to enable api gateway" }}{{ end }} diff --git a/charts/consul/templates/api-gateway-controller-podsecuritypolicy.yaml b/charts/consul/templates/api-gateway-controller-podsecuritypolicy.yaml index 390d084303..3ac3a05ba3 100644 --- a/charts/consul/templates/api-gateway-controller-podsecuritypolicy.yaml +++ b/charts/consul/templates/api-gateway-controller-podsecuritypolicy.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if and .Values.apiGateway.enabled .Values.global.enablePodSecurityPolicies }} apiVersion: policy/v1beta1 kind: PodSecurityPolicy diff --git a/charts/consul/templates/api-gateway-controller-service.yaml b/charts/consul/templates/api-gateway-controller-service.yaml index aa79ff9fc3..c2ed00cd88 100644 --- a/charts/consul/templates/api-gateway-controller-service.yaml +++ b/charts/consul/templates/api-gateway-controller-service.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.apiGateway.enabled }} apiVersion: v1 kind: Service diff --git a/charts/consul/templates/api-gateway-controller-serviceaccount.yaml b/charts/consul/templates/api-gateway-controller-serviceaccount.yaml index 98292a8dbe..0ee711f164 100644 --- a/charts/consul/templates/api-gateway-controller-serviceaccount.yaml +++ b/charts/consul/templates/api-gateway-controller-serviceaccount.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.apiGateway.enabled }} apiVersion: v1 kind: ServiceAccount diff --git a/charts/consul/templates/api-gateway-gatewayclass.yaml b/charts/consul/templates/api-gateway-gatewayclass.yaml index d9ba85e633..4a3a59f849 100644 --- a/charts/consul/templates/api-gateway-gatewayclass.yaml +++ b/charts/consul/templates/api-gateway-gatewayclass.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (and .Values.apiGateway.enabled .Values.apiGateway.managedGatewayClass.enabled) }} apiVersion: gateway.networking.k8s.io/v1alpha2 kind: GatewayClass diff --git a/charts/consul/templates/api-gateway-gatewayclassconfig.yaml b/charts/consul/templates/api-gateway-gatewayclassconfig.yaml index ba0e6c63db..feed4e158b 100644 --- a/charts/consul/templates/api-gateway-gatewayclassconfig.yaml +++ b/charts/consul/templates/api-gateway-gatewayclassconfig.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (and .Values.apiGateway.enabled .Values.apiGateway.managedGatewayClass.enabled) }} apiVersion: api-gateway.consul.hashicorp.com/v1alpha1 kind: GatewayClassConfig diff --git a/charts/consul/templates/api-gateway-podsecuritypolicy.yaml b/charts/consul/templates/api-gateway-podsecuritypolicy.yaml index 48f826f995..f445cb77bf 100644 --- a/charts/consul/templates/api-gateway-podsecuritypolicy.yaml +++ b/charts/consul/templates/api-gateway-podsecuritypolicy.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if and .Values.apiGateway.enabled .Values.global.enablePodSecurityPolicies }} apiVersion: policy/v1beta1 kind: PodSecurityPolicy diff --git a/charts/consul/templates/auth-method-clusterrole.yaml b/charts/consul/templates/auth-method-clusterrole.yaml index 6b8f2c5451..bd696f479f 100644 --- a/charts/consul/templates/auth-method-clusterrole.yaml +++ b/charts/consul/templates/auth-method-clusterrole.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.global.acls.manageSystemACLs }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/charts/consul/templates/auth-method-clusterrolebinding.yaml b/charts/consul/templates/auth-method-clusterrolebinding.yaml index 9bd6c64113..9c6b7cef31 100644 --- a/charts/consul/templates/auth-method-clusterrolebinding.yaml +++ b/charts/consul/templates/auth-method-clusterrolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.global.acls.manageSystemACLs }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/charts/consul/templates/auth-method-secret.yaml b/charts/consul/templates/auth-method-secret.yaml index af0aeb4e1b..04fb0c164d 100644 --- a/charts/consul/templates/auth-method-secret.yaml +++ b/charts/consul/templates/auth-method-secret.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.global.acls.manageSystemACLs }} apiVersion: v1 kind: Secret diff --git a/charts/consul/templates/auth-method-serviceaccount.yaml b/charts/consul/templates/auth-method-serviceaccount.yaml index 098339b8c8..453063ecd3 100644 --- a/charts/consul/templates/auth-method-serviceaccount.yaml +++ b/charts/consul/templates/auth-method-serviceaccount.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.global.acls.manageSystemACLs }} apiVersion: v1 kind: ServiceAccount diff --git a/charts/consul/templates/client-config-configmap.yaml b/charts/consul/templates/client-config-configmap.yaml index f9650a100b..9a7122aa02 100644 --- a/charts/consul/templates/client-config-configmap.yaml +++ b/charts/consul/templates/client-config-configmap.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} # ConfigMap with extra configuration specified directly to the chart # for client agents only. diff --git a/charts/consul/templates/client-daemonset.yaml b/charts/consul/templates/client-daemonset.yaml index 09a70b394e..91401eebdf 100644 --- a/charts/consul/templates/client-daemonset.yaml +++ b/charts/consul/templates/client-daemonset.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.global.imageK8s }}{{ fail "global.imageK8s is not a valid key, use global.imageK8S (note the capital 'S')" }}{{ end -}} {{- if (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} diff --git a/charts/consul/templates/client-podsecuritypolicy.yaml b/charts/consul/templates/client-podsecuritypolicy.yaml index 0121bdf586..db87e29f8d 100644 --- a/charts/consul/templates/client-podsecuritypolicy.yaml +++ b/charts/consul/templates/client-podsecuritypolicy.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (and .Values.global.enablePodSecurityPolicies (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled))) }} apiVersion: policy/v1beta1 kind: PodSecurityPolicy diff --git a/charts/consul/templates/client-role.yaml b/charts/consul/templates/client-role.yaml index 7f05b82e6b..aebe8e1a52 100644 --- a/charts/consul/templates/client-role.yaml +++ b/charts/consul/templates/client-role.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} apiVersion: rbac.authorization.k8s.io/v1 kind: Role diff --git a/charts/consul/templates/client-rolebinding.yaml b/charts/consul/templates/client-rolebinding.yaml index b034c70e55..4281a7869e 100644 --- a/charts/consul/templates/client-rolebinding.yaml +++ b/charts/consul/templates/client-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding diff --git a/charts/consul/templates/client-securitycontextconstraints.yaml b/charts/consul/templates/client-securitycontextconstraints.yaml index 07e7711384..7ae5b7ba2a 100644 --- a/charts/consul/templates/client-securitycontextconstraints.yaml +++ b/charts/consul/templates/client-securitycontextconstraints.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (and .Values.global.openshift.enabled (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled))) }} apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints diff --git a/charts/consul/templates/client-serviceaccount.yaml b/charts/consul/templates/client-serviceaccount.yaml index addd757b84..44b74a5c53 100644 --- a/charts/consul/templates/client-serviceaccount.yaml +++ b/charts/consul/templates/client-serviceaccount.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} apiVersion: v1 kind: ServiceAccount diff --git a/charts/consul/templates/cni-clusterrole.yaml b/charts/consul/templates/cni-clusterrole.yaml index 773942cca8..a1cda2b7c5 100644 --- a/charts/consul/templates/cni-clusterrole.yaml +++ b/charts/consul/templates/cni-clusterrole.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.connectInject.cni.enabled }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/charts/consul/templates/cni-clusterrolebinding.yaml b/charts/consul/templates/cni-clusterrolebinding.yaml index 4b860388b6..f86391c259 100644 --- a/charts/consul/templates/cni-clusterrolebinding.yaml +++ b/charts/consul/templates/cni-clusterrolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.connectInject.cni.enabled }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/charts/consul/templates/cni-daemonset.yaml b/charts/consul/templates/cni-daemonset.yaml index ae04d9e657..1eda31998c 100644 --- a/charts/consul/templates/cni-daemonset.yaml +++ b/charts/consul/templates/cni-daemonset.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (and (.Values.connectInject.cni.enabled) (not .Values.connectInject.enabled)) }}{{ fail "connectInject.enabled must be true if connectInject.cni.enabled is true" }}{{ end -}} {{- if .Values.connectInject.cni.enabled }} apiVersion: apps/v1 diff --git a/charts/consul/templates/cni-networkattachmentdefinition.yaml b/charts/consul/templates/cni-networkattachmentdefinition.yaml index 80ef50bac6..056b74fa71 100644 --- a/charts/consul/templates/cni-networkattachmentdefinition.yaml +++ b/charts/consul/templates/cni-networkattachmentdefinition.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (and (.Values.connectInject.cni.enabled) (.Values.connectInject.cni.multus)) }} apiVersion: "k8s.cni.cncf.io/v1" kind: NetworkAttachmentDefinition diff --git a/charts/consul/templates/cni-podsecuritypolicy.yaml b/charts/consul/templates/cni-podsecuritypolicy.yaml index b600ed1b4b..8747387850 100644 --- a/charts/consul/templates/cni-podsecuritypolicy.yaml +++ b/charts/consul/templates/cni-podsecuritypolicy.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (and .Values.connectInject.cni.enabled .Values.global.enablePodSecurityPolicies) }} apiVersion: policy/v1beta1 kind: PodSecurityPolicy diff --git a/charts/consul/templates/cni-resourcequota.yaml b/charts/consul/templates/cni-resourcequota.yaml index 054c3061f5..1b8a8c7332 100644 --- a/charts/consul/templates/cni-resourcequota.yaml +++ b/charts/consul/templates/cni-resourcequota.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.connectInject.cni.enabled }} apiVersion: v1 kind: ResourceQuota diff --git a/charts/consul/templates/cni-securitycontextconstraints.yaml b/charts/consul/templates/cni-securitycontextconstraints.yaml index 2c09dba9b8..d408617925 100644 --- a/charts/consul/templates/cni-securitycontextconstraints.yaml +++ b/charts/consul/templates/cni-securitycontextconstraints.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (and (.Values.connectInject.cni.enabled) (.Values.global.openshift.enabled)) }} apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints diff --git a/charts/consul/templates/cni-serviceaccount.yaml b/charts/consul/templates/cni-serviceaccount.yaml index cf4250b696..b3740d9f4a 100644 --- a/charts/consul/templates/cni-serviceaccount.yaml +++ b/charts/consul/templates/cni-serviceaccount.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.connectInject.cni.enabled }} apiVersion: v1 kind: ServiceAccount diff --git a/charts/consul/templates/connect-inject-clusterrole.yaml b/charts/consul/templates/connect-inject-clusterrole.yaml index f2e12f0ad9..307a9e3aed 100644 --- a/charts/consul/templates/connect-inject-clusterrole.yaml +++ b/charts/consul/templates/connect-inject-clusterrole.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled) }} # The ClusterRole to enable the Connect injector to get, list, watch and patch MutatingWebhookConfiguration. apiVersion: rbac.authorization.k8s.io/v1 diff --git a/charts/consul/templates/connect-inject-clusterrolebinding.yaml b/charts/consul/templates/connect-inject-clusterrolebinding.yaml index c380adb311..de969246a7 100644 --- a/charts/consul/templates/connect-inject-clusterrolebinding.yaml +++ b/charts/consul/templates/connect-inject-clusterrolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled) }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/charts/consul/templates/connect-inject-deployment.yaml b/charts/consul/templates/connect-inject-deployment.yaml index 2b52c1b81c..529cc98787 100644 --- a/charts/consul/templates/connect-inject-deployment.yaml +++ b/charts/consul/templates/connect-inject-deployment.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if and .Values.global.peering.enabled (not .Values.connectInject.enabled) }}{{ fail "setting global.peering.enabled to true requires connectInject.enabled to be true" }}{{ end }} {{- if and .Values.global.peering.enabled (not .Values.global.tls.enabled) }}{{ fail "setting global.peering.enabled to true requires global.tls.enabled to be true" }}{{ end }} {{- if and .Values.global.peering.enabled (not .Values.meshGateway.enabled) }}{{ fail "setting global.peering.enabled to true requires meshGateway.enabled to be true" }}{{ end }} diff --git a/charts/consul/templates/connect-inject-leader-election-role.yaml b/charts/consul/templates/connect-inject-leader-election-role.yaml index 703aaffaac..2efaf64ec0 100644 --- a/charts/consul/templates/connect-inject-leader-election-role.yaml +++ b/charts/consul/templates/connect-inject-leader-election-role.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled) }} apiVersion: rbac.authorization.k8s.io/v1 kind: Role diff --git a/charts/consul/templates/connect-inject-leader-election-rolebinding.yaml b/charts/consul/templates/connect-inject-leader-election-rolebinding.yaml index 9a27d3c868..28c4c9a7de 100644 --- a/charts/consul/templates/connect-inject-leader-election-rolebinding.yaml +++ b/charts/consul/templates/connect-inject-leader-election-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled) }} apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding diff --git a/charts/consul/templates/connect-inject-mutatingwebhookconfiguration.yaml b/charts/consul/templates/connect-inject-mutatingwebhookconfiguration.yaml index afcfd3800f..c01ffc333b 100644 --- a/charts/consul/templates/connect-inject-mutatingwebhookconfiguration.yaml +++ b/charts/consul/templates/connect-inject-mutatingwebhookconfiguration.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled)) }} # The MutatingWebhookConfiguration to enable the Connect injector. apiVersion: admissionregistration.k8s.io/v1 diff --git a/charts/consul/templates/connect-inject-podsecuritypolicy.yaml b/charts/consul/templates/connect-inject-podsecuritypolicy.yaml index 0fafef7c40..5f449eba4d 100644 --- a/charts/consul/templates/connect-inject-podsecuritypolicy.yaml +++ b/charts/consul/templates/connect-inject-podsecuritypolicy.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (and .Values.global.enablePodSecurityPolicies (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled))) }} apiVersion: policy/v1beta1 kind: PodSecurityPolicy diff --git a/charts/consul/templates/connect-inject-service.yaml b/charts/consul/templates/connect-inject-service.yaml index b0284af74d..39ca054b30 100644 --- a/charts/consul/templates/connect-inject-service.yaml +++ b/charts/consul/templates/connect-inject-service.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled)) }} # The service for the Connect sidecar injector apiVersion: v1 diff --git a/charts/consul/templates/connect-inject-serviceaccount.yaml b/charts/consul/templates/connect-inject-serviceaccount.yaml index ea2352c7ac..3ec08e7ffb 100644 --- a/charts/consul/templates/connect-inject-serviceaccount.yaml +++ b/charts/consul/templates/connect-inject-serviceaccount.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled) }} apiVersion: v1 kind: ServiceAccount diff --git a/charts/consul/templates/connect-injector-disruptionbudget.yaml b/charts/consul/templates/connect-injector-disruptionbudget.yaml index 9b9cf2e39e..4c7753a375 100644 --- a/charts/consul/templates/connect-injector-disruptionbudget.yaml +++ b/charts/consul/templates/connect-injector-disruptionbudget.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (and .Values.connectInject.disruptionBudget.enabled (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled))) }} # PodDisruptionBudget to prevent degrading the connectInject cluster through # voluntary cluster changes. diff --git a/charts/consul/templates/crd-exportedservices.yaml b/charts/consul/templates/crd-exportedservices.yaml index 007990372c..5ff9450307 100644 --- a/charts/consul/templates/crd-exportedservices.yaml +++ b/charts/consul/templates/crd-exportedservices.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.connectInject.enabled }} --- apiVersion: apiextensions.k8s.io/v1 diff --git a/charts/consul/templates/crd-ingressgateways.yaml b/charts/consul/templates/crd-ingressgateways.yaml index a01fafd8dd..d7bd768593 100644 --- a/charts/consul/templates/crd-ingressgateways.yaml +++ b/charts/consul/templates/crd-ingressgateways.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.connectInject.enabled }} --- apiVersion: apiextensions.k8s.io/v1 diff --git a/charts/consul/templates/crd-meshes.yaml b/charts/consul/templates/crd-meshes.yaml index 2e33eb9653..2e238c318e 100644 --- a/charts/consul/templates/crd-meshes.yaml +++ b/charts/consul/templates/crd-meshes.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.connectInject.enabled }} --- apiVersion: apiextensions.k8s.io/v1 diff --git a/charts/consul/templates/crd-peeringacceptors.yaml b/charts/consul/templates/crd-peeringacceptors.yaml index e06e830f04..e74bb2d733 100644 --- a/charts/consul/templates/crd-peeringacceptors.yaml +++ b/charts/consul/templates/crd-peeringacceptors.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if and .Values.connectInject.enabled .Values.global.peering.enabled }} --- apiVersion: apiextensions.k8s.io/v1 diff --git a/charts/consul/templates/crd-peeringdialers.yaml b/charts/consul/templates/crd-peeringdialers.yaml index e24401e761..aab95bb61a 100644 --- a/charts/consul/templates/crd-peeringdialers.yaml +++ b/charts/consul/templates/crd-peeringdialers.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if and .Values.connectInject.enabled .Values.global.peering.enabled }} --- apiVersion: apiextensions.k8s.io/v1 diff --git a/charts/consul/templates/crd-proxydefaults.yaml b/charts/consul/templates/crd-proxydefaults.yaml index 749f2e4257..6c34618cb3 100644 --- a/charts/consul/templates/crd-proxydefaults.yaml +++ b/charts/consul/templates/crd-proxydefaults.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.connectInject.enabled }} --- apiVersion: apiextensions.k8s.io/v1 diff --git a/charts/consul/templates/crd-servicedefaults.yaml b/charts/consul/templates/crd-servicedefaults.yaml index 5c6ecc7476..0cb99dc97b 100644 --- a/charts/consul/templates/crd-servicedefaults.yaml +++ b/charts/consul/templates/crd-servicedefaults.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.connectInject.enabled }} --- apiVersion: apiextensions.k8s.io/v1 diff --git a/charts/consul/templates/crd-serviceintentions.yaml b/charts/consul/templates/crd-serviceintentions.yaml index cdbb5413b0..18ab5b9d97 100644 --- a/charts/consul/templates/crd-serviceintentions.yaml +++ b/charts/consul/templates/crd-serviceintentions.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.connectInject.enabled }} --- apiVersion: apiextensions.k8s.io/v1 diff --git a/charts/consul/templates/crd-serviceresolvers.yaml b/charts/consul/templates/crd-serviceresolvers.yaml index e058052e97..f8989a8cc6 100644 --- a/charts/consul/templates/crd-serviceresolvers.yaml +++ b/charts/consul/templates/crd-serviceresolvers.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.connectInject.enabled }} --- apiVersion: apiextensions.k8s.io/v1 diff --git a/charts/consul/templates/crd-servicerouters.yaml b/charts/consul/templates/crd-servicerouters.yaml index 5052facc06..c927745f45 100644 --- a/charts/consul/templates/crd-servicerouters.yaml +++ b/charts/consul/templates/crd-servicerouters.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.connectInject.enabled }} --- apiVersion: apiextensions.k8s.io/v1 diff --git a/charts/consul/templates/crd-servicesplitters.yaml b/charts/consul/templates/crd-servicesplitters.yaml index a2af050c3d..4c28e5a506 100644 --- a/charts/consul/templates/crd-servicesplitters.yaml +++ b/charts/consul/templates/crd-servicesplitters.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.connectInject.enabled }} --- apiVersion: apiextensions.k8s.io/v1 diff --git a/charts/consul/templates/crd-terminatinggateways.yaml b/charts/consul/templates/crd-terminatinggateways.yaml index 583c218be8..2ed7143f8b 100644 --- a/charts/consul/templates/crd-terminatinggateways.yaml +++ b/charts/consul/templates/crd-terminatinggateways.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.connectInject.enabled }} --- apiVersion: apiextensions.k8s.io/v1 diff --git a/charts/consul/templates/create-federation-secret-job.yaml b/charts/consul/templates/create-federation-secret-job.yaml index 4f83a1f82a..a90b2610bf 100644 --- a/charts/consul/templates/create-federation-secret-job.yaml +++ b/charts/consul/templates/create-federation-secret-job.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.global.federation.createFederationSecret }} {{- if not .Values.global.federation.enabled }}{{ fail "global.federation.enabled must be true when global.federation.createFederationSecret is true" }}{{ end }} {{- if and (not .Values.global.acls.createReplicationToken) .Values.global.acls.manageSystemACLs }}{{ fail "global.acls.createReplicationToken must be true when global.acls.manageSystemACLs is true because the federation secret must include the replication token" }}{{ end }} diff --git a/charts/consul/templates/create-federation-secret-podsecuritypolicy.yaml b/charts/consul/templates/create-federation-secret-podsecuritypolicy.yaml index 8217311992..e05659807d 100644 --- a/charts/consul/templates/create-federation-secret-podsecuritypolicy.yaml +++ b/charts/consul/templates/create-federation-secret-podsecuritypolicy.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.global.enablePodSecurityPolicies }} {{- if .Values.global.federation.createFederationSecret }} apiVersion: policy/v1beta1 diff --git a/charts/consul/templates/create-federation-secret-role.yaml b/charts/consul/templates/create-federation-secret-role.yaml index 086932a831..6d285b968a 100644 --- a/charts/consul/templates/create-federation-secret-role.yaml +++ b/charts/consul/templates/create-federation-secret-role.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.global.federation.createFederationSecret }} apiVersion: rbac.authorization.k8s.io/v1 kind: Role diff --git a/charts/consul/templates/create-federation-secret-rolebinding.yaml b/charts/consul/templates/create-federation-secret-rolebinding.yaml index 3db8e7cb06..e9c326448f 100644 --- a/charts/consul/templates/create-federation-secret-rolebinding.yaml +++ b/charts/consul/templates/create-federation-secret-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.global.federation.createFederationSecret }} apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding diff --git a/charts/consul/templates/create-federation-secret-serviceaccount.yaml b/charts/consul/templates/create-federation-secret-serviceaccount.yaml index e398ec69c4..843e75b61b 100644 --- a/charts/consul/templates/create-federation-secret-serviceaccount.yaml +++ b/charts/consul/templates/create-federation-secret-serviceaccount.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.global.federation.createFederationSecret }} apiVersion: v1 kind: ServiceAccount diff --git a/charts/consul/templates/dns-service.yaml b/charts/consul/templates/dns-service.yaml index 5bb446bc19..03b3a4cc9b 100644 --- a/charts/consul/templates/dns-service.yaml +++ b/charts/consul/templates/dns-service.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.dns.enabled | toString) "-") .Values.dns.enabled) (and (eq (.Values.dns.enabled | toString) "-") .Values.connectInject.transparentProxy.defaultEnabled)) }} # Service for Consul DNS. apiVersion: v1 diff --git a/charts/consul/templates/enterprise-license-job.yaml b/charts/consul/templates/enterprise-license-job.yaml index 0122690104..e6be55878a 100644 --- a/charts/consul/templates/enterprise-license-job.yaml +++ b/charts/consul/templates/enterprise-license-job.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.server.enterpriseLicense }}{{ fail "server.enterpriseLicense has been moved to global.enterpriseLicense" }}{{ end -}} {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey (not .Values.global.enterpriseLicense.enableLicenseAutoload)) }} diff --git a/charts/consul/templates/enterprise-license-podsecuritypolicy.yaml b/charts/consul/templates/enterprise-license-podsecuritypolicy.yaml index cf96367473..24adf32765 100644 --- a/charts/consul/templates/enterprise-license-podsecuritypolicy.yaml +++ b/charts/consul/templates/enterprise-license-podsecuritypolicy.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey (not .Values.global.enterpriseLicense.enableLicenseAutoload)) }} {{- if .Values.global.enablePodSecurityPolicies }} diff --git a/charts/consul/templates/enterprise-license-role.yaml b/charts/consul/templates/enterprise-license-role.yaml index 6a1b7fdffa..7536d51c60 100644 --- a/charts/consul/templates/enterprise-license-role.yaml +++ b/charts/consul/templates/enterprise-license-role.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey (not .Values.global.enterpriseLicense.enableLicenseAutoload)) }} apiVersion: rbac.authorization.k8s.io/v1 diff --git a/charts/consul/templates/enterprise-license-rolebinding.yaml b/charts/consul/templates/enterprise-license-rolebinding.yaml index a21118b431..5b479613db 100644 --- a/charts/consul/templates/enterprise-license-rolebinding.yaml +++ b/charts/consul/templates/enterprise-license-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey (not .Values.global.enterpriseLicense.enableLicenseAutoload)) }} apiVersion: rbac.authorization.k8s.io/v1 diff --git a/charts/consul/templates/enterprise-license-serviceaccount.yaml b/charts/consul/templates/enterprise-license-serviceaccount.yaml index 31c9da841e..6286281350 100644 --- a/charts/consul/templates/enterprise-license-serviceaccount.yaml +++ b/charts/consul/templates/enterprise-license-serviceaccount.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey (not .Values.global.enterpriseLicense.enableLicenseAutoload)) }} apiVersion: v1 diff --git a/charts/consul/templates/expose-servers-service.yaml b/charts/consul/templates/expose-servers-service.yaml index d86cec9042..56743bbee6 100644 --- a/charts/consul/templates/expose-servers-service.yaml +++ b/charts/consul/templates/expose-servers-service.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- $serverExposeServiceEnabled := (or (and (ne (.Values.server.exposeService.enabled | toString) "-") .Values.server.exposeService.enabled) (and (eq (.Values.server.exposeService.enabled | toString) "-") .Values.global.adminPartitions.enabled)) -}} {{- if (and $serverEnabled $serverExposeServiceEnabled) }} diff --git a/charts/consul/templates/gossip-encryption-autogenerate-job.yaml b/charts/consul/templates/gossip-encryption-autogenerate-job.yaml index 9d296478a1..fc7548366a 100644 --- a/charts/consul/templates/gossip-encryption-autogenerate-job.yaml +++ b/charts/consul/templates/gossip-encryption-autogenerate-job.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.global.gossipEncryption.autoGenerate }} {{- if (or .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey) }} {{ fail "If global.gossipEncryption.autoGenerate is true, global.gossipEncryption.secretName and global.gossipEncryption.secretKey must not be set." }} diff --git a/charts/consul/templates/gossip-encryption-autogenerate-podsecuritypolicy.yaml b/charts/consul/templates/gossip-encryption-autogenerate-podsecuritypolicy.yaml index 209b3aa343..54095be3a0 100644 --- a/charts/consul/templates/gossip-encryption-autogenerate-podsecuritypolicy.yaml +++ b/charts/consul/templates/gossip-encryption-autogenerate-podsecuritypolicy.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if and .Values.global.gossipEncryption.autoGenerate .Values.global.enablePodSecurityPolicies }} --- apiVersion: policy/v1beta1 diff --git a/charts/consul/templates/gossip-encryption-autogenerate-role.yaml b/charts/consul/templates/gossip-encryption-autogenerate-role.yaml index 8c51c96ffe..70b6058bda 100644 --- a/charts/consul/templates/gossip-encryption-autogenerate-role.yaml +++ b/charts/consul/templates/gossip-encryption-autogenerate-role.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.global.gossipEncryption.autoGenerate }} apiVersion: rbac.authorization.k8s.io/v1 kind: Role diff --git a/charts/consul/templates/gossip-encryption-autogenerate-rolebinding.yaml b/charts/consul/templates/gossip-encryption-autogenerate-rolebinding.yaml index 7118475f64..961d14e240 100644 --- a/charts/consul/templates/gossip-encryption-autogenerate-rolebinding.yaml +++ b/charts/consul/templates/gossip-encryption-autogenerate-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.global.gossipEncryption.autoGenerate }} apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding diff --git a/charts/consul/templates/gossip-encryption-autogenerate-serviceaccount.yaml b/charts/consul/templates/gossip-encryption-autogenerate-serviceaccount.yaml index 1fd620237f..4ea1c6bb39 100644 --- a/charts/consul/templates/gossip-encryption-autogenerate-serviceaccount.yaml +++ b/charts/consul/templates/gossip-encryption-autogenerate-serviceaccount.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.global.gossipEncryption.autoGenerate }} apiVersion: v1 kind: ServiceAccount diff --git a/charts/consul/templates/ingress-gateways-deployment.yaml b/charts/consul/templates/ingress-gateways-deployment.yaml index 4f72031855..8a0f35328c 100644 --- a/charts/consul/templates/ingress-gateways-deployment.yaml +++ b/charts/consul/templates/ingress-gateways-deployment.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.ingressGateways.enabled }} {{- if not .Values.connectInject.enabled }}{{ fail "connectInject.enabled must be true" }}{{ end -}} {{- if and .Values.global.adminPartitions.enabled (not .Values.global.enableConsulNamespaces) }}{{ fail "global.enableConsulNamespaces must be true if global.adminPartitions.enabled=true" }}{{ end }} diff --git a/charts/consul/templates/ingress-gateways-podsecuritypolicy.yaml b/charts/consul/templates/ingress-gateways-podsecuritypolicy.yaml index f7354da2b3..abcf971598 100644 --- a/charts/consul/templates/ingress-gateways-podsecuritypolicy.yaml +++ b/charts/consul/templates/ingress-gateways-podsecuritypolicy.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (and .Values.global.enablePodSecurityPolicies .Values.ingressGateways.enabled) }} {{- $root := . }} {{- range .Values.ingressGateways.gateways }} diff --git a/charts/consul/templates/ingress-gateways-role.yaml b/charts/consul/templates/ingress-gateways-role.yaml index 49e8486e58..ea4567163b 100644 --- a/charts/consul/templates/ingress-gateways-role.yaml +++ b/charts/consul/templates/ingress-gateways-role.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.ingressGateways.enabled }} {{- $root := . }} diff --git a/charts/consul/templates/ingress-gateways-rolebinding.yaml b/charts/consul/templates/ingress-gateways-rolebinding.yaml index 601de775f4..015395ec16 100644 --- a/charts/consul/templates/ingress-gateways-rolebinding.yaml +++ b/charts/consul/templates/ingress-gateways-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.ingressGateways.enabled }} {{- $root := . }} {{- range .Values.ingressGateways.gateways }} diff --git a/charts/consul/templates/ingress-gateways-service.yaml b/charts/consul/templates/ingress-gateways-service.yaml index cf54a740fe..5f0ca22c39 100644 --- a/charts/consul/templates/ingress-gateways-service.yaml +++ b/charts/consul/templates/ingress-gateways-service.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.ingressGateways.enabled }} {{- $root := . }} diff --git a/charts/consul/templates/ingress-gateways-serviceaccount.yaml b/charts/consul/templates/ingress-gateways-serviceaccount.yaml index cea6cafc21..0dc1e4564a 100644 --- a/charts/consul/templates/ingress-gateways-serviceaccount.yaml +++ b/charts/consul/templates/ingress-gateways-serviceaccount.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.ingressGateways.enabled }} {{- $root := . }} {{- $defaults := .Values.ingressGateways.defaults }} diff --git a/charts/consul/templates/mesh-gateway-clusterrole.yaml b/charts/consul/templates/mesh-gateway-clusterrole.yaml index b951418b26..fe4c7ca348 100644 --- a/charts/consul/templates/mesh-gateway-clusterrole.yaml +++ b/charts/consul/templates/mesh-gateway-clusterrole.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.meshGateway.enabled }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/charts/consul/templates/mesh-gateway-clusterrolebinding.yaml b/charts/consul/templates/mesh-gateway-clusterrolebinding.yaml index f8150ebb53..028dea8a34 100644 --- a/charts/consul/templates/mesh-gateway-clusterrolebinding.yaml +++ b/charts/consul/templates/mesh-gateway-clusterrolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.meshGateway.enabled }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/charts/consul/templates/mesh-gateway-deployment.yaml b/charts/consul/templates/mesh-gateway-deployment.yaml index 2b2bdc8c2a..8e2c0cc791 100644 --- a/charts/consul/templates/mesh-gateway-deployment.yaml +++ b/charts/consul/templates/mesh-gateway-deployment.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.meshGateway.enabled }} {{- if not .Values.connectInject.enabled }}{{ fail "connectInject.enabled must be true" }}{{ end -}} {{- if and .Values.global.acls.manageSystemACLs (ne .Values.meshGateway.consulServiceName "") (ne .Values.meshGateway.consulServiceName "mesh-gateway") }}{{ fail "if global.acls.manageSystemACLs is true, meshGateway.consulServiceName cannot be set" }}{{ end -}} diff --git a/charts/consul/templates/mesh-gateway-podsecuritypolicy.yaml b/charts/consul/templates/mesh-gateway-podsecuritypolicy.yaml index b5bbb2fa03..712f58c0cf 100644 --- a/charts/consul/templates/mesh-gateway-podsecuritypolicy.yaml +++ b/charts/consul/templates/mesh-gateway-podsecuritypolicy.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if and .Values.global.enablePodSecurityPolicies .Values.meshGateway.enabled }} apiVersion: policy/v1beta1 kind: PodSecurityPolicy diff --git a/charts/consul/templates/mesh-gateway-service.yaml b/charts/consul/templates/mesh-gateway-service.yaml index 5fdceca8df..3dbfb0486a 100644 --- a/charts/consul/templates/mesh-gateway-service.yaml +++ b/charts/consul/templates/mesh-gateway-service.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if and .Values.meshGateway.enabled }} apiVersion: v1 kind: Service diff --git a/charts/consul/templates/mesh-gateway-serviceaccount.yaml b/charts/consul/templates/mesh-gateway-serviceaccount.yaml index 8c2da5ae06..4ba2b6788d 100644 --- a/charts/consul/templates/mesh-gateway-serviceaccount.yaml +++ b/charts/consul/templates/mesh-gateway-serviceaccount.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.meshGateway.enabled }} apiVersion: v1 kind: ServiceAccount diff --git a/charts/consul/templates/partition-init-job.yaml b/charts/consul/templates/partition-init-job.yaml index db73ef783b..00a500f519 100644 --- a/charts/consul/templates/partition-init-job.yaml +++ b/charts/consul/templates/partition-init-job.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (and .Values.global.adminPartitions.enabled (not $serverEnabled) (ne .Values.global.adminPartitions.name "default")) }} {{- template "consul.reservedNamesFailer" (list .Values.global.adminPartitions.name "global.adminPartitions.name") }} diff --git a/charts/consul/templates/partition-init-podsecuritypolicy.yaml b/charts/consul/templates/partition-init-podsecuritypolicy.yaml index 2bc6782394..4cf3f3e038 100644 --- a/charts/consul/templates/partition-init-podsecuritypolicy.yaml +++ b/charts/consul/templates/partition-init-podsecuritypolicy.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (and .Values.global.adminPartitions.enabled .Values.global.enablePodSecurityPolicies (not $serverEnabled)) }} apiVersion: policy/v1beta1 diff --git a/charts/consul/templates/partition-init-role.yaml b/charts/consul/templates/partition-init-role.yaml index c13a5378eb..592c10e413 100644 --- a/charts/consul/templates/partition-init-role.yaml +++ b/charts/consul/templates/partition-init-role.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (and .Values.global.adminPartitions.enabled (not $serverEnabled)) }} apiVersion: rbac.authorization.k8s.io/v1 diff --git a/charts/consul/templates/partition-init-rolebinding.yaml b/charts/consul/templates/partition-init-rolebinding.yaml index 432d6df6ec..e70b8d63ba 100644 --- a/charts/consul/templates/partition-init-rolebinding.yaml +++ b/charts/consul/templates/partition-init-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (and .Values.global.adminPartitions.enabled (not $serverEnabled)) }} apiVersion: rbac.authorization.k8s.io/v1 diff --git a/charts/consul/templates/partition-init-serviceaccount.yaml b/charts/consul/templates/partition-init-serviceaccount.yaml index 65fcf43b08..5352b5aea6 100644 --- a/charts/consul/templates/partition-init-serviceaccount.yaml +++ b/charts/consul/templates/partition-init-serviceaccount.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (and .Values.global.adminPartitions.enabled (not $serverEnabled)) }} apiVersion: v1 diff --git a/charts/consul/templates/partition-name-configmap.yaml b/charts/consul/templates/partition-name-configmap.yaml index ee330b0f46..bc3e4566c3 100644 --- a/charts/consul/templates/partition-name-configmap.yaml +++ b/charts/consul/templates/partition-name-configmap.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (and .Values.global.adminPartitions.enabled (not $serverEnabled)) }} # Immutable ConfigMap which saves the partition name. Attempting to update this configmap diff --git a/charts/consul/templates/prometheus.yaml b/charts/consul/templates/prometheus.yaml index 4dcede1745..670fd76946 100644 --- a/charts/consul/templates/prometheus.yaml +++ b/charts/consul/templates/prometheus.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.prometheus.enabled }} # This file is auto-generated, see addons/gen.sh --- diff --git a/charts/consul/templates/server-acl-init-cleanup-job.yaml b/charts/consul/templates/server-acl-init-cleanup-job.yaml index 35b0877ab4..63c509d883 100644 --- a/charts/consul/templates/server-acl-init-cleanup-job.yaml +++ b/charts/consul/templates/server-acl-init-cleanup-job.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (or $serverEnabled .Values.externalServers.enabled) }} {{- if .Values.global.acls.manageSystemACLs }} diff --git a/charts/consul/templates/server-acl-init-cleanup-podsecuritypolicy.yaml b/charts/consul/templates/server-acl-init-cleanup-podsecuritypolicy.yaml index dd5dad24df..1ea8ce28b4 100644 --- a/charts/consul/templates/server-acl-init-cleanup-podsecuritypolicy.yaml +++ b/charts/consul/templates/server-acl-init-cleanup-podsecuritypolicy.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (or $serverEnabled .Values.externalServers.enabled) }} {{- if .Values.global.acls.manageSystemACLs }} diff --git a/charts/consul/templates/server-acl-init-cleanup-role.yaml b/charts/consul/templates/server-acl-init-cleanup-role.yaml index 0a2f296a60..e18200adfe 100644 --- a/charts/consul/templates/server-acl-init-cleanup-role.yaml +++ b/charts/consul/templates/server-acl-init-cleanup-role.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (or $serverEnabled .Values.externalServers.enabled) }} {{- if .Values.global.acls.manageSystemACLs }} diff --git a/charts/consul/templates/server-acl-init-cleanup-rolebinding.yaml b/charts/consul/templates/server-acl-init-cleanup-rolebinding.yaml index 268eaa5677..c2365d63bd 100644 --- a/charts/consul/templates/server-acl-init-cleanup-rolebinding.yaml +++ b/charts/consul/templates/server-acl-init-cleanup-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (or $serverEnabled .Values.externalServers.enabled) }} {{- if .Values.global.acls.manageSystemACLs }} diff --git a/charts/consul/templates/server-acl-init-cleanup-serviceaccount.yaml b/charts/consul/templates/server-acl-init-cleanup-serviceaccount.yaml index 604e6d784c..030bd38516 100644 --- a/charts/consul/templates/server-acl-init-cleanup-serviceaccount.yaml +++ b/charts/consul/templates/server-acl-init-cleanup-serviceaccount.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (or $serverEnabled .Values.externalServers.enabled) }} {{- if .Values.global.acls.manageSystemACLs }} diff --git a/charts/consul/templates/server-acl-init-job.yaml b/charts/consul/templates/server-acl-init-job.yaml index e62db41ec2..3e42ce553b 100644 --- a/charts/consul/templates/server-acl-init-job.yaml +++ b/charts/consul/templates/server-acl-init-job.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (and $serverEnabled .Values.externalServers.enabled) }}{{ fail "only one of server.enabled or externalServers.enabled can be set" }}{{ end -}} {{- if (or $serverEnabled .Values.externalServers.enabled) }} diff --git a/charts/consul/templates/server-acl-init-podsecuritypolicy.yaml b/charts/consul/templates/server-acl-init-podsecuritypolicy.yaml index 9bf93e2551..aa35d9ba07 100644 --- a/charts/consul/templates/server-acl-init-podsecuritypolicy.yaml +++ b/charts/consul/templates/server-acl-init-podsecuritypolicy.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (or $serverEnabled .Values.externalServers.enabled) }} {{- if .Values.global.acls.manageSystemACLs }} diff --git a/charts/consul/templates/server-acl-init-role.yaml b/charts/consul/templates/server-acl-init-role.yaml index eb7b6a928e..d165c34492 100644 --- a/charts/consul/templates/server-acl-init-role.yaml +++ b/charts/consul/templates/server-acl-init-role.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (or $serverEnabled .Values.externalServers.enabled) }} {{- if .Values.global.acls.manageSystemACLs }} diff --git a/charts/consul/templates/server-acl-init-rolebinding.yaml b/charts/consul/templates/server-acl-init-rolebinding.yaml index fda4726d9f..ff125f2c5e 100644 --- a/charts/consul/templates/server-acl-init-rolebinding.yaml +++ b/charts/consul/templates/server-acl-init-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (or $serverEnabled .Values.externalServers.enabled) }} {{- if .Values.global.acls.manageSystemACLs }} diff --git a/charts/consul/templates/server-acl-init-serviceaccount.yaml b/charts/consul/templates/server-acl-init-serviceaccount.yaml index c0e257de96..1b0129d168 100644 --- a/charts/consul/templates/server-acl-init-serviceaccount.yaml +++ b/charts/consul/templates/server-acl-init-serviceaccount.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (or $serverEnabled .Values.externalServers.enabled) }} {{- if .Values.global.acls.manageSystemACLs }} diff --git a/charts/consul/templates/server-config-configmap.yaml b/charts/consul/templates/server-config-configmap.yaml index f7dd85f166..6ae8037606 100644 --- a/charts/consul/templates/server-config-configmap.yaml +++ b/charts/consul/templates/server-config-configmap.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} # StatefulSet to run the actual Consul server cluster. apiVersion: v1 diff --git a/charts/consul/templates/server-disruptionbudget.yaml b/charts/consul/templates/server-disruptionbudget.yaml index edf9c1c57f..e4806c21f9 100644 --- a/charts/consul/templates/server-disruptionbudget.yaml +++ b/charts/consul/templates/server-disruptionbudget.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (and .Values.server.disruptionBudget.enabled (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled))) }} # PodDisruptionBudget to prevent degrading the server cluster through # voluntary cluster changes. diff --git a/charts/consul/templates/server-podsecuritypolicy.yaml b/charts/consul/templates/server-podsecuritypolicy.yaml index 09e8d75bd1..4049679043 100644 --- a/charts/consul/templates/server-podsecuritypolicy.yaml +++ b/charts/consul/templates/server-podsecuritypolicy.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (and .Values.global.enablePodSecurityPolicies (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled))) }} apiVersion: policy/v1beta1 kind: PodSecurityPolicy diff --git a/charts/consul/templates/server-role.yaml b/charts/consul/templates/server-role.yaml index 202518bf67..a3d432c299 100644 --- a/charts/consul/templates/server-role.yaml +++ b/charts/consul/templates/server-role.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} apiVersion: rbac.authorization.k8s.io/v1 kind: Role diff --git a/charts/consul/templates/server-rolebinding.yaml b/charts/consul/templates/server-rolebinding.yaml index 8ab705ddbc..c317cab99d 100644 --- a/charts/consul/templates/server-rolebinding.yaml +++ b/charts/consul/templates/server-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding diff --git a/charts/consul/templates/server-securitycontextconstraints.yaml b/charts/consul/templates/server-securitycontextconstraints.yaml index 8edd784ea7..593a842ec2 100644 --- a/charts/consul/templates/server-securitycontextconstraints.yaml +++ b/charts/consul/templates/server-securitycontextconstraints.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (and .Values.global.openshift.enabled .Values.server.exposeGossipAndRPCPorts (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled))) }} apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints diff --git a/charts/consul/templates/server-service.yaml b/charts/consul/templates/server-service.yaml index a392f0e76b..e3ae931a3a 100644 --- a/charts/consul/templates/server-service.yaml +++ b/charts/consul/templates/server-service.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} # Headless service for Consul server DNS entries. This service should only # point to Consul servers. For access to an agent, one should assume that diff --git a/charts/consul/templates/server-serviceaccount.yaml b/charts/consul/templates/server-serviceaccount.yaml index a1617975ae..ff8cc2022a 100644 --- a/charts/consul/templates/server-serviceaccount.yaml +++ b/charts/consul/templates/server-serviceaccount.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} apiVersion: v1 kind: ServiceAccount diff --git a/charts/consul/templates/server-snapshot-agent-configmap.yaml b/charts/consul/templates/server-snapshot-agent-configmap.yaml index da68d1509c..cff5e0d840 100644 --- a/charts/consul/templates/server-snapshot-agent-configmap.yaml +++ b/charts/consul/templates/server-snapshot-agent-configmap.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.server.snapshotAgent.enabled }} apiVersion: v1 kind: ConfigMap diff --git a/charts/consul/templates/server-statefulset.yaml b/charts/consul/templates/server-statefulset.yaml index 8b73306fd7..e389509d88 100644 --- a/charts/consul/templates/server-statefulset.yaml +++ b/charts/consul/templates/server-statefulset.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if and .Values.global.federation.enabled .Values.global.adminPartitions.enabled }}{{ fail "If global.federation.enabled is true, global.adminPartitions.enabled must be false because they are mutually exclusive" }}{{ end }} {{- if and .Values.global.federation.enabled (not .Values.global.tls.enabled) }}{{ fail "If global.federation.enabled is true, global.tls.enabled must be true because federation is only supported with TLS enabled" }}{{ end }} diff --git a/charts/consul/templates/sync-catalog-clusterrole.yaml b/charts/consul/templates/sync-catalog-clusterrole.yaml index 0b0837c0df..5055530f6e 100644 --- a/charts/consul/templates/sync-catalog-clusterrole.yaml +++ b/charts/consul/templates/sync-catalog-clusterrole.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- $syncEnabled := (or (and (ne (.Values.syncCatalog.enabled | toString) "-") .Values.syncCatalog.enabled) (and (eq (.Values.syncCatalog.enabled | toString) "-") .Values.global.enabled)) }} {{- if $syncEnabled }} apiVersion: rbac.authorization.k8s.io/v1 diff --git a/charts/consul/templates/sync-catalog-clusterrolebinding.yaml b/charts/consul/templates/sync-catalog-clusterrolebinding.yaml index 818823cca3..f5e3e0a98a 100644 --- a/charts/consul/templates/sync-catalog-clusterrolebinding.yaml +++ b/charts/consul/templates/sync-catalog-clusterrolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- $syncEnabled := (or (and (ne (.Values.syncCatalog.enabled | toString) "-") .Values.syncCatalog.enabled) (and (eq (.Values.syncCatalog.enabled | toString) "-") .Values.global.enabled)) }} {{- if $syncEnabled }} apiVersion: rbac.authorization.k8s.io/v1 diff --git a/charts/consul/templates/sync-catalog-deployment.yaml b/charts/consul/templates/sync-catalog-deployment.yaml index f2815d9627..94d97fcfc1 100644 --- a/charts/consul/templates/sync-catalog-deployment.yaml +++ b/charts/consul/templates/sync-catalog-deployment.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.syncCatalog.enabled | toString) "-") .Values.syncCatalog.enabled) (and (eq (.Values.syncCatalog.enabled | toString) "-") .Values.global.enabled)) }} {{- template "consul.reservedNamesFailer" (list .Values.syncCatalog.consulNamespaces.consulDestinationNamespace "syncCatalog.consulNamespaces.consulDestinationNamespace") }} {{ template "consul.validateRequiredCloudSecretsExist" . }} diff --git a/charts/consul/templates/sync-catalog-podsecuritypolicy.yaml b/charts/consul/templates/sync-catalog-podsecuritypolicy.yaml index cc70feaab1..0737265591 100644 --- a/charts/consul/templates/sync-catalog-podsecuritypolicy.yaml +++ b/charts/consul/templates/sync-catalog-podsecuritypolicy.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (and .Values.global.enablePodSecurityPolicies (or (and (ne (.Values.syncCatalog.enabled | toString) "-") .Values.syncCatalog.enabled) (and (eq (.Values.syncCatalog.enabled | toString) "-") .Values.global.enabled))) }} apiVersion: policy/v1beta1 kind: PodSecurityPolicy diff --git a/charts/consul/templates/sync-catalog-serviceaccount.yaml b/charts/consul/templates/sync-catalog-serviceaccount.yaml index deab1ad075..a5b6023956 100644 --- a/charts/consul/templates/sync-catalog-serviceaccount.yaml +++ b/charts/consul/templates/sync-catalog-serviceaccount.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- $syncEnabled := (or (and (ne (.Values.syncCatalog.enabled | toString) "-") .Values.syncCatalog.enabled) (and (eq (.Values.syncCatalog.enabled | toString) "-") .Values.global.enabled)) }} {{- if $syncEnabled }} apiVersion: v1 diff --git a/charts/consul/templates/terminating-gateways-deployment.yaml b/charts/consul/templates/terminating-gateways-deployment.yaml index 2f2cb9a921..2fcc27448f 100644 --- a/charts/consul/templates/terminating-gateways-deployment.yaml +++ b/charts/consul/templates/terminating-gateways-deployment.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.terminatingGateways.enabled }} {{- if not .Values.connectInject.enabled }}{{ fail "connectInject.enabled must be true" }}{{ end -}} {{- if and .Values.global.adminPartitions.enabled (not .Values.global.enableConsulNamespaces) }}{{ fail "global.enableConsulNamespaces must be true if global.adminPartitions.enabled=true" }}{{ end }} diff --git a/charts/consul/templates/terminating-gateways-podsecuritypolicy.yaml b/charts/consul/templates/terminating-gateways-podsecuritypolicy.yaml index 97ad2af961..a529f902bd 100644 --- a/charts/consul/templates/terminating-gateways-podsecuritypolicy.yaml +++ b/charts/consul/templates/terminating-gateways-podsecuritypolicy.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (and .Values.global.enablePodSecurityPolicies .Values.terminatingGateways.enabled) }} {{- $root := . }} {{- range .Values.terminatingGateways.gateways }} diff --git a/charts/consul/templates/terminating-gateways-role.yaml b/charts/consul/templates/terminating-gateways-role.yaml index 4ae280ca81..71e3cd0e1e 100644 --- a/charts/consul/templates/terminating-gateways-role.yaml +++ b/charts/consul/templates/terminating-gateways-role.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.terminatingGateways.enabled }} {{- $root := . }} diff --git a/charts/consul/templates/terminating-gateways-rolebinding.yaml b/charts/consul/templates/terminating-gateways-rolebinding.yaml index 4271f8f59c..cf6d533e91 100644 --- a/charts/consul/templates/terminating-gateways-rolebinding.yaml +++ b/charts/consul/templates/terminating-gateways-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.terminatingGateways.enabled }} {{- $root := . }} {{- range .Values.terminatingGateways.gateways }} diff --git a/charts/consul/templates/terminating-gateways-service.yaml b/charts/consul/templates/terminating-gateways-service.yaml index 124900e727..8515897037 100644 --- a/charts/consul/templates/terminating-gateways-service.yaml +++ b/charts/consul/templates/terminating-gateways-service.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.terminatingGateways.enabled }} {{- $root := . }} diff --git a/charts/consul/templates/terminating-gateways-serviceaccount.yaml b/charts/consul/templates/terminating-gateways-serviceaccount.yaml index 211fb5c72f..47f65fb937 100644 --- a/charts/consul/templates/terminating-gateways-serviceaccount.yaml +++ b/charts/consul/templates/terminating-gateways-serviceaccount.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.terminatingGateways.enabled }} {{- $root := . }} {{- $defaults := .Values.terminatingGateways.defaults }} diff --git a/charts/consul/templates/tests/test-runner.yaml b/charts/consul/templates/tests/test-runner.yaml index b8b078003b..985605c2e5 100644 --- a/charts/consul/templates/tests/test-runner.yaml +++ b/charts/consul/templates/tests/test-runner.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if .Values.tests.enabled }} apiVersion: v1 kind: Pod diff --git a/charts/consul/templates/tls-init-cleanup-job.yaml b/charts/consul/templates/tls-init-cleanup-job.yaml index ba29bb84ae..cfc389ca44 100644 --- a/charts/consul/templates/tls-init-cleanup-job.yaml +++ b/charts/consul/templates/tls-init-cleanup-job.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} {{- if not .Values.global.secretsBackend.vault.enabled }} diff --git a/charts/consul/templates/tls-init-cleanup-podsecuritypolicy.yaml b/charts/consul/templates/tls-init-cleanup-podsecuritypolicy.yaml index ed99d5f297..db28affcc2 100644 --- a/charts/consul/templates/tls-init-cleanup-podsecuritypolicy.yaml +++ b/charts/consul/templates/tls-init-cleanup-podsecuritypolicy.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and (and .Values.global.tls.enabled .Values.global.enablePodSecurityPolicies) (not .Values.server.serverCert.secretName)) }} {{- if not .Values.global.secretsBackend.vault.enabled }} diff --git a/charts/consul/templates/tls-init-cleanup-role.yaml b/charts/consul/templates/tls-init-cleanup-role.yaml index aa66e3edc4..00a70aee17 100644 --- a/charts/consul/templates/tls-init-cleanup-role.yaml +++ b/charts/consul/templates/tls-init-cleanup-role.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} {{- if not .Values.global.secretsBackend.vault.enabled }} diff --git a/charts/consul/templates/tls-init-cleanup-rolebinding.yaml b/charts/consul/templates/tls-init-cleanup-rolebinding.yaml index 0d3bfe38e7..a2abcc4b71 100644 --- a/charts/consul/templates/tls-init-cleanup-rolebinding.yaml +++ b/charts/consul/templates/tls-init-cleanup-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} {{- if not .Values.global.secretsBackend.vault.enabled }} diff --git a/charts/consul/templates/tls-init-cleanup-serviceaccount.yaml b/charts/consul/templates/tls-init-cleanup-serviceaccount.yaml index 57e40dd3af..60f1e7402f 100644 --- a/charts/consul/templates/tls-init-cleanup-serviceaccount.yaml +++ b/charts/consul/templates/tls-init-cleanup-serviceaccount.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} {{- if not .Values.global.secretsBackend.vault.enabled }} diff --git a/charts/consul/templates/tls-init-job.yaml b/charts/consul/templates/tls-init-job.yaml index d002ae7a75..d27e31d44b 100644 --- a/charts/consul/templates/tls-init-job.yaml +++ b/charts/consul/templates/tls-init-job.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} {{- if not .Values.global.secretsBackend.vault.enabled }} diff --git a/charts/consul/templates/tls-init-podsecuritypolicy.yaml b/charts/consul/templates/tls-init-podsecuritypolicy.yaml index 5d2a393955..936097cee9 100644 --- a/charts/consul/templates/tls-init-podsecuritypolicy.yaml +++ b/charts/consul/templates/tls-init-podsecuritypolicy.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and (and .Values.global.tls.enabled .Values.global.enablePodSecurityPolicies) (not .Values.server.serverCert.secretName)) }} {{- if not .Values.global.secretsBackend.vault.enabled }} diff --git a/charts/consul/templates/tls-init-role.yaml b/charts/consul/templates/tls-init-role.yaml index 216602ee9f..49a15ad479 100644 --- a/charts/consul/templates/tls-init-role.yaml +++ b/charts/consul/templates/tls-init-role.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} {{- if not .Values.global.secretsBackend.vault.enabled }} diff --git a/charts/consul/templates/tls-init-rolebinding.yaml b/charts/consul/templates/tls-init-rolebinding.yaml index 9b68d97d8c..3217a80909 100644 --- a/charts/consul/templates/tls-init-rolebinding.yaml +++ b/charts/consul/templates/tls-init-rolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} {{- if not .Values.global.secretsBackend.vault.enabled }} diff --git a/charts/consul/templates/tls-init-serviceaccount.yaml b/charts/consul/templates/tls-init-serviceaccount.yaml index f8504da94c..6f3b9407b1 100644 --- a/charts/consul/templates/tls-init-serviceaccount.yaml +++ b/charts/consul/templates/tls-init-serviceaccount.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} {{- if not .Values.global.secretsBackend.vault.enabled }} diff --git a/charts/consul/templates/ui-ingress.yaml b/charts/consul/templates/ui-ingress.yaml index 0414a7cc2d..b1b5dbc8fa 100644 --- a/charts/consul/templates/ui-ingress.yaml +++ b/charts/consul/templates/ui-ingress.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (and (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) (or (and (ne (.Values.ui.enabled | toString) "-") .Values.ui.enabled) (and (eq (.Values.ui.enabled | toString) "-") .Values.global.enabled)) (or (and (ne (.Values.ui.service.enabled | toString) "-") .Values.ui.service.enabled) (and (eq (.Values.ui.service.enabled | toString) "-") .Values.global.enabled))) }} {{- if (and (ne (.Values.ui.ingress.enabled | toString) "-") .Values.ui.ingress.enabled) }} {{- $serviceName := printf "%s-%s" (include "consul.fullname" .) "ui" -}} diff --git a/charts/consul/templates/ui-service.yaml b/charts/consul/templates/ui-service.yaml index dc2abf4fc5..6a8045082e 100644 --- a/charts/consul/templates/ui-service.yaml +++ b/charts/consul/templates/ui-service.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{- if (and (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) (or (and (ne (.Values.ui.enabled | toString) "-") .Values.ui.enabled) (and (eq (.Values.ui.enabled | toString) "-") .Values.global.enabled)) (or (and (ne (.Values.ui.service.enabled | toString) "-") .Values.ui.service.enabled) (and (eq (.Values.ui.service.enabled | toString) "-") .Values.global.enabled))) }} # UI Service for Consul Server apiVersion: v1 diff --git a/charts/consul/templates/webhook-cert-manager-clusterrole.yaml b/charts/consul/templates/webhook-cert-manager-clusterrole.yaml index e13e2dc741..d7b89db85e 100644 --- a/charts/consul/templates/webhook-cert-manager-clusterrole.yaml +++ b/charts/consul/templates/webhook-cert-manager-clusterrole.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} {{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} apiVersion: rbac.authorization.k8s.io/v1 diff --git a/charts/consul/templates/webhook-cert-manager-clusterrolebinding.yaml b/charts/consul/templates/webhook-cert-manager-clusterrolebinding.yaml index 472ef4ee1d..fa9b6f46c5 100644 --- a/charts/consul/templates/webhook-cert-manager-clusterrolebinding.yaml +++ b/charts/consul/templates/webhook-cert-manager-clusterrolebinding.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} {{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} apiVersion: rbac.authorization.k8s.io/v1 diff --git a/charts/consul/templates/webhook-cert-manager-configmap.yaml b/charts/consul/templates/webhook-cert-manager-configmap.yaml index 293dd32d9f..c4b747a795 100644 --- a/charts/consul/templates/webhook-cert-manager-configmap.yaml +++ b/charts/consul/templates/webhook-cert-manager-configmap.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} {{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} apiVersion: v1 diff --git a/charts/consul/templates/webhook-cert-manager-deployment.yaml b/charts/consul/templates/webhook-cert-manager-deployment.yaml index dd93c039d2..558a5d43d5 100644 --- a/charts/consul/templates/webhook-cert-manager-deployment.yaml +++ b/charts/consul/templates/webhook-cert-manager-deployment.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} {{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} apiVersion: apps/v1 diff --git a/charts/consul/templates/webhook-cert-manager-podsecuritypolicy.yaml b/charts/consul/templates/webhook-cert-manager-podsecuritypolicy.yaml index 4d685edc39..d4e5413d52 100644 --- a/charts/consul/templates/webhook-cert-manager-podsecuritypolicy.yaml +++ b/charts/consul/templates/webhook-cert-manager-podsecuritypolicy.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} {{- if (and .Values.global.enablePodSecurityPolicies (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled))) }} {{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} diff --git a/charts/consul/templates/webhook-cert-manager-serviceaccount.yaml b/charts/consul/templates/webhook-cert-manager-serviceaccount.yaml index 68c54f3c27..c4f594945f 100644 --- a/charts/consul/templates/webhook-cert-manager-serviceaccount.yaml +++ b/charts/consul/templates/webhook-cert-manager-serviceaccount.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + {{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} {{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} apiVersion: v1 diff --git a/charts/consul/test/docker/Test.dockerfile b/charts/consul/test/docker/Test.dockerfile index 85f3a607e3..e6a4caa6e0 100644 --- a/charts/consul/test/docker/Test.dockerfile +++ b/charts/consul/test/docker/Test.dockerfile @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + # This Dockerfile installs all the dependencies necessary to run the unit and # acceptance tests. This image also contains gcloud so you can run tests # against a GKE cluster easily. diff --git a/charts/consul/test/terraform/aks/main.tf b/charts/consul/test/terraform/aks/main.tf index 1db5145531..d077471ec9 100644 --- a/charts/consul/test/terraform/aks/main.tf +++ b/charts/consul/test/terraform/aks/main.tf @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + provider "azurerm" { version = "3.40.0" features {} diff --git a/charts/consul/test/terraform/aks/outputs.tf b/charts/consul/test/terraform/aks/outputs.tf index 9ba75d10f8..2fc01b8a1d 100644 --- a/charts/consul/test/terraform/aks/outputs.tf +++ b/charts/consul/test/terraform/aks/outputs.tf @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + output "kubeconfigs" { value = local_file.kubeconfigs.*.filename } \ No newline at end of file diff --git a/charts/consul/test/terraform/aks/variables.tf b/charts/consul/test/terraform/aks/variables.tf index bb9dbef537..554d1b0965 100644 --- a/charts/consul/test/terraform/aks/variables.tf +++ b/charts/consul/test/terraform/aks/variables.tf @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + variable "location" { default = "West US 2" description = "The location to launch this AKS cluster in." diff --git a/charts/consul/test/terraform/eks/main.tf b/charts/consul/test/terraform/eks/main.tf index ca48a5a8fe..07c58a2705 100644 --- a/charts/consul/test/terraform/eks/main.tf +++ b/charts/consul/test/terraform/eks/main.tf @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + provider "aws" { version = ">= 2.28.1" region = var.region diff --git a/charts/consul/test/terraform/eks/outputs.tf b/charts/consul/test/terraform/eks/outputs.tf index 7e88046b1b..1d971bf0b5 100644 --- a/charts/consul/test/terraform/eks/outputs.tf +++ b/charts/consul/test/terraform/eks/outputs.tf @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + output "kubeconfigs" { value = [for cl in module.eks : pathexpand(format("~/.kube/%s", cl.cluster_id))] } diff --git a/charts/consul/test/terraform/eks/variables.tf b/charts/consul/test/terraform/eks/variables.tf index 05f383168b..548cddeb33 100644 --- a/charts/consul/test/terraform/eks/variables.tf +++ b/charts/consul/test/terraform/eks/variables.tf @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + variable "region" { default = "us-west-2" description = "AWS region" diff --git a/charts/consul/test/terraform/gke/main.tf b/charts/consul/test/terraform/gke/main.tf index 1bd574ce2c..f8ff19b912 100644 --- a/charts/consul/test/terraform/gke/main.tf +++ b/charts/consul/test/terraform/gke/main.tf @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + provider "google" { project = var.project version = "~> 3.49.0" diff --git a/charts/consul/test/terraform/gke/outputs.tf b/charts/consul/test/terraform/gke/outputs.tf index 95b68d0296..a0ffac907f 100644 --- a/charts/consul/test/terraform/gke/outputs.tf +++ b/charts/consul/test/terraform/gke/outputs.tf @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + output "cluster_ids" { value = google_container_cluster.cluster.*.id } diff --git a/charts/consul/test/terraform/gke/variables.tf b/charts/consul/test/terraform/gke/variables.tf index ef4a429116..1eebe64145 100644 --- a/charts/consul/test/terraform/gke/variables.tf +++ b/charts/consul/test/terraform/gke/variables.tf @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + variable "project" { description = < .' to build one. # diff --git a/control-plane/api/common/common.go b/control-plane/api/common/common.go index 7c761b6477..2c579ba715 100644 --- a/control-plane/api/common/common.go +++ b/control-plane/api/common/common.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + // Package common holds code that isn't tied to a particular CRD version or type. package common diff --git a/control-plane/api/common/configentry.go b/control-plane/api/common/configentry.go index 2d83ce05b0..3559d8a6bc 100644 --- a/control-plane/api/common/configentry.go +++ b/control-plane/api/common/configentry.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package common import ( diff --git a/control-plane/api/common/configentry_webhook.go b/control-plane/api/common/configentry_webhook.go index 4028a5e3cb..4b8a482d6c 100644 --- a/control-plane/api/common/configentry_webhook.go +++ b/control-plane/api/common/configentry_webhook.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package common import ( diff --git a/control-plane/api/common/configentry_webhook_test.go b/control-plane/api/common/configentry_webhook_test.go index cf79efea85..07f24cff20 100644 --- a/control-plane/api/common/configentry_webhook_test.go +++ b/control-plane/api/common/configentry_webhook_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package common import ( diff --git a/control-plane/api/v1alpha1/exportedservices_types.go b/control-plane/api/v1alpha1/exportedservices_types.go index e05f17a177..e4df1cee0d 100644 --- a/control-plane/api/v1alpha1/exportedservices_types.go +++ b/control-plane/api/v1alpha1/exportedservices_types.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/exportedservices_types_test.go b/control-plane/api/v1alpha1/exportedservices_types_test.go index 8826166a76..e94eb36cbe 100644 --- a/control-plane/api/v1alpha1/exportedservices_types_test.go +++ b/control-plane/api/v1alpha1/exportedservices_types_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/exportedservices_webhook.go b/control-plane/api/v1alpha1/exportedservices_webhook.go index 5a3d2cb2f1..6c870427ac 100644 --- a/control-plane/api/v1alpha1/exportedservices_webhook.go +++ b/control-plane/api/v1alpha1/exportedservices_webhook.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/exportedservices_webhook_test.go b/control-plane/api/v1alpha1/exportedservices_webhook_test.go index 6548c131f7..41476bd822 100644 --- a/control-plane/api/v1alpha1/exportedservices_webhook_test.go +++ b/control-plane/api/v1alpha1/exportedservices_webhook_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/groupversion_info.go b/control-plane/api/v1alpha1/groupversion_info.go index cdbe085af4..3657e9b048 100644 --- a/control-plane/api/v1alpha1/groupversion_info.go +++ b/control-plane/api/v1alpha1/groupversion_info.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + // Package v1alpha1 contains API Schema definitions for the consul.hashicorp.com v1alpha1 API group // +kubebuilder:object:generate=true // +groupName=consul.hashicorp.com diff --git a/control-plane/api/v1alpha1/ingressgateway_types.go b/control-plane/api/v1alpha1/ingressgateway_types.go index c94b6e1458..64e024fbd5 100644 --- a/control-plane/api/v1alpha1/ingressgateway_types.go +++ b/control-plane/api/v1alpha1/ingressgateway_types.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/ingressgateway_types_test.go b/control-plane/api/v1alpha1/ingressgateway_types_test.go index 4942d38e11..dd1c3835e0 100644 --- a/control-plane/api/v1alpha1/ingressgateway_types_test.go +++ b/control-plane/api/v1alpha1/ingressgateway_types_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/ingressgateway_webhook.go b/control-plane/api/v1alpha1/ingressgateway_webhook.go index 7f8ba37558..04e31a0a3e 100644 --- a/control-plane/api/v1alpha1/ingressgateway_webhook.go +++ b/control-plane/api/v1alpha1/ingressgateway_webhook.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/mesh_types.go b/control-plane/api/v1alpha1/mesh_types.go index 502e567829..9a2df631f2 100644 --- a/control-plane/api/v1alpha1/mesh_types.go +++ b/control-plane/api/v1alpha1/mesh_types.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/mesh_types_test.go b/control-plane/api/v1alpha1/mesh_types_test.go index 392c38d354..e20ce19d47 100644 --- a/control-plane/api/v1alpha1/mesh_types_test.go +++ b/control-plane/api/v1alpha1/mesh_types_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/mesh_webhook.go b/control-plane/api/v1alpha1/mesh_webhook.go index 5c714c4e5f..1c0ea3088e 100644 --- a/control-plane/api/v1alpha1/mesh_webhook.go +++ b/control-plane/api/v1alpha1/mesh_webhook.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/mesh_webhook_test.go b/control-plane/api/v1alpha1/mesh_webhook_test.go index 55b0c3a77d..def1ebf54f 100644 --- a/control-plane/api/v1alpha1/mesh_webhook_test.go +++ b/control-plane/api/v1alpha1/mesh_webhook_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/peeringacceptor_types.go b/control-plane/api/v1alpha1/peeringacceptor_types.go index 032870cb80..e1ca013475 100644 --- a/control-plane/api/v1alpha1/peeringacceptor_types.go +++ b/control-plane/api/v1alpha1/peeringacceptor_types.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/peeringacceptor_types_test.go b/control-plane/api/v1alpha1/peeringacceptor_types_test.go index 33d437f46a..d7c05e7e2c 100644 --- a/control-plane/api/v1alpha1/peeringacceptor_types_test.go +++ b/control-plane/api/v1alpha1/peeringacceptor_types_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/peeringacceptor_webhook.go b/control-plane/api/v1alpha1/peeringacceptor_webhook.go index 60367c1384..2bb48b3580 100644 --- a/control-plane/api/v1alpha1/peeringacceptor_webhook.go +++ b/control-plane/api/v1alpha1/peeringacceptor_webhook.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/peeringacceptor_webhook_test.go b/control-plane/api/v1alpha1/peeringacceptor_webhook_test.go index a65966881a..b97f7cf97c 100644 --- a/control-plane/api/v1alpha1/peeringacceptor_webhook_test.go +++ b/control-plane/api/v1alpha1/peeringacceptor_webhook_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/peeringdialer_types.go b/control-plane/api/v1alpha1/peeringdialer_types.go index 4ddde3fe2f..89c16bf3ad 100644 --- a/control-plane/api/v1alpha1/peeringdialer_types.go +++ b/control-plane/api/v1alpha1/peeringdialer_types.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/peeringdialer_types_test.go b/control-plane/api/v1alpha1/peeringdialer_types_test.go index 7e358facb8..69c3569acd 100644 --- a/control-plane/api/v1alpha1/peeringdialer_types_test.go +++ b/control-plane/api/v1alpha1/peeringdialer_types_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/peeringdialer_webhook.go b/control-plane/api/v1alpha1/peeringdialer_webhook.go index fc0b1c38f6..76c2011e4c 100644 --- a/control-plane/api/v1alpha1/peeringdialer_webhook.go +++ b/control-plane/api/v1alpha1/peeringdialer_webhook.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/peeringdialer_webhook_test.go b/control-plane/api/v1alpha1/peeringdialer_webhook_test.go index e8b206e3e6..62b941d6a1 100644 --- a/control-plane/api/v1alpha1/peeringdialer_webhook_test.go +++ b/control-plane/api/v1alpha1/peeringdialer_webhook_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/proxydefaults_types.go b/control-plane/api/v1alpha1/proxydefaults_types.go index 543d8f7ae4..8a211aed56 100644 --- a/control-plane/api/v1alpha1/proxydefaults_types.go +++ b/control-plane/api/v1alpha1/proxydefaults_types.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/proxydefaults_types_test.go b/control-plane/api/v1alpha1/proxydefaults_types_test.go index 225068c136..011ab7d724 100644 --- a/control-plane/api/v1alpha1/proxydefaults_types_test.go +++ b/control-plane/api/v1alpha1/proxydefaults_types_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/proxydefaults_webhook.go b/control-plane/api/v1alpha1/proxydefaults_webhook.go index 3873516074..ced4853b12 100644 --- a/control-plane/api/v1alpha1/proxydefaults_webhook.go +++ b/control-plane/api/v1alpha1/proxydefaults_webhook.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/proxydefaults_webhook_test.go b/control-plane/api/v1alpha1/proxydefaults_webhook_test.go index 3136540089..9ba6241e57 100644 --- a/control-plane/api/v1alpha1/proxydefaults_webhook_test.go +++ b/control-plane/api/v1alpha1/proxydefaults_webhook_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/servicedefaults_types.go b/control-plane/api/v1alpha1/servicedefaults_types.go index 06da8b1d2c..304bce2db6 100644 --- a/control-plane/api/v1alpha1/servicedefaults_types.go +++ b/control-plane/api/v1alpha1/servicedefaults_types.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/servicedefaults_types_test.go b/control-plane/api/v1alpha1/servicedefaults_types_test.go index 33ec6d2f40..9f9e7ebbda 100644 --- a/control-plane/api/v1alpha1/servicedefaults_types_test.go +++ b/control-plane/api/v1alpha1/servicedefaults_types_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/servicedefaults_webhook.go b/control-plane/api/v1alpha1/servicedefaults_webhook.go index f79e68bcde..124aeff5f6 100644 --- a/control-plane/api/v1alpha1/servicedefaults_webhook.go +++ b/control-plane/api/v1alpha1/servicedefaults_webhook.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/serviceintentions_types.go b/control-plane/api/v1alpha1/serviceintentions_types.go index a0a240639a..82a5dcfdc8 100644 --- a/control-plane/api/v1alpha1/serviceintentions_types.go +++ b/control-plane/api/v1alpha1/serviceintentions_types.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/serviceintentions_types_test.go b/control-plane/api/v1alpha1/serviceintentions_types_test.go index f9be4120f2..99b391039c 100644 --- a/control-plane/api/v1alpha1/serviceintentions_types_test.go +++ b/control-plane/api/v1alpha1/serviceintentions_types_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/serviceintentions_webhook.go b/control-plane/api/v1alpha1/serviceintentions_webhook.go index ddc6488690..fef2401fb3 100644 --- a/control-plane/api/v1alpha1/serviceintentions_webhook.go +++ b/control-plane/api/v1alpha1/serviceintentions_webhook.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/serviceintentions_webhook_test.go b/control-plane/api/v1alpha1/serviceintentions_webhook_test.go index e6095e8351..c6934557c8 100644 --- a/control-plane/api/v1alpha1/serviceintentions_webhook_test.go +++ b/control-plane/api/v1alpha1/serviceintentions_webhook_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/serviceresolver_types.go b/control-plane/api/v1alpha1/serviceresolver_types.go index 4fc637b35f..58711dfde6 100644 --- a/control-plane/api/v1alpha1/serviceresolver_types.go +++ b/control-plane/api/v1alpha1/serviceresolver_types.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/serviceresolver_types_test.go b/control-plane/api/v1alpha1/serviceresolver_types_test.go index fd4fc25a60..b3c552172f 100644 --- a/control-plane/api/v1alpha1/serviceresolver_types_test.go +++ b/control-plane/api/v1alpha1/serviceresolver_types_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/serviceresolver_webhook.go b/control-plane/api/v1alpha1/serviceresolver_webhook.go index ca5f9d9482..88996e2f8d 100644 --- a/control-plane/api/v1alpha1/serviceresolver_webhook.go +++ b/control-plane/api/v1alpha1/serviceresolver_webhook.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/servicerouter_types.go b/control-plane/api/v1alpha1/servicerouter_types.go index 9f8f7fc3fd..d5baf37368 100644 --- a/control-plane/api/v1alpha1/servicerouter_types.go +++ b/control-plane/api/v1alpha1/servicerouter_types.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/servicerouter_types_test.go b/control-plane/api/v1alpha1/servicerouter_types_test.go index eb0568db81..d0919871ce 100644 --- a/control-plane/api/v1alpha1/servicerouter_types_test.go +++ b/control-plane/api/v1alpha1/servicerouter_types_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/servicerouter_webhook.go b/control-plane/api/v1alpha1/servicerouter_webhook.go index f6837fcf7b..cdcc3ba439 100644 --- a/control-plane/api/v1alpha1/servicerouter_webhook.go +++ b/control-plane/api/v1alpha1/servicerouter_webhook.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/servicesplitter_types.go b/control-plane/api/v1alpha1/servicesplitter_types.go index b61b1a320b..d94dbb7120 100644 --- a/control-plane/api/v1alpha1/servicesplitter_types.go +++ b/control-plane/api/v1alpha1/servicesplitter_types.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/servicesplitter_types_test.go b/control-plane/api/v1alpha1/servicesplitter_types_test.go index 48e9eeac54..e2ade99f1c 100644 --- a/control-plane/api/v1alpha1/servicesplitter_types_test.go +++ b/control-plane/api/v1alpha1/servicesplitter_types_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/servicesplitter_webhook.go b/control-plane/api/v1alpha1/servicesplitter_webhook.go index c0020c88b8..42dbbbf54a 100644 --- a/control-plane/api/v1alpha1/servicesplitter_webhook.go +++ b/control-plane/api/v1alpha1/servicesplitter_webhook.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/shared_types.go b/control-plane/api/v1alpha1/shared_types.go index edcaba5a46..28d53b8926 100644 --- a/control-plane/api/v1alpha1/shared_types.go +++ b/control-plane/api/v1alpha1/shared_types.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/status.go b/control-plane/api/v1alpha1/status.go index d7cd0e0b78..0e11bf930b 100644 --- a/control-plane/api/v1alpha1/status.go +++ b/control-plane/api/v1alpha1/status.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/terminatinggateway_types.go b/control-plane/api/v1alpha1/terminatinggateway_types.go index 6e708b5d44..cf453160ff 100644 --- a/control-plane/api/v1alpha1/terminatinggateway_types.go +++ b/control-plane/api/v1alpha1/terminatinggateway_types.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/terminatinggateway_types_test.go b/control-plane/api/v1alpha1/terminatinggateway_types_test.go index 9d8ba9948d..2daf93c6a4 100644 --- a/control-plane/api/v1alpha1/terminatinggateway_types_test.go +++ b/control-plane/api/v1alpha1/terminatinggateway_types_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/terminatinggateway_webhook.go b/control-plane/api/v1alpha1/terminatinggateway_webhook.go index b0427b87ca..481a1a1f15 100644 --- a/control-plane/api/v1alpha1/terminatinggateway_webhook.go +++ b/control-plane/api/v1alpha1/terminatinggateway_webhook.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/build-support/functions/00-vars.sh b/control-plane/build-support/functions/00-vars.sh index 1f03013c32..484344703c 100644 --- a/control-plane/build-support/functions/00-vars.sh +++ b/control-plane/build-support/functions/00-vars.sh @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + # GPG Key ID to use for publically released builds HASHICORP_GPG_KEY="348FFC4C" diff --git a/control-plane/build-support/functions/10-util.sh b/control-plane/build-support/functions/10-util.sh index 8367c22743..26c43cb610 100644 --- a/control-plane/build-support/functions/10-util.sh +++ b/control-plane/build-support/functions/10-util.sh @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + function err { if test "${COLORIZE}" -eq 1 then diff --git a/control-plane/build-support/functions/20-build.sh b/control-plane/build-support/functions/20-build.sh index ddde7b6acf..a4f36ee3e4 100644 --- a/control-plane/build-support/functions/20-build.sh +++ b/control-plane/build-support/functions/20-build.sh @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + function refresh_docker_images { # Arguments: # $1 - Path to top level Consul source diff --git a/control-plane/build-support/functions/40-publish.sh b/control-plane/build-support/functions/40-publish.sh index 975c835bc0..aae9a5f719 100644 --- a/control-plane/build-support/functions/40-publish.sh +++ b/control-plane/build-support/functions/40-publish.sh @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + function hashicorp_release { # Arguments: # $1 - Path to directory containing all of the release artifacts diff --git a/control-plane/build-support/scripts/build-local.sh b/control-plane/build-support/scripts/build-local.sh index 95d18e0ba6..453310b0b7 100755 --- a/control-plane/build-support/scripts/build-local.sh +++ b/control-plane/build-support/scripts/build-local.sh @@ -1,4 +1,7 @@ #!/bin/bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + SCRIPT_NAME="$(basename ${BASH_SOURCE[0]})" pushd $(dirname ${BASH_SOURCE[0]}) > /dev/null SCRIPT_DIR=$(pwd) diff --git a/control-plane/build-support/scripts/functions.sh b/control-plane/build-support/scripts/functions.sh index 0301c0d3a1..590666eb7d 100644 --- a/control-plane/build-support/scripts/functions.sh +++ b/control-plane/build-support/scripts/functions.sh @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + # # NOTE: This file is meant to be sourced from other bash scripts/shells # diff --git a/control-plane/build-support/scripts/terraformfmtcheck.sh b/control-plane/build-support/scripts/terraformfmtcheck.sh index 0f962a1c1b..8608a88e30 100755 --- a/control-plane/build-support/scripts/terraformfmtcheck.sh +++ b/control-plane/build-support/scripts/terraformfmtcheck.sh @@ -1,4 +1,7 @@ #!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + # Check terraform fmt echo "==> Checking that code complies with terraform fmt requirements..." diff --git a/control-plane/build-support/scripts/version.sh b/control-plane/build-support/scripts/version.sh index f91b0c3917..fce325e03c 100755 --- a/control-plane/build-support/scripts/version.sh +++ b/control-plane/build-support/scripts/version.sh @@ -1,4 +1,7 @@ #!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + version_file=$1 version=$(awk '$1 == "Version" && $2 == "=" { gsub(/"/, "", $3); print $3 }' < "${version_file}") diff --git a/control-plane/catalog/to-consul/annotation.go b/control-plane/catalog/to-consul/annotation.go index 5cd6023181..27e37ca217 100644 --- a/control-plane/catalog/to-consul/annotation.go +++ b/control-plane/catalog/to-consul/annotation.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package catalog const ( diff --git a/control-plane/catalog/to-consul/resource.go b/control-plane/catalog/to-consul/resource.go index 09d8aa6c5d..e25257c8f8 100644 --- a/control-plane/catalog/to-consul/resource.go +++ b/control-plane/catalog/to-consul/resource.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package catalog import ( diff --git a/control-plane/catalog/to-consul/resource_test.go b/control-plane/catalog/to-consul/resource_test.go index 9ba94123ef..d75f03d692 100644 --- a/control-plane/catalog/to-consul/resource_test.go +++ b/control-plane/catalog/to-consul/resource_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package catalog import ( diff --git a/control-plane/catalog/to-consul/service_id.go b/control-plane/catalog/to-consul/service_id.go index 1aa3071497..8300871b73 100644 --- a/control-plane/catalog/to-consul/service_id.go +++ b/control-plane/catalog/to-consul/service_id.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package catalog import ( diff --git a/control-plane/catalog/to-consul/syncer.go b/control-plane/catalog/to-consul/syncer.go index 19e0aaca6f..9f1df18ba6 100644 --- a/control-plane/catalog/to-consul/syncer.go +++ b/control-plane/catalog/to-consul/syncer.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package catalog import ( diff --git a/control-plane/catalog/to-consul/syncer_ent_test.go b/control-plane/catalog/to-consul/syncer_ent_test.go index fbe2cbd494..5dfb158d23 100644 --- a/control-plane/catalog/to-consul/syncer_ent_test.go +++ b/control-plane/catalog/to-consul/syncer_ent_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + //go:build enterprise package catalog diff --git a/control-plane/catalog/to-consul/syncer_test.go b/control-plane/catalog/to-consul/syncer_test.go index d8d9b0f402..3fae7a3d16 100644 --- a/control-plane/catalog/to-consul/syncer_test.go +++ b/control-plane/catalog/to-consul/syncer_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package catalog import ( diff --git a/control-plane/catalog/to-consul/testing.go b/control-plane/catalog/to-consul/testing.go index e6541c6ba1..5f19017cbe 100644 --- a/control-plane/catalog/to-consul/testing.go +++ b/control-plane/catalog/to-consul/testing.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package catalog import ( diff --git a/control-plane/catalog/to-k8s/sink.go b/control-plane/catalog/to-k8s/sink.go index fa8821989e..6e201253df 100644 --- a/control-plane/catalog/to-k8s/sink.go +++ b/control-plane/catalog/to-k8s/sink.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package catalog import ( diff --git a/control-plane/catalog/to-k8s/sink_test.go b/control-plane/catalog/to-k8s/sink_test.go index fbce7bbaaf..cfba502268 100644 --- a/control-plane/catalog/to-k8s/sink_test.go +++ b/control-plane/catalog/to-k8s/sink_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package catalog import ( diff --git a/control-plane/catalog/to-k8s/source.go b/control-plane/catalog/to-k8s/source.go index 5a384e760a..ab34089d8e 100644 --- a/control-plane/catalog/to-k8s/source.go +++ b/control-plane/catalog/to-k8s/source.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package catalog import ( diff --git a/control-plane/catalog/to-k8s/source_test.go b/control-plane/catalog/to-k8s/source_test.go index ca00a1e954..66afb4b608 100644 --- a/control-plane/catalog/to-k8s/source_test.go +++ b/control-plane/catalog/to-k8s/source_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package catalog import ( diff --git a/control-plane/catalog/to-k8s/testing.go b/control-plane/catalog/to-k8s/testing.go index d7181bdbea..1eb731a17f 100644 --- a/control-plane/catalog/to-k8s/testing.go +++ b/control-plane/catalog/to-k8s/testing.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package catalog import ( diff --git a/control-plane/cni/config/config.go b/control-plane/cni/config/config.go index f22d3ff79b..a9dafd0ab7 100644 --- a/control-plane/cni/config/config.go +++ b/control-plane/cni/config/config.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package config const ( diff --git a/control-plane/cni/main.go b/control-plane/cni/main.go index 7b05b5c6cd..e35f5ad811 100644 --- a/control-plane/cni/main.go +++ b/control-plane/cni/main.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package main import ( diff --git a/control-plane/cni/main_test.go b/control-plane/cni/main_test.go index 740e15c646..7c289a9825 100644 --- a/control-plane/cni/main_test.go +++ b/control-plane/cni/main_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package main import ( diff --git a/control-plane/commands.go b/control-plane/commands.go index ec3b7ca612..4b7cbed362 100644 --- a/control-plane/commands.go +++ b/control-plane/commands.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package main import ( diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml index da1a66fd74..6352ac3af1 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml index 16ac322090..fd8ebc86ff 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml index 7ad173afbf..4850ad152e 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml index e782ef472f..50df179f04 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml index d5103252a5..01e4363f14 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml index 2563cbcf77..5c8d4a5082 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml index 4f335a923d..7744a8fe7a 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml index a0cc7a6343..8e186af1a7 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml index a84fc0bd88..9bd5096376 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml index de071bd0ef..5b9c9d3c1f 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml index df8bbbfbdf..aa2b592c94 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml index 8e6c449ef8..b465cd9494 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/control-plane/config/rbac/role.yaml b/control-plane/config/rbac/role.yaml index 8daf050ec0..245f09568f 100644 --- a/control-plane/config/rbac/role.yaml +++ b/control-plane/config/rbac/role.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/control-plane/config/webhook/manifests.yaml b/control-plane/config/webhook/manifests.yaml index c1ffb5e0fe..d064b50acb 100644 --- a/control-plane/config/webhook/manifests.yaml +++ b/control-plane/config/webhook/manifests.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + --- apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration diff --git a/control-plane/connect-inject/common/common.go b/control-plane/connect-inject/common/common.go index 9611797c9f..67182e6d0a 100644 --- a/control-plane/connect-inject/common/common.go +++ b/control-plane/connect-inject/common/common.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package common import ( diff --git a/control-plane/connect-inject/common/common_test.go b/control-plane/connect-inject/common/common_test.go index e43ccf8255..3f995e2874 100644 --- a/control-plane/connect-inject/common/common_test.go +++ b/control-plane/connect-inject/common/common_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package common import ( diff --git a/control-plane/connect-inject/constants/annotations_and_labels.go b/control-plane/connect-inject/constants/annotations_and_labels.go index 637e028202..fa5c7da26c 100644 --- a/control-plane/connect-inject/constants/annotations_and_labels.go +++ b/control-plane/connect-inject/constants/annotations_and_labels.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package constants const ( diff --git a/control-plane/connect-inject/constants/constants.go b/control-plane/connect-inject/constants/constants.go index e371677629..30b7cddac0 100644 --- a/control-plane/connect-inject/constants/constants.go +++ b/control-plane/connect-inject/constants/constants.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package constants const ( diff --git a/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks.go b/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks.go index f54fb71d11..c71ee9ba55 100644 --- a/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks.go +++ b/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package endpoints import ( diff --git a/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks_test.go b/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks_test.go index 189587106d..8c74b4f718 100644 --- a/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks_test.go +++ b/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package endpoints import ( diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go index 800df9cc24..f408a21ea1 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package endpoints import ( diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go index 4303bafbc8..5c87c2442d 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + //go:build enterprise package endpoints diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go index 63cde6404b..19c78925a6 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package endpoints import ( diff --git a/control-plane/connect-inject/controllers/peering/peering_acceptor_controller.go b/control-plane/connect-inject/controllers/peering/peering_acceptor_controller.go index 044de55998..3a1251aae6 100644 --- a/control-plane/connect-inject/controllers/peering/peering_acceptor_controller.go +++ b/control-plane/connect-inject/controllers/peering/peering_acceptor_controller.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package peering import ( diff --git a/control-plane/connect-inject/controllers/peering/peering_acceptor_controller_test.go b/control-plane/connect-inject/controllers/peering/peering_acceptor_controller_test.go index 15a3740816..f3b3e6a844 100644 --- a/control-plane/connect-inject/controllers/peering/peering_acceptor_controller_test.go +++ b/control-plane/connect-inject/controllers/peering/peering_acceptor_controller_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package peering import ( diff --git a/control-plane/connect-inject/controllers/peering/peering_dialer_controller.go b/control-plane/connect-inject/controllers/peering/peering_dialer_controller.go index 98646b1654..37de792cb6 100644 --- a/control-plane/connect-inject/controllers/peering/peering_dialer_controller.go +++ b/control-plane/connect-inject/controllers/peering/peering_dialer_controller.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package peering import ( diff --git a/control-plane/connect-inject/controllers/peering/peering_dialer_controller_test.go b/control-plane/connect-inject/controllers/peering/peering_dialer_controller_test.go index ba33afd765..e211fe856e 100644 --- a/control-plane/connect-inject/controllers/peering/peering_dialer_controller_test.go +++ b/control-plane/connect-inject/controllers/peering/peering_dialer_controller_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package peering import ( diff --git a/control-plane/connect-inject/metrics/metrics_configuration.go b/control-plane/connect-inject/metrics/metrics_configuration.go index 651fb87184..f5b819af3d 100644 --- a/control-plane/connect-inject/metrics/metrics_configuration.go +++ b/control-plane/connect-inject/metrics/metrics_configuration.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package metrics import ( diff --git a/control-plane/connect-inject/metrics/metrics_configuration_test.go b/control-plane/connect-inject/metrics/metrics_configuration_test.go index 2f41b05744..ec19d4f55a 100644 --- a/control-plane/connect-inject/metrics/metrics_configuration_test.go +++ b/control-plane/connect-inject/metrics/metrics_configuration_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package metrics import ( diff --git a/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go b/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go index ad3333ba1b..70f3be82da 100644 --- a/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go +++ b/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package webhook import ( diff --git a/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go b/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go index 37aa1619bf..f7cb7bc594 100644 --- a/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go +++ b/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package webhook import ( diff --git a/control-plane/connect-inject/webhook/container_env.go b/control-plane/connect-inject/webhook/container_env.go index 7c65dcd77c..2b2d7f5471 100644 --- a/control-plane/connect-inject/webhook/container_env.go +++ b/control-plane/connect-inject/webhook/container_env.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package webhook import ( diff --git a/control-plane/connect-inject/webhook/container_env_test.go b/control-plane/connect-inject/webhook/container_env_test.go index 62200e7bbd..50d38832c0 100644 --- a/control-plane/connect-inject/webhook/container_env_test.go +++ b/control-plane/connect-inject/webhook/container_env_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package webhook import ( diff --git a/control-plane/connect-inject/webhook/container_init.go b/control-plane/connect-inject/webhook/container_init.go index 328882bc04..b33c8f4d3e 100644 --- a/control-plane/connect-inject/webhook/container_init.go +++ b/control-plane/connect-inject/webhook/container_init.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package webhook import ( diff --git a/control-plane/connect-inject/webhook/container_init_test.go b/control-plane/connect-inject/webhook/container_init_test.go index 8e0b551b24..0e2de79bb1 100644 --- a/control-plane/connect-inject/webhook/container_init_test.go +++ b/control-plane/connect-inject/webhook/container_init_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package webhook import ( diff --git a/control-plane/connect-inject/webhook/container_volume.go b/control-plane/connect-inject/webhook/container_volume.go index b33567469b..76ba461c7b 100644 --- a/control-plane/connect-inject/webhook/container_volume.go +++ b/control-plane/connect-inject/webhook/container_volume.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package webhook import ( diff --git a/control-plane/connect-inject/webhook/dns.go b/control-plane/connect-inject/webhook/dns.go index ed4e95703b..3f73928ece 100644 --- a/control-plane/connect-inject/webhook/dns.go +++ b/control-plane/connect-inject/webhook/dns.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package webhook import ( diff --git a/control-plane/connect-inject/webhook/dns_test.go b/control-plane/connect-inject/webhook/dns_test.go index d6392c5317..e8d718557e 100644 --- a/control-plane/connect-inject/webhook/dns_test.go +++ b/control-plane/connect-inject/webhook/dns_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package webhook import ( diff --git a/control-plane/connect-inject/webhook/health_checks_test.go b/control-plane/connect-inject/webhook/health_checks_test.go index 9279d8f140..53e2781509 100644 --- a/control-plane/connect-inject/webhook/health_checks_test.go +++ b/control-plane/connect-inject/webhook/health_checks_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package webhook import ( diff --git a/control-plane/connect-inject/webhook/heath_checks.go b/control-plane/connect-inject/webhook/heath_checks.go index 42d6da08e1..8986f9e985 100644 --- a/control-plane/connect-inject/webhook/heath_checks.go +++ b/control-plane/connect-inject/webhook/heath_checks.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package webhook import ( diff --git a/control-plane/connect-inject/webhook/mesh_webhook.go b/control-plane/connect-inject/webhook/mesh_webhook.go index 503d3182b4..7105e19e67 100644 --- a/control-plane/connect-inject/webhook/mesh_webhook.go +++ b/control-plane/connect-inject/webhook/mesh_webhook.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package webhook import ( diff --git a/control-plane/connect-inject/webhook/mesh_webhook_ent_test.go b/control-plane/connect-inject/webhook/mesh_webhook_ent_test.go index 34071a686c..e6da9b1f49 100644 --- a/control-plane/connect-inject/webhook/mesh_webhook_ent_test.go +++ b/control-plane/connect-inject/webhook/mesh_webhook_ent_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + //go:build enterprise package webhook diff --git a/control-plane/connect-inject/webhook/mesh_webhook_test.go b/control-plane/connect-inject/webhook/mesh_webhook_test.go index 602ba63239..9b93d8d984 100644 --- a/control-plane/connect-inject/webhook/mesh_webhook_test.go +++ b/control-plane/connect-inject/webhook/mesh_webhook_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package webhook import ( diff --git a/control-plane/connect-inject/webhook/redirect_traffic.go b/control-plane/connect-inject/webhook/redirect_traffic.go index eab23a2b91..7066929dae 100644 --- a/control-plane/connect-inject/webhook/redirect_traffic.go +++ b/control-plane/connect-inject/webhook/redirect_traffic.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package webhook import ( diff --git a/control-plane/connect-inject/webhook/redirect_traffic_test.go b/control-plane/connect-inject/webhook/redirect_traffic_test.go index 2ad9940fbe..f94871fa96 100644 --- a/control-plane/connect-inject/webhook/redirect_traffic_test.go +++ b/control-plane/connect-inject/webhook/redirect_traffic_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package webhook import ( diff --git a/control-plane/consul/consul.go b/control-plane/consul/consul.go index bb46308ff8..3cf916ffbf 100644 --- a/control-plane/consul/consul.go +++ b/control-plane/consul/consul.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package consul import ( diff --git a/control-plane/consul/consul_test.go b/control-plane/consul/consul_test.go index 02430b0f48..04afe08033 100644 --- a/control-plane/consul/consul_test.go +++ b/control-plane/consul/consul_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package consul import ( diff --git a/control-plane/controller/configentry_controller.go b/control-plane/controller/configentry_controller.go index 8ae90a56a6..593fb1514f 100644 --- a/control-plane/controller/configentry_controller.go +++ b/control-plane/controller/configentry_controller.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package controller import ( diff --git a/control-plane/controller/configentry_controller_ent_test.go b/control-plane/controller/configentry_controller_ent_test.go index 61a6aef947..cfe9985e56 100644 --- a/control-plane/controller/configentry_controller_ent_test.go +++ b/control-plane/controller/configentry_controller_ent_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + //go:build enterprise package controller_test diff --git a/control-plane/controller/configentry_controller_test.go b/control-plane/controller/configentry_controller_test.go index 83b9e3eecf..47f28f02d1 100644 --- a/control-plane/controller/configentry_controller_test.go +++ b/control-plane/controller/configentry_controller_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package controller import ( diff --git a/control-plane/controller/exportedservices_controller.go b/control-plane/controller/exportedservices_controller.go index 84e767d6dc..90d7261d9a 100644 --- a/control-plane/controller/exportedservices_controller.go +++ b/control-plane/controller/exportedservices_controller.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package controller import ( diff --git a/control-plane/controller/exportedservices_controller_ent_test.go b/control-plane/controller/exportedservices_controller_ent_test.go index dd91c49b57..aba193bdd9 100644 --- a/control-plane/controller/exportedservices_controller_ent_test.go +++ b/control-plane/controller/exportedservices_controller_ent_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + //go:build enterprise package controller_test diff --git a/control-plane/controller/ingressgateway_controller.go b/control-plane/controller/ingressgateway_controller.go index 7e656b3d29..fffc3c5a06 100644 --- a/control-plane/controller/ingressgateway_controller.go +++ b/control-plane/controller/ingressgateway_controller.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package controller import ( diff --git a/control-plane/controller/mesh_controller.go b/control-plane/controller/mesh_controller.go index e15f49fca0..9f7d8cd7c8 100644 --- a/control-plane/controller/mesh_controller.go +++ b/control-plane/controller/mesh_controller.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package controller import ( diff --git a/control-plane/controller/proxydefaults_controller.go b/control-plane/controller/proxydefaults_controller.go index a63e121522..7499928ea4 100644 --- a/control-plane/controller/proxydefaults_controller.go +++ b/control-plane/controller/proxydefaults_controller.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package controller import ( diff --git a/control-plane/controller/servicedefaults_controller.go b/control-plane/controller/servicedefaults_controller.go index 7dd73914e4..b96ff7e566 100644 --- a/control-plane/controller/servicedefaults_controller.go +++ b/control-plane/controller/servicedefaults_controller.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package controller import ( diff --git a/control-plane/controller/serviceintentions_controller.go b/control-plane/controller/serviceintentions_controller.go index 3b70447517..43298a23f7 100644 --- a/control-plane/controller/serviceintentions_controller.go +++ b/control-plane/controller/serviceintentions_controller.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package controller import ( diff --git a/control-plane/controller/serviceresolver_controller.go b/control-plane/controller/serviceresolver_controller.go index 3e01e680ea..cca014ab50 100644 --- a/control-plane/controller/serviceresolver_controller.go +++ b/control-plane/controller/serviceresolver_controller.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package controller import ( diff --git a/control-plane/controller/servicerouter_controller.go b/control-plane/controller/servicerouter_controller.go index 7db983dec2..6ed1e52fad 100644 --- a/control-plane/controller/servicerouter_controller.go +++ b/control-plane/controller/servicerouter_controller.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package controller import ( diff --git a/control-plane/controller/servicesplitter_controller.go b/control-plane/controller/servicesplitter_controller.go index 9d07845dbb..dc5e2a8dd3 100644 --- a/control-plane/controller/servicesplitter_controller.go +++ b/control-plane/controller/servicesplitter_controller.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package controller import ( diff --git a/control-plane/controller/terminatinggateway_controller.go b/control-plane/controller/terminatinggateway_controller.go index a8db2d851e..10af041c39 100644 --- a/control-plane/controller/terminatinggateway_controller.go +++ b/control-plane/controller/terminatinggateway_controller.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package controller import ( diff --git a/control-plane/hack/lint-api-new-client/main.go b/control-plane/hack/lint-api-new-client/main.go index c4c6d896b1..6297f0abb1 100644 --- a/control-plane/hack/lint-api-new-client/main.go +++ b/control-plane/hack/lint-api-new-client/main.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + // Parses golang code looking for github.com/hashicorp/consul/api.NewClient() // being used in non-test code. If it finds this, it will error. // The purpose of this lint is that we actually want to use our internal diff --git a/control-plane/helper/cert/notify.go b/control-plane/helper/cert/notify.go index 201ca34dd5..076c923119 100644 --- a/control-plane/helper/cert/notify.go +++ b/control-plane/helper/cert/notify.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package cert import ( diff --git a/control-plane/helper/cert/notify_test.go b/control-plane/helper/cert/notify_test.go index 8813816f86..7e7cbf6d68 100644 --- a/control-plane/helper/cert/notify_test.go +++ b/control-plane/helper/cert/notify_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package cert import ( diff --git a/control-plane/helper/cert/source.go b/control-plane/helper/cert/source.go index dcc4e3640c..5a85cb271d 100644 --- a/control-plane/helper/cert/source.go +++ b/control-plane/helper/cert/source.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package cert import ( diff --git a/control-plane/helper/cert/source_gen.go b/control-plane/helper/cert/source_gen.go index e9c79ed390..9d54f2836c 100644 --- a/control-plane/helper/cert/source_gen.go +++ b/control-plane/helper/cert/source_gen.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package cert import ( diff --git a/control-plane/helper/cert/source_gen_test.go b/control-plane/helper/cert/source_gen_test.go index b68ffc7e13..fd31de654e 100644 --- a/control-plane/helper/cert/source_gen_test.go +++ b/control-plane/helper/cert/source_gen_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package cert import ( diff --git a/control-plane/helper/cert/tls_util.go b/control-plane/helper/cert/tls_util.go index 37e2f4ea97..d5552d60f3 100644 --- a/control-plane/helper/cert/tls_util.go +++ b/control-plane/helper/cert/tls_util.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package cert import ( diff --git a/control-plane/helper/coalesce/coalesce.go b/control-plane/helper/coalesce/coalesce.go index d3e96e6be8..bfe341d1d6 100644 --- a/control-plane/helper/coalesce/coalesce.go +++ b/control-plane/helper/coalesce/coalesce.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package coalesce import ( diff --git a/control-plane/helper/coalesce/coalesce_test.go b/control-plane/helper/coalesce/coalesce_test.go index 8489fed8b4..d2c37135c7 100644 --- a/control-plane/helper/coalesce/coalesce_test.go +++ b/control-plane/helper/coalesce/coalesce_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package coalesce import ( diff --git a/control-plane/helper/controller/controller.go b/control-plane/helper/controller/controller.go index 6edfb9d89c..87cdde1a6f 100644 --- a/control-plane/helper/controller/controller.go +++ b/control-plane/helper/controller/controller.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + // Package controller contains a reusable abstraction for efficiently // watching for changes in resources in a Kubernetes cluster. package controller diff --git a/control-plane/helper/controller/controller_test.go b/control-plane/helper/controller/controller_test.go index 43fe677280..a7e960a6aa 100644 --- a/control-plane/helper/controller/controller_test.go +++ b/control-plane/helper/controller/controller_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package controller import ( diff --git a/control-plane/helper/controller/resource.go b/control-plane/helper/controller/resource.go index 959d101488..9634db51ae 100644 --- a/control-plane/helper/controller/resource.go +++ b/control-plane/helper/controller/resource.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package controller import ( diff --git a/control-plane/helper/controller/testing.go b/control-plane/helper/controller/testing.go index 7575276bf5..e4809c70bd 100644 --- a/control-plane/helper/controller/testing.go +++ b/control-plane/helper/controller/testing.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package controller import ( diff --git a/control-plane/helper/go-discover/discover.go b/control-plane/helper/go-discover/discover.go index 845a7b144b..140586634a 100644 --- a/control-plane/helper/go-discover/discover.go +++ b/control-plane/helper/go-discover/discover.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package godiscover import ( diff --git a/control-plane/helper/go-discover/discover_test.go b/control-plane/helper/go-discover/discover_test.go index b655dc388e..5c25261516 100644 --- a/control-plane/helper/go-discover/discover_test.go +++ b/control-plane/helper/go-discover/discover_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package godiscover import ( diff --git a/control-plane/helper/go-discover/mocks/mock_provider.go b/control-plane/helper/go-discover/mocks/mock_provider.go index 51afb86a84..dfdab445da 100644 --- a/control-plane/helper/go-discover/mocks/mock_provider.go +++ b/control-plane/helper/go-discover/mocks/mock_provider.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package mocks import ( diff --git a/control-plane/helper/mutating-webhook-configuration/mutating_webhook_configuration.go b/control-plane/helper/mutating-webhook-configuration/mutating_webhook_configuration.go index c3c93b5204..093b1ef908 100644 --- a/control-plane/helper/mutating-webhook-configuration/mutating_webhook_configuration.go +++ b/control-plane/helper/mutating-webhook-configuration/mutating_webhook_configuration.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package mutatingwebhookconfiguration import ( diff --git a/control-plane/helper/mutating-webhook-configuration/mutating_webhook_configuration_test.go b/control-plane/helper/mutating-webhook-configuration/mutating_webhook_configuration_test.go index e247c71d14..be1a3b5c64 100644 --- a/control-plane/helper/mutating-webhook-configuration/mutating_webhook_configuration_test.go +++ b/control-plane/helper/mutating-webhook-configuration/mutating_webhook_configuration_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package mutatingwebhookconfiguration import ( diff --git a/control-plane/helper/parsetags/parsetags.go b/control-plane/helper/parsetags/parsetags.go index e9d9625338..caec75bb1d 100644 --- a/control-plane/helper/parsetags/parsetags.go +++ b/control-plane/helper/parsetags/parsetags.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package parsetags import ( diff --git a/control-plane/helper/parsetags/parsetags_test.go b/control-plane/helper/parsetags/parsetags_test.go index f403a2f711..2a6b9ad47f 100644 --- a/control-plane/helper/parsetags/parsetags_test.go +++ b/control-plane/helper/parsetags/parsetags_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package parsetags import ( diff --git a/control-plane/helper/test/test_util.go b/control-plane/helper/test/test_util.go index 0ad4601fde..e29e44de59 100644 --- a/control-plane/helper/test/test_util.go +++ b/control-plane/helper/test/test_util.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package test import ( diff --git a/control-plane/main.go b/control-plane/main.go index 7ec1340290..a4ccc9630c 100644 --- a/control-plane/main.go +++ b/control-plane/main.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package main import ( diff --git a/control-plane/namespaces/namespaces.go b/control-plane/namespaces/namespaces.go index c17aa1f788..84b8c15ee4 100644 --- a/control-plane/namespaces/namespaces.go +++ b/control-plane/namespaces/namespaces.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + // Package namespaces handles interaction with Consul namespaces needed across // commands. package namespaces diff --git a/control-plane/namespaces/namespaces_test.go b/control-plane/namespaces/namespaces_test.go index 7b6a061a65..a2c9989ae8 100644 --- a/control-plane/namespaces/namespaces_test.go +++ b/control-plane/namespaces/namespaces_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + //go:build enterprise package namespaces diff --git a/control-plane/subcommand/acl-init/command.go b/control-plane/subcommand/acl-init/command.go index af85128ea8..77e8f87d87 100644 --- a/control-plane/subcommand/acl-init/command.go +++ b/control-plane/subcommand/acl-init/command.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package aclinit import ( diff --git a/control-plane/subcommand/acl-init/command_test.go b/control-plane/subcommand/acl-init/command_test.go index c9f5703459..acdafc939f 100644 --- a/control-plane/subcommand/acl-init/command_test.go +++ b/control-plane/subcommand/acl-init/command_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package aclinit import ( diff --git a/control-plane/subcommand/auth.go b/control-plane/subcommand/auth.go index 58a9b801e8..6389e7d6f7 100644 --- a/control-plane/subcommand/auth.go +++ b/control-plane/subcommand/auth.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package subcommand import ( diff --git a/control-plane/subcommand/common/common.go b/control-plane/subcommand/common/common.go index e3a569ddf6..b7927e4490 100644 --- a/control-plane/subcommand/common/common.go +++ b/control-plane/subcommand/common/common.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + // Package common holds code needed by multiple commands. package common diff --git a/control-plane/subcommand/common/common_test.go b/control-plane/subcommand/common/common_test.go index 6021bd0b49..9e50302b17 100644 --- a/control-plane/subcommand/common/common_test.go +++ b/control-plane/subcommand/common/common_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package common import ( diff --git a/control-plane/subcommand/common/test_util.go b/control-plane/subcommand/common/test_util.go index 13d9017fe4..3399b40e2b 100644 --- a/control-plane/subcommand/common/test_util.go +++ b/control-plane/subcommand/common/test_util.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package common import ( diff --git a/control-plane/subcommand/connect-init/command.go b/control-plane/subcommand/connect-init/command.go index 4750e9455c..a5fbe9066c 100644 --- a/control-plane/subcommand/connect-init/command.go +++ b/control-plane/subcommand/connect-init/command.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package connectinit import ( diff --git a/control-plane/subcommand/connect-init/command_ent_test.go b/control-plane/subcommand/connect-init/command_ent_test.go index ecdc34122e..b3ef7109e0 100644 --- a/control-plane/subcommand/connect-init/command_ent_test.go +++ b/control-plane/subcommand/connect-init/command_ent_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + //go:build enterprise package connectinit diff --git a/control-plane/subcommand/connect-init/command_test.go b/control-plane/subcommand/connect-init/command_test.go index 14bdc5280c..69abc8f1ad 100644 --- a/control-plane/subcommand/connect-init/command_test.go +++ b/control-plane/subcommand/connect-init/command_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package connectinit import ( diff --git a/control-plane/subcommand/consul-logout/command.go b/control-plane/subcommand/consul-logout/command.go index b0836b71f0..a6b599dccd 100644 --- a/control-plane/subcommand/consul-logout/command.go +++ b/control-plane/subcommand/consul-logout/command.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package consullogout import ( diff --git a/control-plane/subcommand/consul-logout/command_test.go b/control-plane/subcommand/consul-logout/command_test.go index 22412ea752..e7e3a00f38 100644 --- a/control-plane/subcommand/consul-logout/command_test.go +++ b/control-plane/subcommand/consul-logout/command_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package consullogout import ( diff --git a/control-plane/subcommand/create-federation-secret/command.go b/control-plane/subcommand/create-federation-secret/command.go index 3a96e18134..52600aedda 100644 --- a/control-plane/subcommand/create-federation-secret/command.go +++ b/control-plane/subcommand/create-federation-secret/command.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package createfederationsecret import ( diff --git a/control-plane/subcommand/create-federation-secret/command_test.go b/control-plane/subcommand/create-federation-secret/command_test.go index d0f85fa686..a90ff692e2 100644 --- a/control-plane/subcommand/create-federation-secret/command_test.go +++ b/control-plane/subcommand/create-federation-secret/command_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package createfederationsecret import ( diff --git a/control-plane/subcommand/delete-completed-job/command.go b/control-plane/subcommand/delete-completed-job/command.go index 273e621a2a..f6f594393d 100644 --- a/control-plane/subcommand/delete-completed-job/command.go +++ b/control-plane/subcommand/delete-completed-job/command.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package deletecompletedjob import ( diff --git a/control-plane/subcommand/delete-completed-job/command_test.go b/control-plane/subcommand/delete-completed-job/command_test.go index 0da056fc88..abfbb0e788 100644 --- a/control-plane/subcommand/delete-completed-job/command_test.go +++ b/control-plane/subcommand/delete-completed-job/command_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package deletecompletedjob import ( diff --git a/control-plane/subcommand/flags/consul.go b/control-plane/subcommand/flags/consul.go index 1294729a6b..9368b95b3d 100644 --- a/control-plane/subcommand/flags/consul.go +++ b/control-plane/subcommand/flags/consul.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package flags import ( diff --git a/control-plane/subcommand/flags/consul_test.go b/control-plane/subcommand/flags/consul_test.go index b53025daa6..7f35dc8575 100644 --- a/control-plane/subcommand/flags/consul_test.go +++ b/control-plane/subcommand/flags/consul_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package flags import ( diff --git a/control-plane/subcommand/flags/flag_map_value.go b/control-plane/subcommand/flags/flag_map_value.go index 9ddb4dad08..c647f57bf5 100644 --- a/control-plane/subcommand/flags/flag_map_value.go +++ b/control-plane/subcommand/flags/flag_map_value.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package flags import ( diff --git a/control-plane/subcommand/flags/flag_map_value_test.go b/control-plane/subcommand/flags/flag_map_value_test.go index a3b7659cc4..ea68b6b312 100644 --- a/control-plane/subcommand/flags/flag_map_value_test.go +++ b/control-plane/subcommand/flags/flag_map_value_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package flags import ( diff --git a/control-plane/subcommand/flags/flag_slice_value.go b/control-plane/subcommand/flags/flag_slice_value.go index 11e838e683..42c45562b2 100644 --- a/control-plane/subcommand/flags/flag_slice_value.go +++ b/control-plane/subcommand/flags/flag_slice_value.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package flags import "strings" diff --git a/control-plane/subcommand/flags/flag_slice_value_test.go b/control-plane/subcommand/flags/flag_slice_value_test.go index e361c12b10..24cf4df695 100644 --- a/control-plane/subcommand/flags/flag_slice_value_test.go +++ b/control-plane/subcommand/flags/flag_slice_value_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package flags import ( diff --git a/control-plane/subcommand/flags/flags.go b/control-plane/subcommand/flags/flags.go index e290e876d7..6739684bf0 100644 --- a/control-plane/subcommand/flags/flags.go +++ b/control-plane/subcommand/flags/flags.go @@ -1,2 +1,5 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + // Package flags holds common flags that are shared between our commands. package flags diff --git a/control-plane/subcommand/flags/http.go b/control-plane/subcommand/flags/http.go index 74db3c26dc..21ccb45df1 100644 --- a/control-plane/subcommand/flags/http.go +++ b/control-plane/subcommand/flags/http.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package flags import ( diff --git a/control-plane/subcommand/flags/http_test.go b/control-plane/subcommand/flags/http_test.go index a44f79931c..3292661933 100644 --- a/control-plane/subcommand/flags/http_test.go +++ b/control-plane/subcommand/flags/http_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package flags import ( diff --git a/control-plane/subcommand/flags/k8s.go b/control-plane/subcommand/flags/k8s.go index 31a2284f65..15f1a996b9 100644 --- a/control-plane/subcommand/flags/k8s.go +++ b/control-plane/subcommand/flags/k8s.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package flags import ( diff --git a/control-plane/subcommand/flags/mapset.go b/control-plane/subcommand/flags/mapset.go index c58cc9a3a2..d97419a827 100644 --- a/control-plane/subcommand/flags/mapset.go +++ b/control-plane/subcommand/flags/mapset.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package flags import "github.com/deckarep/golang-set" diff --git a/control-plane/subcommand/flags/usage.go b/control-plane/subcommand/flags/usage.go index 960ce85926..df63d9c8a6 100644 --- a/control-plane/subcommand/flags/usage.go +++ b/control-plane/subcommand/flags/usage.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package flags import ( diff --git a/control-plane/subcommand/flags/usage_test.go b/control-plane/subcommand/flags/usage_test.go index 801888e549..7e27ad77fb 100644 --- a/control-plane/subcommand/flags/usage_test.go +++ b/control-plane/subcommand/flags/usage_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package flags import ( diff --git a/control-plane/subcommand/get-consul-client-ca/command.go b/control-plane/subcommand/get-consul-client-ca/command.go index a154d778e9..619f08625d 100644 --- a/control-plane/subcommand/get-consul-client-ca/command.go +++ b/control-plane/subcommand/get-consul-client-ca/command.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package getconsulclientca import ( diff --git a/control-plane/subcommand/get-consul-client-ca/command_test.go b/control-plane/subcommand/get-consul-client-ca/command_test.go index 9c48e63712..466e11a11e 100644 --- a/control-plane/subcommand/get-consul-client-ca/command_test.go +++ b/control-plane/subcommand/get-consul-client-ca/command_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package getconsulclientca import ( diff --git a/control-plane/subcommand/gossip-encryption-autogenerate/command.go b/control-plane/subcommand/gossip-encryption-autogenerate/command.go index 3668a20c35..cf871eca69 100644 --- a/control-plane/subcommand/gossip-encryption-autogenerate/command.go +++ b/control-plane/subcommand/gossip-encryption-autogenerate/command.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package gossipencryptionautogenerate import ( diff --git a/control-plane/subcommand/gossip-encryption-autogenerate/command_test.go b/control-plane/subcommand/gossip-encryption-autogenerate/command_test.go index 91d7101232..55b32bdb06 100644 --- a/control-plane/subcommand/gossip-encryption-autogenerate/command_test.go +++ b/control-plane/subcommand/gossip-encryption-autogenerate/command_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package gossipencryptionautogenerate import ( diff --git a/control-plane/subcommand/inject-connect/command.go b/control-plane/subcommand/inject-connect/command.go index 93566387cd..e570832ee9 100644 --- a/control-plane/subcommand/inject-connect/command.go +++ b/control-plane/subcommand/inject-connect/command.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package connectinject import ( diff --git a/control-plane/subcommand/inject-connect/command_test.go b/control-plane/subcommand/inject-connect/command_test.go index 5f067cf7c2..9c64020376 100644 --- a/control-plane/subcommand/inject-connect/command_test.go +++ b/control-plane/subcommand/inject-connect/command_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package connectinject import ( diff --git a/control-plane/subcommand/install-cni/binary.go b/control-plane/subcommand/install-cni/binary.go index 2429770109..5bf25ab607 100644 --- a/control-plane/subcommand/install-cni/binary.go +++ b/control-plane/subcommand/install-cni/binary.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package installcni import ( diff --git a/control-plane/subcommand/install-cni/binary_test.go b/control-plane/subcommand/install-cni/binary_test.go index e65f61c63b..397751d42c 100644 --- a/control-plane/subcommand/install-cni/binary_test.go +++ b/control-plane/subcommand/install-cni/binary_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package installcni import ( diff --git a/control-plane/subcommand/install-cni/cniconfig.go b/control-plane/subcommand/install-cni/cniconfig.go index 922d7283dd..448c9efa92 100644 --- a/control-plane/subcommand/install-cni/cniconfig.go +++ b/control-plane/subcommand/install-cni/cniconfig.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package installcni import ( diff --git a/control-plane/subcommand/install-cni/cniconfig_test.go b/control-plane/subcommand/install-cni/cniconfig_test.go index b6e2154adb..06fa848074 100644 --- a/control-plane/subcommand/install-cni/cniconfig_test.go +++ b/control-plane/subcommand/install-cni/cniconfig_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package installcni import ( diff --git a/control-plane/subcommand/install-cni/command.go b/control-plane/subcommand/install-cni/command.go index 7c481a0800..53abe7cda1 100644 --- a/control-plane/subcommand/install-cni/command.go +++ b/control-plane/subcommand/install-cni/command.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package installcni import ( diff --git a/control-plane/subcommand/install-cni/command_test.go b/control-plane/subcommand/install-cni/command_test.go index 5cb9bea91e..d5ee65f928 100644 --- a/control-plane/subcommand/install-cni/command_test.go +++ b/control-plane/subcommand/install-cni/command_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package installcni import ( diff --git a/control-plane/subcommand/install-cni/kubeconfig.go b/control-plane/subcommand/install-cni/kubeconfig.go index ca93759578..467130dea9 100644 --- a/control-plane/subcommand/install-cni/kubeconfig.go +++ b/control-plane/subcommand/install-cni/kubeconfig.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package installcni import ( diff --git a/control-plane/subcommand/install-cni/kubeconfig_test.go b/control-plane/subcommand/install-cni/kubeconfig_test.go index 899ad3f600..8197115db3 100644 --- a/control-plane/subcommand/install-cni/kubeconfig_test.go +++ b/control-plane/subcommand/install-cni/kubeconfig_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package installcni import ( diff --git a/control-plane/subcommand/partition-init/command.go b/control-plane/subcommand/partition-init/command.go index 7ca70b50a7..72c4ceeff0 100644 --- a/control-plane/subcommand/partition-init/command.go +++ b/control-plane/subcommand/partition-init/command.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package partition_init import ( diff --git a/control-plane/subcommand/partition-init/command_ent_test.go b/control-plane/subcommand/partition-init/command_ent_test.go index 5bb1868b39..182412c8aa 100644 --- a/control-plane/subcommand/partition-init/command_ent_test.go +++ b/control-plane/subcommand/partition-init/command_ent_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + //go:build enterprise package partition_init diff --git a/control-plane/subcommand/server-acl-init/anonymous_token.go b/control-plane/subcommand/server-acl-init/anonymous_token.go index 3423ee78da..32c19ec208 100644 --- a/control-plane/subcommand/server-acl-init/anonymous_token.go +++ b/control-plane/subcommand/server-acl-init/anonymous_token.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package serveraclinit import ( diff --git a/control-plane/subcommand/server-acl-init/command.go b/control-plane/subcommand/server-acl-init/command.go index a444f65aaa..7ff0ae2268 100644 --- a/control-plane/subcommand/server-acl-init/command.go +++ b/control-plane/subcommand/server-acl-init/command.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package serveraclinit import ( diff --git a/control-plane/subcommand/server-acl-init/command_ent_test.go b/control-plane/subcommand/server-acl-init/command_ent_test.go index fa56f3bb3a..a67a2d0e41 100644 --- a/control-plane/subcommand/server-acl-init/command_ent_test.go +++ b/control-plane/subcommand/server-acl-init/command_ent_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + //go:build enterprise package serveraclinit diff --git a/control-plane/subcommand/server-acl-init/command_test.go b/control-plane/subcommand/server-acl-init/command_test.go index ffe60af593..68ce4c1e02 100644 --- a/control-plane/subcommand/server-acl-init/command_test.go +++ b/control-plane/subcommand/server-acl-init/command_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package serveraclinit import ( diff --git a/control-plane/subcommand/server-acl-init/connect_inject.go b/control-plane/subcommand/server-acl-init/connect_inject.go index e732dae452..58a36b988f 100644 --- a/control-plane/subcommand/server-acl-init/connect_inject.go +++ b/control-plane/subcommand/server-acl-init/connect_inject.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package serveraclinit import ( diff --git a/control-plane/subcommand/server-acl-init/connect_inject_test.go b/control-plane/subcommand/server-acl-init/connect_inject_test.go index e7144146b7..03e47c8ba6 100644 --- a/control-plane/subcommand/server-acl-init/connect_inject_test.go +++ b/control-plane/subcommand/server-acl-init/connect_inject_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package serveraclinit import ( diff --git a/control-plane/subcommand/server-acl-init/create_or_update.go b/control-plane/subcommand/server-acl-init/create_or_update.go index 833b923b90..d14fbc845c 100644 --- a/control-plane/subcommand/server-acl-init/create_or_update.go +++ b/control-plane/subcommand/server-acl-init/create_or_update.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package serveraclinit import ( diff --git a/control-plane/subcommand/server-acl-init/create_or_update_test.go b/control-plane/subcommand/server-acl-init/create_or_update_test.go index 259707f85d..6aff677dda 100644 --- a/control-plane/subcommand/server-acl-init/create_or_update_test.go +++ b/control-plane/subcommand/server-acl-init/create_or_update_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package serveraclinit import ( diff --git a/control-plane/subcommand/server-acl-init/k8s_secrets_backend.go b/control-plane/subcommand/server-acl-init/k8s_secrets_backend.go index 4e3efcf65b..93d9d0d2d8 100644 --- a/control-plane/subcommand/server-acl-init/k8s_secrets_backend.go +++ b/control-plane/subcommand/server-acl-init/k8s_secrets_backend.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package serveraclinit import ( diff --git a/control-plane/subcommand/server-acl-init/rules.go b/control-plane/subcommand/server-acl-init/rules.go index ee6ae41e40..440327ad53 100644 --- a/control-plane/subcommand/server-acl-init/rules.go +++ b/control-plane/subcommand/server-acl-init/rules.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package serveraclinit import ( diff --git a/control-plane/subcommand/server-acl-init/rules_test.go b/control-plane/subcommand/server-acl-init/rules_test.go index 22e63ed0ce..fd4f1ce929 100644 --- a/control-plane/subcommand/server-acl-init/rules_test.go +++ b/control-plane/subcommand/server-acl-init/rules_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package serveraclinit import ( diff --git a/control-plane/subcommand/server-acl-init/secrets_backend.go b/control-plane/subcommand/server-acl-init/secrets_backend.go index 4b4d5c2fc4..e0d74462cf 100644 --- a/control-plane/subcommand/server-acl-init/secrets_backend.go +++ b/control-plane/subcommand/server-acl-init/secrets_backend.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package serveraclinit type SecretsBackendType string diff --git a/control-plane/subcommand/server-acl-init/servers.go b/control-plane/subcommand/server-acl-init/servers.go index 01e9a58145..c530f648e5 100644 --- a/control-plane/subcommand/server-acl-init/servers.go +++ b/control-plane/subcommand/server-acl-init/servers.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package serveraclinit import ( diff --git a/control-plane/subcommand/server-acl-init/test_fake_secrets_backend.go b/control-plane/subcommand/server-acl-init/test_fake_secrets_backend.go index 5c9a63f1a5..87826f6c60 100644 --- a/control-plane/subcommand/server-acl-init/test_fake_secrets_backend.go +++ b/control-plane/subcommand/server-acl-init/test_fake_secrets_backend.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package serveraclinit type FakeSecretsBackend struct { diff --git a/control-plane/subcommand/server-acl-init/vault_secrets_backend.go b/control-plane/subcommand/server-acl-init/vault_secrets_backend.go index 2706567878..5a9097cca3 100644 --- a/control-plane/subcommand/server-acl-init/vault_secrets_backend.go +++ b/control-plane/subcommand/server-acl-init/vault_secrets_backend.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package serveraclinit import ( diff --git a/control-plane/subcommand/sync-catalog/command.go b/control-plane/subcommand/sync-catalog/command.go index f890b44f34..1530cf9d53 100644 --- a/control-plane/subcommand/sync-catalog/command.go +++ b/control-plane/subcommand/sync-catalog/command.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package synccatalog import ( diff --git a/control-plane/subcommand/sync-catalog/command_ent_test.go b/control-plane/subcommand/sync-catalog/command_ent_test.go index fac330c557..fb6c6c4347 100644 --- a/control-plane/subcommand/sync-catalog/command_ent_test.go +++ b/control-plane/subcommand/sync-catalog/command_ent_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + //go:build enterprise package synccatalog diff --git a/control-plane/subcommand/sync-catalog/command_test.go b/control-plane/subcommand/sync-catalog/command_test.go index 8228986d00..0223931cc1 100644 --- a/control-plane/subcommand/sync-catalog/command_test.go +++ b/control-plane/subcommand/sync-catalog/command_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package synccatalog import ( diff --git a/control-plane/subcommand/tls-init/command.go b/control-plane/subcommand/tls-init/command.go index 354a26fdc5..c2498a3125 100644 --- a/control-plane/subcommand/tls-init/command.go +++ b/control-plane/subcommand/tls-init/command.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package tls_init import ( diff --git a/control-plane/subcommand/tls-init/command_test.go b/control-plane/subcommand/tls-init/command_test.go index ae3cbd8982..81f5893543 100644 --- a/control-plane/subcommand/tls-init/command_test.go +++ b/control-plane/subcommand/tls-init/command_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package tls_init import ( diff --git a/control-plane/subcommand/version/command.go b/control-plane/subcommand/version/command.go index 58768a1f92..1f6b2aed01 100644 --- a/control-plane/subcommand/version/command.go +++ b/control-plane/subcommand/version/command.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package version import ( diff --git a/control-plane/subcommand/webhook-cert-manager/command.go b/control-plane/subcommand/webhook-cert-manager/command.go index 214a1d41e2..4d85565b62 100644 --- a/control-plane/subcommand/webhook-cert-manager/command.go +++ b/control-plane/subcommand/webhook-cert-manager/command.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package webhookcertmanager import ( diff --git a/control-plane/subcommand/webhook-cert-manager/command_test.go b/control-plane/subcommand/webhook-cert-manager/command_test.go index 7e302d5261..31c98b0ebe 100644 --- a/control-plane/subcommand/webhook-cert-manager/command_test.go +++ b/control-plane/subcommand/webhook-cert-manager/command_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package webhookcertmanager import ( diff --git a/control-plane/subcommand/webhook-cert-manager/mocks/mocks.go b/control-plane/subcommand/webhook-cert-manager/mocks/mocks.go index d700b192ac..5efa09eb29 100644 --- a/control-plane/subcommand/webhook-cert-manager/mocks/mocks.go +++ b/control-plane/subcommand/webhook-cert-manager/mocks/mocks.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package mocks import ( diff --git a/control-plane/version/version.go b/control-plane/version/version.go index 8ae06829bb..81433c0a5f 100644 --- a/control-plane/version/version.go +++ b/control-plane/version/version.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package version import ( diff --git a/hack/aws-acceptance-test-cleanup/main.go b/hack/aws-acceptance-test-cleanup/main.go index b72b88c12f..d62e5e4405 100644 --- a/hack/aws-acceptance-test-cleanup/main.go +++ b/hack/aws-acceptance-test-cleanup/main.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package main // This script deletes AWS resources created for acceptance tests that have diff --git a/hack/copy-crds-to-chart/main.go b/hack/copy-crds-to-chart/main.go index 7085bdb9e6..0f94a77b5d 100644 --- a/hack/copy-crds-to-chart/main.go +++ b/hack/copy-crds-to-chart/main.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + // Script to copy generated CRD yaml into chart directory and modify it to match // the expected chart format (e.g. formatted YAML). package main diff --git a/hack/helm-reference-gen/doc_node.go b/hack/helm-reference-gen/doc_node.go index a183ead969..cf28cf9374 100644 --- a/hack/helm-reference-gen/doc_node.go +++ b/hack/helm-reference-gen/doc_node.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package main import ( diff --git a/hack/helm-reference-gen/fixtures/full-values.yaml b/hack/helm-reference-gen/fixtures/full-values.yaml index b0eec07666..a79fbc6944 100644 --- a/hack/helm-reference-gen/fixtures/full-values.yaml +++ b/hack/helm-reference-gen/fixtures/full-values.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + # Available parameters and their default values for the Consul chart. # Holds values that affect multiple components of the chart. diff --git a/hack/helm-reference-gen/main.go b/hack/helm-reference-gen/main.go index ae9b17610e..0bc623cff0 100644 --- a/hack/helm-reference-gen/main.go +++ b/hack/helm-reference-gen/main.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package main // This script generates markdown documentation out of the values.yaml file diff --git a/hack/helm-reference-gen/main_test.go b/hack/helm-reference-gen/main_test.go index 03e8648218..fc91032203 100644 --- a/hack/helm-reference-gen/main_test.go +++ b/hack/helm-reference-gen/main_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package main import ( diff --git a/hack/helm-reference-gen/parse_error.go b/hack/helm-reference-gen/parse_error.go index f474664f1f..914737db5f 100644 --- a/hack/helm-reference-gen/parse_error.go +++ b/hack/helm-reference-gen/parse_error.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package main import "fmt" From ed2c3d6a77361140d9c35a0999cd9284d5723e84 Mon Sep 17 00:00:00 2001 From: Ronald Ekambi Date: Wed, 22 Mar 2023 10:46:09 -0400 Subject: [PATCH 107/340] fix tests --- .github/workflows/backport-checker.yml | 2 ++ .github/workflows/backport.yml | 2 ++ .github/workflows/build.yml | 2 ++ .github/workflows/changelog-checker.yml | 2 ++ .github/workflows/jira-issues.yaml | 2 ++ .github/workflows/jira-pr.yaml | 2 ++ .github/workflows/reusable-acceptance.yml | 2 ++ .github/workflows/reusable-golangci-lint.yml | 2 ++ .github/workflows/reusable-unit.yml | 2 ++ .github/workflows/test.yml | 2 ++ cli/helm/chart_test.go | 6 +++--- 11 files changed, 23 insertions(+), 3 deletions(-) diff --git a/.github/workflows/backport-checker.yml b/.github/workflows/backport-checker.yml index 5bcac5a38e..a70790c0c0 100644 --- a/.github/workflows/backport-checker.yml +++ b/.github/workflows/backport-checker.yml @@ -1,3 +1,5 @@ +# Copyright (c) HashiCorp, Inc. + # This workflow checks that there is either a 'pr/no-backport' label applied to a PR # or there is a backport/.txt file associated with a PR for a backport label diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 9c241d9ada..0283034a66 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -1,3 +1,5 @@ +# Copyright (c) HashiCorp, Inc. + --- name: Backport Assistant Runner diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9ce46f1704..de0d18d648 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,3 +1,5 @@ +# Copyright (c) HashiCorp, Inc. + name: build on: workflow_dispatch: diff --git a/.github/workflows/changelog-checker.yml b/.github/workflows/changelog-checker.yml index ae2e88170b..3595781825 100644 --- a/.github/workflows/changelog-checker.yml +++ b/.github/workflows/changelog-checker.yml @@ -1,3 +1,5 @@ +# Copyright (c) HashiCorp, Inc. + # This workflow checks that there is either a 'pr/no-changelog' label applied to a PR # or there is a .changelog/.txt file associated with a PR for a changelog entry diff --git a/.github/workflows/jira-issues.yaml b/.github/workflows/jira-issues.yaml index 18705db8e8..dc743e9328 100644 --- a/.github/workflows/jira-issues.yaml +++ b/.github/workflows/jira-issues.yaml @@ -1,3 +1,5 @@ +# Copyright (c) HashiCorp, Inc. + on: issues: types: [opened, closed, deleted, reopened] diff --git a/.github/workflows/jira-pr.yaml b/.github/workflows/jira-pr.yaml index 5c0ba71cd2..c07a92ee77 100644 --- a/.github/workflows/jira-pr.yaml +++ b/.github/workflows/jira-pr.yaml @@ -1,3 +1,5 @@ +# Copyright (c) HashiCorp, Inc. + on: pull_request_target: types: [opened, closed, reopened] diff --git a/.github/workflows/reusable-acceptance.yml b/.github/workflows/reusable-acceptance.yml index e4401932d3..cf1e11e3f5 100644 --- a/.github/workflows/reusable-acceptance.yml +++ b/.github/workflows/reusable-acceptance.yml @@ -1,3 +1,5 @@ +# Copyright (c) HashiCorp, Inc. + name: reusable-acceptance on: diff --git a/.github/workflows/reusable-golangci-lint.yml b/.github/workflows/reusable-golangci-lint.yml index 30b6a0a3b3..e8fcd79440 100644 --- a/.github/workflows/reusable-golangci-lint.yml +++ b/.github/workflows/reusable-golangci-lint.yml @@ -1,3 +1,5 @@ +# Copyright (c) HashiCorp, Inc. + name: golangci-lint on: diff --git a/.github/workflows/reusable-unit.yml b/.github/workflows/reusable-unit.yml index 8ba09377cc..dd2dd4e4a0 100644 --- a/.github/workflows/reusable-unit.yml +++ b/.github/workflows/reusable-unit.yml @@ -1,3 +1,5 @@ +# Copyright (c) HashiCorp, Inc. + name: reusable-unit on: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 803aed69a4..6cb3ab1937 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,3 +1,5 @@ +# Copyright (c) HashiCorp, Inc. + name: test on: push: diff --git a/cli/helm/chart_test.go b/cli/helm/chart_test.go index 10917ad739..f0e33e092b 100644 --- a/cli/helm/chart_test.go +++ b/cli/helm/chart_test.go @@ -38,10 +38,10 @@ func TestLoadChart(t *testing.T) { func TestReadChartFiles(t *testing.T) { directory := "test_fixtures/consul" expectedFiles := map[string]string{ - "Chart.yaml": "# This is a mock Helm Chart.yaml file used for testing.\napiVersion: v2\nname: Foo\nversion: 0.1.0\ndescription: Mock Helm Chart for testing.", - "values.yaml": "# This is a mock Helm values.yaml file used for testing.\nkey: value", + "Chart.yaml": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\n# This is a mock Helm Chart.yaml file used for testing.\napiVersion: v2\nname: Foo\nversion: 0.1.0\ndescription: Mock Helm Chart for testing.", + "values.yaml": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\n# This is a mock Helm values.yaml file used for testing.\nkey: value", "templates/_helpers.tpl": "helpers", - "templates/foo.yaml": "foo: bar\n", + "templates/foo.yaml": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\nfoo: bar\n", } files, err := readChartFiles(testChartFiles, directory) From 4f00647c984ed921460d96f6783e2a1348542af3 Mon Sep 17 00:00:00 2001 From: Ronald Ekambi Date: Tue, 28 Mar 2023 09:37:26 -0400 Subject: [PATCH 108/340] fix charts --- .../consul/templates/api-gateway-controller-clusterrole.yaml | 3 --- .../templates/api-gateway-controller-clusterrolebinding.yaml | 3 --- charts/consul/templates/api-gateway-controller-deployment.yaml | 3 --- .../templates/api-gateway-controller-podsecuritypolicy.yaml | 3 --- charts/consul/templates/api-gateway-controller-service.yaml | 3 --- .../templates/api-gateway-controller-serviceaccount.yaml | 3 --- charts/consul/templates/api-gateway-gatewayclass.yaml | 3 --- charts/consul/templates/api-gateway-gatewayclassconfig.yaml | 3 --- charts/consul/templates/api-gateway-podsecuritypolicy.yaml | 3 --- charts/consul/templates/auth-method-clusterrole.yaml | 3 --- charts/consul/templates/auth-method-clusterrolebinding.yaml | 3 --- charts/consul/templates/auth-method-secret.yaml | 3 --- charts/consul/templates/auth-method-serviceaccount.yaml | 3 --- charts/consul/templates/client-config-configmap.yaml | 3 --- charts/consul/templates/client-daemonset.yaml | 3 --- charts/consul/templates/client-podsecuritypolicy.yaml | 3 --- charts/consul/templates/client-role.yaml | 3 --- charts/consul/templates/client-rolebinding.yaml | 3 --- charts/consul/templates/client-securitycontextconstraints.yaml | 3 --- charts/consul/templates/client-serviceaccount.yaml | 3 --- charts/consul/templates/cni-clusterrole.yaml | 3 --- charts/consul/templates/cni-clusterrolebinding.yaml | 3 --- charts/consul/templates/cni-daemonset.yaml | 3 --- charts/consul/templates/cni-networkattachmentdefinition.yaml | 3 --- charts/consul/templates/cni-podsecuritypolicy.yaml | 3 --- charts/consul/templates/cni-resourcequota.yaml | 3 --- charts/consul/templates/cni-securitycontextconstraints.yaml | 3 --- charts/consul/templates/cni-serviceaccount.yaml | 3 --- charts/consul/templates/connect-inject-clusterrole.yaml | 3 --- charts/consul/templates/connect-inject-clusterrolebinding.yaml | 3 --- charts/consul/templates/connect-inject-deployment.yaml | 3 --- .../consul/templates/connect-inject-leader-election-role.yaml | 3 --- .../templates/connect-inject-leader-election-rolebinding.yaml | 3 --- .../templates/connect-inject-mutatingwebhookconfiguration.yaml | 3 --- charts/consul/templates/connect-inject-podsecuritypolicy.yaml | 3 --- charts/consul/templates/connect-inject-service.yaml | 3 --- charts/consul/templates/connect-inject-serviceaccount.yaml | 3 --- charts/consul/templates/connect-injector-disruptionbudget.yaml | 3 --- charts/consul/templates/crd-exportedservices.yaml | 3 --- charts/consul/templates/crd-ingressgateways.yaml | 3 --- charts/consul/templates/crd-meshes.yaml | 3 --- charts/consul/templates/crd-peeringacceptors.yaml | 3 --- charts/consul/templates/crd-peeringdialers.yaml | 3 --- charts/consul/templates/crd-proxydefaults.yaml | 3 --- charts/consul/templates/crd-servicedefaults.yaml | 3 --- charts/consul/templates/crd-serviceintentions.yaml | 3 --- charts/consul/templates/crd-serviceresolvers.yaml | 3 --- charts/consul/templates/crd-servicerouters.yaml | 3 --- charts/consul/templates/crd-servicesplitters.yaml | 3 --- charts/consul/templates/crd-terminatinggateways.yaml | 3 --- charts/consul/templates/create-federation-secret-job.yaml | 3 --- .../templates/create-federation-secret-podsecuritypolicy.yaml | 3 --- charts/consul/templates/create-federation-secret-role.yaml | 3 --- .../consul/templates/create-federation-secret-rolebinding.yaml | 3 --- .../templates/create-federation-secret-serviceaccount.yaml | 3 --- charts/consul/templates/dns-service.yaml | 3 --- charts/consul/templates/enterprise-license-job.yaml | 3 --- .../consul/templates/enterprise-license-podsecuritypolicy.yaml | 3 --- charts/consul/templates/enterprise-license-role.yaml | 3 --- charts/consul/templates/enterprise-license-rolebinding.yaml | 3 --- charts/consul/templates/enterprise-license-serviceaccount.yaml | 3 --- charts/consul/templates/expose-servers-service.yaml | 3 --- .../consul/templates/gossip-encryption-autogenerate-job.yaml | 3 --- .../gossip-encryption-autogenerate-podsecuritypolicy.yaml | 3 --- .../consul/templates/gossip-encryption-autogenerate-role.yaml | 3 --- .../templates/gossip-encryption-autogenerate-rolebinding.yaml | 3 --- .../gossip-encryption-autogenerate-serviceaccount.yaml | 3 --- charts/consul/templates/ingress-gateways-deployment.yaml | 3 --- .../consul/templates/ingress-gateways-podsecuritypolicy.yaml | 3 --- charts/consul/templates/ingress-gateways-role.yaml | 3 --- charts/consul/templates/ingress-gateways-rolebinding.yaml | 3 --- charts/consul/templates/ingress-gateways-service.yaml | 3 --- charts/consul/templates/ingress-gateways-serviceaccount.yaml | 3 --- charts/consul/templates/mesh-gateway-clusterrole.yaml | 3 --- charts/consul/templates/mesh-gateway-clusterrolebinding.yaml | 3 --- charts/consul/templates/mesh-gateway-deployment.yaml | 3 --- charts/consul/templates/mesh-gateway-podsecuritypolicy.yaml | 3 --- charts/consul/templates/mesh-gateway-service.yaml | 3 --- charts/consul/templates/mesh-gateway-serviceaccount.yaml | 3 --- charts/consul/templates/partition-init-job.yaml | 3 --- charts/consul/templates/partition-init-podsecuritypolicy.yaml | 3 --- charts/consul/templates/partition-init-role.yaml | 3 --- charts/consul/templates/partition-init-rolebinding.yaml | 3 --- charts/consul/templates/partition-init-serviceaccount.yaml | 3 --- charts/consul/templates/partition-name-configmap.yaml | 3 --- charts/consul/templates/prometheus.yaml | 3 --- charts/consul/templates/server-acl-init-cleanup-job.yaml | 3 --- .../templates/server-acl-init-cleanup-podsecuritypolicy.yaml | 3 --- charts/consul/templates/server-acl-init-cleanup-role.yaml | 3 --- .../consul/templates/server-acl-init-cleanup-rolebinding.yaml | 3 --- .../templates/server-acl-init-cleanup-serviceaccount.yaml | 3 --- charts/consul/templates/server-acl-init-job.yaml | 3 --- charts/consul/templates/server-acl-init-podsecuritypolicy.yaml | 3 --- charts/consul/templates/server-acl-init-role.yaml | 3 --- charts/consul/templates/server-acl-init-rolebinding.yaml | 3 --- charts/consul/templates/server-acl-init-serviceaccount.yaml | 3 --- charts/consul/templates/server-config-configmap.yaml | 3 --- charts/consul/templates/server-disruptionbudget.yaml | 3 --- charts/consul/templates/server-podsecuritypolicy.yaml | 3 --- charts/consul/templates/server-role.yaml | 3 --- charts/consul/templates/server-rolebinding.yaml | 3 --- charts/consul/templates/server-securitycontextconstraints.yaml | 3 --- charts/consul/templates/server-service.yaml | 3 --- charts/consul/templates/server-serviceaccount.yaml | 3 --- charts/consul/templates/server-snapshot-agent-configmap.yaml | 3 --- charts/consul/templates/server-statefulset.yaml | 3 --- charts/consul/templates/sync-catalog-clusterrole.yaml | 3 --- charts/consul/templates/sync-catalog-clusterrolebinding.yaml | 3 --- charts/consul/templates/sync-catalog-deployment.yaml | 3 --- charts/consul/templates/sync-catalog-podsecuritypolicy.yaml | 3 --- charts/consul/templates/sync-catalog-serviceaccount.yaml | 3 --- charts/consul/templates/terminating-gateways-deployment.yaml | 3 --- .../templates/terminating-gateways-podsecuritypolicy.yaml | 3 --- charts/consul/templates/terminating-gateways-role.yaml | 3 --- charts/consul/templates/terminating-gateways-rolebinding.yaml | 3 --- charts/consul/templates/terminating-gateways-service.yaml | 3 --- .../consul/templates/terminating-gateways-serviceaccount.yaml | 3 --- charts/consul/templates/tests/test-runner.yaml | 3 --- charts/consul/templates/tls-init-cleanup-job.yaml | 3 --- .../consul/templates/tls-init-cleanup-podsecuritypolicy.yaml | 3 --- charts/consul/templates/tls-init-cleanup-role.yaml | 3 --- charts/consul/templates/tls-init-cleanup-rolebinding.yaml | 3 --- charts/consul/templates/tls-init-cleanup-serviceaccount.yaml | 3 --- charts/consul/templates/tls-init-job.yaml | 3 --- charts/consul/templates/tls-init-podsecuritypolicy.yaml | 3 --- charts/consul/templates/tls-init-role.yaml | 3 --- charts/consul/templates/tls-init-rolebinding.yaml | 3 --- charts/consul/templates/tls-init-serviceaccount.yaml | 3 --- charts/consul/templates/ui-ingress.yaml | 3 --- charts/consul/templates/ui-service.yaml | 3 --- charts/consul/templates/webhook-cert-manager-clusterrole.yaml | 3 --- .../templates/webhook-cert-manager-clusterrolebinding.yaml | 3 --- charts/consul/templates/webhook-cert-manager-configmap.yaml | 3 --- charts/consul/templates/webhook-cert-manager-deployment.yaml | 3 --- .../templates/webhook-cert-manager-podsecuritypolicy.yaml | 3 --- .../consul/templates/webhook-cert-manager-serviceaccount.yaml | 3 --- 136 files changed, 408 deletions(-) diff --git a/charts/consul/templates/api-gateway-controller-clusterrole.yaml b/charts/consul/templates/api-gateway-controller-clusterrole.yaml index 5d2d45987b..eac2bd1f69 100644 --- a/charts/consul/templates/api-gateway-controller-clusterrole.yaml +++ b/charts/consul/templates/api-gateway-controller-clusterrole.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.apiGateway.enabled }} # The ClusterRole to enable the API Gateway controller to access required api endpoints. apiVersion: rbac.authorization.k8s.io/v1 diff --git a/charts/consul/templates/api-gateway-controller-clusterrolebinding.yaml b/charts/consul/templates/api-gateway-controller-clusterrolebinding.yaml index c874f5c7af..d083a08129 100644 --- a/charts/consul/templates/api-gateway-controller-clusterrolebinding.yaml +++ b/charts/consul/templates/api-gateway-controller-clusterrolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.apiGateway.enabled }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/charts/consul/templates/api-gateway-controller-deployment.yaml b/charts/consul/templates/api-gateway-controller-deployment.yaml index 4d757cfa2c..86517d7140 100644 --- a/charts/consul/templates/api-gateway-controller-deployment.yaml +++ b/charts/consul/templates/api-gateway-controller-deployment.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.apiGateway.enabled }} {{- if not .Values.client.grpc }}{{ fail "client.grpc must be true for api gateway" }}{{ end }} {{- if not .Values.apiGateway.image}}{{ fail "apiGateway.image must be set to enable api gateway" }}{{ end }} diff --git a/charts/consul/templates/api-gateway-controller-podsecuritypolicy.yaml b/charts/consul/templates/api-gateway-controller-podsecuritypolicy.yaml index 3ac3a05ba3..390d084303 100644 --- a/charts/consul/templates/api-gateway-controller-podsecuritypolicy.yaml +++ b/charts/consul/templates/api-gateway-controller-podsecuritypolicy.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if and .Values.apiGateway.enabled .Values.global.enablePodSecurityPolicies }} apiVersion: policy/v1beta1 kind: PodSecurityPolicy diff --git a/charts/consul/templates/api-gateway-controller-service.yaml b/charts/consul/templates/api-gateway-controller-service.yaml index c2ed00cd88..aa79ff9fc3 100644 --- a/charts/consul/templates/api-gateway-controller-service.yaml +++ b/charts/consul/templates/api-gateway-controller-service.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.apiGateway.enabled }} apiVersion: v1 kind: Service diff --git a/charts/consul/templates/api-gateway-controller-serviceaccount.yaml b/charts/consul/templates/api-gateway-controller-serviceaccount.yaml index 0ee711f164..98292a8dbe 100644 --- a/charts/consul/templates/api-gateway-controller-serviceaccount.yaml +++ b/charts/consul/templates/api-gateway-controller-serviceaccount.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.apiGateway.enabled }} apiVersion: v1 kind: ServiceAccount diff --git a/charts/consul/templates/api-gateway-gatewayclass.yaml b/charts/consul/templates/api-gateway-gatewayclass.yaml index 4a3a59f849..d9ba85e633 100644 --- a/charts/consul/templates/api-gateway-gatewayclass.yaml +++ b/charts/consul/templates/api-gateway-gatewayclass.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (and .Values.apiGateway.enabled .Values.apiGateway.managedGatewayClass.enabled) }} apiVersion: gateway.networking.k8s.io/v1alpha2 kind: GatewayClass diff --git a/charts/consul/templates/api-gateway-gatewayclassconfig.yaml b/charts/consul/templates/api-gateway-gatewayclassconfig.yaml index feed4e158b..ba0e6c63db 100644 --- a/charts/consul/templates/api-gateway-gatewayclassconfig.yaml +++ b/charts/consul/templates/api-gateway-gatewayclassconfig.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (and .Values.apiGateway.enabled .Values.apiGateway.managedGatewayClass.enabled) }} apiVersion: api-gateway.consul.hashicorp.com/v1alpha1 kind: GatewayClassConfig diff --git a/charts/consul/templates/api-gateway-podsecuritypolicy.yaml b/charts/consul/templates/api-gateway-podsecuritypolicy.yaml index f445cb77bf..48f826f995 100644 --- a/charts/consul/templates/api-gateway-podsecuritypolicy.yaml +++ b/charts/consul/templates/api-gateway-podsecuritypolicy.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if and .Values.apiGateway.enabled .Values.global.enablePodSecurityPolicies }} apiVersion: policy/v1beta1 kind: PodSecurityPolicy diff --git a/charts/consul/templates/auth-method-clusterrole.yaml b/charts/consul/templates/auth-method-clusterrole.yaml index bd696f479f..6b8f2c5451 100644 --- a/charts/consul/templates/auth-method-clusterrole.yaml +++ b/charts/consul/templates/auth-method-clusterrole.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.global.acls.manageSystemACLs }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/charts/consul/templates/auth-method-clusterrolebinding.yaml b/charts/consul/templates/auth-method-clusterrolebinding.yaml index 9c6b7cef31..9bd6c64113 100644 --- a/charts/consul/templates/auth-method-clusterrolebinding.yaml +++ b/charts/consul/templates/auth-method-clusterrolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.global.acls.manageSystemACLs }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/charts/consul/templates/auth-method-secret.yaml b/charts/consul/templates/auth-method-secret.yaml index 04fb0c164d..af0aeb4e1b 100644 --- a/charts/consul/templates/auth-method-secret.yaml +++ b/charts/consul/templates/auth-method-secret.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.global.acls.manageSystemACLs }} apiVersion: v1 kind: Secret diff --git a/charts/consul/templates/auth-method-serviceaccount.yaml b/charts/consul/templates/auth-method-serviceaccount.yaml index 453063ecd3..098339b8c8 100644 --- a/charts/consul/templates/auth-method-serviceaccount.yaml +++ b/charts/consul/templates/auth-method-serviceaccount.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.global.acls.manageSystemACLs }} apiVersion: v1 kind: ServiceAccount diff --git a/charts/consul/templates/client-config-configmap.yaml b/charts/consul/templates/client-config-configmap.yaml index 9a7122aa02..f9650a100b 100644 --- a/charts/consul/templates/client-config-configmap.yaml +++ b/charts/consul/templates/client-config-configmap.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} # ConfigMap with extra configuration specified directly to the chart # for client agents only. diff --git a/charts/consul/templates/client-daemonset.yaml b/charts/consul/templates/client-daemonset.yaml index 91401eebdf..09a70b394e 100644 --- a/charts/consul/templates/client-daemonset.yaml +++ b/charts/consul/templates/client-daemonset.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.global.imageK8s }}{{ fail "global.imageK8s is not a valid key, use global.imageK8S (note the capital 'S')" }}{{ end -}} {{- if (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} diff --git a/charts/consul/templates/client-podsecuritypolicy.yaml b/charts/consul/templates/client-podsecuritypolicy.yaml index db87e29f8d..0121bdf586 100644 --- a/charts/consul/templates/client-podsecuritypolicy.yaml +++ b/charts/consul/templates/client-podsecuritypolicy.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (and .Values.global.enablePodSecurityPolicies (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled))) }} apiVersion: policy/v1beta1 kind: PodSecurityPolicy diff --git a/charts/consul/templates/client-role.yaml b/charts/consul/templates/client-role.yaml index aebe8e1a52..7f05b82e6b 100644 --- a/charts/consul/templates/client-role.yaml +++ b/charts/consul/templates/client-role.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} apiVersion: rbac.authorization.k8s.io/v1 kind: Role diff --git a/charts/consul/templates/client-rolebinding.yaml b/charts/consul/templates/client-rolebinding.yaml index 4281a7869e..b034c70e55 100644 --- a/charts/consul/templates/client-rolebinding.yaml +++ b/charts/consul/templates/client-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding diff --git a/charts/consul/templates/client-securitycontextconstraints.yaml b/charts/consul/templates/client-securitycontextconstraints.yaml index 7ae5b7ba2a..07e7711384 100644 --- a/charts/consul/templates/client-securitycontextconstraints.yaml +++ b/charts/consul/templates/client-securitycontextconstraints.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (and .Values.global.openshift.enabled (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled))) }} apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints diff --git a/charts/consul/templates/client-serviceaccount.yaml b/charts/consul/templates/client-serviceaccount.yaml index 44b74a5c53..addd757b84 100644 --- a/charts/consul/templates/client-serviceaccount.yaml +++ b/charts/consul/templates/client-serviceaccount.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} apiVersion: v1 kind: ServiceAccount diff --git a/charts/consul/templates/cni-clusterrole.yaml b/charts/consul/templates/cni-clusterrole.yaml index a1cda2b7c5..773942cca8 100644 --- a/charts/consul/templates/cni-clusterrole.yaml +++ b/charts/consul/templates/cni-clusterrole.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.connectInject.cni.enabled }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/charts/consul/templates/cni-clusterrolebinding.yaml b/charts/consul/templates/cni-clusterrolebinding.yaml index f86391c259..4b860388b6 100644 --- a/charts/consul/templates/cni-clusterrolebinding.yaml +++ b/charts/consul/templates/cni-clusterrolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.connectInject.cni.enabled }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/charts/consul/templates/cni-daemonset.yaml b/charts/consul/templates/cni-daemonset.yaml index 1eda31998c..ae04d9e657 100644 --- a/charts/consul/templates/cni-daemonset.yaml +++ b/charts/consul/templates/cni-daemonset.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (and (.Values.connectInject.cni.enabled) (not .Values.connectInject.enabled)) }}{{ fail "connectInject.enabled must be true if connectInject.cni.enabled is true" }}{{ end -}} {{- if .Values.connectInject.cni.enabled }} apiVersion: apps/v1 diff --git a/charts/consul/templates/cni-networkattachmentdefinition.yaml b/charts/consul/templates/cni-networkattachmentdefinition.yaml index 056b74fa71..80ef50bac6 100644 --- a/charts/consul/templates/cni-networkattachmentdefinition.yaml +++ b/charts/consul/templates/cni-networkattachmentdefinition.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (and (.Values.connectInject.cni.enabled) (.Values.connectInject.cni.multus)) }} apiVersion: "k8s.cni.cncf.io/v1" kind: NetworkAttachmentDefinition diff --git a/charts/consul/templates/cni-podsecuritypolicy.yaml b/charts/consul/templates/cni-podsecuritypolicy.yaml index 8747387850..b600ed1b4b 100644 --- a/charts/consul/templates/cni-podsecuritypolicy.yaml +++ b/charts/consul/templates/cni-podsecuritypolicy.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (and .Values.connectInject.cni.enabled .Values.global.enablePodSecurityPolicies) }} apiVersion: policy/v1beta1 kind: PodSecurityPolicy diff --git a/charts/consul/templates/cni-resourcequota.yaml b/charts/consul/templates/cni-resourcequota.yaml index 1b8a8c7332..054c3061f5 100644 --- a/charts/consul/templates/cni-resourcequota.yaml +++ b/charts/consul/templates/cni-resourcequota.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.connectInject.cni.enabled }} apiVersion: v1 kind: ResourceQuota diff --git a/charts/consul/templates/cni-securitycontextconstraints.yaml b/charts/consul/templates/cni-securitycontextconstraints.yaml index d408617925..2c09dba9b8 100644 --- a/charts/consul/templates/cni-securitycontextconstraints.yaml +++ b/charts/consul/templates/cni-securitycontextconstraints.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (and (.Values.connectInject.cni.enabled) (.Values.global.openshift.enabled)) }} apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints diff --git a/charts/consul/templates/cni-serviceaccount.yaml b/charts/consul/templates/cni-serviceaccount.yaml index b3740d9f4a..cf4250b696 100644 --- a/charts/consul/templates/cni-serviceaccount.yaml +++ b/charts/consul/templates/cni-serviceaccount.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.connectInject.cni.enabled }} apiVersion: v1 kind: ServiceAccount diff --git a/charts/consul/templates/connect-inject-clusterrole.yaml b/charts/consul/templates/connect-inject-clusterrole.yaml index 307a9e3aed..f2e12f0ad9 100644 --- a/charts/consul/templates/connect-inject-clusterrole.yaml +++ b/charts/consul/templates/connect-inject-clusterrole.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled) }} # The ClusterRole to enable the Connect injector to get, list, watch and patch MutatingWebhookConfiguration. apiVersion: rbac.authorization.k8s.io/v1 diff --git a/charts/consul/templates/connect-inject-clusterrolebinding.yaml b/charts/consul/templates/connect-inject-clusterrolebinding.yaml index de969246a7..c380adb311 100644 --- a/charts/consul/templates/connect-inject-clusterrolebinding.yaml +++ b/charts/consul/templates/connect-inject-clusterrolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled) }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/charts/consul/templates/connect-inject-deployment.yaml b/charts/consul/templates/connect-inject-deployment.yaml index 529cc98787..2b52c1b81c 100644 --- a/charts/consul/templates/connect-inject-deployment.yaml +++ b/charts/consul/templates/connect-inject-deployment.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if and .Values.global.peering.enabled (not .Values.connectInject.enabled) }}{{ fail "setting global.peering.enabled to true requires connectInject.enabled to be true" }}{{ end }} {{- if and .Values.global.peering.enabled (not .Values.global.tls.enabled) }}{{ fail "setting global.peering.enabled to true requires global.tls.enabled to be true" }}{{ end }} {{- if and .Values.global.peering.enabled (not .Values.meshGateway.enabled) }}{{ fail "setting global.peering.enabled to true requires meshGateway.enabled to be true" }}{{ end }} diff --git a/charts/consul/templates/connect-inject-leader-election-role.yaml b/charts/consul/templates/connect-inject-leader-election-role.yaml index 2efaf64ec0..703aaffaac 100644 --- a/charts/consul/templates/connect-inject-leader-election-role.yaml +++ b/charts/consul/templates/connect-inject-leader-election-role.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled) }} apiVersion: rbac.authorization.k8s.io/v1 kind: Role diff --git a/charts/consul/templates/connect-inject-leader-election-rolebinding.yaml b/charts/consul/templates/connect-inject-leader-election-rolebinding.yaml index 28c4c9a7de..9a27d3c868 100644 --- a/charts/consul/templates/connect-inject-leader-election-rolebinding.yaml +++ b/charts/consul/templates/connect-inject-leader-election-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled) }} apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding diff --git a/charts/consul/templates/connect-inject-mutatingwebhookconfiguration.yaml b/charts/consul/templates/connect-inject-mutatingwebhookconfiguration.yaml index c01ffc333b..afcfd3800f 100644 --- a/charts/consul/templates/connect-inject-mutatingwebhookconfiguration.yaml +++ b/charts/consul/templates/connect-inject-mutatingwebhookconfiguration.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled)) }} # The MutatingWebhookConfiguration to enable the Connect injector. apiVersion: admissionregistration.k8s.io/v1 diff --git a/charts/consul/templates/connect-inject-podsecuritypolicy.yaml b/charts/consul/templates/connect-inject-podsecuritypolicy.yaml index 5f449eba4d..0fafef7c40 100644 --- a/charts/consul/templates/connect-inject-podsecuritypolicy.yaml +++ b/charts/consul/templates/connect-inject-podsecuritypolicy.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (and .Values.global.enablePodSecurityPolicies (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled))) }} apiVersion: policy/v1beta1 kind: PodSecurityPolicy diff --git a/charts/consul/templates/connect-inject-service.yaml b/charts/consul/templates/connect-inject-service.yaml index 39ca054b30..b0284af74d 100644 --- a/charts/consul/templates/connect-inject-service.yaml +++ b/charts/consul/templates/connect-inject-service.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled)) }} # The service for the Connect sidecar injector apiVersion: v1 diff --git a/charts/consul/templates/connect-inject-serviceaccount.yaml b/charts/consul/templates/connect-inject-serviceaccount.yaml index 3ec08e7ffb..ea2352c7ac 100644 --- a/charts/consul/templates/connect-inject-serviceaccount.yaml +++ b/charts/consul/templates/connect-inject-serviceaccount.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled) }} apiVersion: v1 kind: ServiceAccount diff --git a/charts/consul/templates/connect-injector-disruptionbudget.yaml b/charts/consul/templates/connect-injector-disruptionbudget.yaml index 4c7753a375..9b9cf2e39e 100644 --- a/charts/consul/templates/connect-injector-disruptionbudget.yaml +++ b/charts/consul/templates/connect-injector-disruptionbudget.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (and .Values.connectInject.disruptionBudget.enabled (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled))) }} # PodDisruptionBudget to prevent degrading the connectInject cluster through # voluntary cluster changes. diff --git a/charts/consul/templates/crd-exportedservices.yaml b/charts/consul/templates/crd-exportedservices.yaml index 5ff9450307..007990372c 100644 --- a/charts/consul/templates/crd-exportedservices.yaml +++ b/charts/consul/templates/crd-exportedservices.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.connectInject.enabled }} --- apiVersion: apiextensions.k8s.io/v1 diff --git a/charts/consul/templates/crd-ingressgateways.yaml b/charts/consul/templates/crd-ingressgateways.yaml index d7bd768593..a01fafd8dd 100644 --- a/charts/consul/templates/crd-ingressgateways.yaml +++ b/charts/consul/templates/crd-ingressgateways.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.connectInject.enabled }} --- apiVersion: apiextensions.k8s.io/v1 diff --git a/charts/consul/templates/crd-meshes.yaml b/charts/consul/templates/crd-meshes.yaml index 2e238c318e..2e33eb9653 100644 --- a/charts/consul/templates/crd-meshes.yaml +++ b/charts/consul/templates/crd-meshes.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.connectInject.enabled }} --- apiVersion: apiextensions.k8s.io/v1 diff --git a/charts/consul/templates/crd-peeringacceptors.yaml b/charts/consul/templates/crd-peeringacceptors.yaml index e74bb2d733..e06e830f04 100644 --- a/charts/consul/templates/crd-peeringacceptors.yaml +++ b/charts/consul/templates/crd-peeringacceptors.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if and .Values.connectInject.enabled .Values.global.peering.enabled }} --- apiVersion: apiextensions.k8s.io/v1 diff --git a/charts/consul/templates/crd-peeringdialers.yaml b/charts/consul/templates/crd-peeringdialers.yaml index aab95bb61a..e24401e761 100644 --- a/charts/consul/templates/crd-peeringdialers.yaml +++ b/charts/consul/templates/crd-peeringdialers.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if and .Values.connectInject.enabled .Values.global.peering.enabled }} --- apiVersion: apiextensions.k8s.io/v1 diff --git a/charts/consul/templates/crd-proxydefaults.yaml b/charts/consul/templates/crd-proxydefaults.yaml index 6c34618cb3..749f2e4257 100644 --- a/charts/consul/templates/crd-proxydefaults.yaml +++ b/charts/consul/templates/crd-proxydefaults.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.connectInject.enabled }} --- apiVersion: apiextensions.k8s.io/v1 diff --git a/charts/consul/templates/crd-servicedefaults.yaml b/charts/consul/templates/crd-servicedefaults.yaml index 0cb99dc97b..5c6ecc7476 100644 --- a/charts/consul/templates/crd-servicedefaults.yaml +++ b/charts/consul/templates/crd-servicedefaults.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.connectInject.enabled }} --- apiVersion: apiextensions.k8s.io/v1 diff --git a/charts/consul/templates/crd-serviceintentions.yaml b/charts/consul/templates/crd-serviceintentions.yaml index 18ab5b9d97..cdbb5413b0 100644 --- a/charts/consul/templates/crd-serviceintentions.yaml +++ b/charts/consul/templates/crd-serviceintentions.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.connectInject.enabled }} --- apiVersion: apiextensions.k8s.io/v1 diff --git a/charts/consul/templates/crd-serviceresolvers.yaml b/charts/consul/templates/crd-serviceresolvers.yaml index f8989a8cc6..e058052e97 100644 --- a/charts/consul/templates/crd-serviceresolvers.yaml +++ b/charts/consul/templates/crd-serviceresolvers.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.connectInject.enabled }} --- apiVersion: apiextensions.k8s.io/v1 diff --git a/charts/consul/templates/crd-servicerouters.yaml b/charts/consul/templates/crd-servicerouters.yaml index c927745f45..5052facc06 100644 --- a/charts/consul/templates/crd-servicerouters.yaml +++ b/charts/consul/templates/crd-servicerouters.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.connectInject.enabled }} --- apiVersion: apiextensions.k8s.io/v1 diff --git a/charts/consul/templates/crd-servicesplitters.yaml b/charts/consul/templates/crd-servicesplitters.yaml index 4c28e5a506..a2af050c3d 100644 --- a/charts/consul/templates/crd-servicesplitters.yaml +++ b/charts/consul/templates/crd-servicesplitters.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.connectInject.enabled }} --- apiVersion: apiextensions.k8s.io/v1 diff --git a/charts/consul/templates/crd-terminatinggateways.yaml b/charts/consul/templates/crd-terminatinggateways.yaml index 2ed7143f8b..583c218be8 100644 --- a/charts/consul/templates/crd-terminatinggateways.yaml +++ b/charts/consul/templates/crd-terminatinggateways.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.connectInject.enabled }} --- apiVersion: apiextensions.k8s.io/v1 diff --git a/charts/consul/templates/create-federation-secret-job.yaml b/charts/consul/templates/create-federation-secret-job.yaml index a90b2610bf..4f83a1f82a 100644 --- a/charts/consul/templates/create-federation-secret-job.yaml +++ b/charts/consul/templates/create-federation-secret-job.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.global.federation.createFederationSecret }} {{- if not .Values.global.federation.enabled }}{{ fail "global.federation.enabled must be true when global.federation.createFederationSecret is true" }}{{ end }} {{- if and (not .Values.global.acls.createReplicationToken) .Values.global.acls.manageSystemACLs }}{{ fail "global.acls.createReplicationToken must be true when global.acls.manageSystemACLs is true because the federation secret must include the replication token" }}{{ end }} diff --git a/charts/consul/templates/create-federation-secret-podsecuritypolicy.yaml b/charts/consul/templates/create-federation-secret-podsecuritypolicy.yaml index e05659807d..8217311992 100644 --- a/charts/consul/templates/create-federation-secret-podsecuritypolicy.yaml +++ b/charts/consul/templates/create-federation-secret-podsecuritypolicy.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.global.enablePodSecurityPolicies }} {{- if .Values.global.federation.createFederationSecret }} apiVersion: policy/v1beta1 diff --git a/charts/consul/templates/create-federation-secret-role.yaml b/charts/consul/templates/create-federation-secret-role.yaml index 6d285b968a..086932a831 100644 --- a/charts/consul/templates/create-federation-secret-role.yaml +++ b/charts/consul/templates/create-federation-secret-role.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.global.federation.createFederationSecret }} apiVersion: rbac.authorization.k8s.io/v1 kind: Role diff --git a/charts/consul/templates/create-federation-secret-rolebinding.yaml b/charts/consul/templates/create-federation-secret-rolebinding.yaml index e9c326448f..3db8e7cb06 100644 --- a/charts/consul/templates/create-federation-secret-rolebinding.yaml +++ b/charts/consul/templates/create-federation-secret-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.global.federation.createFederationSecret }} apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding diff --git a/charts/consul/templates/create-federation-secret-serviceaccount.yaml b/charts/consul/templates/create-federation-secret-serviceaccount.yaml index 843e75b61b..e398ec69c4 100644 --- a/charts/consul/templates/create-federation-secret-serviceaccount.yaml +++ b/charts/consul/templates/create-federation-secret-serviceaccount.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.global.federation.createFederationSecret }} apiVersion: v1 kind: ServiceAccount diff --git a/charts/consul/templates/dns-service.yaml b/charts/consul/templates/dns-service.yaml index 03b3a4cc9b..5bb446bc19 100644 --- a/charts/consul/templates/dns-service.yaml +++ b/charts/consul/templates/dns-service.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.dns.enabled | toString) "-") .Values.dns.enabled) (and (eq (.Values.dns.enabled | toString) "-") .Values.connectInject.transparentProxy.defaultEnabled)) }} # Service for Consul DNS. apiVersion: v1 diff --git a/charts/consul/templates/enterprise-license-job.yaml b/charts/consul/templates/enterprise-license-job.yaml index e6be55878a..0122690104 100644 --- a/charts/consul/templates/enterprise-license-job.yaml +++ b/charts/consul/templates/enterprise-license-job.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.server.enterpriseLicense }}{{ fail "server.enterpriseLicense has been moved to global.enterpriseLicense" }}{{ end -}} {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey (not .Values.global.enterpriseLicense.enableLicenseAutoload)) }} diff --git a/charts/consul/templates/enterprise-license-podsecuritypolicy.yaml b/charts/consul/templates/enterprise-license-podsecuritypolicy.yaml index 24adf32765..cf96367473 100644 --- a/charts/consul/templates/enterprise-license-podsecuritypolicy.yaml +++ b/charts/consul/templates/enterprise-license-podsecuritypolicy.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey (not .Values.global.enterpriseLicense.enableLicenseAutoload)) }} {{- if .Values.global.enablePodSecurityPolicies }} diff --git a/charts/consul/templates/enterprise-license-role.yaml b/charts/consul/templates/enterprise-license-role.yaml index 7536d51c60..6a1b7fdffa 100644 --- a/charts/consul/templates/enterprise-license-role.yaml +++ b/charts/consul/templates/enterprise-license-role.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey (not .Values.global.enterpriseLicense.enableLicenseAutoload)) }} apiVersion: rbac.authorization.k8s.io/v1 diff --git a/charts/consul/templates/enterprise-license-rolebinding.yaml b/charts/consul/templates/enterprise-license-rolebinding.yaml index 5b479613db..a21118b431 100644 --- a/charts/consul/templates/enterprise-license-rolebinding.yaml +++ b/charts/consul/templates/enterprise-license-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey (not .Values.global.enterpriseLicense.enableLicenseAutoload)) }} apiVersion: rbac.authorization.k8s.io/v1 diff --git a/charts/consul/templates/enterprise-license-serviceaccount.yaml b/charts/consul/templates/enterprise-license-serviceaccount.yaml index 6286281350..31c9da841e 100644 --- a/charts/consul/templates/enterprise-license-serviceaccount.yaml +++ b/charts/consul/templates/enterprise-license-serviceaccount.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey (not .Values.global.enterpriseLicense.enableLicenseAutoload)) }} apiVersion: v1 diff --git a/charts/consul/templates/expose-servers-service.yaml b/charts/consul/templates/expose-servers-service.yaml index 56743bbee6..d86cec9042 100644 --- a/charts/consul/templates/expose-servers-service.yaml +++ b/charts/consul/templates/expose-servers-service.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- $serverExposeServiceEnabled := (or (and (ne (.Values.server.exposeService.enabled | toString) "-") .Values.server.exposeService.enabled) (and (eq (.Values.server.exposeService.enabled | toString) "-") .Values.global.adminPartitions.enabled)) -}} {{- if (and $serverEnabled $serverExposeServiceEnabled) }} diff --git a/charts/consul/templates/gossip-encryption-autogenerate-job.yaml b/charts/consul/templates/gossip-encryption-autogenerate-job.yaml index fc7548366a..9d296478a1 100644 --- a/charts/consul/templates/gossip-encryption-autogenerate-job.yaml +++ b/charts/consul/templates/gossip-encryption-autogenerate-job.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.global.gossipEncryption.autoGenerate }} {{- if (or .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey) }} {{ fail "If global.gossipEncryption.autoGenerate is true, global.gossipEncryption.secretName and global.gossipEncryption.secretKey must not be set." }} diff --git a/charts/consul/templates/gossip-encryption-autogenerate-podsecuritypolicy.yaml b/charts/consul/templates/gossip-encryption-autogenerate-podsecuritypolicy.yaml index 54095be3a0..209b3aa343 100644 --- a/charts/consul/templates/gossip-encryption-autogenerate-podsecuritypolicy.yaml +++ b/charts/consul/templates/gossip-encryption-autogenerate-podsecuritypolicy.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if and .Values.global.gossipEncryption.autoGenerate .Values.global.enablePodSecurityPolicies }} --- apiVersion: policy/v1beta1 diff --git a/charts/consul/templates/gossip-encryption-autogenerate-role.yaml b/charts/consul/templates/gossip-encryption-autogenerate-role.yaml index 70b6058bda..8c51c96ffe 100644 --- a/charts/consul/templates/gossip-encryption-autogenerate-role.yaml +++ b/charts/consul/templates/gossip-encryption-autogenerate-role.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.global.gossipEncryption.autoGenerate }} apiVersion: rbac.authorization.k8s.io/v1 kind: Role diff --git a/charts/consul/templates/gossip-encryption-autogenerate-rolebinding.yaml b/charts/consul/templates/gossip-encryption-autogenerate-rolebinding.yaml index 961d14e240..7118475f64 100644 --- a/charts/consul/templates/gossip-encryption-autogenerate-rolebinding.yaml +++ b/charts/consul/templates/gossip-encryption-autogenerate-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.global.gossipEncryption.autoGenerate }} apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding diff --git a/charts/consul/templates/gossip-encryption-autogenerate-serviceaccount.yaml b/charts/consul/templates/gossip-encryption-autogenerate-serviceaccount.yaml index 4ea1c6bb39..1fd620237f 100644 --- a/charts/consul/templates/gossip-encryption-autogenerate-serviceaccount.yaml +++ b/charts/consul/templates/gossip-encryption-autogenerate-serviceaccount.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.global.gossipEncryption.autoGenerate }} apiVersion: v1 kind: ServiceAccount diff --git a/charts/consul/templates/ingress-gateways-deployment.yaml b/charts/consul/templates/ingress-gateways-deployment.yaml index 8a0f35328c..4f72031855 100644 --- a/charts/consul/templates/ingress-gateways-deployment.yaml +++ b/charts/consul/templates/ingress-gateways-deployment.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.ingressGateways.enabled }} {{- if not .Values.connectInject.enabled }}{{ fail "connectInject.enabled must be true" }}{{ end -}} {{- if and .Values.global.adminPartitions.enabled (not .Values.global.enableConsulNamespaces) }}{{ fail "global.enableConsulNamespaces must be true if global.adminPartitions.enabled=true" }}{{ end }} diff --git a/charts/consul/templates/ingress-gateways-podsecuritypolicy.yaml b/charts/consul/templates/ingress-gateways-podsecuritypolicy.yaml index abcf971598..f7354da2b3 100644 --- a/charts/consul/templates/ingress-gateways-podsecuritypolicy.yaml +++ b/charts/consul/templates/ingress-gateways-podsecuritypolicy.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (and .Values.global.enablePodSecurityPolicies .Values.ingressGateways.enabled) }} {{- $root := . }} {{- range .Values.ingressGateways.gateways }} diff --git a/charts/consul/templates/ingress-gateways-role.yaml b/charts/consul/templates/ingress-gateways-role.yaml index ea4567163b..49e8486e58 100644 --- a/charts/consul/templates/ingress-gateways-role.yaml +++ b/charts/consul/templates/ingress-gateways-role.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.ingressGateways.enabled }} {{- $root := . }} diff --git a/charts/consul/templates/ingress-gateways-rolebinding.yaml b/charts/consul/templates/ingress-gateways-rolebinding.yaml index 015395ec16..601de775f4 100644 --- a/charts/consul/templates/ingress-gateways-rolebinding.yaml +++ b/charts/consul/templates/ingress-gateways-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.ingressGateways.enabled }} {{- $root := . }} {{- range .Values.ingressGateways.gateways }} diff --git a/charts/consul/templates/ingress-gateways-service.yaml b/charts/consul/templates/ingress-gateways-service.yaml index 5f0ca22c39..cf54a740fe 100644 --- a/charts/consul/templates/ingress-gateways-service.yaml +++ b/charts/consul/templates/ingress-gateways-service.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.ingressGateways.enabled }} {{- $root := . }} diff --git a/charts/consul/templates/ingress-gateways-serviceaccount.yaml b/charts/consul/templates/ingress-gateways-serviceaccount.yaml index 0dc1e4564a..cea6cafc21 100644 --- a/charts/consul/templates/ingress-gateways-serviceaccount.yaml +++ b/charts/consul/templates/ingress-gateways-serviceaccount.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.ingressGateways.enabled }} {{- $root := . }} {{- $defaults := .Values.ingressGateways.defaults }} diff --git a/charts/consul/templates/mesh-gateway-clusterrole.yaml b/charts/consul/templates/mesh-gateway-clusterrole.yaml index fe4c7ca348..b951418b26 100644 --- a/charts/consul/templates/mesh-gateway-clusterrole.yaml +++ b/charts/consul/templates/mesh-gateway-clusterrole.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.meshGateway.enabled }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/charts/consul/templates/mesh-gateway-clusterrolebinding.yaml b/charts/consul/templates/mesh-gateway-clusterrolebinding.yaml index 028dea8a34..f8150ebb53 100644 --- a/charts/consul/templates/mesh-gateway-clusterrolebinding.yaml +++ b/charts/consul/templates/mesh-gateway-clusterrolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.meshGateway.enabled }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/charts/consul/templates/mesh-gateway-deployment.yaml b/charts/consul/templates/mesh-gateway-deployment.yaml index 8e2c0cc791..2b2bdc8c2a 100644 --- a/charts/consul/templates/mesh-gateway-deployment.yaml +++ b/charts/consul/templates/mesh-gateway-deployment.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.meshGateway.enabled }} {{- if not .Values.connectInject.enabled }}{{ fail "connectInject.enabled must be true" }}{{ end -}} {{- if and .Values.global.acls.manageSystemACLs (ne .Values.meshGateway.consulServiceName "") (ne .Values.meshGateway.consulServiceName "mesh-gateway") }}{{ fail "if global.acls.manageSystemACLs is true, meshGateway.consulServiceName cannot be set" }}{{ end -}} diff --git a/charts/consul/templates/mesh-gateway-podsecuritypolicy.yaml b/charts/consul/templates/mesh-gateway-podsecuritypolicy.yaml index 712f58c0cf..b5bbb2fa03 100644 --- a/charts/consul/templates/mesh-gateway-podsecuritypolicy.yaml +++ b/charts/consul/templates/mesh-gateway-podsecuritypolicy.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if and .Values.global.enablePodSecurityPolicies .Values.meshGateway.enabled }} apiVersion: policy/v1beta1 kind: PodSecurityPolicy diff --git a/charts/consul/templates/mesh-gateway-service.yaml b/charts/consul/templates/mesh-gateway-service.yaml index 3dbfb0486a..5fdceca8df 100644 --- a/charts/consul/templates/mesh-gateway-service.yaml +++ b/charts/consul/templates/mesh-gateway-service.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if and .Values.meshGateway.enabled }} apiVersion: v1 kind: Service diff --git a/charts/consul/templates/mesh-gateway-serviceaccount.yaml b/charts/consul/templates/mesh-gateway-serviceaccount.yaml index 4ba2b6788d..8c2da5ae06 100644 --- a/charts/consul/templates/mesh-gateway-serviceaccount.yaml +++ b/charts/consul/templates/mesh-gateway-serviceaccount.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.meshGateway.enabled }} apiVersion: v1 kind: ServiceAccount diff --git a/charts/consul/templates/partition-init-job.yaml b/charts/consul/templates/partition-init-job.yaml index 00a500f519..db73ef783b 100644 --- a/charts/consul/templates/partition-init-job.yaml +++ b/charts/consul/templates/partition-init-job.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (and .Values.global.adminPartitions.enabled (not $serverEnabled) (ne .Values.global.adminPartitions.name "default")) }} {{- template "consul.reservedNamesFailer" (list .Values.global.adminPartitions.name "global.adminPartitions.name") }} diff --git a/charts/consul/templates/partition-init-podsecuritypolicy.yaml b/charts/consul/templates/partition-init-podsecuritypolicy.yaml index 4cf3f3e038..2bc6782394 100644 --- a/charts/consul/templates/partition-init-podsecuritypolicy.yaml +++ b/charts/consul/templates/partition-init-podsecuritypolicy.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (and .Values.global.adminPartitions.enabled .Values.global.enablePodSecurityPolicies (not $serverEnabled)) }} apiVersion: policy/v1beta1 diff --git a/charts/consul/templates/partition-init-role.yaml b/charts/consul/templates/partition-init-role.yaml index 592c10e413..c13a5378eb 100644 --- a/charts/consul/templates/partition-init-role.yaml +++ b/charts/consul/templates/partition-init-role.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (and .Values.global.adminPartitions.enabled (not $serverEnabled)) }} apiVersion: rbac.authorization.k8s.io/v1 diff --git a/charts/consul/templates/partition-init-rolebinding.yaml b/charts/consul/templates/partition-init-rolebinding.yaml index e70b8d63ba..432d6df6ec 100644 --- a/charts/consul/templates/partition-init-rolebinding.yaml +++ b/charts/consul/templates/partition-init-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (and .Values.global.adminPartitions.enabled (not $serverEnabled)) }} apiVersion: rbac.authorization.k8s.io/v1 diff --git a/charts/consul/templates/partition-init-serviceaccount.yaml b/charts/consul/templates/partition-init-serviceaccount.yaml index 5352b5aea6..65fcf43b08 100644 --- a/charts/consul/templates/partition-init-serviceaccount.yaml +++ b/charts/consul/templates/partition-init-serviceaccount.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (and .Values.global.adminPartitions.enabled (not $serverEnabled)) }} apiVersion: v1 diff --git a/charts/consul/templates/partition-name-configmap.yaml b/charts/consul/templates/partition-name-configmap.yaml index bc3e4566c3..ee330b0f46 100644 --- a/charts/consul/templates/partition-name-configmap.yaml +++ b/charts/consul/templates/partition-name-configmap.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (and .Values.global.adminPartitions.enabled (not $serverEnabled)) }} # Immutable ConfigMap which saves the partition name. Attempting to update this configmap diff --git a/charts/consul/templates/prometheus.yaml b/charts/consul/templates/prometheus.yaml index 670fd76946..4dcede1745 100644 --- a/charts/consul/templates/prometheus.yaml +++ b/charts/consul/templates/prometheus.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.prometheus.enabled }} # This file is auto-generated, see addons/gen.sh --- diff --git a/charts/consul/templates/server-acl-init-cleanup-job.yaml b/charts/consul/templates/server-acl-init-cleanup-job.yaml index 63c509d883..35b0877ab4 100644 --- a/charts/consul/templates/server-acl-init-cleanup-job.yaml +++ b/charts/consul/templates/server-acl-init-cleanup-job.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (or $serverEnabled .Values.externalServers.enabled) }} {{- if .Values.global.acls.manageSystemACLs }} diff --git a/charts/consul/templates/server-acl-init-cleanup-podsecuritypolicy.yaml b/charts/consul/templates/server-acl-init-cleanup-podsecuritypolicy.yaml index 1ea8ce28b4..dd5dad24df 100644 --- a/charts/consul/templates/server-acl-init-cleanup-podsecuritypolicy.yaml +++ b/charts/consul/templates/server-acl-init-cleanup-podsecuritypolicy.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (or $serverEnabled .Values.externalServers.enabled) }} {{- if .Values.global.acls.manageSystemACLs }} diff --git a/charts/consul/templates/server-acl-init-cleanup-role.yaml b/charts/consul/templates/server-acl-init-cleanup-role.yaml index e18200adfe..0a2f296a60 100644 --- a/charts/consul/templates/server-acl-init-cleanup-role.yaml +++ b/charts/consul/templates/server-acl-init-cleanup-role.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (or $serverEnabled .Values.externalServers.enabled) }} {{- if .Values.global.acls.manageSystemACLs }} diff --git a/charts/consul/templates/server-acl-init-cleanup-rolebinding.yaml b/charts/consul/templates/server-acl-init-cleanup-rolebinding.yaml index c2365d63bd..268eaa5677 100644 --- a/charts/consul/templates/server-acl-init-cleanup-rolebinding.yaml +++ b/charts/consul/templates/server-acl-init-cleanup-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (or $serverEnabled .Values.externalServers.enabled) }} {{- if .Values.global.acls.manageSystemACLs }} diff --git a/charts/consul/templates/server-acl-init-cleanup-serviceaccount.yaml b/charts/consul/templates/server-acl-init-cleanup-serviceaccount.yaml index 030bd38516..604e6d784c 100644 --- a/charts/consul/templates/server-acl-init-cleanup-serviceaccount.yaml +++ b/charts/consul/templates/server-acl-init-cleanup-serviceaccount.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (or $serverEnabled .Values.externalServers.enabled) }} {{- if .Values.global.acls.manageSystemACLs }} diff --git a/charts/consul/templates/server-acl-init-job.yaml b/charts/consul/templates/server-acl-init-job.yaml index 3e42ce553b..e62db41ec2 100644 --- a/charts/consul/templates/server-acl-init-job.yaml +++ b/charts/consul/templates/server-acl-init-job.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (and $serverEnabled .Values.externalServers.enabled) }}{{ fail "only one of server.enabled or externalServers.enabled can be set" }}{{ end -}} {{- if (or $serverEnabled .Values.externalServers.enabled) }} diff --git a/charts/consul/templates/server-acl-init-podsecuritypolicy.yaml b/charts/consul/templates/server-acl-init-podsecuritypolicy.yaml index aa35d9ba07..9bf93e2551 100644 --- a/charts/consul/templates/server-acl-init-podsecuritypolicy.yaml +++ b/charts/consul/templates/server-acl-init-podsecuritypolicy.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (or $serverEnabled .Values.externalServers.enabled) }} {{- if .Values.global.acls.manageSystemACLs }} diff --git a/charts/consul/templates/server-acl-init-role.yaml b/charts/consul/templates/server-acl-init-role.yaml index d165c34492..eb7b6a928e 100644 --- a/charts/consul/templates/server-acl-init-role.yaml +++ b/charts/consul/templates/server-acl-init-role.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (or $serverEnabled .Values.externalServers.enabled) }} {{- if .Values.global.acls.manageSystemACLs }} diff --git a/charts/consul/templates/server-acl-init-rolebinding.yaml b/charts/consul/templates/server-acl-init-rolebinding.yaml index ff125f2c5e..fda4726d9f 100644 --- a/charts/consul/templates/server-acl-init-rolebinding.yaml +++ b/charts/consul/templates/server-acl-init-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (or $serverEnabled .Values.externalServers.enabled) }} {{- if .Values.global.acls.manageSystemACLs }} diff --git a/charts/consul/templates/server-acl-init-serviceaccount.yaml b/charts/consul/templates/server-acl-init-serviceaccount.yaml index 1b0129d168..c0e257de96 100644 --- a/charts/consul/templates/server-acl-init-serviceaccount.yaml +++ b/charts/consul/templates/server-acl-init-serviceaccount.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} {{- if (or $serverEnabled .Values.externalServers.enabled) }} {{- if .Values.global.acls.manageSystemACLs }} diff --git a/charts/consul/templates/server-config-configmap.yaml b/charts/consul/templates/server-config-configmap.yaml index 6ae8037606..f7dd85f166 100644 --- a/charts/consul/templates/server-config-configmap.yaml +++ b/charts/consul/templates/server-config-configmap.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} # StatefulSet to run the actual Consul server cluster. apiVersion: v1 diff --git a/charts/consul/templates/server-disruptionbudget.yaml b/charts/consul/templates/server-disruptionbudget.yaml index e4806c21f9..edf9c1c57f 100644 --- a/charts/consul/templates/server-disruptionbudget.yaml +++ b/charts/consul/templates/server-disruptionbudget.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (and .Values.server.disruptionBudget.enabled (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled))) }} # PodDisruptionBudget to prevent degrading the server cluster through # voluntary cluster changes. diff --git a/charts/consul/templates/server-podsecuritypolicy.yaml b/charts/consul/templates/server-podsecuritypolicy.yaml index 4049679043..09e8d75bd1 100644 --- a/charts/consul/templates/server-podsecuritypolicy.yaml +++ b/charts/consul/templates/server-podsecuritypolicy.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (and .Values.global.enablePodSecurityPolicies (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled))) }} apiVersion: policy/v1beta1 kind: PodSecurityPolicy diff --git a/charts/consul/templates/server-role.yaml b/charts/consul/templates/server-role.yaml index a3d432c299..202518bf67 100644 --- a/charts/consul/templates/server-role.yaml +++ b/charts/consul/templates/server-role.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} apiVersion: rbac.authorization.k8s.io/v1 kind: Role diff --git a/charts/consul/templates/server-rolebinding.yaml b/charts/consul/templates/server-rolebinding.yaml index c317cab99d..8ab705ddbc 100644 --- a/charts/consul/templates/server-rolebinding.yaml +++ b/charts/consul/templates/server-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding diff --git a/charts/consul/templates/server-securitycontextconstraints.yaml b/charts/consul/templates/server-securitycontextconstraints.yaml index 593a842ec2..8edd784ea7 100644 --- a/charts/consul/templates/server-securitycontextconstraints.yaml +++ b/charts/consul/templates/server-securitycontextconstraints.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (and .Values.global.openshift.enabled .Values.server.exposeGossipAndRPCPorts (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled))) }} apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints diff --git a/charts/consul/templates/server-service.yaml b/charts/consul/templates/server-service.yaml index e3ae931a3a..a392f0e76b 100644 --- a/charts/consul/templates/server-service.yaml +++ b/charts/consul/templates/server-service.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} # Headless service for Consul server DNS entries. This service should only # point to Consul servers. For access to an agent, one should assume that diff --git a/charts/consul/templates/server-serviceaccount.yaml b/charts/consul/templates/server-serviceaccount.yaml index ff8cc2022a..a1617975ae 100644 --- a/charts/consul/templates/server-serviceaccount.yaml +++ b/charts/consul/templates/server-serviceaccount.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} apiVersion: v1 kind: ServiceAccount diff --git a/charts/consul/templates/server-snapshot-agent-configmap.yaml b/charts/consul/templates/server-snapshot-agent-configmap.yaml index cff5e0d840..da68d1509c 100644 --- a/charts/consul/templates/server-snapshot-agent-configmap.yaml +++ b/charts/consul/templates/server-snapshot-agent-configmap.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.server.snapshotAgent.enabled }} apiVersion: v1 kind: ConfigMap diff --git a/charts/consul/templates/server-statefulset.yaml b/charts/consul/templates/server-statefulset.yaml index e389509d88..8b73306fd7 100644 --- a/charts/consul/templates/server-statefulset.yaml +++ b/charts/consul/templates/server-statefulset.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if and .Values.global.federation.enabled .Values.global.adminPartitions.enabled }}{{ fail "If global.federation.enabled is true, global.adminPartitions.enabled must be false because they are mutually exclusive" }}{{ end }} {{- if and .Values.global.federation.enabled (not .Values.global.tls.enabled) }}{{ fail "If global.federation.enabled is true, global.tls.enabled must be true because federation is only supported with TLS enabled" }}{{ end }} diff --git a/charts/consul/templates/sync-catalog-clusterrole.yaml b/charts/consul/templates/sync-catalog-clusterrole.yaml index 5055530f6e..0b0837c0df 100644 --- a/charts/consul/templates/sync-catalog-clusterrole.yaml +++ b/charts/consul/templates/sync-catalog-clusterrole.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- $syncEnabled := (or (and (ne (.Values.syncCatalog.enabled | toString) "-") .Values.syncCatalog.enabled) (and (eq (.Values.syncCatalog.enabled | toString) "-") .Values.global.enabled)) }} {{- if $syncEnabled }} apiVersion: rbac.authorization.k8s.io/v1 diff --git a/charts/consul/templates/sync-catalog-clusterrolebinding.yaml b/charts/consul/templates/sync-catalog-clusterrolebinding.yaml index f5e3e0a98a..818823cca3 100644 --- a/charts/consul/templates/sync-catalog-clusterrolebinding.yaml +++ b/charts/consul/templates/sync-catalog-clusterrolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- $syncEnabled := (or (and (ne (.Values.syncCatalog.enabled | toString) "-") .Values.syncCatalog.enabled) (and (eq (.Values.syncCatalog.enabled | toString) "-") .Values.global.enabled)) }} {{- if $syncEnabled }} apiVersion: rbac.authorization.k8s.io/v1 diff --git a/charts/consul/templates/sync-catalog-deployment.yaml b/charts/consul/templates/sync-catalog-deployment.yaml index 94d97fcfc1..f2815d9627 100644 --- a/charts/consul/templates/sync-catalog-deployment.yaml +++ b/charts/consul/templates/sync-catalog-deployment.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.syncCatalog.enabled | toString) "-") .Values.syncCatalog.enabled) (and (eq (.Values.syncCatalog.enabled | toString) "-") .Values.global.enabled)) }} {{- template "consul.reservedNamesFailer" (list .Values.syncCatalog.consulNamespaces.consulDestinationNamespace "syncCatalog.consulNamespaces.consulDestinationNamespace") }} {{ template "consul.validateRequiredCloudSecretsExist" . }} diff --git a/charts/consul/templates/sync-catalog-podsecuritypolicy.yaml b/charts/consul/templates/sync-catalog-podsecuritypolicy.yaml index 0737265591..cc70feaab1 100644 --- a/charts/consul/templates/sync-catalog-podsecuritypolicy.yaml +++ b/charts/consul/templates/sync-catalog-podsecuritypolicy.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (and .Values.global.enablePodSecurityPolicies (or (and (ne (.Values.syncCatalog.enabled | toString) "-") .Values.syncCatalog.enabled) (and (eq (.Values.syncCatalog.enabled | toString) "-") .Values.global.enabled))) }} apiVersion: policy/v1beta1 kind: PodSecurityPolicy diff --git a/charts/consul/templates/sync-catalog-serviceaccount.yaml b/charts/consul/templates/sync-catalog-serviceaccount.yaml index a5b6023956..deab1ad075 100644 --- a/charts/consul/templates/sync-catalog-serviceaccount.yaml +++ b/charts/consul/templates/sync-catalog-serviceaccount.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- $syncEnabled := (or (and (ne (.Values.syncCatalog.enabled | toString) "-") .Values.syncCatalog.enabled) (and (eq (.Values.syncCatalog.enabled | toString) "-") .Values.global.enabled)) }} {{- if $syncEnabled }} apiVersion: v1 diff --git a/charts/consul/templates/terminating-gateways-deployment.yaml b/charts/consul/templates/terminating-gateways-deployment.yaml index 2fcc27448f..2f2cb9a921 100644 --- a/charts/consul/templates/terminating-gateways-deployment.yaml +++ b/charts/consul/templates/terminating-gateways-deployment.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.terminatingGateways.enabled }} {{- if not .Values.connectInject.enabled }}{{ fail "connectInject.enabled must be true" }}{{ end -}} {{- if and .Values.global.adminPartitions.enabled (not .Values.global.enableConsulNamespaces) }}{{ fail "global.enableConsulNamespaces must be true if global.adminPartitions.enabled=true" }}{{ end }} diff --git a/charts/consul/templates/terminating-gateways-podsecuritypolicy.yaml b/charts/consul/templates/terminating-gateways-podsecuritypolicy.yaml index a529f902bd..97ad2af961 100644 --- a/charts/consul/templates/terminating-gateways-podsecuritypolicy.yaml +++ b/charts/consul/templates/terminating-gateways-podsecuritypolicy.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (and .Values.global.enablePodSecurityPolicies .Values.terminatingGateways.enabled) }} {{- $root := . }} {{- range .Values.terminatingGateways.gateways }} diff --git a/charts/consul/templates/terminating-gateways-role.yaml b/charts/consul/templates/terminating-gateways-role.yaml index 71e3cd0e1e..4ae280ca81 100644 --- a/charts/consul/templates/terminating-gateways-role.yaml +++ b/charts/consul/templates/terminating-gateways-role.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.terminatingGateways.enabled }} {{- $root := . }} diff --git a/charts/consul/templates/terminating-gateways-rolebinding.yaml b/charts/consul/templates/terminating-gateways-rolebinding.yaml index cf6d533e91..4271f8f59c 100644 --- a/charts/consul/templates/terminating-gateways-rolebinding.yaml +++ b/charts/consul/templates/terminating-gateways-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.terminatingGateways.enabled }} {{- $root := . }} {{- range .Values.terminatingGateways.gateways }} diff --git a/charts/consul/templates/terminating-gateways-service.yaml b/charts/consul/templates/terminating-gateways-service.yaml index 8515897037..124900e727 100644 --- a/charts/consul/templates/terminating-gateways-service.yaml +++ b/charts/consul/templates/terminating-gateways-service.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.terminatingGateways.enabled }} {{- $root := . }} diff --git a/charts/consul/templates/terminating-gateways-serviceaccount.yaml b/charts/consul/templates/terminating-gateways-serviceaccount.yaml index 47f65fb937..211fb5c72f 100644 --- a/charts/consul/templates/terminating-gateways-serviceaccount.yaml +++ b/charts/consul/templates/terminating-gateways-serviceaccount.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.terminatingGateways.enabled }} {{- $root := . }} {{- $defaults := .Values.terminatingGateways.defaults }} diff --git a/charts/consul/templates/tests/test-runner.yaml b/charts/consul/templates/tests/test-runner.yaml index 985605c2e5..b8b078003b 100644 --- a/charts/consul/templates/tests/test-runner.yaml +++ b/charts/consul/templates/tests/test-runner.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if .Values.tests.enabled }} apiVersion: v1 kind: Pod diff --git a/charts/consul/templates/tls-init-cleanup-job.yaml b/charts/consul/templates/tls-init-cleanup-job.yaml index cfc389ca44..ba29bb84ae 100644 --- a/charts/consul/templates/tls-init-cleanup-job.yaml +++ b/charts/consul/templates/tls-init-cleanup-job.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} {{- if not .Values.global.secretsBackend.vault.enabled }} diff --git a/charts/consul/templates/tls-init-cleanup-podsecuritypolicy.yaml b/charts/consul/templates/tls-init-cleanup-podsecuritypolicy.yaml index db28affcc2..ed99d5f297 100644 --- a/charts/consul/templates/tls-init-cleanup-podsecuritypolicy.yaml +++ b/charts/consul/templates/tls-init-cleanup-podsecuritypolicy.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and (and .Values.global.tls.enabled .Values.global.enablePodSecurityPolicies) (not .Values.server.serverCert.secretName)) }} {{- if not .Values.global.secretsBackend.vault.enabled }} diff --git a/charts/consul/templates/tls-init-cleanup-role.yaml b/charts/consul/templates/tls-init-cleanup-role.yaml index 00a70aee17..aa66e3edc4 100644 --- a/charts/consul/templates/tls-init-cleanup-role.yaml +++ b/charts/consul/templates/tls-init-cleanup-role.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} {{- if not .Values.global.secretsBackend.vault.enabled }} diff --git a/charts/consul/templates/tls-init-cleanup-rolebinding.yaml b/charts/consul/templates/tls-init-cleanup-rolebinding.yaml index a2abcc4b71..0d3bfe38e7 100644 --- a/charts/consul/templates/tls-init-cleanup-rolebinding.yaml +++ b/charts/consul/templates/tls-init-cleanup-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} {{- if not .Values.global.secretsBackend.vault.enabled }} diff --git a/charts/consul/templates/tls-init-cleanup-serviceaccount.yaml b/charts/consul/templates/tls-init-cleanup-serviceaccount.yaml index 60f1e7402f..57e40dd3af 100644 --- a/charts/consul/templates/tls-init-cleanup-serviceaccount.yaml +++ b/charts/consul/templates/tls-init-cleanup-serviceaccount.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} {{- if not .Values.global.secretsBackend.vault.enabled }} diff --git a/charts/consul/templates/tls-init-job.yaml b/charts/consul/templates/tls-init-job.yaml index d27e31d44b..d002ae7a75 100644 --- a/charts/consul/templates/tls-init-job.yaml +++ b/charts/consul/templates/tls-init-job.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} {{- if not .Values.global.secretsBackend.vault.enabled }} diff --git a/charts/consul/templates/tls-init-podsecuritypolicy.yaml b/charts/consul/templates/tls-init-podsecuritypolicy.yaml index 936097cee9..5d2a393955 100644 --- a/charts/consul/templates/tls-init-podsecuritypolicy.yaml +++ b/charts/consul/templates/tls-init-podsecuritypolicy.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and (and .Values.global.tls.enabled .Values.global.enablePodSecurityPolicies) (not .Values.server.serverCert.secretName)) }} {{- if not .Values.global.secretsBackend.vault.enabled }} diff --git a/charts/consul/templates/tls-init-role.yaml b/charts/consul/templates/tls-init-role.yaml index 49a15ad479..216602ee9f 100644 --- a/charts/consul/templates/tls-init-role.yaml +++ b/charts/consul/templates/tls-init-role.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} {{- if not .Values.global.secretsBackend.vault.enabled }} diff --git a/charts/consul/templates/tls-init-rolebinding.yaml b/charts/consul/templates/tls-init-rolebinding.yaml index 3217a80909..9b68d97d8c 100644 --- a/charts/consul/templates/tls-init-rolebinding.yaml +++ b/charts/consul/templates/tls-init-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} {{- if not .Values.global.secretsBackend.vault.enabled }} diff --git a/charts/consul/templates/tls-init-serviceaccount.yaml b/charts/consul/templates/tls-init-serviceaccount.yaml index 6f3b9407b1..f8504da94c 100644 --- a/charts/consul/templates/tls-init-serviceaccount.yaml +++ b/charts/consul/templates/tls-init-serviceaccount.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} {{- if not .Values.global.secretsBackend.vault.enabled }} diff --git a/charts/consul/templates/ui-ingress.yaml b/charts/consul/templates/ui-ingress.yaml index b1b5dbc8fa..0414a7cc2d 100644 --- a/charts/consul/templates/ui-ingress.yaml +++ b/charts/consul/templates/ui-ingress.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (and (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) (or (and (ne (.Values.ui.enabled | toString) "-") .Values.ui.enabled) (and (eq (.Values.ui.enabled | toString) "-") .Values.global.enabled)) (or (and (ne (.Values.ui.service.enabled | toString) "-") .Values.ui.service.enabled) (and (eq (.Values.ui.service.enabled | toString) "-") .Values.global.enabled))) }} {{- if (and (ne (.Values.ui.ingress.enabled | toString) "-") .Values.ui.ingress.enabled) }} {{- $serviceName := printf "%s-%s" (include "consul.fullname" .) "ui" -}} diff --git a/charts/consul/templates/ui-service.yaml b/charts/consul/templates/ui-service.yaml index 6a8045082e..dc2abf4fc5 100644 --- a/charts/consul/templates/ui-service.yaml +++ b/charts/consul/templates/ui-service.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{- if (and (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) (or (and (ne (.Values.ui.enabled | toString) "-") .Values.ui.enabled) (and (eq (.Values.ui.enabled | toString) "-") .Values.global.enabled)) (or (and (ne (.Values.ui.service.enabled | toString) "-") .Values.ui.service.enabled) (and (eq (.Values.ui.service.enabled | toString) "-") .Values.global.enabled))) }} # UI Service for Consul Server apiVersion: v1 diff --git a/charts/consul/templates/webhook-cert-manager-clusterrole.yaml b/charts/consul/templates/webhook-cert-manager-clusterrole.yaml index d7b89db85e..e13e2dc741 100644 --- a/charts/consul/templates/webhook-cert-manager-clusterrole.yaml +++ b/charts/consul/templates/webhook-cert-manager-clusterrole.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} {{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} apiVersion: rbac.authorization.k8s.io/v1 diff --git a/charts/consul/templates/webhook-cert-manager-clusterrolebinding.yaml b/charts/consul/templates/webhook-cert-manager-clusterrolebinding.yaml index fa9b6f46c5..472ef4ee1d 100644 --- a/charts/consul/templates/webhook-cert-manager-clusterrolebinding.yaml +++ b/charts/consul/templates/webhook-cert-manager-clusterrolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} {{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} apiVersion: rbac.authorization.k8s.io/v1 diff --git a/charts/consul/templates/webhook-cert-manager-configmap.yaml b/charts/consul/templates/webhook-cert-manager-configmap.yaml index c4b747a795..293dd32d9f 100644 --- a/charts/consul/templates/webhook-cert-manager-configmap.yaml +++ b/charts/consul/templates/webhook-cert-manager-configmap.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} {{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} apiVersion: v1 diff --git a/charts/consul/templates/webhook-cert-manager-deployment.yaml b/charts/consul/templates/webhook-cert-manager-deployment.yaml index 558a5d43d5..dd93c039d2 100644 --- a/charts/consul/templates/webhook-cert-manager-deployment.yaml +++ b/charts/consul/templates/webhook-cert-manager-deployment.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} {{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} apiVersion: apps/v1 diff --git a/charts/consul/templates/webhook-cert-manager-podsecuritypolicy.yaml b/charts/consul/templates/webhook-cert-manager-podsecuritypolicy.yaml index d4e5413d52..4d685edc39 100644 --- a/charts/consul/templates/webhook-cert-manager-podsecuritypolicy.yaml +++ b/charts/consul/templates/webhook-cert-manager-podsecuritypolicy.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} {{- if (and .Values.global.enablePodSecurityPolicies (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled))) }} {{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} diff --git a/charts/consul/templates/webhook-cert-manager-serviceaccount.yaml b/charts/consul/templates/webhook-cert-manager-serviceaccount.yaml index c4f594945f..68c54f3c27 100644 --- a/charts/consul/templates/webhook-cert-manager-serviceaccount.yaml +++ b/charts/consul/templates/webhook-cert-manager-serviceaccount.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - {{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} {{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} apiVersion: v1 From c6068ca1efa659c30beb8fc2301a197100374507 Mon Sep 17 00:00:00 2001 From: Ronald Ekambi Date: Tue, 28 Mar 2023 09:47:28 -0400 Subject: [PATCH 109/340] fix copywrite ignore file --- .copywrite.hcl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.copywrite.hcl b/.copywrite.hcl index 6a47e1061d..9c2e0c0310 100644 --- a/.copywrite.hcl +++ b/.copywrite.hcl @@ -8,7 +8,9 @@ project { # Supports doublestar glob patterns for more flexibility in defining which # files or folders should be ignored header_ignore = [ - # "vendors/**", - # "**autogen**", + + # ignoring charts templates as adding copyright headers breaks all tests + "charts/consul/templates/**", + ] } From faf41e865262f34f9be6d5be95e9ec7a5180c2c9 Mon Sep 17 00:00:00 2001 From: Maliz Date: Fri, 24 Mar 2023 13:23:26 -0700 Subject: [PATCH 110/340] add failover policy to service resolver annd proxy default --- .../consul/templates/crd-proxydefaults.yaml | 8 +++++ .../templates/crd-serviceresolvers.yaml | 8 +++++ .../api/v1alpha1/proxydefaults_types.go | 4 +++ .../api/v1alpha1/proxydefaults_types_test.go | 12 +++++++ .../api/v1alpha1/serviceresolver_types.go | 9 +++-- .../v1alpha1/serviceresolver_types_test.go | 36 +++++++++++++++++++ control-plane/api/v1alpha1/shared_types.go | 29 +++++++++++++++ .../consul.hashicorp.com_proxydefaults.yaml | 8 +++++ ...consul.hashicorp.com_serviceresolvers.yaml | 8 +++++ control-plane/go.mod | 2 +- control-plane/go.sum | 6 ++++ 11 files changed, 127 insertions(+), 3 deletions(-) diff --git a/charts/consul/templates/crd-proxydefaults.yaml b/charts/consul/templates/crd-proxydefaults.yaml index 749f2e4257..1d78d687a7 100644 --- a/charts/consul/templates/crd-proxydefaults.yaml +++ b/charts/consul/templates/crd-proxydefaults.yaml @@ -143,6 +143,14 @@ spec: type: object type: array type: object + failoverPolicy: + description: FailoverPolicy specifies the exact mechanism used for failover. + properties: + mode: + description: Mode specifies the type of failover that will be performed. + Valid values are "default", "" (equivalent to "default") and "order-by-locality". + type: string + type: object meshGateway: description: MeshGateway controls the default mesh gateway configuration for this service. diff --git a/charts/consul/templates/crd-serviceresolvers.yaml b/charts/consul/templates/crd-serviceresolvers.yaml index e058052e97..16d46b9615 100644 --- a/charts/consul/templates/crd-serviceresolvers.yaml +++ b/charts/consul/templates/crd-serviceresolvers.yaml @@ -79,6 +79,14 @@ spec: service from to form the failover group of instances. If empty the current namespace is used. type: string + policy: + description: FailoverPolicy specifies the exact mechanism used for failover. + properties: + mode: + description: Mode specifies the type of failover that will be performed. + Valid values are "default", "" (equivalent to "default") and "order-by-locality". + type: string + type: object service: description: Service is the service to resolve instead of the default as the failover group of instances during failover. diff --git a/control-plane/api/v1alpha1/proxydefaults_types.go b/control-plane/api/v1alpha1/proxydefaults_types.go index 8a211aed56..f83b12ecfe 100644 --- a/control-plane/api/v1alpha1/proxydefaults_types.go +++ b/control-plane/api/v1alpha1/proxydefaults_types.go @@ -82,6 +82,8 @@ type ProxyDefaultsSpec struct { AccessLogs *AccessLogs `json:"accessLogs,omitempty"` // EnvoyExtensions are a list of extensions to modify Envoy proxy configuration. EnvoyExtensions EnvoyExtensions `json:"envoyExtensions,omitempty"` + // FailoverPolicy specifies the exact mechanism used for failover. + FailoverPolicy *FailoverPolicy `json:"failoverPolicy,omitempty"` } func (in *ProxyDefaults) GetObjectMeta() metav1.ObjectMeta { @@ -174,6 +176,7 @@ func (in *ProxyDefaults) ToConsul(datacenter string) capi.ConfigEntry { TransparentProxy: in.Spec.TransparentProxy.toConsul(), AccessLogs: in.Spec.AccessLogs.toConsul(), EnvoyExtensions: in.Spec.EnvoyExtensions.toConsul(), + FailoverPolicy: in.Spec.FailoverPolicy.toConsul(), Meta: meta(datacenter), } } @@ -209,6 +212,7 @@ func (in *ProxyDefaults) Validate(_ common.ConsulMeta) error { } allErrs = append(allErrs, in.Spec.Expose.validate(path.Child("expose"))...) allErrs = append(allErrs, in.Spec.EnvoyExtensions.validate(path.Child("envoyExtensions"))...) + allErrs = append(allErrs, in.Spec.FailoverPolicy.validate(path.Child("failoverPolicy"))...) if len(allErrs) > 0 { return apierrors.NewInvalid( diff --git a/control-plane/api/v1alpha1/proxydefaults_types_test.go b/control-plane/api/v1alpha1/proxydefaults_types_test.go index 011ab7d724..c194db2b9a 100644 --- a/control-plane/api/v1alpha1/proxydefaults_types_test.go +++ b/control-plane/api/v1alpha1/proxydefaults_types_test.go @@ -93,6 +93,9 @@ func TestProxyDefaults_MatchesConsul(t *testing.T) { Required: true, }, }, + FailoverPolicy: &FailoverPolicy{ + Mode: "default", + }, }, }, Theirs: &capi.ProxyConfigEntry{ @@ -151,6 +154,9 @@ func TestProxyDefaults_MatchesConsul(t *testing.T) { Required: true, }, }, + FailoverPolicy: &capi.ServiceResolverFailoverPolicy{ + Mode: "default", + }, }, Matches: true, }, @@ -303,6 +309,9 @@ func TestProxyDefaults_ToConsul(t *testing.T) { Required: true, }, }, + FailoverPolicy: &FailoverPolicy{ + Mode: "default", + }, }, }, Exp: &capi.ProxyConfigEntry{ @@ -362,6 +371,9 @@ func TestProxyDefaults_ToConsul(t *testing.T) { Required: true, }, }, + FailoverPolicy: &capi.ServiceResolverFailoverPolicy{ + Mode: "default", + }, Meta: map[string]string{ common.SourceKey: common.SourceValue, common.DatacenterKey: "datacenter", diff --git a/control-plane/api/v1alpha1/serviceresolver_types.go b/control-plane/api/v1alpha1/serviceresolver_types.go index 58711dfde6..4bf6f5f4ad 100644 --- a/control-plane/api/v1alpha1/serviceresolver_types.go +++ b/control-plane/api/v1alpha1/serviceresolver_types.go @@ -131,6 +131,8 @@ type ServiceResolverFailover struct { Datacenters []string `json:"datacenters,omitempty"` // Targets specifies a fixed list of failover targets to try during failover. Targets []ServiceResolverFailoverTarget `json:"targets,omitempty"` + // Policy specifies the exact mechanism used for failover. + Policy *FailoverPolicy `json:"policy,omitempty"` } type ServiceResolverFailoverTarget struct { @@ -398,6 +400,9 @@ func (in ServiceResolverFailover) toConsul() capi.ServiceResolverFailover { Namespace: in.Namespace, Datacenters: in.Datacenters, Targets: targets, + Policy: &capi.ServiceResolverFailoverPolicy { + Mode: in.Policy.Mode, + }, } } @@ -507,7 +512,7 @@ func (in *ServiceResolver) validateEnterprise(consulMeta common.ConsulMeta) fiel } func (in *ServiceResolverFailover) isEmpty() bool { - return in.Service == "" && in.ServiceSubset == "" && in.Namespace == "" && len(in.Datacenters) == 0 && len(in.Targets) == 0 + return in.Service == "" && in.ServiceSubset == "" && in.Namespace == "" && len(in.Datacenters) == 0 && len(in.Targets) == 0 && in.Policy == nil } func (in *ServiceResolverFailover) validate(path *field.Path) *field.Error { @@ -515,7 +520,7 @@ func (in *ServiceResolverFailover) validate(path *field.Path) *field.Error { // NOTE: We're passing "{}" here as our value because we know that the // error is we have an empty object. return field.Invalid(path, "{}", - "service, serviceSubset, namespace, datacenters, and targets cannot all be empty at once") + "service, serviceSubset, namespace, datacenters, policy, and targets cannot all be empty at once") } return nil } diff --git a/control-plane/api/v1alpha1/serviceresolver_types_test.go b/control-plane/api/v1alpha1/serviceresolver_types_test.go index b3c552172f..6ac9e71999 100644 --- a/control-plane/api/v1alpha1/serviceresolver_types_test.go +++ b/control-plane/api/v1alpha1/serviceresolver_types_test.go @@ -70,18 +70,27 @@ func TestServiceResolver_MatchesConsul(t *testing.T) { ServiceSubset: "failover_subset1", Namespace: "failover_namespace1", Datacenters: []string{"failover1_dc1", "failover1_dc2"}, + Policy: &FailoverPolicy{ + Mode: "default", + }, }, "failover2": { Service: "failover2", ServiceSubset: "failover_subset2", Namespace: "failover_namespace2", Datacenters: []string{"failover2_dc1", "failover2_dc2"}, + Policy: &FailoverPolicy{ + Mode: "", + }, }, "failover3": { Targets: []ServiceResolverFailoverTarget{ {Peer: "failover_peer3"}, {Partition: "failover_partition3", Namespace: "failover_namespace3"}, }, + Policy: &FailoverPolicy{ + Mode: "order-by-locality", + }, }, }, ConnectTimeout: metav1.Duration{Duration: 1 * time.Second}, @@ -137,18 +146,27 @@ func TestServiceResolver_MatchesConsul(t *testing.T) { ServiceSubset: "failover_subset1", Namespace: "failover_namespace1", Datacenters: []string{"failover1_dc1", "failover1_dc2"}, + Policy: &capi.ServiceResolverFailoverPolicy{ + Mode: "default", + }, }, "failover2": { Service: "failover2", ServiceSubset: "failover_subset2", Namespace: "failover_namespace2", Datacenters: []string{"failover2_dc1", "failover2_dc2"}, + Policy: &capi.ServiceResolverFailoverPolicy{ + Mode: "", + }, }, "failover3": { Targets: []capi.ServiceResolverFailoverTarget{ {Peer: "failover_peer3"}, {Partition: "failover_partition3", Namespace: "failover_namespace3"}, }, + Policy: &capi.ServiceResolverFailoverPolicy{ + Mode: "order-by-locality", + }, }, }, ConnectTimeout: 1 * time.Second, @@ -253,18 +271,27 @@ func TestServiceResolver_ToConsul(t *testing.T) { ServiceSubset: "failover_subset1", Namespace: "failover_namespace1", Datacenters: []string{"failover1_dc1", "failover1_dc2"}, + Policy: &FailoverPolicy{ + Mode: "default", + }, }, "failover2": { Service: "failover2", ServiceSubset: "failover_subset2", Namespace: "failover_namespace2", Datacenters: []string{"failover2_dc1", "failover2_dc2"}, + Policy: &FailoverPolicy{ + Mode: "", + }, }, "failover3": { Targets: []ServiceResolverFailoverTarget{ {Peer: "failover_peer3"}, {Partition: "failover_partition3", Namespace: "failover_namespace3"}, }, + Policy: &FailoverPolicy{ + Mode: "order-by-locality", + }, }, }, ConnectTimeout: metav1.Duration{Duration: 1 * time.Second}, @@ -320,18 +347,27 @@ func TestServiceResolver_ToConsul(t *testing.T) { ServiceSubset: "failover_subset1", Namespace: "failover_namespace1", Datacenters: []string{"failover1_dc1", "failover1_dc2"}, + Policy: &capi.ServiceResolverFailoverPolicy{ + Mode: "default", + }, }, "failover2": { Service: "failover2", ServiceSubset: "failover_subset2", Namespace: "failover_namespace2", Datacenters: []string{"failover2_dc1", "failover2_dc2"}, + Policy: &capi.ServiceResolverFailoverPolicy{ + Mode: "", + }, }, "failover3": { Targets: []capi.ServiceResolverFailoverTarget{ {Peer: "failover_peer3"}, {Partition: "failover_partition3", Namespace: "failover_namespace3"}, }, + Policy: &capi.ServiceResolverFailoverPolicy{ + Mode: "order-by-locality", + }, }, }, ConnectTimeout: 1 * time.Second, diff --git a/control-plane/api/v1alpha1/shared_types.go b/control-plane/api/v1alpha1/shared_types.go index 28d53b8926..3e98a41b89 100644 --- a/control-plane/api/v1alpha1/shared_types.go +++ b/control-plane/api/v1alpha1/shared_types.go @@ -245,6 +245,35 @@ func (in EnvoyExtension) validate(path *field.Path) *field.Error { return nil } +// FailoverPolicy specifies the exact mechanism used for failover. +type FailoverPolicy struct { + // Mode specifies the type of failover that will be performed. Valid values are + // "default", "" (equivalent to "default") and "order-by-locality". + Mode string `json:",omitempty"` +} + +func (in *FailoverPolicy) toConsul() *capi.ServiceResolverFailoverPolicy { + if in == nil { + return nil + } + + return &capi.ServiceResolverFailoverPolicy{ + Mode: in.Mode, + } +} + +func (in *FailoverPolicy) validate(path *field.Path) field.ErrorList { + var errs field.ErrorList + if in == nil { + return nil + } + modes := []string{"default", "order-by-locality"} + if !sliceContains(modes, in.Mode) { + errs = append(errs, field.Invalid(path.Child("mode"), in.Mode, notInSliceMessage(modes))) + } + return errs +} + func notInSliceMessage(slice []string) string { return fmt.Sprintf(`must be one of "%s"`, strings.Join(slice, `", "`)) } diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml index 5c8d4a5082..a2cd1e539d 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml @@ -139,6 +139,14 @@ spec: type: object type: array type: object + failoverPolicy: + description: FailoverPolicy specifies the exact mechanism used for failover. + properties: + mode: + description: Mode specifies the type of failover that will be performed. + Valid values are "default", "" (equivalent to "default") and "order-by-locality". + type: string + type: object meshGateway: description: MeshGateway controls the default mesh gateway configuration for this service. diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml index 9bd5096376..da2f665fcc 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml @@ -75,6 +75,14 @@ spec: service from to form the failover group of instances. If empty the current namespace is used. type: string + policy: + description: FailoverPolicy specifies the exact mechanism used for failover. + properties: + mode: + description: Mode specifies the type of failover that will be performed. + Valid values are "default", "" (equivalent to "default") and "order-by-locality". + type: string + type: object service: description: Service is the service to resolve instead of the default as the failover group of instances during failover. diff --git a/control-plane/go.mod b/control-plane/go.mod index b0d67085ec..d5cc893d58 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -10,7 +10,7 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af65262de8 github.com/hashicorp/consul-server-connection-manager v0.1.0 - github.com/hashicorp/consul/api v1.20.0 + github.com/hashicorp/consul/api v1.10.1-0.20230324190300-a168d0e667ac github.com/hashicorp/consul/sdk v0.13.1 github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f github.com/hashicorp/go-hclog v1.2.2 diff --git a/control-plane/go.sum b/control-plane/go.sum index 06dfbb8b10..94e3b6f51c 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -353,6 +353,12 @@ github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af6526 github.com/hashicorp/consul-server-connection-manager v0.1.0 h1:XCweGvMHzra88rYv2zxwwuUOjBUdcQmNKVrnQmt/muo= github.com/hashicorp/consul-server-connection-manager v0.1.0/go.mod h1:XVVlO+Yk7aiRpspiHZkrrFVn9BJIiOPnQIzqytPxGaU= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/api v1.10.1-0.20230315182124-152c75349ea9 h1:tWnkjV/2K5i6nbY68pNniulRLMwE3U7rwr+O3zLN7xg= +github.com/hashicorp/consul/api v1.10.1-0.20230315182124-152c75349ea9/go.mod h1:f8zVJwBcLdr1IQnfdfszjUM0xzp31Zl3bpws3pL9uFM= +github.com/hashicorp/consul/api v1.10.1-0.20230317144806-eaa39f4ef59a h1:eXpcnIZKlLaSM+aCp6cvEbNDRao7mqqM9DkdCvIFVN8= +github.com/hashicorp/consul/api v1.10.1-0.20230317144806-eaa39f4ef59a/go.mod h1:f8zVJwBcLdr1IQnfdfszjUM0xzp31Zl3bpws3pL9uFM= +github.com/hashicorp/consul/api v1.10.1-0.20230324190300-a168d0e667ac h1:jl7+FYcrlMS/m9SLCz4Ehm2qh/vueJstPEXkIjQj6ug= +github.com/hashicorp/consul/api v1.10.1-0.20230324190300-a168d0e667ac/go.mod h1:f8zVJwBcLdr1IQnfdfszjUM0xzp31Zl3bpws3pL9uFM= github.com/hashicorp/consul/api v1.20.0 h1:9IHTjNVSZ7MIwjlW3N3a7iGiykCMDpxZu8jsxFJh0yc= github.com/hashicorp/consul/api v1.20.0/go.mod h1:nR64eD44KQ59Of/ECwt2vUmIK2DKsDzAwTmwmLl8Wpo= github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3usQMXZF6ziK3SzXU= From fd2b2655aa313947f554364a4c87bf44642a6f01 Mon Sep 17 00:00:00 2001 From: Maliz Date: Tue, 28 Mar 2023 09:14:12 -0700 Subject: [PATCH 111/340] change default mode to sequential --- charts/consul/templates/crd-proxydefaults.yaml | 2 +- charts/consul/templates/crd-serviceresolvers.yaml | 2 +- control-plane/api/v1alpha1/proxydefaults_types_test.go | 8 ++++---- control-plane/api/v1alpha1/serviceresolver_types.go | 2 +- control-plane/api/v1alpha1/serviceresolver_types_test.go | 8 ++++---- control-plane/api/v1alpha1/shared_types.go | 2 +- .../crd/bases/consul.hashicorp.com_proxydefaults.yaml | 2 +- .../crd/bases/consul.hashicorp.com_serviceresolvers.yaml | 2 +- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/charts/consul/templates/crd-proxydefaults.yaml b/charts/consul/templates/crd-proxydefaults.yaml index 1d78d687a7..550943762b 100644 --- a/charts/consul/templates/crd-proxydefaults.yaml +++ b/charts/consul/templates/crd-proxydefaults.yaml @@ -148,7 +148,7 @@ spec: properties: mode: description: Mode specifies the type of failover that will be performed. - Valid values are "default", "" (equivalent to "default") and "order-by-locality". + Valid values are "sequential", "" (equivalent to "sequential") and "order-by-locality". type: string type: object meshGateway: diff --git a/charts/consul/templates/crd-serviceresolvers.yaml b/charts/consul/templates/crd-serviceresolvers.yaml index 16d46b9615..12d6070e51 100644 --- a/charts/consul/templates/crd-serviceresolvers.yaml +++ b/charts/consul/templates/crd-serviceresolvers.yaml @@ -84,7 +84,7 @@ spec: properties: mode: description: Mode specifies the type of failover that will be performed. - Valid values are "default", "" (equivalent to "default") and "order-by-locality". + Valid values are "sequential", "" (equivalent to "sequential") and "order-by-locality". type: string type: object service: diff --git a/control-plane/api/v1alpha1/proxydefaults_types_test.go b/control-plane/api/v1alpha1/proxydefaults_types_test.go index c194db2b9a..19371dae1c 100644 --- a/control-plane/api/v1alpha1/proxydefaults_types_test.go +++ b/control-plane/api/v1alpha1/proxydefaults_types_test.go @@ -94,7 +94,7 @@ func TestProxyDefaults_MatchesConsul(t *testing.T) { }, }, FailoverPolicy: &FailoverPolicy{ - Mode: "default", + Mode: "sequential", }, }, }, @@ -155,7 +155,7 @@ func TestProxyDefaults_MatchesConsul(t *testing.T) { }, }, FailoverPolicy: &capi.ServiceResolverFailoverPolicy{ - Mode: "default", + Mode: "sequential", }, }, Matches: true, @@ -310,7 +310,7 @@ func TestProxyDefaults_ToConsul(t *testing.T) { }, }, FailoverPolicy: &FailoverPolicy{ - Mode: "default", + Mode: "sequential", }, }, }, @@ -372,7 +372,7 @@ func TestProxyDefaults_ToConsul(t *testing.T) { }, }, FailoverPolicy: &capi.ServiceResolverFailoverPolicy{ - Mode: "default", + Mode: "sequential", }, Meta: map[string]string{ common.SourceKey: common.SourceValue, diff --git a/control-plane/api/v1alpha1/serviceresolver_types.go b/control-plane/api/v1alpha1/serviceresolver_types.go index 4bf6f5f4ad..576be448fb 100644 --- a/control-plane/api/v1alpha1/serviceresolver_types.go +++ b/control-plane/api/v1alpha1/serviceresolver_types.go @@ -400,7 +400,7 @@ func (in ServiceResolverFailover) toConsul() capi.ServiceResolverFailover { Namespace: in.Namespace, Datacenters: in.Datacenters, Targets: targets, - Policy: &capi.ServiceResolverFailoverPolicy { + Policy: &capi.ServiceResolverFailoverPolicy{ Mode: in.Policy.Mode, }, } diff --git a/control-plane/api/v1alpha1/serviceresolver_types_test.go b/control-plane/api/v1alpha1/serviceresolver_types_test.go index 6ac9e71999..fbf1dd9cf2 100644 --- a/control-plane/api/v1alpha1/serviceresolver_types_test.go +++ b/control-plane/api/v1alpha1/serviceresolver_types_test.go @@ -71,7 +71,7 @@ func TestServiceResolver_MatchesConsul(t *testing.T) { Namespace: "failover_namespace1", Datacenters: []string{"failover1_dc1", "failover1_dc2"}, Policy: &FailoverPolicy{ - Mode: "default", + Mode: "sequential", }, }, "failover2": { @@ -147,7 +147,7 @@ func TestServiceResolver_MatchesConsul(t *testing.T) { Namespace: "failover_namespace1", Datacenters: []string{"failover1_dc1", "failover1_dc2"}, Policy: &capi.ServiceResolverFailoverPolicy{ - Mode: "default", + Mode: "sequential", }, }, "failover2": { @@ -272,7 +272,7 @@ func TestServiceResolver_ToConsul(t *testing.T) { Namespace: "failover_namespace1", Datacenters: []string{"failover1_dc1", "failover1_dc2"}, Policy: &FailoverPolicy{ - Mode: "default", + Mode: "sequential", }, }, "failover2": { @@ -348,7 +348,7 @@ func TestServiceResolver_ToConsul(t *testing.T) { Namespace: "failover_namespace1", Datacenters: []string{"failover1_dc1", "failover1_dc2"}, Policy: &capi.ServiceResolverFailoverPolicy{ - Mode: "default", + Mode: "sequential", }, }, "failover2": { diff --git a/control-plane/api/v1alpha1/shared_types.go b/control-plane/api/v1alpha1/shared_types.go index 3e98a41b89..8f000f4087 100644 --- a/control-plane/api/v1alpha1/shared_types.go +++ b/control-plane/api/v1alpha1/shared_types.go @@ -267,7 +267,7 @@ func (in *FailoverPolicy) validate(path *field.Path) field.ErrorList { if in == nil { return nil } - modes := []string{"default", "order-by-locality"} + modes := []string{"sequential", "order-by-locality"} if !sliceContains(modes, in.Mode) { errs = append(errs, field.Invalid(path.Child("mode"), in.Mode, notInSliceMessage(modes))) } diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml index a2cd1e539d..a277d6b9a9 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml @@ -144,7 +144,7 @@ spec: properties: mode: description: Mode specifies the type of failover that will be performed. - Valid values are "default", "" (equivalent to "default") and "order-by-locality". + Valid values are "sequential", "" (equivalent to "sequential") and "order-by-locality". type: string type: object meshGateway: diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml index da2f665fcc..508e754c58 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml @@ -80,7 +80,7 @@ spec: properties: mode: description: Mode specifies the type of failover that will be performed. - Valid values are "default", "" (equivalent to "default") and "order-by-locality". + Valid values are "sequential", "" (equivalent to "sequential") and "order-by-locality". type: string type: object service: From ec4165a8499ecbe0bd2d47764c21abdfa5f4590a Mon Sep 17 00:00:00 2001 From: Maliz Date: Wed, 29 Mar 2023 11:49:22 -0700 Subject: [PATCH 112/340] add changelog --- .changelog/2030.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/2030.txt diff --git a/.changelog/2030.txt b/.changelog/2030.txt new file mode 100644 index 0000000000..46516d9513 --- /dev/null +++ b/.changelog/2030.txt @@ -0,0 +1,3 @@ +```release-note:improvement +helm: add failover policy field to service resolver and proxy default CRDs +``` \ No newline at end of file From 6c82e99a31ae056e8b948e1fee4d0c6c2f53049d Mon Sep 17 00:00:00 2001 From: Maliz Date: Fri, 31 Mar 2023 12:57:51 -0700 Subject: [PATCH 113/340] add region field to failover policy --- .../consul/templates/crd-proxydefaults.yaml | 6 ++++ .../templates/crd-serviceresolvers.yaml | 6 ++++ .../api/v1alpha1/proxydefaults_types_test.go | 12 ++++--- .../api/v1alpha1/serviceresolver_types.go | 3 +- .../v1alpha1/serviceresolver_types_test.go | 36 ++++++++++++------- control-plane/api/v1alpha1/shared_types.go | 6 ++-- .../consul.hashicorp.com_proxydefaults.yaml | 6 ++++ ...consul.hashicorp.com_serviceresolvers.yaml | 6 ++++ control-plane/go.mod | 2 +- control-plane/go.sum | 4 +++ 10 files changed, 67 insertions(+), 20 deletions(-) diff --git a/charts/consul/templates/crd-proxydefaults.yaml b/charts/consul/templates/crd-proxydefaults.yaml index 550943762b..aaaa3228a1 100644 --- a/charts/consul/templates/crd-proxydefaults.yaml +++ b/charts/consul/templates/crd-proxydefaults.yaml @@ -150,6 +150,12 @@ spec: description: Mode specifies the type of failover that will be performed. Valid values are "sequential", "" (equivalent to "sequential") and "order-by-locality". type: string + regions: + description: The ordered list of the regions of the failover targets. + Valid values can be "us-west-1", "us-west-2", and so on. + items: + type: string + type: array type: object meshGateway: description: MeshGateway controls the default mesh gateway configuration diff --git a/charts/consul/templates/crd-serviceresolvers.yaml b/charts/consul/templates/crd-serviceresolvers.yaml index 12d6070e51..842506d642 100644 --- a/charts/consul/templates/crd-serviceresolvers.yaml +++ b/charts/consul/templates/crd-serviceresolvers.yaml @@ -86,6 +86,12 @@ spec: description: Mode specifies the type of failover that will be performed. Valid values are "sequential", "" (equivalent to "sequential") and "order-by-locality". type: string + regions: + description: The ordered list of the regions of the failover targets. + Valid values can be "us-west-1", "us-west-2", and so on. + items: + type: string + type: array type: object service: description: Service is the service to resolve instead of the diff --git a/control-plane/api/v1alpha1/proxydefaults_types_test.go b/control-plane/api/v1alpha1/proxydefaults_types_test.go index 19371dae1c..f056281a34 100644 --- a/control-plane/api/v1alpha1/proxydefaults_types_test.go +++ b/control-plane/api/v1alpha1/proxydefaults_types_test.go @@ -94,7 +94,8 @@ func TestProxyDefaults_MatchesConsul(t *testing.T) { }, }, FailoverPolicy: &FailoverPolicy{ - Mode: "sequential", + Mode: "sequential", + Regions: []string{"us-west-1"}, }, }, }, @@ -155,7 +156,8 @@ func TestProxyDefaults_MatchesConsul(t *testing.T) { }, }, FailoverPolicy: &capi.ServiceResolverFailoverPolicy{ - Mode: "sequential", + Mode: "sequential", + Regions: []string{"us-west-1"}, }, }, Matches: true, @@ -310,7 +312,8 @@ func TestProxyDefaults_ToConsul(t *testing.T) { }, }, FailoverPolicy: &FailoverPolicy{ - Mode: "sequential", + Mode: "sequential", + Regions: []string{"us-west-1"}, }, }, }, @@ -372,7 +375,8 @@ func TestProxyDefaults_ToConsul(t *testing.T) { }, }, FailoverPolicy: &capi.ServiceResolverFailoverPolicy{ - Mode: "sequential", + Mode: "sequential", + Regions: []string{"us-west-1"}, }, Meta: map[string]string{ common.SourceKey: common.SourceValue, diff --git a/control-plane/api/v1alpha1/serviceresolver_types.go b/control-plane/api/v1alpha1/serviceresolver_types.go index 576be448fb..48483550ba 100644 --- a/control-plane/api/v1alpha1/serviceresolver_types.go +++ b/control-plane/api/v1alpha1/serviceresolver_types.go @@ -401,7 +401,8 @@ func (in ServiceResolverFailover) toConsul() capi.ServiceResolverFailover { Datacenters: in.Datacenters, Targets: targets, Policy: &capi.ServiceResolverFailoverPolicy{ - Mode: in.Policy.Mode, + Mode: in.Policy.Mode, + Regions: in.Policy.Regions, }, } } diff --git a/control-plane/api/v1alpha1/serviceresolver_types_test.go b/control-plane/api/v1alpha1/serviceresolver_types_test.go index fbf1dd9cf2..ed92fa9f60 100644 --- a/control-plane/api/v1alpha1/serviceresolver_types_test.go +++ b/control-plane/api/v1alpha1/serviceresolver_types_test.go @@ -71,7 +71,8 @@ func TestServiceResolver_MatchesConsul(t *testing.T) { Namespace: "failover_namespace1", Datacenters: []string{"failover1_dc1", "failover1_dc2"}, Policy: &FailoverPolicy{ - Mode: "sequential", + Mode: "sequential", + Regions: []string{"us-west-2"}, }, }, "failover2": { @@ -80,7 +81,8 @@ func TestServiceResolver_MatchesConsul(t *testing.T) { Namespace: "failover_namespace2", Datacenters: []string{"failover2_dc1", "failover2_dc2"}, Policy: &FailoverPolicy{ - Mode: "", + Mode: "", + Regions: []string{"us-west-1"}, }, }, "failover3": { @@ -89,7 +91,8 @@ func TestServiceResolver_MatchesConsul(t *testing.T) { {Partition: "failover_partition3", Namespace: "failover_namespace3"}, }, Policy: &FailoverPolicy{ - Mode: "order-by-locality", + Mode: "order-by-locality", + Regions: []string{"us-east-1"}, }, }, }, @@ -147,7 +150,8 @@ func TestServiceResolver_MatchesConsul(t *testing.T) { Namespace: "failover_namespace1", Datacenters: []string{"failover1_dc1", "failover1_dc2"}, Policy: &capi.ServiceResolverFailoverPolicy{ - Mode: "sequential", + Mode: "sequential", + Regions: []string{"us-west-2"}, }, }, "failover2": { @@ -156,7 +160,8 @@ func TestServiceResolver_MatchesConsul(t *testing.T) { Namespace: "failover_namespace2", Datacenters: []string{"failover2_dc1", "failover2_dc2"}, Policy: &capi.ServiceResolverFailoverPolicy{ - Mode: "", + Mode: "", + Regions: []string{"us-west-1"}, }, }, "failover3": { @@ -165,7 +170,8 @@ func TestServiceResolver_MatchesConsul(t *testing.T) { {Partition: "failover_partition3", Namespace: "failover_namespace3"}, }, Policy: &capi.ServiceResolverFailoverPolicy{ - Mode: "order-by-locality", + Mode: "order-by-locality", + Regions: []string{"us-east-1"}, }, }, }, @@ -272,7 +278,8 @@ func TestServiceResolver_ToConsul(t *testing.T) { Namespace: "failover_namespace1", Datacenters: []string{"failover1_dc1", "failover1_dc2"}, Policy: &FailoverPolicy{ - Mode: "sequential", + Mode: "sequential", + Regions: []string{"us-west-2"}, }, }, "failover2": { @@ -281,7 +288,8 @@ func TestServiceResolver_ToConsul(t *testing.T) { Namespace: "failover_namespace2", Datacenters: []string{"failover2_dc1", "failover2_dc2"}, Policy: &FailoverPolicy{ - Mode: "", + Mode: "", + Regions: []string{"us-west-1"}, }, }, "failover3": { @@ -290,7 +298,8 @@ func TestServiceResolver_ToConsul(t *testing.T) { {Partition: "failover_partition3", Namespace: "failover_namespace3"}, }, Policy: &FailoverPolicy{ - Mode: "order-by-locality", + Mode: "order-by-locality", + Regions: []string{"us-east-1"}, }, }, }, @@ -348,7 +357,8 @@ func TestServiceResolver_ToConsul(t *testing.T) { Namespace: "failover_namespace1", Datacenters: []string{"failover1_dc1", "failover1_dc2"}, Policy: &capi.ServiceResolverFailoverPolicy{ - Mode: "sequential", + Mode: "sequential", + Regions: []string{"us-west-2"}, }, }, "failover2": { @@ -357,7 +367,8 @@ func TestServiceResolver_ToConsul(t *testing.T) { Namespace: "failover_namespace2", Datacenters: []string{"failover2_dc1", "failover2_dc2"}, Policy: &capi.ServiceResolverFailoverPolicy{ - Mode: "", + Mode: "", + Regions: []string{"us-west-1"}, }, }, "failover3": { @@ -366,7 +377,8 @@ func TestServiceResolver_ToConsul(t *testing.T) { {Partition: "failover_partition3", Namespace: "failover_namespace3"}, }, Policy: &capi.ServiceResolverFailoverPolicy{ - Mode: "order-by-locality", + Mode: "order-by-locality", + Regions: []string{"us-east-1"}, }, }, }, diff --git a/control-plane/api/v1alpha1/shared_types.go b/control-plane/api/v1alpha1/shared_types.go index 8f000f4087..c0c59963c6 100644 --- a/control-plane/api/v1alpha1/shared_types.go +++ b/control-plane/api/v1alpha1/shared_types.go @@ -249,7 +249,8 @@ func (in EnvoyExtension) validate(path *field.Path) *field.Error { type FailoverPolicy struct { // Mode specifies the type of failover that will be performed. Valid values are // "default", "" (equivalent to "default") and "order-by-locality". - Mode string `json:",omitempty"` + Mode string `json:",omitempty"` + Regions []string `json:",omitempty"` } func (in *FailoverPolicy) toConsul() *capi.ServiceResolverFailoverPolicy { @@ -258,7 +259,8 @@ func (in *FailoverPolicy) toConsul() *capi.ServiceResolverFailoverPolicy { } return &capi.ServiceResolverFailoverPolicy{ - Mode: in.Mode, + Mode: in.Mode, + Regions: in.Regions, } } diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml index a277d6b9a9..0f90e95e16 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml @@ -146,6 +146,12 @@ spec: description: Mode specifies the type of failover that will be performed. Valid values are "sequential", "" (equivalent to "sequential") and "order-by-locality". type: string + regions: + description: The ordered list of the regions of the failover targets. + Valid values can be "us-west-1", "us-west-2", and so on. + items: + type: string + type: array type: object meshGateway: description: MeshGateway controls the default mesh gateway configuration diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml index 508e754c58..a36784cc77 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml @@ -82,6 +82,12 @@ spec: description: Mode specifies the type of failover that will be performed. Valid values are "sequential", "" (equivalent to "sequential") and "order-by-locality". type: string + regions: + description: The ordered list of the regions of the failover targets. + Valid values can be "us-west-1", "us-west-2", and so on. + items: + type: string + type: array type: object service: description: Service is the service to resolve instead of the diff --git a/control-plane/go.mod b/control-plane/go.mod index d5cc893d58..b139e1cd3f 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -10,7 +10,7 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af65262de8 github.com/hashicorp/consul-server-connection-manager v0.1.0 - github.com/hashicorp/consul/api v1.10.1-0.20230324190300-a168d0e667ac + github.com/hashicorp/consul/api v1.10.1-0.20230331190547-fc64a702f43c github.com/hashicorp/consul/sdk v0.13.1 github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f github.com/hashicorp/go-hclog v1.2.2 diff --git a/control-plane/go.sum b/control-plane/go.sum index 94e3b6f51c..e108ac64b0 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -359,6 +359,10 @@ github.com/hashicorp/consul/api v1.10.1-0.20230317144806-eaa39f4ef59a h1:eXpcnIZ github.com/hashicorp/consul/api v1.10.1-0.20230317144806-eaa39f4ef59a/go.mod h1:f8zVJwBcLdr1IQnfdfszjUM0xzp31Zl3bpws3pL9uFM= github.com/hashicorp/consul/api v1.10.1-0.20230324190300-a168d0e667ac h1:jl7+FYcrlMS/m9SLCz4Ehm2qh/vueJstPEXkIjQj6ug= github.com/hashicorp/consul/api v1.10.1-0.20230324190300-a168d0e667ac/go.mod h1:f8zVJwBcLdr1IQnfdfszjUM0xzp31Zl3bpws3pL9uFM= +github.com/hashicorp/consul/api v1.10.1-0.20230329135459-3e69ff1cd299 h1:JAevohOFaTL9XDpzYwN/2mkGPBm2QxTNcN41lys13Yc= +github.com/hashicorp/consul/api v1.10.1-0.20230329135459-3e69ff1cd299/go.mod h1:f8zVJwBcLdr1IQnfdfszjUM0xzp31Zl3bpws3pL9uFM= +github.com/hashicorp/consul/api v1.10.1-0.20230331190547-fc64a702f43c h1:MfDuWW38RozPodXT2aGc5jakoYo9EjgYpZl+vR3//Wg= +github.com/hashicorp/consul/api v1.10.1-0.20230331190547-fc64a702f43c/go.mod h1:f8zVJwBcLdr1IQnfdfszjUM0xzp31Zl3bpws3pL9uFM= github.com/hashicorp/consul/api v1.20.0 h1:9IHTjNVSZ7MIwjlW3N3a7iGiykCMDpxZu8jsxFJh0yc= github.com/hashicorp/consul/api v1.20.0/go.mod h1:nR64eD44KQ59Of/ECwt2vUmIK2DKsDzAwTmwmLl8Wpo= github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3usQMXZF6ziK3SzXU= From b4e313e26a4c423925cd0585879bc2712ce47b73 Mon Sep 17 00:00:00 2001 From: Maliz Date: Fri, 31 Mar 2023 13:26:48 -0700 Subject: [PATCH 114/340] go mod tidy --- control-plane/go.sum | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/control-plane/go.sum b/control-plane/go.sum index e108ac64b0..d794bad47f 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -353,18 +353,8 @@ github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af6526 github.com/hashicorp/consul-server-connection-manager v0.1.0 h1:XCweGvMHzra88rYv2zxwwuUOjBUdcQmNKVrnQmt/muo= github.com/hashicorp/consul-server-connection-manager v0.1.0/go.mod h1:XVVlO+Yk7aiRpspiHZkrrFVn9BJIiOPnQIzqytPxGaU= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.10.1-0.20230315182124-152c75349ea9 h1:tWnkjV/2K5i6nbY68pNniulRLMwE3U7rwr+O3zLN7xg= -github.com/hashicorp/consul/api v1.10.1-0.20230315182124-152c75349ea9/go.mod h1:f8zVJwBcLdr1IQnfdfszjUM0xzp31Zl3bpws3pL9uFM= -github.com/hashicorp/consul/api v1.10.1-0.20230317144806-eaa39f4ef59a h1:eXpcnIZKlLaSM+aCp6cvEbNDRao7mqqM9DkdCvIFVN8= -github.com/hashicorp/consul/api v1.10.1-0.20230317144806-eaa39f4ef59a/go.mod h1:f8zVJwBcLdr1IQnfdfszjUM0xzp31Zl3bpws3pL9uFM= -github.com/hashicorp/consul/api v1.10.1-0.20230324190300-a168d0e667ac h1:jl7+FYcrlMS/m9SLCz4Ehm2qh/vueJstPEXkIjQj6ug= -github.com/hashicorp/consul/api v1.10.1-0.20230324190300-a168d0e667ac/go.mod h1:f8zVJwBcLdr1IQnfdfszjUM0xzp31Zl3bpws3pL9uFM= -github.com/hashicorp/consul/api v1.10.1-0.20230329135459-3e69ff1cd299 h1:JAevohOFaTL9XDpzYwN/2mkGPBm2QxTNcN41lys13Yc= -github.com/hashicorp/consul/api v1.10.1-0.20230329135459-3e69ff1cd299/go.mod h1:f8zVJwBcLdr1IQnfdfszjUM0xzp31Zl3bpws3pL9uFM= github.com/hashicorp/consul/api v1.10.1-0.20230331190547-fc64a702f43c h1:MfDuWW38RozPodXT2aGc5jakoYo9EjgYpZl+vR3//Wg= github.com/hashicorp/consul/api v1.10.1-0.20230331190547-fc64a702f43c/go.mod h1:f8zVJwBcLdr1IQnfdfszjUM0xzp31Zl3bpws3pL9uFM= -github.com/hashicorp/consul/api v1.20.0 h1:9IHTjNVSZ7MIwjlW3N3a7iGiykCMDpxZu8jsxFJh0yc= -github.com/hashicorp/consul/api v1.20.0/go.mod h1:nR64eD44KQ59Of/ECwt2vUmIK2DKsDzAwTmwmLl8Wpo= github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3usQMXZF6ziK3SzXU= github.com/hashicorp/consul/proto-public v0.1.0/go.mod h1:vs2KkuWwtjkIgA5ezp4YKPzQp4GitV+q/+PvksrA92k= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= From adbca8052c1a651a014f29d7562f6058c60e27ad Mon Sep 17 00:00:00 2001 From: Maliz Date: Fri, 31 Mar 2023 14:17:44 -0700 Subject: [PATCH 115/340] add test for validate function --- .../api/v1alpha1/proxydefaults_types_test.go | 13 +++++++++++++ control-plane/api/v1alpha1/shared_types.go | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/control-plane/api/v1alpha1/proxydefaults_types_test.go b/control-plane/api/v1alpha1/proxydefaults_types_test.go index f056281a34..16642ad277 100644 --- a/control-plane/api/v1alpha1/proxydefaults_types_test.go +++ b/control-plane/api/v1alpha1/proxydefaults_types_test.go @@ -624,6 +624,19 @@ func TestProxyDefaults_Validate(t *testing.T) { }, expectedErrMsg: `proxydefaults.consul.hashicorp.com "global" is invalid: spec.envoyExtensions.envoyExtension[0].arguments: Invalid value: "{\"SOME_INVALID_JSON\"}": must be valid map value: invalid character '}' after object key`, }, + "failoverPolicy.mode invalid": { + input: &ProxyDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "global", + }, + Spec: ProxyDefaultsSpec{ + FailoverPolicy: &FailoverPolicy{ + Mode: "wrong-mode", + }, + }, + }, + expectedErrMsg: `proxydefaults.consul.hashicorp.com "global" is invalid: spec.failoverPolicy.mode: Invalid value: "wrong-mode": must be one of "", "sequential", "order-by-locality"`, + }, "multi-error": { input: &ProxyDefaults{ ObjectMeta: metav1.ObjectMeta{ diff --git a/control-plane/api/v1alpha1/shared_types.go b/control-plane/api/v1alpha1/shared_types.go index c0c59963c6..fdac9a3fea 100644 --- a/control-plane/api/v1alpha1/shared_types.go +++ b/control-plane/api/v1alpha1/shared_types.go @@ -269,7 +269,7 @@ func (in *FailoverPolicy) validate(path *field.Path) field.ErrorList { if in == nil { return nil } - modes := []string{"sequential", "order-by-locality"} + modes := []string{"", "sequential", "order-by-locality"} if !sliceContains(modes, in.Mode) { errs = append(errs, field.Invalid(path.Child("mode"), in.Mode, notInSliceMessage(modes))) } From 5ec0a7f51c2235ea23404c45c572a7a96b912843 Mon Sep 17 00:00:00 2001 From: Maliz Date: Fri, 31 Mar 2023 14:54:13 -0700 Subject: [PATCH 116/340] update test for validate --- control-plane/api/v1alpha1/serviceresolver_types_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/control-plane/api/v1alpha1/serviceresolver_types_test.go b/control-plane/api/v1alpha1/serviceresolver_types_test.go index ed92fa9f60..c82394784d 100644 --- a/control-plane/api/v1alpha1/serviceresolver_types_test.go +++ b/control-plane/api/v1alpha1/serviceresolver_types_test.go @@ -646,8 +646,8 @@ func TestServiceResolver_Validate(t *testing.T) { }, namespacesEnabled: false, expectedErrMsgs: []string{ - "spec.failover[failA]: Invalid value: \"{}\": service, serviceSubset, namespace, datacenters, and targets cannot all be empty at once", - "spec.failover[failB]: Invalid value: \"{}\": service, serviceSubset, namespace, datacenters, and targets cannot all be empty at once", + "spec.failover[failA]: Invalid value: \"{}\": service, serviceSubset, namespace, datacenters, policy, and targets cannot all be empty at once", + "spec.failover[failB]: Invalid value: \"{}\": service, serviceSubset, namespace, datacenters, policy, and targets cannot all be empty at once", }, }, "hashPolicy.field invalid": { From 36e3d5cdb1e8e6b1a137aa4d7e62b916698ad336 Mon Sep 17 00:00:00 2001 From: Maliz Date: Mon, 3 Apr 2023 15:10:27 -0700 Subject: [PATCH 117/340] update circle ci config --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e0e59a56d9..cda1e54ba8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -26,7 +26,7 @@ aks-terraform-path: &aks-terraform-path charts/consul/test/terraform/aks openshift-terraform-path: &openshift-terraform-path charts/consul/test/terraform/openshift # This image is built from test/docker/Test.dockerfile consul-helm-test-image: &consul-helm-test-image docker.mirror.hashicorp.services/hashicorpdev/consul-helm-test:0.15.0 -consul-test-image: &consul-test-image hashicorppreview/consul-enterprise:1.15-dev +consul-test-image: &consul-test-image hashicorppreview/consul-enterprise:1.16-dev ######################## # COMMANDS From 3a18ca1674826b1ea80df0c8a7807abc87e3d9bc Mon Sep 17 00:00:00 2001 From: Maliz Date: Mon, 3 Apr 2023 15:12:34 -0700 Subject: [PATCH 118/340] update gha config --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6cb3ab1937..6e6212e0d8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,8 +8,8 @@ env: TEST_RESULTS: /tmp/test-results # path to where test results are saved GOTESTSUM_VERSION: 1.8.2 # You cannot use environment variables with workflows. The gotestsum version is hardcoded in the reusable workflows too. # We use docker images to copy the consul binary for unit tests. - CONSUL_OSS_DOCKER_IMAGE: hashicorppreview/consul:1.14-dev # Consul's OSS version to use in tests - CONSUL_ENT_DOCKER_IMAGE: hashicorppreview/consul-enterprise:1.14-dev # Consul's enterprise version to use in tests + CONSUL_OSS_DOCKER_IMAGE: hashicorppreview/consul:1.16-dev # Consul's OSS version to use in tests + CONSUL_ENT_DOCKER_IMAGE: hashicorppreview/consul-enterprise:1.16-dev # Consul's enterprise version to use in tests jobs: terraform-fmt-check: From 58a1434ed923c4e29c16ac25935dcf6dbaccd3fa Mon Sep 17 00:00:00 2001 From: Maliz Date: Tue, 4 Apr 2023 11:06:10 -0700 Subject: [PATCH 119/340] update acl test error message --- .../controllers/endpoints/endpoints_controller_ent_test.go | 6 +++--- .../controllers/endpoints/endpoints_controller_test.go | 4 ++-- control-plane/subcommand/common/common.go | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go index 5c87c2442d..2a2eda3a3a 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go @@ -1564,7 +1564,7 @@ func TestReconcileUpdateEndpointWithNamespaces(t *testing.T) { // Read the token from Consul. token, _, err := consulClient.ACL().TokenRead(tokenID, nil) if deregisteredServices.Contains(serviceID) { - require.EqualError(t, err, "Unexpected response code: 403 (ACL not found)") + require.EqualError(t, err, "Unexpected response code: 403 (token does not exist: ACL not found)") } else { require.NoError(t, err, "token should exist for service instance: "+serviceID) require.NotNil(t, token) @@ -1822,7 +1822,7 @@ func TestReconcileDeleteEndpointWithNamespaces(t *testing.T) { if tt.enableACLs { _, _, err = consulClient.ACL().TokenRead(token.AccessorID, nil) - require.EqualError(t, err, "Unexpected response code: 403 (ACL not found)") + require.EqualError(t, err, "Unexpected response code: 403 (token does not exist: ACL not found)") } }) } @@ -2107,7 +2107,7 @@ func TestReconcileDeleteGatewayWithNamespaces(t *testing.T) { if tt.enableACLs { _, _, err = consulClient.ACL().TokenRead(token.AccessorID, nil) - require.EqualError(t, err, "Unexpected response code: 403 (ACL not found)") + require.EqualError(t, err, "Unexpected response code: 403 (token does not exist: ACL not found)") } }) } diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go index 19c78925a6..9de882ff89 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go @@ -3442,7 +3442,7 @@ func TestReconcileUpdateEndpoint(t *testing.T) { // Read the token from Consul. token, _, err := consulClient.ACL().TokenRead(tokenID, nil) if deregisteredServices.Contains(sID) { - require.EqualError(t, err, "Unexpected response code: 403 (ACL not found)") + require.EqualError(t, err, "Unexpected response code: 403 (token does not exist: ACL not found)") } else { require.NoError(t, err, "token should exist for service instance: "+sID) require.NotNil(t, token) @@ -4040,7 +4040,7 @@ func TestReconcileDeleteEndpoint(t *testing.T) { if tt.enableACLs { _, _, err = consulClient.ACL().TokenRead(token.AccessorID, nil) - require.EqualError(t, err, "Unexpected response code: 403 (ACL not found)") + require.EqualError(t, err, "Unexpected response code: 403 (token does not exist: ACL not found)") } }) } diff --git a/control-plane/subcommand/common/common.go b/control-plane/subcommand/common/common.go index b7927e4490..b2ccc4be16 100644 --- a/control-plane/subcommand/common/common.go +++ b/control-plane/subcommand/common/common.go @@ -166,9 +166,9 @@ func ConsulLogin(client *api.Client, params LoginParams, log hclog.Logger) (stri // with that token. This is because clients talk to servers in the stale consistency mode // to decrease the load on the servers (see https://www.consul.io/docs/architecture/consensus#stale). // In that case, it's possible that the token isn't replicated - // to that server instance yet. The client will then get an "ACL not found" error + // to that server instance yet. The client will then get an "token does not exist: ACL not found" error // and subsequently cache this not found response. Then on any API call with the token, - // we will keep hitting the same "ACL not found" error + // we will keep hitting the same "token does not exist: ACL not found" error // until the cache entry expires (determined by the `acl_token_ttl` which defaults to 30 seconds). // This is not great because it will delay app start up time by 30 seconds in most cases // (if you are running 3 servers, then the probability of ending up on a follower is close to 2/3). @@ -182,7 +182,7 @@ func ConsulLogin(client *api.Client, params LoginParams, log hclog.Logger) (stri // for this call and the next call to reach different servers and those servers to have different // states from each other. // For example, this call can reach a leader and succeed, while the next call can go to a follower - // that is still behind the leader and get an "ACL not found" error. + // that is still behind the leader and get an "token does not exist: ACL not found" error. // However, this is a pretty unlikely case because // clients have sticky connections to a server, and those connections get rebalanced only every 2-3min. // And so, this workaround should work in a vast majority of cases. From 22211654e52c64403cc5a04f7b8128541ef2f607 Mon Sep 17 00:00:00 2001 From: Maliz Date: Tue, 4 Apr 2023 11:21:42 -0700 Subject: [PATCH 120/340] update acl test error message --- .../controllers/endpoints/endpoints_controller_ent_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go index 2a2eda3a3a..428d52039e 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go @@ -1564,7 +1564,7 @@ func TestReconcileUpdateEndpointWithNamespaces(t *testing.T) { // Read the token from Consul. token, _, err := consulClient.ACL().TokenRead(tokenID, nil) if deregisteredServices.Contains(serviceID) { - require.EqualError(t, err, "Unexpected response code: 403 (token does not exist: ACL not found)") + require.EqualError(t, err, "Unexpected response code: 403 (ACL not found)") } else { require.NoError(t, err, "token should exist for service instance: "+serviceID) require.NotNil(t, token) From 446e8c11a150edfc8b77c37ae86aeeeb989061ab Mon Sep 17 00:00:00 2001 From: Maliz Date: Tue, 4 Apr 2023 11:42:01 -0700 Subject: [PATCH 121/340] update acl test error message --- .../controllers/endpoints/endpoints_controller_ent_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go index 428d52039e..17609eba4f 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go @@ -1564,7 +1564,7 @@ func TestReconcileUpdateEndpointWithNamespaces(t *testing.T) { // Read the token from Consul. token, _, err := consulClient.ACL().TokenRead(tokenID, nil) if deregisteredServices.Contains(serviceID) { - require.EqualError(t, err, "Unexpected response code: 403 (ACL not found)") + require.EqualError(t, err, "Unexpected response code: 403 (token not found in namespace kube: ACL not found)") } else { require.NoError(t, err, "token should exist for service instance: "+serviceID) require.NotNil(t, token) From ac3289721890ba156a15dd4f62de27c74036a896 Mon Sep 17 00:00:00 2001 From: Maliz Date: Tue, 4 Apr 2023 12:00:00 -0700 Subject: [PATCH 122/340] update acl test --- .../controllers/endpoints/endpoints_controller_ent_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go index 17609eba4f..223fda9d8d 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go @@ -1564,7 +1564,7 @@ func TestReconcileUpdateEndpointWithNamespaces(t *testing.T) { // Read the token from Consul. token, _, err := consulClient.ACL().TokenRead(tokenID, nil) if deregisteredServices.Contains(serviceID) { - require.EqualError(t, err, "Unexpected response code: 403 (token not found in namespace kube: ACL not found)") + require.EqualError(t, err, "Unexpected response code: 403 (token not found in namespace "+ns+": ACL not found)") } else { require.NoError(t, err, "token should exist for service instance: "+serviceID) require.NotNil(t, token) From 7cebf6e7309be545403aa3f034f0f886fcf5c5cb Mon Sep 17 00:00:00 2001 From: Maliz Date: Tue, 4 Apr 2023 12:13:50 -0700 Subject: [PATCH 123/340] update acl test --- .../controllers/endpoints/endpoints_controller_ent_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go index 223fda9d8d..c902c9619e 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go @@ -1564,7 +1564,9 @@ func TestReconcileUpdateEndpointWithNamespaces(t *testing.T) { // Read the token from Consul. token, _, err := consulClient.ACL().TokenRead(tokenID, nil) if deregisteredServices.Contains(serviceID) { - require.EqualError(t, err, "Unexpected response code: 403 (token not found in namespace "+ns+": ACL not found)") + require.EqualError(t, err, + fmt.Sprintf("Unexpected response code: 403 (token not found in namespace %s: ACL not found)", + ts.SourceKubeNS)) } else { require.NoError(t, err, "token should exist for service instance: "+serviceID) require.NotNil(t, token) From 064453be83435df9840973d56d328212b5d9a6a9 Mon Sep 17 00:00:00 2001 From: Maliz Date: Tue, 4 Apr 2023 13:33:03 -0700 Subject: [PATCH 124/340] update acl test --- .../controllers/endpoints/endpoints_controller_ent_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go index c902c9619e..16c4ba4763 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go @@ -1566,7 +1566,7 @@ func TestReconcileUpdateEndpointWithNamespaces(t *testing.T) { if deregisteredServices.Contains(serviceID) { require.EqualError(t, err, fmt.Sprintf("Unexpected response code: 403 (token not found in namespace %s: ACL not found)", - ts.SourceKubeNS)) + ts.ExpConsulNS)) } else { require.NoError(t, err, "token should exist for service instance: "+serviceID) require.NotNil(t, token) From e51fddf2e0e1ec946b152530a7c005323a10f25a Mon Sep 17 00:00:00 2001 From: Maliz Date: Tue, 4 Apr 2023 14:07:59 -0700 Subject: [PATCH 125/340] update acl test --- .../controllers/endpoints/endpoints_controller_ent_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go index 16c4ba4763..65f9874c97 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go @@ -1824,7 +1824,9 @@ func TestReconcileDeleteEndpointWithNamespaces(t *testing.T) { if tt.enableACLs { _, _, err = consulClient.ACL().TokenRead(token.AccessorID, nil) - require.EqualError(t, err, "Unexpected response code: 403 (token does not exist: ACL not found)") + require.EqualError(t, err, + fmt.Sprintf("Unexpected response code: 403 (token not found in namespace %s: ACL not found)", + ts.ExpConsulNS)) } }) } @@ -2109,7 +2111,8 @@ func TestReconcileDeleteGatewayWithNamespaces(t *testing.T) { if tt.enableACLs { _, _, err = consulClient.ACL().TokenRead(token.AccessorID, nil) - require.EqualError(t, err, "Unexpected response code: 403 (token does not exist: ACL not found)") + require.EqualError(t, err, + "Unexpected response code: 403 (token not found in namespace default: ACL not found)") } }) } From 610c946bf3a25eb1f5fd85bd9cae443e59405c13 Mon Sep 17 00:00:00 2001 From: Maliz Date: Tue, 4 Apr 2023 14:24:04 -0700 Subject: [PATCH 126/340] update acl test --- .../controllers/endpoints/endpoints_controller_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go index 9de882ff89..4f4b289dd8 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go @@ -3442,7 +3442,7 @@ func TestReconcileUpdateEndpoint(t *testing.T) { // Read the token from Consul. token, _, err := consulClient.ACL().TokenRead(tokenID, nil) if deregisteredServices.Contains(sID) { - require.EqualError(t, err, "Unexpected response code: 403 (token does not exist: ACL not found)") + require.EqualError(t, err, "Unexpected response code: 403 (token not found in namespace default: ACL not found)") } else { require.NoError(t, err, "token should exist for service instance: "+sID) require.NotNil(t, token) @@ -4040,7 +4040,7 @@ func TestReconcileDeleteEndpoint(t *testing.T) { if tt.enableACLs { _, _, err = consulClient.ACL().TokenRead(token.AccessorID, nil) - require.EqualError(t, err, "Unexpected response code: 403 (token does not exist: ACL not found)") + require.EqualError(t, err, "Unexpected response code: 403 (token not found in namespace default: ACL not found)") } }) } From 9a10ce1882edb51eeeae0aa8c91dc44b7b21be0a Mon Sep 17 00:00:00 2001 From: Maliz Date: Tue, 4 Apr 2023 15:06:29 -0700 Subject: [PATCH 127/340] update acl test in oss --- .../controllers/endpoints/endpoints_controller_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go index 4f4b289dd8..9de882ff89 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go @@ -3442,7 +3442,7 @@ func TestReconcileUpdateEndpoint(t *testing.T) { // Read the token from Consul. token, _, err := consulClient.ACL().TokenRead(tokenID, nil) if deregisteredServices.Contains(sID) { - require.EqualError(t, err, "Unexpected response code: 403 (token not found in namespace default: ACL not found)") + require.EqualError(t, err, "Unexpected response code: 403 (token does not exist: ACL not found)") } else { require.NoError(t, err, "token should exist for service instance: "+sID) require.NotNil(t, token) @@ -4040,7 +4040,7 @@ func TestReconcileDeleteEndpoint(t *testing.T) { if tt.enableACLs { _, _, err = consulClient.ACL().TokenRead(token.AccessorID, nil) - require.EqualError(t, err, "Unexpected response code: 403 (token not found in namespace default: ACL not found)") + require.EqualError(t, err, "Unexpected response code: 403 (token does not exist: ACL not found)") } }) } From b10598edfeae4ac944eb524714ebefe0283404a4 Mon Sep 17 00:00:00 2001 From: Maliz Date: Tue, 4 Apr 2023 15:41:17 -0700 Subject: [PATCH 128/340] update acl test to pass in oss and ent --- .../controllers/endpoints/endpoints_controller_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go index 9de882ff89..c7751f20a8 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go @@ -3442,7 +3442,7 @@ func TestReconcileUpdateEndpoint(t *testing.T) { // Read the token from Consul. token, _, err := consulClient.ACL().TokenRead(tokenID, nil) if deregisteredServices.Contains(sID) { - require.EqualError(t, err, "Unexpected response code: 403 (token does not exist: ACL not found)") + require.Contains(t, err.Error(), "Unexpected response code: 403") } else { require.NoError(t, err, "token should exist for service instance: "+sID) require.NotNil(t, token) @@ -4040,7 +4040,7 @@ func TestReconcileDeleteEndpoint(t *testing.T) { if tt.enableACLs { _, _, err = consulClient.ACL().TokenRead(token.AccessorID, nil) - require.EqualError(t, err, "Unexpected response code: 403 (token does not exist: ACL not found)") + require.Contains(t, err.Error(), "Unexpected response code: 403") } }) } From 2cff14e07bd734dbd187a5bb9939aeb36282bb59 Mon Sep 17 00:00:00 2001 From: Thomas Eckert Date: Tue, 4 Apr 2023 23:34:25 -0400 Subject: [PATCH 129/340] Fix the indentation of the copyAnnotations example (#2037) --- charts/consul/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 42f08af255..73b805680b 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -2927,7 +2927,7 @@ apiGateway: # ```yaml # service: # annotations: | - # - external-dns.alpha.kubernetes.io/hostname + # - external-dns.alpha.kubernetes.io/hostname # ``` # # @type: string From ed8325b514c08f9772987a4b5e4b4be0de24a8f0 Mon Sep 17 00:00:00 2001 From: Maliz Date: Wed, 5 Apr 2023 10:26:01 -0700 Subject: [PATCH 130/340] make all acl error comparisons consistent in tests --- .../endpoints/endpoints_controller_ent_test.go | 11 +++-------- .../endpoints/endpoints_controller_test.go | 4 ++-- control-plane/subcommand/common/common.go | 6 +++--- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go index 65f9874c97..abb84c7116 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go @@ -1564,9 +1564,7 @@ func TestReconcileUpdateEndpointWithNamespaces(t *testing.T) { // Read the token from Consul. token, _, err := consulClient.ACL().TokenRead(tokenID, nil) if deregisteredServices.Contains(serviceID) { - require.EqualError(t, err, - fmt.Sprintf("Unexpected response code: 403 (token not found in namespace %s: ACL not found)", - ts.ExpConsulNS)) + require.Contains(t, err.Error(), "ACL not found") } else { require.NoError(t, err, "token should exist for service instance: "+serviceID) require.NotNil(t, token) @@ -1824,9 +1822,7 @@ func TestReconcileDeleteEndpointWithNamespaces(t *testing.T) { if tt.enableACLs { _, _, err = consulClient.ACL().TokenRead(token.AccessorID, nil) - require.EqualError(t, err, - fmt.Sprintf("Unexpected response code: 403 (token not found in namespace %s: ACL not found)", - ts.ExpConsulNS)) + require.Contains(t, err.Error(), "ACL not found") } }) } @@ -2111,8 +2107,7 @@ func TestReconcileDeleteGatewayWithNamespaces(t *testing.T) { if tt.enableACLs { _, _, err = consulClient.ACL().TokenRead(token.AccessorID, nil) - require.EqualError(t, err, - "Unexpected response code: 403 (token not found in namespace default: ACL not found)") + require.Contains(t, err.Error(), "ACL not found") } }) } diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go index c7751f20a8..7ea04a4e52 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go @@ -3442,7 +3442,7 @@ func TestReconcileUpdateEndpoint(t *testing.T) { // Read the token from Consul. token, _, err := consulClient.ACL().TokenRead(tokenID, nil) if deregisteredServices.Contains(sID) { - require.Contains(t, err.Error(), "Unexpected response code: 403") + require.Contains(t, err.Error(), "ACL not found") } else { require.NoError(t, err, "token should exist for service instance: "+sID) require.NotNil(t, token) @@ -4040,7 +4040,7 @@ func TestReconcileDeleteEndpoint(t *testing.T) { if tt.enableACLs { _, _, err = consulClient.ACL().TokenRead(token.AccessorID, nil) - require.Contains(t, err.Error(), "Unexpected response code: 403") + require.Contains(t, err.Error(), "ACL not found") } }) } diff --git a/control-plane/subcommand/common/common.go b/control-plane/subcommand/common/common.go index b2ccc4be16..b7927e4490 100644 --- a/control-plane/subcommand/common/common.go +++ b/control-plane/subcommand/common/common.go @@ -166,9 +166,9 @@ func ConsulLogin(client *api.Client, params LoginParams, log hclog.Logger) (stri // with that token. This is because clients talk to servers in the stale consistency mode // to decrease the load on the servers (see https://www.consul.io/docs/architecture/consensus#stale). // In that case, it's possible that the token isn't replicated - // to that server instance yet. The client will then get an "token does not exist: ACL not found" error + // to that server instance yet. The client will then get an "ACL not found" error // and subsequently cache this not found response. Then on any API call with the token, - // we will keep hitting the same "token does not exist: ACL not found" error + // we will keep hitting the same "ACL not found" error // until the cache entry expires (determined by the `acl_token_ttl` which defaults to 30 seconds). // This is not great because it will delay app start up time by 30 seconds in most cases // (if you are running 3 servers, then the probability of ending up on a follower is close to 2/3). @@ -182,7 +182,7 @@ func ConsulLogin(client *api.Client, params LoginParams, log hclog.Logger) (stri // for this call and the next call to reach different servers and those servers to have different // states from each other. // For example, this call can reach a leader and succeed, while the next call can go to a follower - // that is still behind the leader and get an "token does not exist: ACL not found" error. + // that is still behind the leader and get an "ACL not found" error. // However, this is a pretty unlikely case because // clients have sticky connections to a server, and those connections get rebalanced only every 2-3min. // And so, this workaround should work in a vast majority of cases. From 2fb794450c8d64a502ebdb296f6836de7be06d59 Mon Sep 17 00:00:00 2001 From: John Murret Date: Mon, 10 Apr 2023 12:16:46 -0600 Subject: [PATCH 131/340] test image form consul-enterprise --- .circleci/config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index cda1e54ba8..8ef947c103 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -177,6 +177,7 @@ commands: -enable-multi-cluster \ ${ENABLE_ENTERPRISE:+-enable-enterprise} \ -debug-directory="$TEST_RESULTS/debug" \ + -consul-image=hashicorpdev/consul:1bd3c09 \ -consul-k8s-image=<< parameters.consul-k8s-image >> then echo "Tests in ${pkg} failed, aborting early" @@ -208,6 +209,7 @@ commands: ${ENABLE_ENTERPRISE:+-enable-enterprise} \ -enable-multi-cluster \ -debug-directory="$TEST_RESULTS/debug" \ + -consul-image=hashicorpdev/consul:1bd3c09 \ -consul-k8s-image=<< parameters.consul-k8s-image >> ######################## From 0e771fcd49be3af400caec8da0f46e52ccd29b88 Mon Sep 17 00:00:00 2001 From: John Murret Date: Mon, 10 Apr 2023 12:17:35 -0600 Subject: [PATCH 132/340] Revert "test image form consul-enterprise" This reverts commit 2fb794450c8d64a502ebdb296f6836de7be06d59. --- .circleci/config.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8ef947c103..cda1e54ba8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -177,7 +177,6 @@ commands: -enable-multi-cluster \ ${ENABLE_ENTERPRISE:+-enable-enterprise} \ -debug-directory="$TEST_RESULTS/debug" \ - -consul-image=hashicorpdev/consul:1bd3c09 \ -consul-k8s-image=<< parameters.consul-k8s-image >> then echo "Tests in ${pkg} failed, aborting early" @@ -209,7 +208,6 @@ commands: ${ENABLE_ENTERPRISE:+-enable-enterprise} \ -enable-multi-cluster \ -debug-directory="$TEST_RESULTS/debug" \ - -consul-image=hashicorpdev/consul:1bd3c09 \ -consul-k8s-image=<< parameters.consul-k8s-image >> ######################## From 10c7a56500c0a750a1568c1a4055c44f8677fc37 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Thu, 13 Apr 2023 17:27:57 -0400 Subject: [PATCH 133/340] Convert acceptance to use github actions (#2046) * Terraform: increase node sizes * update GKE to use already created subnets * Dispatch: dispatch to consul-k8s-workflows --- .github/workflows/nightly-acceptance.yml | 27 ++ .github/workflows/pr.yml | 31 ++ .github/workflows/reusable-acceptance.yml | 159 ------- .github/workflows/reusable-golangci-lint.yml | 36 -- .github/workflows/reusable-unit.yml | 59 --- .github/workflows/test.yml | 401 ------------------ .../workflows/weekly-acceptance-0-49-x.yml | 29 ++ .github/workflows/weekly-acceptance-1-0-x.yml | 30 ++ .github/workflows/weekly-acceptance-1-1-x.yml | 30 ++ acceptance/framework/consul/helm_cluster.go | 8 +- acceptance/framework/helpers/helpers.go | 4 +- acceptance/framework/k8s/helpers.go | 6 +- acceptance/framework/k8s/kubectl.go | 2 +- .../framework/portforward/port_forward.go | 2 +- acceptance/framework/vault/vault_cluster.go | 2 +- .../config_entries_namespaces_test.go | 6 +- .../config-entries/config_entries_test.go | 2 +- .../tests/consul-dns/consul_dns_test.go | 2 +- acceptance/tests/metrics/metrics_test.go | 2 +- .../tests/partitions/partitions_sync_test.go | 2 +- charts/consul/test/terraform/aks/main.tf | 2 +- charts/consul/test/terraform/eks/main.tf | 2 +- charts/consul/test/terraform/gke/main.tf | 11 +- charts/consul/test/terraform/gke/variables.tf | 6 + .../subcommand/connect-init/command_test.go | 4 +- .../subcommand/consul-logout/command_test.go | 6 +- .../create-federation-secret/command_test.go | 32 +- .../get-consul-client-ca/command_test.go | 10 +- 28 files changed, 209 insertions(+), 704 deletions(-) create mode 100644 .github/workflows/nightly-acceptance.yml create mode 100644 .github/workflows/pr.yml delete mode 100644 .github/workflows/reusable-acceptance.yml delete mode 100644 .github/workflows/reusable-golangci-lint.yml delete mode 100644 .github/workflows/reusable-unit.yml delete mode 100644 .github/workflows/test.yml create mode 100644 .github/workflows/weekly-acceptance-0-49-x.yml create mode 100644 .github/workflows/weekly-acceptance-1-0-x.yml create mode 100644 .github/workflows/weekly-acceptance-1-1-x.yml diff --git a/.github/workflows/nightly-acceptance.yml b/.github/workflows/nightly-acceptance.yml new file mode 100644 index 0000000000..b8b7f50798 --- /dev/null +++ b/.github/workflows/nightly-acceptance.yml @@ -0,0 +1,27 @@ +# Dispatch to the consul-k8s-workflows with a nightly cron +name: nightly-acceptance +on: + schedule: + # * is a special character in YAML so you have to quote this string + # Run nightly at 12AM UTC/8PM EST/5PM PST + - cron: '0 0 * * *' + +# these should be the only settings that you will ever need to change +env: + CONSUL_IMAGE: hashicorppreview/consul-enterprise:1.16-dev # Consul's enterprise version to use in tests + BRANCH: ${{ github.ref_name }} + CONTEXT: "nightly" + +jobs: + cloud: + name: cloud + runs-on: ubuntu-latest + steps: + - uses: benc-uk/workflow-dispatch@v1.2.2 + name: cloud + with: + workflow: cloud.yml + repo: hashicorp/consul-k8s-workflows + ref: main + token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} + inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}", "consul-image":"${{ env.CONSUL_IMAGE }}" }' diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000000..d006c2514c --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,31 @@ +# Dispatch to the consul-k8s-workflows when a PR is created and on merges to main/release* +name: pr +on: + pull_request: + push: + # Sequence of patterns matched against refs/heads + branches: + # Push events on main branch + - main + # Push events to branches matching refs/heads/release/** + - "release/**" + +# these should be the only settings that you will ever need to change +env: + CONSUL_IMAGE: hashicorppreview/consul-enterprise:1.16-dev # Consul's enterprise version to use in tests. We use this consul image on release branches too + BRANCH: ${{ github.head_ref || github.ref_name }} + CONTEXT: "${{ github.actor }}" + +jobs: + test: + name: test + runs-on: ubuntu-latest + steps: + - uses: benc-uk/workflow-dispatch@v1.2.2 + name: test + with: + workflow: test.yml + repo: hashicorp/consul-k8s-workflows + ref: main + token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} + inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}", "consul-image":"${{ env.CONSUL_IMAGE }}" }' diff --git a/.github/workflows/reusable-acceptance.yml b/.github/workflows/reusable-acceptance.yml deleted file mode 100644 index cf1e11e3f5..0000000000 --- a/.github/workflows/reusable-acceptance.yml +++ /dev/null @@ -1,159 +0,0 @@ -# Copyright (c) HashiCorp, Inc. - -name: reusable-acceptance - -on: - workflow_call: - inputs: - name: - required: true - type: string - additional-flags: - required: false - type: string - default: "" - consul-k8s-image: - required: false - type: string - directory: - required: true - type: string - go-version: - required: true - type: string - gotestsum-version: - required: true - type: string - kind-version: - required: false - type: string - default: "v1.24.6" - checkout-ref: - required: false - type: string - default: ${{ github.sha }} - secrets: - CONSUL_ENT_LICENSE: - required: true - VAULT_LICENSE: - required: true - -# Environment variables can only be used at the step level -env: - TEST_RESULTS: /tmp/test-results # path to where test results are saved - CONSUL_ENT_LICENSE: ${{ secrets.CONSUL_ENT_LICENSE }} - VAULT_LICENSE: ${{ secrets.VAULT_LICENSE }} - CONSUL_K8S_IMAGE: ${{ inputs.consul-k8s-image }} - -jobs: - job: - runs-on: [custom, linux, xl] - strategy: - matrix: - include: - - {runner: "0", test-packages: "basic consul-dns metrics"} - - {runner: "1", test-packages: "connect"} - - {runner: "2", test-packages: "controller example"} - - {runner: "3", test-packages: "ingress-gateway"} - - {runner: "4", test-packages: "partitions"} - - {runner: "5", test-packages: "peering"} - - {runner: "6", test-packages: "snapshot-agent vault wan-federation"} - - {runner: "7", test-packages: "cli sync terminating-gateway"} - - fail-fast: false - steps: - - name: Checkout code - uses: actions/checkout@v3 - with: - ref: ${{ inputs.checkout-ref }} - - - name: Setup go - uses: actions/setup-go@v3 - with: - go-version: ${{ inputs.go-version }} - - - name: Setup go mod cache - uses: actions/cache@v3 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - - name: Install pre-requisites # Install gotestsum, kind, kubectl, and helm - run: | - wget https://github.com/gotestyourself/gotestsum/releases/download/v1.6.4/gotestsum_1.6.4_linux_amd64.tar.gz - sudo tar -C /usr/local/bin -xzf gotestsum_1.6.4_linux_amd64.tar.gz - rm gotestsum_1.6.4_linux_amd64.tar.gz - - curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.15.0/kind-linux-amd64 - chmod +x ./kind - sudo mv ./kind /usr/local/bin/kind - - curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl" - chmod +x ./kubectl - sudo mv ./kubectl /usr/local/bin/kubectl - - wget https://get.helm.sh/helm-v3.9.4-linux-amd64.tar.gz - tar -zxvf helm-v3.9.4-linux-amd64.tar.gz - sudo mv linux-amd64/helm /usr/local/bin/helm - - - run: mkdir -p ${{ env.TEST_RESULTS }} - - - name: go mod download - working-directory: ${{ inputs.directory }} - run: go mod download - - - name: Create kind clusters - run: | - kind create cluster --name dc1 --image kindest/node:${{ inputs.kind-version }} - kind create cluster --name dc2 --image kindest/node:${{ inputs.kind-version }} - - - name: Build CLI - run: | - sudo make cli-dev - consul-k8s version - - # We have to run the tests for each package separately so that we can - # exit early if any test fails (-failfast only works within a single - # package). - - name: Run acceptance tests ${{ matrix.runner }} - working-directory: ${{ inputs.directory }} - if: github.repository_owner == 'hashicorp' # This prevents running on forks - run: | - exit_code=0 - echo "Running packages: ${{ matrix.test-packages }}" - for pkg in $(echo ${{ matrix.test-packages }}) - do - fullpkg="github.com/hashicorp/consul-k8s/${{ inputs.directory }}/${pkg}" - echo "Testing package: ${fullpkg}" - if ! gotestsum --format=testname --jsonfile=jsonfile-${pkg////-} -- ${fullpkg} -p 1 -timeout 2h -failfast \ - ${{ inputs.additional-flags }} \ - -enable-enterprise \ - -enable-multi-cluster \ - -debug-directory=${{ env.TEST_RESULTS }}/debug \ - -consul-k8s-image=${{ inputs.consul-k8s-image }} - then - echo "Tests in ${pkg} failed, aborting early" - exit_code=1 - break - fi - done - gotestsum --format=testname --raw-command --junitfile "${{ env.TEST_RESULTS }}/gotestsum-report.xml" -- cat jsonfile* - exit $exit_code - - - name: Upload tests - if: always() - uses: actions/upload-artifact@v3 - with: - name: ${{ inputs.name }}-${{ matrix.test-packages }}-gotestsum-report.xml - path: ${{ env.TEST_RESULTS }}/gotestsum-report.xml - - - name: Upload debug (on failure) - if: failure() - uses: actions/upload-artifact@v3 - with: - name: ${{ inputs.name }}-${{ matrix.test-packages }}-debug-info - path: ${{ env.TEST_RESULTS }}/debug diff --git a/.github/workflows/reusable-golangci-lint.yml b/.github/workflows/reusable-golangci-lint.yml deleted file mode 100644 index e8fcd79440..0000000000 --- a/.github/workflows/reusable-golangci-lint.yml +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) HashiCorp, Inc. - -name: golangci-lint - -on: - workflow_call: - inputs: - directory: - required: true - type: string - go-version: - required: true - type: string - args: - required: false - type: string - -jobs: - job: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Setup go - uses: actions/setup-go@v3 - with: - go-version: ${{ inputs.go-version }} - - - name: golangci-lint-${{inputs.directory}} - uses: golangci/golangci-lint-action@v3.4.0 - with: - version: v1.51 - working-directory: ${{inputs.directory}} - args: ${{inputs.args}} diff --git a/.github/workflows/reusable-unit.yml b/.github/workflows/reusable-unit.yml deleted file mode 100644 index dd2dd4e4a0..0000000000 --- a/.github/workflows/reusable-unit.yml +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) HashiCorp, Inc. - -name: reusable-unit - -on: - workflow_call: - inputs: - directory: - required: true - type: string - go-version: - required: true - type: string - -# Environment variables can only be used at the step level -env: - TEST_RESULTS: /tmp/test-results # path to where test results are saved - GOTESTSUM_VERSION: 1.8.2 - -jobs: - job: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup go - uses: actions/setup-go@v3 - with: - go-version: ${{inputs.go-version}} - - - name: Setup go mod cache - uses: actions/cache@v3 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - - name: Install gotestsum - run: | - wget https://github.com/gotestyourself/gotestsum/releases/download/v${{env.GOTESTSUM_VERSION}}/gotestsum_${{env.GOTESTSUM_VERSION}}_linux_amd64.tar.gz - sudo tar -C /usr/local/bin -xzf gotestsum_${{env.GOTESTSUM_VERSION}}_linux_amd64.tar.gz - rm gotestsum_${{env.GOTESTSUM_VERSION}}_linux_amd64.tar.gz - - - run: mkdir -p ${{env.TEST_RESULTS}} - - - name: go mod download - working-directory: ${{inputs.directory}} - run: go mod download - - - name: Run tests - working-directory: ${{inputs.directory}} - run: | - gotestsum --format=testname --junitfile ${{env.TEST_RESULTS}}/gotestsum-report.xml ./... -- -p 4 - diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 6e6212e0d8..0000000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,401 +0,0 @@ -# Copyright (c) HashiCorp, Inc. - -name: test -on: - push: - -env: - TEST_RESULTS: /tmp/test-results # path to where test results are saved - GOTESTSUM_VERSION: 1.8.2 # You cannot use environment variables with workflows. The gotestsum version is hardcoded in the reusable workflows too. - # We use docker images to copy the consul binary for unit tests. - CONSUL_OSS_DOCKER_IMAGE: hashicorppreview/consul:1.16-dev # Consul's OSS version to use in tests - CONSUL_ENT_DOCKER_IMAGE: hashicorppreview/consul-enterprise:1.16-dev # Consul's enterprise version to use in tests - -jobs: - terraform-fmt-check: - name: "Terraform format check" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Setup Terraform - uses: hashicorp/setup-terraform@v2 - with: - terraform_version: TERRAFORM_VERSION - terraform_wrapper: false - - name: Run Terraform checks - run: | - make terraform-fmt-check TERRAFORM_DIR="${{ github.workspace }}" - - get-go-version: - name: "Determine Go toolchain version" - runs-on: ubuntu-latest - outputs: - go-version: ${{ steps.get-go-version.outputs.go-version }} - steps: - - uses: actions/checkout@v3 - - name: Determine Go version - id: get-go-version - # We use .go-version as our source of truth for current Go - # version, because "goenv" can react to it automatically. - run: | - echo "Building with Go $(cat .go-version)" - echo "::set-output name=go-version::$(cat .go-version)" - - get-product-version: - runs-on: ubuntu-latest - outputs: - product-version: ${{ steps.get-product-version.outputs.product-version }} - steps: - - uses: actions/checkout@v3 - - name: get product version - id: get-product-version - run: | - make version - echo "::set-output name=product-version::$(make version)" - - validate-helm-gen: - needs: - - get-go-version - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup go - uses: actions/setup-go@v3 - with: - go-version: ${{ needs.get-go-version.outputs.go-version }} - - - name: Setup go mod cache - uses: actions/cache@v3 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - - name: Validate helm gen - working-directory: hack/helm-reference-gen - run: | - go run ./... -validate - - golangci-lint-helm-gen: - needs: - - get-go-version - uses: ./.github/workflows/reusable-golangci-lint.yml - with: - directory: hack/helm-reference-gen - go-version: ${{ needs.get-go-version.outputs.go-version }} - #TODO: This is a workaround in order to get pipelines working. godot and staticcheck fail for helm-reference-gen - args: "--no-config --disable-all --enable gofmt,govet" - - unit-helm-gen: - needs: [get-go-version, golangci-lint-helm-gen, validate-helm-gen] - uses: ./.github/workflows/reusable-unit.yml - with: - directory: hack/helm-reference-gen - go-version: ${{ needs.get-go-version.outputs.go-version }} - - unit-test-helm-templates: - needs: - - unit-helm-gen - runs-on: ubuntu-latest - container: - image: docker.mirror.hashicorp.services/hashicorpdev/consul-helm-test:0.15.0 - options: --user 1001 - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Run Unit Tests - working-directory: charts/consul - run: bats --jobs 4 ./test/unit - - lint-control-plane: - needs: - - get-go-version - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup go - uses: actions/setup-go@v3 - with: - go-version: ${{ needs.get-go-version.outputs.go-version }} - - - run: go install github.com/hashicorp/lint-consul-retry@master && lint-consul-retry - - - name: Run lint - working-directory: control-plane - run: go run hack/lint-api-new-client/main.go - - golangci-lint-control-plane: - needs: - - get-go-version - uses: ./.github/workflows/reusable-golangci-lint.yml - with: - directory: control-plane - go-version: ${{ needs.get-go-version.outputs.go-version }} - - test-control-plane: - needs: [get-go-version, lint-control-plane, golangci-lint-control-plane] - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup go - uses: actions/setup-go@v3 - with: - go-version: ${{ needs.get-go-version.outputs.go-version }} - - - name: Setup go mod cache - uses: actions/cache@v3 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - - name: Install gotestsum - run: | - wget https://github.com/gotestyourself/gotestsum/releases/download/v${{env.GOTESTSUM_VERSION}}/gotestsum_${{env.GOTESTSUM_VERSION}}_linux_amd64.tar.gz - sudo tar -C /usr/local/bin -xzf gotestsum_${{env.GOTESTSUM_VERSION}}_linux_amd64.tar.gz - rm gotestsum_${{env.GOTESTSUM_VERSION}}_linux_amd64.tar.gz - - - run: mkdir -p ${{env.TEST_RESULTS}} - - run: echo "$HOME/bin" >> $GITHUB_PATH - - - name: Download consul - working-directory: control-plane - run: | - mkdir -p $HOME/bin - container_id=$(docker create ${{env.CONSUL_OSS_DOCKER_IMAGE}}) - docker cp "$container_id:/bin/consul" $HOME/bin/consul - docker rm "$container_id" - - name: Run go tests - working-directory: control-plane - run: | - PACKAGE_NAMES=$(go list ./...) - gotestsum --format=testname --junitfile ${{env.TEST_RESULTS}}/gotestsum-report.xml -- -p 4 $PACKAGE_NAMES - - test-enterprise-control-plane: - if: github.repository_owner == 'hashicorp' # Do not run on forks as this requires secrets - needs: [get-go-version, lint-control-plane, golangci-lint-control-plane] - runs-on: ubuntu-latest - env: - CONSUL_LICENSE: ${{secrets.CONSUL_LICENSE}} - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup go - uses: actions/setup-go@v3 - with: - go-version: ${{ needs.get-go-version.outputs.go-version }} - - - name: Setup go mod cache - uses: actions/cache@v3 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - - name: Install gotestsum - run: | - wget https://github.com/gotestyourself/gotestsum/releases/download/v${{env.GOTESTSUM_VERSION}}/gotestsum_${{env.GOTESTSUM_VERSION}}_linux_amd64.tar.gz - sudo tar -C /usr/local/bin -xzf gotestsum_${{env.GOTESTSUM_VERSION}}_linux_amd64.tar.gz - rm gotestsum_${{env.GOTESTSUM_VERSION}}_linux_amd64.tar.gz - - - run: mkdir -p ${{env.TEST_RESULTS}} - - run: echo "$HOME/bin" >> $GITHUB_PATH - - - name: Download consul - working-directory: control-plane - run: | - mkdir -p $HOME/bin - container_id=$(docker create ${{env.CONSUL_ENT_DOCKER_IMAGE}}) - docker cp "$container_id:/bin/consul" $HOME/bin/consul - docker rm "$container_id" - - - name: Run go tests - working-directory: control-plane - run: | - PACKAGE_NAMES=$(go list ./...) - gotestsum --format=testname --junitfile ${{env.TEST_RESULTS}}/gotestsum-report.xml -- -tags=enterprise -p 4 $PACKAGE_NAMES - - build-distros: - needs: [get-go-version, get-product-version] - runs-on: ubuntu-latest - strategy: - matrix: - include: - # cli - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s" } - # control-plane - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } - # consul-cni - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni" } - - fail-fast: true - - name: Go ${{ matrix.go }} ${{ matrix.goos }} ${{ matrix.goarch }} ${{ matrix.component }} build - steps: - - uses: actions/checkout@v3 - - - name: Setup go - uses: actions/setup-go@v3 - with: - go-version: ${{ matrix.go }} - - - name: Build - env: - GOOS: ${{ matrix.goos }} - GOARCH: ${{ matrix.goarch }} - CGO_ENABLED: 0 - working-directory: ${{ matrix.component }} - run: | - mkdir -p dist out - - export GIT_COMMIT=$(git rev-parse --short HEAD) - export GIT_DIRTY=$(test -n "$(git status --porcelain)" && echo "+CHANGES") - export GIT_IMPORT=github.com/hashicorp/consul-k8s/${{ matrix.component }}/version - export GOLDFLAGS="-X ${GIT_IMPORT}.GitCommit=${GIT_COMMIT}${GIT_DIRTY} -X ${GIT_IMPORT}.GitDescribe=${{ needs.get-product-version.outputs.product-version }}" - - CGO_ENABLED=0 go build -o dist/${{ matrix.bin_name }} -ldflags "${GOLDFLAGS}" . - zip -r -j out/${{ matrix.pkg_name }}_${{ needs.get-product-version.outputs.product-version }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip dist/ - - - name: Upload built binaries - uses: actions/upload-artifact@v3 - with: - name: ${{ matrix.pkg_name }}_${{ needs.get-product-version.outputs.product-version }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip - path: ${{ matrix.component}}/out/${{ matrix.pkg_name }}_${{ needs.get-product-version.outputs.product-version }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip - - golangci-lint-acceptance: - needs: - - get-go-version - uses: ./.github/workflows/reusable-golangci-lint.yml - with: - directory: acceptance - go-version: ${{ needs.get-go-version.outputs.go-version }} - - unit-acceptance-framework: - needs: [get-go-version, golangci-lint-acceptance] - uses: ./.github/workflows/reusable-unit.yml - with: - directory: acceptance/framework - go-version: ${{ needs.get-go-version.outputs.go-version }} - - golangci-lint-cli: - needs: - - get-go-version - uses: ./.github/workflows/reusable-golangci-lint.yml - with: - directory: cli - go-version: ${{ needs.get-go-version.outputs.go-version }} - - unit-cli: - needs: [get-go-version, golangci-lint-cli] - uses: ./.github/workflows/reusable-unit.yml - with: - directory: cli - go-version: ${{ needs.get-go-version.outputs.go-version }} - - # upload dev docker image - dev-upload-docker: - if: github.repository_owner == 'hashicorp' # Do not run on forks as this requires secrets - needs: [ get-product-version, build-distros ] - runs-on: ubuntu-latest - strategy: - matrix: - arch: ["amd64"] - env: - repo: ${{ github.event.repository.name }} - version: ${{ needs.get-product-version.outputs.product-version }} - steps: - - uses: actions/checkout@v3 - - uses: actions/download-artifact@v3 - with: - name: consul-cni_${{ needs.get-product-version.outputs.product-version }}_linux_${{ matrix.arch }}.zip - path: control-plane/dist/cni/linux/${{ matrix.arch }} - - uses: actions/download-artifact@v3 - with: - name: consul-k8s-control-plane_${{ needs.get-product-version.outputs.product-version }}_linux_${{ matrix.arch }}.zip - path: control-plane/dist/linux/${{ matrix.arch }} - - name: extract consul-cni zip - env: - ZIP_LOCATION: control-plane/dist/cni/linux/${{ matrix.arch }} - run: | - cd "${ZIP_LOCATION}" - unzip -j *.zip - - name: extract control-plane zip - env: - ZIP_LOCATION: control-plane/dist/linux/${{ matrix.arch }} - run: | - cd "${ZIP_LOCATION}" - unzip -j *.zip - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - name: Login to Docker Hub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKER_USER }} - password: ${{ secrets.DOCKER_PASS }} - - name: Docker Build (Action) - uses: docker/build-push-action@v3 - with: - push: true - context: control-plane - platforms: ${{ matrix.arch }} - target: release-default - tags: docker.io/hashicorppreview/${{ env.repo }}-control-plane:${{ env.version }}-pr-${{ github.sha }} - -# Disable GHA acceptance tests until GHA formally supported -# acceptance: -# needs: [ get-product-version, dev-upload-docker, get-go-version ] -# uses: ./.github/workflows/reusable-acceptance.yml -# with: -# name: acceptance -# directory: acceptance/tests -# go-version: ${{ needs.get-go-version.outputs.go-version }} -# additional-flags: "-use-kind -kubecontext=kind-dc1 -secondary-kubecontext=kind-dc2 -consul-image=docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.14-dev" -# gotestsum-version: 1.8.2 -# consul-k8s-image: docker.io/hashicorppreview/${{ github.event.repository.name }}-control-plane:${{ needs.get-product-version.outputs.product-version }}-pr-${{ github.sha }} -# secrets: -# CONSUL_ENT_LICENSE: ${{ secrets.CONSUL_ENT_LICENSE }} -# -# acceptance-tproxy: -# needs: [ get-product-version, dev-upload-docker, get-go-version ] -# uses: ./.github/workflows/reusable-acceptance.yml -# with: -# name: acceptance-tproxy -# directory: acceptance/tests -# go-version: ${{ needs.get-go-version.outputs.go-version }} -# additional-flags: "-use-kind -kubecontext=kind-dc1 -secondary-kubecontext=kind-dc2 -enable-transparent-proxy -consul-image=docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.14-dev" -# gotestsum-version: 1.8.2 -# consul-k8s-image: docker.io/hashicorppreview/${{ github.event.repository.name }}-control-plane:${{ needs.get-product-version.outputs.product-version }}-pr-${{ github.sha }} -# secrets: -# CONSUL_ENT_LICENSE: ${{ secrets.CONSUL_ENT_LICENSE }} -# -# acceptance-cni: -# needs: [ get-product-version, dev-upload-docker, get-go-version ] -# uses: ./.github/workflows/reusable-acceptance.yml -# with: -# name: acceptance -# directory: acceptance/tests -# go-version: ${{ needs.get-go-version.outputs.go-version }} -# additional-flags: "-use-kind -kubecontext=kind-dc1 -secondary-kubecontext=kind-dc2 -enable-transparent-proxy -enable-cni -consul-image=docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.14-dev" -# gotestsum-version: 1.8.2 -# consul-k8s-image: docker.io/hashicorppreview/${{ github.event.repository.name }}-control-plane:${{ needs.get-product-version.outputs.product-version }}-pr-${{ github.sha }} -# secrets: -# CONSUL_ENT_LICENSE: ${{ secrets.CONSUL_ENT_LICENSE }} - - diff --git a/.github/workflows/weekly-acceptance-0-49-x.yml b/.github/workflows/weekly-acceptance-0-49-x.yml new file mode 100644 index 0000000000..7025bcb241 --- /dev/null +++ b/.github/workflows/weekly-acceptance-0-49-x.yml @@ -0,0 +1,29 @@ +# Dispatch to the consul-k8s-workflows with a weekly cron +# +# A separate file is needed for each release because the cron schedules are different for each release. +name: weekly-acceptance-0-49-x +on: + schedule: + # * is a special character in YAML so you have to quote this string + # Run weekly on Monday at 3AM UTC/11PM EST/8PM PST + - cron: '0 3 * * 1' + +# these should be the only settings that you will ever need to change +env: + CONSUL_IMAGE: hashicorppreview/consul-enterprise:1.13-dev # Consul's enterprise version to use in tests + BRANCH: "release/0.49.x" + CONTEXT: "weekly" + +jobs: + cloud: + name: cloud + runs-on: ubuntu-latest + steps: + - uses: benc-uk/workflow-dispatch@v1.2.2 + name: cloud + with: + workflow: cloud.yml + repo: hashicorp/consul-k8s-workflows + ref: main + token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} + inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}", "consul-image":"${{ env.CONSUL_IMAGE }}" }' diff --git a/.github/workflows/weekly-acceptance-1-0-x.yml b/.github/workflows/weekly-acceptance-1-0-x.yml new file mode 100644 index 0000000000..4aa49594f3 --- /dev/null +++ b/.github/workflows/weekly-acceptance-1-0-x.yml @@ -0,0 +1,30 @@ +# Dispatch to the consul-k8s-workflows with a weekly cron +# +# A separate file is needed for each release because the cron schedules are different for each release. +name: weekly-acceptance-1-0-x +on: + schedule: + # * is a special character in YAML so you have to quote this string + # Run weekly on Tuesday at 3AM UTC/11PM EST/8PM PST + - cron: '0 3 * * 2' + + +# these should be the only settings that you will ever need to change +env: + CONSUL_IMAGE: hashicorppreview/consul-enterprise:1.14-dev # Consul's enterprise version to use in tests + BRANCH: "release/1.0.x" + CONTEXT: "weekly" + +jobs: + cloud: + name: cloud + runs-on: ubuntu-latest + steps: + - uses: benc-uk/workflow-dispatch@v1.2.2 + name: cloud + with: + workflow: cloud.yml + repo: hashicorp/consul-k8s-workflows + ref: main + token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} + inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}", "consul-image":"${{ env.CONSUL_IMAGE }}" }' diff --git a/.github/workflows/weekly-acceptance-1-1-x.yml b/.github/workflows/weekly-acceptance-1-1-x.yml new file mode 100644 index 0000000000..1ffc6f8684 --- /dev/null +++ b/.github/workflows/weekly-acceptance-1-1-x.yml @@ -0,0 +1,30 @@ +# Dispatch to the consul-k8s-workflows with a weekly cron +# +# A separate file is needed for each release because the cron schedules are different for each release. +name: weekly-acceptance-1-1-x +on: + schedule: + # * is a special character in YAML so you have to quote this string + # Run weekly on Wednesday at 3AM UTC/11PM EST/8PM PST + - cron: '0 3 * * 3' + + +# these should be the only settings that you will ever need to change +env: + CONSUL_IMAGE: hashicorppreview/consul-enterprise:1.15-dev # Consul's enterprise version to use in tests + BRANCH: "release/1.1.x" + CONTEXT: "weekly" + +jobs: + cloud: + name: cloud + runs-on: ubuntu-latest + steps: + - uses: benc-uk/workflow-dispatch@v1.2.2 + name: cloud + with: + workflow: cloud.yml + repo: hashicorp/consul-k8s-workflows + ref: main + token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} + inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}", "consul-image":"${{ env.CONSUL_IMAGE }}" }' diff --git a/acceptance/framework/consul/helm_cluster.go b/acceptance/framework/consul/helm_cluster.go index f34aab6455..e03e3615fc 100644 --- a/acceptance/framework/consul/helm_cluster.go +++ b/acceptance/framework/consul/helm_cluster.go @@ -146,14 +146,14 @@ func (h *HelmCluster) Destroy(t *testing.T) { "--wait": nil, } - retry.RunWith(&retry.Counter{Wait: 1 * time.Second, Count: 15}, t, func(r *retry.R) { + retry.RunWith(&retry.Counter{Wait: 2 * time.Second, Count: 30}, t, func(r *retry.R) { err := helm.DeleteE(t, h.helmOptions, h.releaseName, false) require.NoError(r, err) }) // Retry because sometimes certain resources (like PVC) take time to delete // in cloud providers. - retry.RunWith(&retry.Counter{Wait: 1 * time.Second, Count: 600}, t, func(r *retry.R) { + retry.RunWith(&retry.Counter{Wait: 2 * time.Second, Count: 600}, t, func(r *retry.R) { // Force delete any pods that have h.releaseName in their name because sometimes // graceful termination takes a long time and since this is an uninstall // we don't care that they're stopped gracefully. @@ -330,7 +330,7 @@ func (h *HelmCluster) SetupConsulClient(t *testing.T, secure bool) (client *api. if h.ACLToken != "" { config.Token = h.ACLToken } else { - retry.RunWith(&retry.Counter{Wait: 1 * time.Second, Count: 600}, t, func(r *retry.R) { + retry.RunWith(&retry.Counter{Wait: 2 * time.Second, Count: 600}, t, func(r *retry.R) { // Get the ACL token. First, attempt to read it from the bootstrap token (this will be true in primary Consul servers). // If the bootstrap token doesn't exist, it means we are running against a secondary cluster // and will try to read the replication token from the federation secret. @@ -534,7 +534,7 @@ func defaultValues() map[string]string { func CreateK8sSecret(t *testing.T, client kubernetes.Interface, cfg *config.TestConfig, namespace, secretName, secretKey, secret string) { - retry.RunWith(&retry.Counter{Wait: 1 * time.Second, Count: 15}, t, func(r *retry.R) { + retry.RunWith(&retry.Counter{Wait: 2 * time.Second, Count: 15}, t, func(r *retry.R) { _, err := client.CoreV1().Secrets(namespace).Get(context.Background(), secretName, metav1.GetOptions{}) if errors.IsNotFound(err) { _, err := client.CoreV1().Secrets(namespace).Create(context.Background(), &corev1.Secret{ diff --git a/acceptance/framework/helpers/helpers.go b/acceptance/framework/helpers/helpers.go index 544079b8fb..3e6ef039b2 100644 --- a/acceptance/framework/helpers/helpers.go +++ b/acceptance/framework/helpers/helpers.go @@ -38,7 +38,7 @@ func CheckForPriorInstallations(t *testing.T, client kubernetes.Interface, optio // Check if there's an existing cluster and fail if there is one. // We may need to retry since this is the first command run once the Kube // cluster is created and sometimes the API server returns errors. - retry.RunWith(&retry.Counter{Wait: 1 * time.Second, Count: 15}, t, func(r *retry.R) { + retry.RunWith(&retry.Counter{Wait: 2 * time.Second, Count: 15}, t, func(r *retry.R) { var err error // NOTE: It's okay to pass in `t` to RunHelmCommandAndGetOutputE despite being in a retry // because we're using RunHelmCommandAndGetOutputE (not RunHelmCommandAndGetOutput) so the `t` won't @@ -58,7 +58,7 @@ func CheckForPriorInstallations(t *testing.T, client kubernetes.Interface, optio // Wait for all pods in the "default" namespace to exit. A previous // release may not be listed by Helm but its pods may still be terminating. - retry.RunWith(&retry.Counter{Wait: 1 * time.Second, Count: 60}, t, func(r *retry.R) { + retry.RunWith(&retry.Counter{Wait: 2 * time.Second, Count: 60}, t, func(r *retry.R) { pods, err := client.CoreV1().Pods(options.KubectlOptions.Namespace).List(context.Background(), metav1.ListOptions{LabelSelector: labelSelector}) require.NoError(r, err) if len(pods.Items) > 0 { diff --git a/acceptance/framework/k8s/helpers.go b/acceptance/framework/k8s/helpers.go index efac5b35db..235d26d061 100644 --- a/acceptance/framework/k8s/helpers.go +++ b/acceptance/framework/k8s/helpers.go @@ -43,10 +43,10 @@ func WaitForAllPodsToBeReady(t *testing.T, client kubernetes.Interface, namespac logger.Logf(t, "Waiting for pods with label %q to be ready.", podLabelSelector) - // Wait up to 11m. + // Wait up to 20m. // On Azure, volume provisioning can sometimes take close to 5 min, // so we need to give a bit more time for pods to become healthy. - counter := &retry.Counter{Count: 11 * 60, Wait: 1 * time.Second} + counter := &retry.Counter{Count: 20 * 60, Wait: 2 * time.Second} retry.RunWith(counter, t, func(r *retry.R) { pods, err := client.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{LabelSelector: podLabelSelector}) require.NoError(r, err) @@ -113,7 +113,7 @@ func ServiceHost(t *testing.T, cfg *config.TestConfig, ctx environment.TestConte var host string // It can take some time for the load balancers to be ready and have an IP/Hostname. // Wait for 5 minutes before failing. - retry.RunWith(&retry.Counter{Wait: 1 * time.Second, Count: 600}, t, func(r *retry.R) { + retry.RunWith(&retry.Counter{Wait: 2 * time.Second, Count: 600}, t, func(r *retry.R) { svc, err := ctx.KubernetesClient(t).CoreV1().Services(ctx.KubectlOptions(t).Namespace).Get(context.Background(), serviceName, metav1.GetOptions{}) require.NoError(t, err) require.NotEmpty(r, svc.Status.LoadBalancer.Ingress) diff --git a/acceptance/framework/k8s/kubectl.go b/acceptance/framework/k8s/kubectl.go index 4416c7e6a9..ea90212e04 100644 --- a/acceptance/framework/k8s/kubectl.go +++ b/acceptance/framework/k8s/kubectl.go @@ -56,7 +56,7 @@ func RunKubectlAndGetOutputWithLoggerE(t *testing.T, options *k8s.KubectlOptions } counter := &retry.Counter{ - Count: 3, + Count: 10, Wait: 1 * time.Second, } var output string diff --git a/acceptance/framework/portforward/port_forward.go b/acceptance/framework/portforward/port_forward.go index 0a48e8d300..3242541cc5 100644 --- a/acceptance/framework/portforward/port_forward.go +++ b/acceptance/framework/portforward/port_forward.go @@ -27,7 +27,7 @@ func CreateTunnelToResourcePort(t *testing.T, resourceName string, remotePort in logger) // Retry creating the port forward since it can fail occasionally. - retry.RunWith(&retry.Counter{Wait: 3 * time.Second, Count: 60}, t, func(r *retry.R) { + retry.RunWith(&retry.Counter{Wait: 5 * time.Second, Count: 60}, t, func(r *retry.R) { // NOTE: It's okay to pass in `t` to ForwardPortE despite being in a retry // because we're using ForwardPortE (not ForwardPort) so the `t` won't // get used to fail the test, just for logging. diff --git a/acceptance/framework/vault/vault_cluster.go b/acceptance/framework/vault/vault_cluster.go index d09b8ed75e..26da56b646 100644 --- a/acceptance/framework/vault/vault_cluster.go +++ b/acceptance/framework/vault/vault_cluster.go @@ -113,7 +113,7 @@ func (v *VaultCluster) SetupVaultClient(t *testing.T) *vapi.Client { v.logger) // Retry creating the port forward since it can fail occasionally. - retry.RunWith(&retry.Counter{Wait: 1 * time.Second, Count: 60}, t, func(r *retry.R) { + retry.RunWith(&retry.Counter{Wait: 5 * time.Second, Count: 60}, t, func(r *retry.R) { // NOTE: It's okay to pass in `t` to ForwardPortE despite being in a retry // because we're using ForwardPortE (not ForwardPort) so the `t` won't // get used to fail the test, just for logging. diff --git a/acceptance/tests/config-entries/config_entries_namespaces_test.go b/acceptance/tests/config-entries/config_entries_namespaces_test.go index 05ea9e5235..c15277a6ee 100644 --- a/acceptance/tests/config-entries/config_entries_namespaces_test.go +++ b/acceptance/tests/config-entries/config_entries_namespaces_test.go @@ -136,7 +136,7 @@ func TestControllerNamespaces(t *testing.T) { // On startup, the controller can take upwards of 1m to perform // leader election so we may need to wait a long time for // the reconcile loop to run (hence the 1m timeout here). - counter := &retry.Counter{Count: 60, Wait: 1 * time.Second} + counter := &retry.Counter{Count: 60, Wait: 2 * time.Second} retry.RunWith(counter, t, func(r *retry.R) { // service-defaults entry, _, err := consulClient.ConfigEntries().Get(api.ServiceDefaults, "defaults", queryOpts) @@ -258,7 +258,7 @@ func TestControllerNamespaces(t *testing.T) { patchSNI := "patch-sni" k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "-n", KubeNS, "terminatinggateway", "terminating-gateway", "-p", fmt.Sprintf(`{"spec": {"services": [{"name":"name","caFile":"caFile","certFile":"certFile","keyFile":"keyFile","sni":"%s"}]}}`, patchSNI), "--type=merge") - counter := &retry.Counter{Count: 10, Wait: 500 * time.Millisecond} + counter := &retry.Counter{Count: 20, Wait: 2 * time.Second} retry.RunWith(counter, t, func(r *retry.R) { // service-defaults entry, _, err := consulClient.ConfigEntries().Get(api.ServiceDefaults, "defaults", queryOpts) @@ -366,7 +366,7 @@ func TestControllerNamespaces(t *testing.T) { logger.Log(t, "deleting terminating-gateway custom resource") k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "-n", KubeNS, "terminatinggateway", "terminating-gateway") - counter := &retry.Counter{Count: 10, Wait: 500 * time.Millisecond} + counter := &retry.Counter{Count: 20, Wait: 2 * time.Second} retry.RunWith(counter, t, func(r *retry.R) { // service-defaults _, _, err := consulClient.ConfigEntries().Get(api.ServiceDefaults, "defaults", queryOpts) diff --git a/acceptance/tests/config-entries/config_entries_test.go b/acceptance/tests/config-entries/config_entries_test.go index d3c0d410a0..4aa7cf11bd 100644 --- a/acceptance/tests/config-entries/config_entries_test.go +++ b/acceptance/tests/config-entries/config_entries_test.go @@ -96,7 +96,7 @@ func TestController(t *testing.T) { // On startup, the controller can take upwards of 1m to perform // leader election so we may need to wait a long time for // the reconcile loop to run (hence the 1m timeout here). - counter := &retry.Counter{Count: 60, Wait: 1 * time.Second} + counter := &retry.Counter{Count: 60, Wait: 2 * time.Second} retry.RunWith(counter, t, func(r *retry.R) { // service-defaults entry, _, err := consulClient.ConfigEntries().Get(api.ServiceDefaults, "defaults", nil) diff --git a/acceptance/tests/consul-dns/consul_dns_test.go b/acceptance/tests/consul-dns/consul_dns_test.go index 15b7d580be..c33de7747d 100644 --- a/acceptance/tests/consul-dns/consul_dns_test.go +++ b/acceptance/tests/consul-dns/consul_dns_test.go @@ -62,7 +62,7 @@ func TestConsulDNS(t *testing.T) { dnsPodName := fmt.Sprintf("%s-dns-pod", releaseName) dnsTestPodArgs := []string{ - "run", "-i", dnsPodName, "--restart", "Never", "--image", "anubhavmishra/tiny-tools", "--", "dig", fmt.Sprintf("@%s-consul-dns", releaseName), "consul.service.consul", + "run", "-it", dnsPodName, "--restart", "Never", "--image", "anubhavmishra/tiny-tools", "--", "dig", fmt.Sprintf("@%s-consul-dns", releaseName), "consul.service.consul", } helpers.Cleanup(t, suite.Config().NoCleanupOnFailure, func() { diff --git a/acceptance/tests/metrics/metrics_test.go b/acceptance/tests/metrics/metrics_test.go index acfe465b00..2a130f38db 100644 --- a/acceptance/tests/metrics/metrics_test.go +++ b/acceptance/tests/metrics/metrics_test.go @@ -132,7 +132,7 @@ func TestAppMetrics(t *testing.T) { // Retry because sometimes the merged metrics server takes a couple hundred milliseconds // to start. - retry.RunWith(&retry.Counter{Count: 3, Wait: 1 * time.Second}, t, func(r *retry.R) { + retry.RunWith(&retry.Counter{Count: 20, Wait: 2 * time.Second}, t, func(r *retry.R) { metricsOutput, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "exec", "deploy/"+StaticClientName, "--", "curl", "--silent", "--show-error", fmt.Sprintf("http://%s:20200/metrics", podIP)) require.NoError(r, err) // This assertion represents the metrics from the envoy sidecar. diff --git a/acceptance/tests/partitions/partitions_sync_test.go b/acceptance/tests/partitions/partitions_sync_test.go index 344d64a780..cf32c97ae3 100644 --- a/acceptance/tests/partitions/partitions_sync_test.go +++ b/acceptance/tests/partitions/partitions_sync_test.go @@ -248,7 +248,7 @@ func TestPartitions_Sync(t *testing.T) { logger.Log(t, "checking that the service has been synced to Consul") var services map[string][]string - counter := &retry.Counter{Count: 20, Wait: 30 * time.Second} + counter := &retry.Counter{Count: 30, Wait: 30 * time.Second} retry.RunWith(counter, t, func(r *retry.R) { var err error // list services in default partition catalog. diff --git a/charts/consul/test/terraform/aks/main.tf b/charts/consul/test/terraform/aks/main.tf index d077471ec9..8cf72a142c 100644 --- a/charts/consul/test/terraform/aks/main.tf +++ b/charts/consul/test/terraform/aks/main.tf @@ -71,7 +71,7 @@ resource "azurerm_kubernetes_cluster" "default" { default_node_pool { name = "default" node_count = 3 - vm_size = "Standard_D2_v2" + vm_size = "Standard_D3_v2" os_disk_size_gb = 30 vnet_subnet_id = azurerm_virtual_network.default[count.index].subnet.*.id[0] } diff --git a/charts/consul/test/terraform/eks/main.tf b/charts/consul/test/terraform/eks/main.tf index 07c58a2705..6301202161 100644 --- a/charts/consul/test/terraform/eks/main.tf +++ b/charts/consul/test/terraform/eks/main.tf @@ -73,7 +73,7 @@ module "eks" { max_capacity = 3 min_capacity = 3 - instance_type = "m5.large" + instance_type = "m5.xlarge" } } diff --git a/charts/consul/test/terraform/gke/main.tf b/charts/consul/test/terraform/gke/main.tf index f8ff19b912..98a063d450 100644 --- a/charts/consul/test/terraform/gke/main.tf +++ b/charts/consul/test/terraform/gke/main.tf @@ -3,7 +3,8 @@ provider "google" { project = var.project - version = "~> 3.49.0" + version = "~> 4.58.0" + zone = var.zone } resource "random_id" "suffix" { @@ -16,6 +17,11 @@ data "google_container_engine_versions" "main" { version_prefix = "1.25." } +# We assume that the subnets are already created to save time. +data "google_compute_subnetwork" "subnet" { + name = var.subnet +} + resource "google_container_cluster" "cluster" { provider = "google" count = var.cluster_count @@ -28,8 +34,9 @@ resource "google_container_cluster" "cluster" { node_version = data.google_container_engine_versions.main.latest_master_version node_config { tags = ["consul-k8s-${random_id.suffix[count.index].dec}"] - machine_type = "e2-standard-4" + machine_type = "e2-standard-8" } + subnetwork = data.google_compute_subnetwork.subnet.name resource_labels = var.labels } diff --git a/charts/consul/test/terraform/gke/variables.tf b/charts/consul/test/terraform/gke/variables.tf index 1eebe64145..f33952850a 100644 --- a/charts/consul/test/terraform/gke/variables.tf +++ b/charts/consul/test/terraform/gke/variables.tf @@ -37,3 +37,9 @@ variable "labels" { default = {} description = "Labels to attach to the created resources." } + +variable "subnet" { + type = string + default = "default" + description = "Subnet to create the cluster in. Currently all clusters use the default subnet and we are running out of IPs" +} diff --git a/control-plane/subcommand/connect-init/command_test.go b/control-plane/subcommand/connect-init/command_test.go index 69abc8f1ad..cb1d32d03b 100644 --- a/control-plane/subcommand/connect-init/command_test.go +++ b/control-plane/subcommand/connect-init/command_test.go @@ -615,7 +615,7 @@ func TestRun_Gateways_Errors(t *testing.T) { "-pod-name", testPodName, "-pod-namespace", testPodNamespace, "-proxy-id-file", proxyFile, - "-consul-api-timeout", "5s", + "-consul-api-timeout", "10s", "-consul-node-name", nodeName, } @@ -729,7 +729,7 @@ func TestRun_InvalidProxyFile(t *testing.T) { "-http-port", strconv.Itoa(serverCfg.Ports.HTTP), "-grpc-port", strconv.Itoa(serverCfg.Ports.GRPC), "-proxy-id-file", randFileName, - "-consul-api-timeout", "5s", + "-consul-api-timeout", "10s", } code := cmd.Run(flags) require.Equal(t, 1, code) diff --git a/control-plane/subcommand/consul-logout/command_test.go b/control-plane/subcommand/consul-logout/command_test.go index e7e3a00f38..1a0744996a 100644 --- a/control-plane/subcommand/consul-logout/command_test.go +++ b/control-plane/subcommand/consul-logout/command_test.go @@ -54,7 +54,7 @@ func TestRun_InvalidSinkFile(t *testing.T) { } code := cmd.Run([]string{ "-token-file", randFileName, - "-consul-api-timeout", "5s", + "-consul-api-timeout", "10s", }) require.Equal(t, 1, code) } @@ -107,7 +107,7 @@ func Test_UnableToLogoutDueToInvalidToken(t *testing.T) { code := cmd.Run([]string{ "-http-addr", fmt.Sprintf("%s://%s", cfg.Scheme, cfg.Address), "-token-file", tokenFile, - "-consul-api-timeout", "5s", + "-consul-api-timeout", "10s", }) require.Equal(t, 1, code, ui.ErrorWriter.String()) require.Contains(t, "Unexpected response code: 403 (ACL not found)", ui.ErrorWriter.String()) @@ -172,7 +172,7 @@ func Test_RunUsingLogin(t *testing.T) { code := cmd.Run([]string{ "-http-addr", fmt.Sprintf("%s://%s", cfg.Scheme, cfg.Address), "-token-file", tokenFile, - "-consul-api-timeout", "5s", + "-consul-api-timeout", "10s", }) require.Equal(t, 0, code, ui.ErrorWriter.String()) diff --git a/control-plane/subcommand/create-federation-secret/command_test.go b/control-plane/subcommand/create-federation-secret/command_test.go index a90ff692e2..c401f1fc7e 100644 --- a/control-plane/subcommand/create-federation-secret/command_test.go +++ b/control-plane/subcommand/create-federation-secret/command_test.go @@ -80,7 +80,7 @@ func TestRun_FlagValidation(t *testing.T) { "-server-ca-key-file=file", "-ca-file", f.Name(), "-mesh-gateway-service-name=name", - "-consul-api-timeout=5s", + "-consul-api-timeout=10s", "-log-level=invalid", }, expErr: "unknown log level: invalid", @@ -117,7 +117,7 @@ func TestRun_CAFileMissing(t *testing.T) { "-server-ca-cert-file", f.Name(), "-server-ca-key-file", f.Name(), "-ca-file=/this/does/not/exist", - "-consul-api-timeout", "5s", + "-consul-api-timeout", "10s", }) require.Equal(t, 1, exitCode, ui.ErrorWriter.String()) require.Contains(t, ui.ErrorWriter.String(), "error reading CA file") @@ -140,7 +140,7 @@ func TestRun_ServerCACertFileMissing(t *testing.T) { "-ca-file", f.Name(), "-server-ca-cert-file=/this/does/not/exist", "-server-ca-key-file", f.Name(), - "-consul-api-timeout", "5s", + "-consul-api-timeout", "10s", }) require.Equal(t, 1, exitCode, ui.ErrorWriter.String()) require.Contains(t, ui.ErrorWriter.String(), "Error reading server CA cert file") @@ -163,7 +163,7 @@ func TestRun_ServerCAKeyFileMissing(t *testing.T) { "-ca-file", f.Name(), "-server-ca-cert-file", f.Name(), "-server-ca-key-file=/this/does/not/exist", - "-consul-api-timeout", "5s", + "-consul-api-timeout", "10s", }) require.Equal(t, 1, exitCode, ui.ErrorWriter.String()) require.Contains(t, ui.ErrorWriter.String(), "Error reading server CA key file") @@ -187,7 +187,7 @@ func TestRun_GossipEncryptionKeyFileMissing(t *testing.T) { "-server-ca-cert-file", f.Name(), "-server-ca-key-file", f.Name(), "-gossip-key-file=/this/does/not/exist", - "-consul-api-timeout", "5s", + "-consul-api-timeout", "10s", }) require.Equal(t, 1, exitCode, ui.ErrorWriter.String()) require.Contains(t, ui.ErrorWriter.String(), "Error reading gossip encryption key file") @@ -211,7 +211,7 @@ func TestRun_GossipEncryptionKeyFileEmpty(t *testing.T) { "-server-ca-cert-file", f.Name(), "-server-ca-key-file", f.Name(), "-gossip-key-file", f.Name(), - "-consul-api-timeout", "5s", + "-consul-api-timeout", "10s", }) require.Equal(t, 1, exitCode, ui.ErrorWriter.String()) require.Contains(t, ui.ErrorWriter.String(), fmt.Sprintf("gossip key file %q was empty", f.Name())) @@ -249,7 +249,7 @@ func TestRun_ReplicationTokenMissingExpectedKey(t *testing.T) { "-server-ca-cert-file", f.Name(), "-server-ca-key-file", f.Name(), "-export-replication-token", - "-consul-api-timeout", "5s", + "-consul-api-timeout", "10s", }) require.Equal(t, 1, exitCode, ui.ErrorWriter.String()) } @@ -448,7 +448,7 @@ func TestRun_ACLs_K8SNamespaces_ResourcePrefixes(tt *testing.T) { "-server-ca-cert-file", caFile, "-server-ca-key-file", keyFile, "-http-addr", fmt.Sprintf("https://%s", a.HTTPSAddr), - "-consul-api-timeout", "5s", + "-consul-api-timeout", "10s", } if c.aclsEnabled { flags = append(flags, "-export-replication-token") @@ -555,7 +555,7 @@ func TestRun_WaitsForMeshGatewayInstances(t *testing.T) { "-server-ca-cert-file", certFile, "-server-ca-key-file", keyFile, "-http-addr", fmt.Sprintf("https://%s", a.HTTPSAddr), - "-consul-api-timeout", "5s", + "-consul-api-timeout", "10s", }) require.Equal(t, 0, exitCode, ui.ErrorWriter.String()) @@ -609,7 +609,7 @@ func TestRun_MeshGatewayNoWANAddr(t *testing.T) { "-server-ca-cert-file", caFile, "-server-ca-key-file", keyFile, "-http-addr", fmt.Sprintf("https://%s", a.HTTPSAddr), - "-consul-api-timeout", "5s", + "-consul-api-timeout", "10s", }) require.Equal(t, 1, exitCode, ui.ErrorWriter.String()) } @@ -695,7 +695,7 @@ func TestRun_MeshGatewayUniqueAddrs(tt *testing.T) { "-server-ca-cert-file", caFile, "-server-ca-key-file", keyFile, "-http-addr", fmt.Sprintf("https://%s", a.HTTPSAddr), - "-consul-api-timeout", "5s", + "-consul-api-timeout", "10s", }) require.Equal(t, 0, exitCode, ui.ErrorWriter.String()) @@ -838,7 +838,7 @@ func TestRun_ReplicationSecretDelay(t *testing.T) { "-server-ca-key-file", keyFile, "-http-addr", fmt.Sprintf("https://%s", a.HTTPSAddr), "-export-replication-token", - "-consul-api-timeout", "5s", + "-consul-api-timeout", "10s", } exitCode := cmd.Run(flags) require.Equal(t, 0, exitCode, ui.ErrorWriter.String()) @@ -908,7 +908,7 @@ func TestRun_UpdatesSecret(t *testing.T) { "-server-ca-cert-file", certFile, "-server-ca-key-file", keyFile, "-http-addr", fmt.Sprintf("https://%s", a.HTTPSAddr), - "-consul-api-timeout", "5s", + "-consul-api-timeout", "10s", }) require.Equal(t, 0, exitCode, ui.ErrorWriter.String()) @@ -950,7 +950,7 @@ func TestRun_UpdatesSecret(t *testing.T) { "-server-ca-cert-file", caFile, "-server-ca-key-file", keyFile, "-http-addr", fmt.Sprintf("https://%s", a.HTTPSAddr), - "-consul-api-timeout", "5s", + "-consul-api-timeout", "10s", }) require.Equal(t, 0, exitCode, ui.ErrorWriter.String()) @@ -1044,7 +1044,7 @@ func TestRun_ConsulClientDelay(t *testing.T) { "-server-ca-cert-file", caFile, "-server-ca-key-file", keyFile, "-http-addr", fmt.Sprintf("https://127.0.0.1:%d", randomPorts[2]), - "-consul-api-timeout", "5s", + "-consul-api-timeout", "10s", } exitCode := cmd.Run(flags) require.Equal(t, 0, exitCode, ui.ErrorWriter.String()) @@ -1112,7 +1112,7 @@ func TestRun_Autoencrypt(t *testing.T) { "-server-ca-cert-file", keyFile, "-server-ca-key-file", keyFile, "-http-addr", fmt.Sprintf("https://%s", a.HTTPSAddr), - "-consul-api-timeout", "5s", + "-consul-api-timeout", "10s", }) require.Equal(t, 0, exitCode, ui.ErrorWriter.String()) diff --git a/control-plane/subcommand/get-consul-client-ca/command_test.go b/control-plane/subcommand/get-consul-client-ca/command_test.go index 466e11a11e..95ff4dbfbb 100644 --- a/control-plane/subcommand/get-consul-client-ca/command_test.go +++ b/control-plane/subcommand/get-consul-client-ca/command_test.go @@ -51,7 +51,7 @@ func TestRun_FlagsValidation(t *testing.T) { flags: []string{ "-output-file=output.pem", "-server-addr=foo.com", - "-consul-api-timeout=5s", + "-consul-api-timeout=10s", "-log-level=invalid-log-level", }, expErr: "unknown log level: invalid-log-level", @@ -106,7 +106,7 @@ func TestRun(t *testing.T) { "-server-port", strings.Split(a.HTTPSAddr, ":")[1], "-ca-file", caFile, "-output-file", outputFile.Name(), - "-consul-api-timeout", "5s", + "-consul-api-timeout", "10s", }) require.Equal(t, 0, exitCode, ui.ErrorWriter.String()) @@ -189,7 +189,7 @@ func TestRun_ConsulServerAvailableLater(t *testing.T) { "-server-port", fmt.Sprintf("%d", randomPorts[2]), "-ca-file", caFile, "-output-file", outputFile.Name(), - "-consul-api-timeout", "5s", + "-consul-api-timeout", "10s", }) require.Equal(t, 0, exitCode, ui.ErrorWriter) @@ -280,7 +280,7 @@ func TestRun_GetsOnlyActiveRoot(t *testing.T) { "-server-port", strings.Split(a.HTTPSAddr, ":")[1], "-ca-file", caFile, "-output-file", outputFile.Name(), - "-consul-api-timeout", "5s", + "-consul-api-timeout", "10s", }) require.Equal(t, 0, exitCode) @@ -348,7 +348,7 @@ func TestRun_WithProvider(t *testing.T) { "-server-port", strings.Split(a.HTTPSAddr, ":")[1], "-output-file", outputFile.Name(), "-ca-file", caFile, - "-consul-api-timeout", "5s", + "-consul-api-timeout", "10s", }) require.Equal(t, 0, exitCode, ui.ErrorWriter.String()) From 3b2d5e870c64345e99ec394e584470bcdbedd79c Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Fri, 14 Apr 2023 10:25:24 -0400 Subject: [PATCH 134/340] Remove CircleCI (#2050) --- .circleci/config.yml | 1381 ------------------------------------------ 1 file changed, 1381 deletions(-) delete mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index cda1e54ba8..0000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,1381 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -# Originally from consul-k8s -version: 2.1 -orbs: - slack: circleci/slack@3.4.2 -# reusable 'executor' object for jobs -executors: - go: - docker: - - image: docker.mirror.hashicorp.services/cimg/go:1.20.1 - environment: - TEST_RESULTS: /tmp/test-results # path to where test results are saved - -slack-channel: &slack-channel C0421KHNAV9 #feed-consul-k8s-ci channel ID -control-plane-path: &control-plane-path control-plane -cli-path: &cli-path cli -acceptance-mod-path: &acceptance-mod-path acceptance -acceptance-test-path: &acceptance-test-path acceptance/tests -acceptance-framework-path: &acceptance-framework-path acceptance/framework -helm-gen-path: &helm-gen-path hack/helm-reference-gen -gke-terraform-path: &gke-terraform-path charts/consul/test/terraform/gke -eks-terraform-path: &eks-terraform-path charts/consul/test/terraform/eks -aks-terraform-path: &aks-terraform-path charts/consul/test/terraform/aks -openshift-terraform-path: &openshift-terraform-path charts/consul/test/terraform/openshift -# This image is built from test/docker/Test.dockerfile -consul-helm-test-image: &consul-helm-test-image docker.mirror.hashicorp.services/hashicorpdev/consul-helm-test:0.15.0 -consul-test-image: &consul-test-image hashicorppreview/consul-enterprise:1.16-dev - -######################## -# COMMANDS -######################## -# Commands define a sequence of steps as a map to be executed and reused in jobs -commands: - install-prereqs: - steps: - - run: - name: Install go, gotestsum, kind, kubectl, and helm - command: | - wget https://golang.org/dl/go1.20.1.linux-amd64.tar.gz - sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.20.1.linux-amd64.tar.gz - rm go1.20.1.linux-amd64.tar.gz - echo 'export PATH=$PATH:/usr/local/go/bin' >> $BASH_ENV - - wget https://github.com/gotestyourself/gotestsum/releases/download/v1.8.2/gotestsum_1.8.2_linux_amd64.tar.gz - sudo tar -C /usr/local/bin -xzf gotestsum_1.8.2_linux_amd64.tar.gz - rm gotestsum_1.8.2_linux_amd64.tar.gz - - curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.17.0/kind-linux-amd64 - chmod +x ./kind - sudo mv ./kind /usr/local/bin/kind - - curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl" - chmod +x ./kubectl - sudo mv ./kubectl /usr/local/bin/kubectl - - wget https://get.helm.sh/helm-v3.9.4-linux-amd64.tar.gz - tar -zxvf helm-v3.9.4-linux-amd64.tar.gz - sudo mv linux-amd64/helm /usr/local/bin/helm - custom-checkout: - description: | - custom-checkout will perform a custom checkout procedure if provided with an alternative git reference, - otherwise it will run the CircleCI defined checkout command. This is needed as the CircleCI defined checkout - command does not support subsequent git actions after being called. - parameters: - git-ref: - type: string - default: "" - steps: - - when: - condition: << parameters.git-ref >> - steps: - - run: - name: Checkout code - command: | - ssh-keyscan github.com >> ~/.ssh/known_hosts - if [ -e '/home/circleci/project/.git' ] ; then - echo 'Fetching into existing repository' - existing_repo='true' - cd '/home/circleci/project' - git remote set-url origin "$CIRCLE_REPOSITORY_URL" || true - else - echo 'Cloning git repository' - existing_repo='false' - mkdir -p '/home/circleci/project' - cd '/home/circleci/project' - git clone --no-checkout "$CIRCLE_REPOSITORY_URL" . - fi - - if [ "$existing_repo" = 'true' ] || [ 'false' = 'true' ]; then - echo 'Fetching from remote repository' - git fetch --force origin - git fetch --force --tags origin - fi - - echo 'Checking out branch/tag' - git checkout --force "<< parameters.git-ref >>" - - unless: - condition: << parameters.git-ref >> - steps: - - checkout - create-kind-clusters: - parameters: - version: - type: string - steps: - - run: - name: Create kind clusters - command: | - kind create cluster --name dc1 --image kindest/node:<< parameters.version >> - kind create cluster --name dc2 --image kindest/node:<< parameters.version >> - create-kind-cni-clusters: - parameters: - version: - type: string - steps: - - run: - name: Create CNI kind clusters - command: | - kind create cluster --config=acceptance/framework/environment/cni-kind/kind.config --name dc1 --image kindest/node:<< parameters.version >> - make kind-cni-calico - kind create cluster --config=acceptance/framework/environment/cni-kind/kind.config --name dc2 --image kindest/node:<< parameters.version >> - make kind-cni-calico - build-cli: - steps: - - run: - name: Build consul-k8s CLI - working_directory: *cli-path - command: | - go build -o ./bin/consul-k8s - sudo cp ./bin/consul-k8s /usr/local/go/bin/ - consul-k8s version - - run-acceptance-tests: - description: | - Runs the Kind acceptance tests using a provided consul-k8s image, or else attempts to use the image referenced by the - branch name and git reference of the current git commit - parameters: - failfast: - type: boolean - default: false - additional-flags: - type: string - consul-k8s-image: - type: string - default: "docker.mirror.hashicorp.services/hashicorpdev/consul-k8s-control-plane:$(git rev-parse --short HEAD)" - go-path: - type: string - default: "/home/circleci/.go_workspace" - steps: - - when: - condition: << parameters.failfast >> - steps: - - run: - name: Run acceptance tests - working_directory: *acceptance-test-path - no_output_timeout: 2h - command: | - # Enterprise tests can't run on fork PRs because they require - # a secret. - if [ -z "$CIRCLE_PR_NUMBER" ]; then - ENABLE_ENTERPRISE=true - fi - - # We have to run the tests for each package separately so that we can - # exit early if any test fails (-failfast only works within a single - # package). - exit_code=0 - pkgs=$(go list ./... | circleci tests split --split-by=timings --timings-type=classname) - echo "Running $(echo $pkgs | wc -w) packages:" - echo $pkgs - for pkg in $pkgs - do - if ! gotestsum --format=testname --no-summary=all --jsonfile=jsonfile-${pkg////-} -- $pkg -p 1 -timeout 2h -failfast \ - << parameters.additional-flags >> \ - -enable-multi-cluster \ - ${ENABLE_ENTERPRISE:+-enable-enterprise} \ - -debug-directory="$TEST_RESULTS/debug" \ - -consul-k8s-image=<< parameters.consul-k8s-image >> - then - echo "Tests in ${pkg} failed, aborting early" - exit_code=1 - break - fi - done - gotestsum --format=testname --raw-command --junitfile "$TEST_RESULTS/gotestsum-report.xml" -- cat jsonfile* - exit $exit_code - - - unless: - condition: << parameters.failfast >> - steps: - - run: - name: Run acceptance tests - working_directory: *acceptance-test-path - no_output_timeout: 2h - command: | - # Enterprise tests can't run on fork PRs because they require - # a secret. - if [ -z "$CIRCLE_PR_NUMBER" ]; then - ENABLE_ENTERPRISE=true - fi - - pkgs=$(go list ./... | circleci tests split --split-by=timings --timings-type=classname) - echo "Running $pkgs" - gotestsum --format testname --junitfile "$TEST_RESULTS/gotestsum-report.xml" -- $pkgs -p 1 -timeout 2h -failfast \ - << parameters.additional-flags >> \ - ${ENABLE_ENTERPRISE:+-enable-enterprise} \ - -enable-multi-cluster \ - -debug-directory="$TEST_RESULTS/debug" \ - -consul-k8s-image=<< parameters.consul-k8s-image >> - -######################## -# JOBS -######################## -# Jobs are a collection of steps. These are used in the workflows to define -# what gets run in the pipeline -jobs: - go-fmt-and-vet-control-plane: - executor: go - steps: - - checkout - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-k8s-modcache-v2-{{ checksum "control-plane/go.mod" }} - - - run: - name: go mod download - working_directory: *control-plane-path - command: go mod download - - # Save go module cache if the go.mod file has changed - - save_cache: - key: consul-k8s-modcache-v2-{{ checksum "control-plane/go.mod" }} - paths: - - "/home/circleci/go/pkg/mod" - - # check go fmt output because it does not report non-zero when there are fmt changes - - run: - name: check go fmt - working_directory: *control-plane-path - command: | - files=$(go fmt ./...) - if [ -n "$files" ]; then - echo "The following file(s) do not conform to go fmt:" - echo "$files" - exit 1 - fi - - run: cd control-plane && go vet ./... - - lint-control-plane: - executor: go - steps: - - checkout - - run: go get -u github.com/hashicorp/lint-consul-retry && lint-consul-retry - - run: - name: run lint - working_directory: *control-plane-path - command: go run hack/lint-api-new-client/main.go - - test-control-plane: - executor: go - environment: - TEST_RESULTS: /tmp/test-results - parallelism: 1 - steps: - - checkout - - run: mkdir -p $TEST_RESULTS - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-k8s-modcache-v2-{{ checksum "control-plane/go.mod" }} - - # run go tests with gotestsum - - run: - name: run go tests - working_directory: *control-plane-path - command: | - # download and install the consul binary - wget https://releases.hashicorp.com/consul/"${CONSUL_VERSION}"/consul_"${CONSUL_VERSION}"_linux_amd64.zip && \ - unzip consul_"${CONSUL_VERSION}"_linux_amd64.zip -d /home/circleci/bin && - rm consul_"${CONSUL_VERSION}"_linux_amd64.zip - PACKAGE_NAMES=$(go list ./...) - gotestsum --format testname --junitfile $TEST_RESULTS/gotestsum-report.xml -- -p 4 $PACKAGE_NAMES - - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - test-enterprise-control-plane: - executor: go - environment: - TEST_RESULTS: /tmp/test-results - parallelism: 1 - steps: - - checkout - - run: mkdir -p $TEST_RESULTS - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-k8s-modcache-v2-{{ checksum "control-plane/go.mod" }} - - # run go tests with gotestsum - - run: - name: run enterprise go tests - working_directory: *control-plane-path - command: | - # download and install the consul binary - wget https://releases.hashicorp.com/consul/"${CONSUL_ENT_VERSION}"/consul_"${CONSUL_ENT_VERSION}"_linux_amd64.zip && \ - unzip consul_"${CONSUL_ENT_VERSION}"_linux_amd64.zip -d /home/circleci/bin && - rm consul_"${CONSUL_ENT_VERSION}"_linux_amd64.zip - PACKAGE_NAMES=$(go list ./...) - gotestsum --format testname --junitfile $TEST_RESULTS/gotestsum-report.xml -- -tags=enterprise -p 4 $PACKAGE_NAMES - - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - build-distro: # defines a parameterized job - description: A job that will build the os/arch distro set by XC_OS and XC_ARCH - parameters: - OS: - description: What OSes to build - default: "" - type: string - ARCH: - description: What architectures to build - default: "" - type: string - executor: go - environment: - GOXPARALLEL: 2 # CircleCI containers are 2 CPU x 4GB RAM - steps: - - checkout - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-k8s-modcache-v2-{{ checksum "control-plane/go.mod" }} - - run: - name: build local - working_directory: *control-plane-path - command: XC_OS="<< parameters.OS >>" XC_ARCH="<< parameters.ARCH >>" ./build-support/scripts/build-local.sh - # persist to downstream job - - persist_to_workspace: - root: . - paths: - - control-plane/pkg/bin - - control-plane/cni/pkg/bin - # save dev build to CircleCI - - store_artifacts: - path: ./control-plane/pkg/bin - - store_artifacts: - path: ./control-plane/cni/pkg/bin - - # upload dev docker image - dev-upload-docker: - machine: - image: ubuntu-2004:202010-01 - environment: - DOCKER_CLI_EXPERIMENTAL: enabled - steps: - - checkout - # get consul-k8s binary - - attach_workspace: - at: . - - run: sudo apt-get update - - run: sudo apt-get install -y qemu-user-static - - run: docker buildx create --use - - run: - name: make ci.dev-docker - working_directory: *control-plane-path - command: make ci.dev-docker - - unit-cli: - executor: go - steps: - - checkout - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-k8s-cli-modcache-v2-{{ checksum "cli/go.mod" }} - - - run: - name: go mod download - working_directory: *cli-path - command: go mod download - - # Save go module cache if the go.mod file has changed - - save_cache: - key: consul-k8s-cli-modcache-v2-{{ checksum "cli/go.mod" }} - paths: - - "/home/circleci/go/pkg/mod" - - - run: mkdir -p $TEST_RESULTS - - - run: - name: Run tests - working_directory: *cli-path - command: | - gotestsum --format testname --junitfile $TEST_RESULTS/gotestsum-report.xml ./... -- -p 4 - - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - go-fmt-and-vet-acceptance: - executor: go - steps: - - checkout - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-helm-acceptance-modcache-v2-{{ checksum "acceptance/go.mod" }} - - - run: - name: go mod download - working_directory: *acceptance-mod-path - command: go mod download - - # Save go module cache if the go.mod file has changed - - save_cache: - key: consul-helm-acceptance-modcache-v2-{{ checksum "acceptance/go.mod" }} - paths: - - "/home/circleci/go/pkg/mod" - - # check go fmt output because it does not report non-zero when there are fmt changes - - run: - name: check go fmt - working_directory: *acceptance-mod-path - command: | - files=$(go fmt ./...) - if [ -n "$files" ]; then - echo "The following file(s) do not conform to go fmt:" - echo "$files" - exit 1 - fi - - - run: - name: go vet - working_directory: *acceptance-mod-path - command: go vet ./... - - go-fmt-and-vet-helm-gen: - executor: go - steps: - - checkout - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-helm-helm-gen-modcache-v2-{{ checksum "charts/consul/hack/helm-reference-gen/go.mod" }} - - - run: - name: go mod download - working_directory: *helm-gen-path - command: go mod download - - # Save go module cache if the go.mod file has changed - - save_cache: - key: consul-helm-helm-gen-modcache-v2-{{ checksum "charts/consul/hack/helm-reference-gen/go.mod" }} - paths: - - "/home/circleci/go/pkg/mod" - - # check go fmt output because it does not report non-zero when there are fmt changes - - run: - name: check go fmt - working_directory: *helm-gen-path - command: | - files=$(go fmt ./...) - if [ -n "$files" ]; then - echo "The following file(s) do not conform to go fmt:" - echo "$files" - exit 1 - fi - - - run: - name: go vet - working_directory: *helm-gen-path - command: go vet ./... - - unit-acceptance-framework: - executor: go - steps: - - checkout - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-helm-acceptance-modcache-v2-{{ checksum "acceptance/go.mod" }} - - - run: mkdir -p $TEST_RESULTS - - - run: - name: Run tests - working_directory: *acceptance-framework-path - command: | - gotestsum --format testname --junitfile $TEST_RESULTS/gotestsum-report.xml ./... -- -p 4 - - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - unit-helm-gen: - executor: go - steps: - - checkout - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-helm-helm-gen-modcache-v2-{{ checksum "charts/consul/hack/helm-reference-gen/go.mod" }} - - - run: mkdir -p $TEST_RESULTS - - - run: - name: Run tests - working_directory: *helm-gen-path - command: | - gotestsum --format testname --junitfile $TEST_RESULTS/gotestsum-report.xml ./... -- -p 4 - - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - validate-helm-gen: - executor: go - steps: - - checkout - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-helm-helm-gen-modcache-v2-{{ checksum "charts/consul/hack/helm-reference-gen/go.mod" }} - - - run: mkdir -p $TEST_RESULTS - - - run: - name: Validate helm gen - working_directory: *helm-gen-path - command: | - go run ./... -validate - - unit-test-helm-templates: - docker: - - image: *consul-helm-test-image - - steps: - - checkout - - - run: - name: Run Unit Tests - working_directory: charts/consul - command: bats --jobs 4 ./test/unit - - ########################### - # KIND ACCEPTANCE TEST JOBS - ########################### - acceptance: - environment: - - TEST_RESULTS: /tmp/test-results - - CONSUL_TEST_IMAGE: *consul-test-image - machine: - image: ubuntu-2004:202010-01 - resource_class: xlarge - parallelism: 6 - steps: - - checkout - - install-prereqs - - create-kind-clusters: - version: "v1.26.0" - - restore_cache: - keys: - - consul-helm-modcache-v2-{{ checksum "acceptance/go.mod" }} - - run: - name: go mod download - working_directory: *acceptance-mod-path - command: go mod download - - save_cache: - key: consul-helm-modcache-v2-{{ checksum "acceptance/go.mod" }} - paths: - - ~/.go_workspace/pkg/mod - - build-cli - - run: mkdir -p $TEST_RESULTS - - run-acceptance-tests: - failfast: true - additional-flags: -use-kind -kubecontext="kind-dc1" -secondary-kubecontext="kind-dc2" -consul-image=$CONSUL_TEST_IMAGE - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - acceptance-tproxy: - environment: - - TEST_RESULTS: /tmp/test-results - - CONSUL_TEST_IMAGE: *consul-test-image - machine: - image: ubuntu-2004:202010-01 - resource_class: xlarge - parallelism: 6 - steps: - - checkout - - install-prereqs - - create-kind-clusters: - version: "v1.26.0" - - restore_cache: - keys: - - consul-helm-modcache-v2-{{ checksum "acceptance/go.mod" }} - - run: - name: go mod download - working_directory: *acceptance-mod-path - command: go mod download - - save_cache: - key: consul-helm-modcache-v2-{{ checksum "acceptance/go.mod" }} - paths: - - ~/.go_workspace/pkg/mod - - build-cli - - run: mkdir -p $TEST_RESULTS - - run-acceptance-tests: - failfast: true - additional-flags: -use-kind -kubecontext="kind-dc1" -secondary-kubecontext="kind-dc2" -enable-transparent-proxy -consul-image=$CONSUL_TEST_IMAGE - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - acceptance-tproxy-cni: - environment: - - TEST_RESULTS: /tmp/test-results - - CONSUL_TEST_IMAGE: *consul-test-image - machine: - image: ubuntu-2004:202010-01 - resource_class: xlarge - parallelism: 3 - steps: - - checkout - - install-prereqs - - create-kind-cni-clusters: - version: "v1.25.3" - - restore_cache: - keys: - - consul-helm-modcache-v2-{{ checksum "acceptance/go.mod" }} - - run: - name: go mod download - working_directory: *acceptance-mod-path - command: go mod download - - save_cache: - key: consul-helm-modcache-v2-{{ checksum "acceptance/go.mod" }} - paths: - - ~/.go_workspace/pkg/mod - - build-cli - - run: mkdir -p $TEST_RESULTS - - run-acceptance-tests: - failfast: true - additional-flags: -use-kind -kubecontext="kind-dc1" -secondary-kubecontext="kind-dc2" -enable-transparent-proxy -enable-cni -consul-image=$CONSUL_TEST_IMAGE - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - ############################## - # CLEANUP CLOUD RESOURCES JOBS - ############################## - cleanup-gcp-resources: - docker: - - image: *consul-helm-test-image - steps: - - run: - name: cleanup leftover resources - command: | - echo "${GOOGLE_CREDENTIALS}" | gcloud auth activate-service-account --key-file=- - clusters=$(gcloud container clusters list --zone us-central1-a --project ${CLOUDSDK_CORE_PROJECT} --format json | jq -r '.[] | select(.name | test("^consul-k8s-\\d+$")) | .name') - for cluster in $clusters; do - echo "Deleting $cluster GKE cluster" - gcloud container clusters delete $cluster --zone us-central1-a --project ${CLOUDSDK_CORE_PROJECT} --quiet - done - - slack/status: - channel: *slack-channel - fail_only: true - failure_message: "GKE cleanup failed" - - cleanup-azure-resources: - docker: - - image: *consul-helm-test-image - steps: - - run: - name: cleanup leftover resources - command: | - az login --service-principal -u "$ARM_CLIENT_ID" -p "$ARM_CLIENT_SECRET" --tenant "$ARM_TENANT_ID" > /dev/null - resource_groups=$(az group list -o json | jq -r '.[] | select(.name | test("^consul-k8s-\\d+$")) | .name') - for group in $resource_groups; do - echo "Deleting $group resource group" - az group delete -n $group --yes - done - - slack/status: - channel: *slack-channel - fail_only: true - failure_message: "AKS cleanup failed" - - cleanup-eks-resources: - docker: - - image: *consul-helm-test-image - steps: - - checkout - - run: - name: cleanup eks resources - command: | - # Assume the role and set environment variables. - aws sts assume-role --role-arn "$AWS_ROLE_ARN" --role-session-name "consul-helm-$CIRCLE_BUILD_NUM" --duration-seconds 10800 > assume-role.json - export AWS_ACCESS_KEY_ID="$(jq -r .Credentials.AccessKeyId assume-role.json)" - export AWS_SECRET_ACCESS_KEY="$(jq -r .Credentials.SecretAccessKey assume-role.json)" - export AWS_SESSION_TOKEN="$(jq -r .Credentials.SessionToken assume-role.json)" - - make ci.aws-acceptance-test-cleanup - - slack/status: - channel: *slack-channel - fail_only: true - failure_message: "EKS cleanup failed" - - ############################# - # CLOUD ACCEPTANCE TEST JOBS - ############################# - acceptance-gke-1-25: - parallelism: 2 - environment: - - TEST_RESULTS: /tmp/test-results - - USE_GKE_GCLOUD_AUTH_PLUGIN: true - - CONSUL_TEST_IMAGE: *consul-test-image - docker: - - image: *consul-helm-test-image - - steps: - - run: - name: Exit if forked PR - command: | - if [ -n "$CIRCLE_PR_NUMBER" ]; then - echo "Skipping acceptance tests for forked PRs; marking step successful." - circleci step halt - fi - - - checkout - - - build-cli - - run: - name: terraform init & apply - working_directory: *gke-terraform-path - command: | - terraform init - echo "${GOOGLE_CREDENTIALS}" | gcloud auth activate-service-account --key-file=- - - # On GKE, we're setting the build number instead of build URL because label values - # cannot contain '/'. - terraform apply \ - -var project=${CLOUDSDK_CORE_PROJECT} \ - -var init_cli=true \ - -var cluster_count=2 \ - -var labels="{\"build_number\": \"$CIRCLE_BUILD_NUM\"}" \ - -auto-approve - - primary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[0]) - secondary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[1]) - - echo "export primary_kubeconfig=$primary_kubeconfig" >> $BASH_ENV - echo "export secondary_kubeconfig=$secondary_kubeconfig" >> $BASH_ENV - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-helm-acceptance-modcache-v2-{{ checksum "acceptance/go.mod" }} - - - run: mkdir -p $TEST_RESULTS - - - run-acceptance-tests: - additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -consul-image=$CONSUL_TEST_IMAGE - - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - - run: - name: terraform destroy - working_directory: *gke-terraform-path - command: | - terraform destroy -var project=${CLOUDSDK_CORE_PROJECT} -auto-approve - when: always - - - slack/status: - channel: *slack-channel - fail_only: true - failure_message: "GKE acceptance tests failed. Check the logs at: ${CIRCLE_BUILD_URL}" - - acceptance-gke-cni-1-25: - parallelism: 2 - environment: - - TEST_RESULTS: /tmp/test-results - - USE_GKE_GCLOUD_AUTH_PLUGIN: true - - CONSUL_TEST_IMAGE: *consul-test-image - docker: - - image: *consul-helm-test-image - - steps: - - run: - name: Exit if forked PR - command: | - if [ -n "$CIRCLE_PR_NUMBER" ]; then - echo "Skipping acceptance tests for forked PRs; marking step successful." - circleci step halt - fi - - - checkout - - - build-cli - - run: - name: terraform init & apply - working_directory: *gke-terraform-path - command: | - terraform init - echo "${GOOGLE_CREDENTIALS}" | gcloud auth activate-service-account --key-file=- - - # On GKE, we're setting the build number instead of build URL because label values - # cannot contain '/'. - terraform apply \ - -var project=${CLOUDSDK_CORE_PROJECT} \ - -var init_cli=true \ - -var cluster_count=2 \ - -var labels="{\"build_number\": \"$CIRCLE_BUILD_NUM\"}" \ - -auto-approve - - primary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[0]) - secondary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[1]) - - echo "export primary_kubeconfig=$primary_kubeconfig" >> $BASH_ENV - echo "export secondary_kubeconfig=$secondary_kubeconfig" >> $BASH_ENV - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-helm-acceptance-modcache-v2-{{ checksum "acceptance/go.mod" }} - - - run: mkdir -p $TEST_RESULTS - - - run-acceptance-tests: - additional-flags: -use-gke -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -enable-cni -consul-image=$CONSUL_TEST_IMAGE - - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - - run: - name: terraform destroy - working_directory: *gke-terraform-path - command: | - terraform destroy -var project=${CLOUDSDK_CORE_PROJECT} -auto-approve - when: always - - - slack/status: - channel: *slack-channel - fail_only: true - failure_message: "GKE CNI acceptance tests failed. Check the logs at: ${CIRCLE_BUILD_URL}" - - acceptance-aks-1-24: - parallelism: 3 - environment: - - TEST_RESULTS: /tmp/test-results - - CONSUL_TEST_IMAGE: *consul-test-image - docker: - - image: *consul-helm-test-image - - steps: - - checkout - - - build-cli - - run: - name: terraform init & apply - working_directory: *aks-terraform-path - command: | - terraform init - - terraform apply \ - -var client_id="$ARM_CLIENT_ID" \ - -var client_secret="$ARM_CLIENT_SECRET" \ - -var cluster_count=2 \ - -var tags="{\"build_url\": \"$CIRCLE_BUILD_URL\"}" \ - -auto-approve - - primary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[0]) - secondary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[1]) - - echo "export primary_kubeconfig=$primary_kubeconfig" >> $BASH_ENV - echo "export secondary_kubeconfig=$secondary_kubeconfig" >> $BASH_ENV - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-helm-acceptance-modcache-v2-{{ checksum "acceptance/go.mod" }} - - - run: mkdir -p $TEST_RESULTS - - - run-acceptance-tests: - additional-flags: -use-aks -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -consul-image=$CONSUL_TEST_IMAGE - - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - - run: - name: terraform destroy - working_directory: *aks-terraform-path - command: | - terraform destroy -auto-approve - when: always - - - slack/status: - channel: *slack-channel - fail_only: true - failure_message: "AKS acceptance tests failed. Check the logs at: ${CIRCLE_BUILD_URL}" - - acceptance-aks-cni-1-24: - parallelism: 3 - environment: - - TEST_RESULTS: /tmp/test-results - - CONSUL_TEST_IMAGE: *consul-test-image - docker: - - image: *consul-helm-test-image - - steps: - - checkout - - - build-cli - - run: - name: terraform init & apply - working_directory: *aks-terraform-path - command: | - terraform init - - terraform apply \ - -var client_id="$ARM_CLIENT_ID" \ - -var client_secret="$ARM_CLIENT_SECRET" \ - -var cluster_count=2 \ - -var tags="{\"build_url\": \"$CIRCLE_BUILD_URL\"}" \ - -auto-approve - - primary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[0]) - secondary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[1]) - - echo "export primary_kubeconfig=$primary_kubeconfig" >> $BASH_ENV - echo "export secondary_kubeconfig=$secondary_kubeconfig" >> $BASH_ENV - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-helm-acceptance-modcache-v2-{{ checksum "acceptance/go.mod" }} - - - run: mkdir -p $TEST_RESULTS - - - run-acceptance-tests: - additional-flags: -use-aks -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -enable-cni -consul-image=$CONSUL_TEST_IMAGE - - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - - run: - name: terraform destroy - working_directory: *aks-terraform-path - command: | - terraform destroy -auto-approve - when: always - - slack/status: - channel: *slack-channel - fail_only: true - failure_message: "AKS CNI acceptance tests failed. Check the logs at: ${CIRCLE_BUILD_URL}" - - acceptance-eks-1-23: - parallelism: 3 - environment: - - TEST_RESULTS: /tmp/test-results - - CONSUL_TEST_IMAGE: *consul-test-image - docker: - - image: *consul-helm-test-image - - steps: - - checkout - - - build-cli - - run: - name: configure aws - command: | - aws configure --profile helm_user set aws_access_key_id "$AWS_ACCESS_KEY_ID" - aws configure --profile helm_user set aws_secret_access_key "$AWS_SECRET_ACCESS_KEY" - aws configure set role_arn "$AWS_ROLE_ARN" - aws configure set source_profile helm_user - - echo "unset AWS_ACCESS_KEY_ID" >> $BASH_ENV - echo "unset AWS_SECRET_ACCESS_KEY" >> $BASH_ENV - - - run: - name: terraform init & apply - working_directory: *eks-terraform-path - command: | - terraform init - - terraform apply -var cluster_count=2 -var tags="{\"build_url\": \"$CIRCLE_BUILD_URL\"}" -auto-approve - - primary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[0]) - secondary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[1]) - - echo "export primary_kubeconfig=$primary_kubeconfig" >> $BASH_ENV - echo "export secondary_kubeconfig=$secondary_kubeconfig" >> $BASH_ENV - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-helm-acceptance-modcache-v2-{{ checksum "acceptance/go.mod" }} - - - run: mkdir -p $TEST_RESULTS - - - run-acceptance-tests: - additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -consul-image=$CONSUL_TEST_IMAGE - - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - - run: - name: terraform destroy - working_directory: *eks-terraform-path - command: | - terraform destroy -var cluster_count=2 -auto-approve - when: always - - - slack/status: - channel: *slack-channel - fail_only: true - failure_message: "EKS acceptance tests failed. Check the logs at: ${CIRCLE_BUILD_URL}" - - acceptance-eks-cni-1-23: - parallelism: 3 - environment: - - TEST_RESULTS: /tmp/test-results - - CONSUL_TEST_IMAGE: *consul-test-image - docker: - - image: *consul-helm-test-image - - steps: - - checkout - - - build-cli - - run: - name: configure aws - command: | - aws configure --profile helm_user set aws_access_key_id "$AWS_ACCESS_KEY_ID" - aws configure --profile helm_user set aws_secret_access_key "$AWS_SECRET_ACCESS_KEY" - aws configure set role_arn "$AWS_ROLE_ARN" - aws configure set source_profile helm_user - - echo "unset AWS_ACCESS_KEY_ID" >> $BASH_ENV - echo "unset AWS_SECRET_ACCESS_KEY" >> $BASH_ENV - - - run: - name: terraform init & apply - working_directory: *eks-terraform-path - command: | - terraform init - - terraform apply -var cluster_count=2 -var tags="{\"build_url\": \"$CIRCLE_BUILD_URL\"}" -auto-approve - - primary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[0]) - secondary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[1]) - - echo "export primary_kubeconfig=$primary_kubeconfig" >> $BASH_ENV - echo "export secondary_kubeconfig=$secondary_kubeconfig" >> $BASH_ENV - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-helm-acceptance-modcache-v2-{{ checksum "acceptance/go.mod" }} - - - run: mkdir -p $TEST_RESULTS - - - run-acceptance-tests: - additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -enable-cni -consul-image=$CONSUL_TEST_IMAGE - - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - - run: - name: terraform destroy - working_directory: *eks-terraform-path - command: | - terraform destroy -var cluster_count=2 -auto-approve - when: always - - - slack/status: - channel: *slack-channel - fail_only: true - failure_message: "EKS CNI acceptance tests failed. Check the logs at: ${CIRCLE_BUILD_URL}" - - acceptance-openshift: - environment: - TEST_RESULTS: /tmp/test-results - CONSUL_TEST_IMAGE: *consul-test-image - parallelism: 1 - docker: - - image: *consul-helm-test-image - - steps: - - checkout - - build-cli - - run: - name: terraform init & apply - working_directory: *openshift-terraform-path - command: | - terraform init - az login --service-principal -u "$ARM_CLIENT_ID" -p "$ARM_CLIENT_SECRET" --tenant "$ARM_TENANT_ID" > /dev/null - terraform apply \ - -var cluster_count=2 \ - -var tags="{\"build_url\": \"$CIRCLE_BUILD_URL\"}" \ - -auto-approve - - primary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[0]) - secondary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[1]) - - echo "export primary_kubeconfig=$primary_kubeconfig" >> $BASH_ENV - echo "export secondary_kubeconfig=$secondary_kubeconfig" >> $BASH_ENV - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-helm-acceptance-modcache-v2-{{ checksum "acceptance/go.mod" }} - - - run: mkdir -p $TEST_RESULTS - - - run-acceptance-tests: - additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-openshift -enable-transparent-proxy -consul-image=$CONSUL_TEST_IMAGE - - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - - run: - name: terraform destroy - working_directory: *openshift-terraform-path - command: | - terraform destroy -auto-approve - when: always - - - slack/status: - channel: *slack-channel - fail_only: true - failure_message: "OpenShift acceptance tests failed. Check the logs at: ${CIRCLE_BUILD_URL}" - - acceptance-kind-1-23-consul-compat-nightly-1-12: - environment: - - TEST_RESULTS: /tmp/test-results - - CONSUL_IMAGE: "docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.12-dev" - - ENVOY_IMAGE: "envoyproxy/envoy:v1.22.2" - - HELM_CHART_VERSION: "0.49.0" - - CONSUL_K8S_IMAGE: "docker.mirror.hashicorp.services/hashicorp/consul-k8s-control-plane:0.49.0" - machine: - image: ubuntu-2004:202010-01 - resource_class: xlarge - steps: - - custom-checkout: - git-ref: "v$HELM_CHART_VERSION" - - install-prereqs - - create-kind-clusters: - version: "v1.23.0" - - restore_cache: - keys: - - consul-helm-modcache-v2-{{ checksum "acceptance/go.mod" }} - - run: - name: go mod download - working_directory: *acceptance-mod-path - command: go mod download - - save_cache: - key: consul-helm-modcache-v2-{{ checksum "acceptance/go.mod" }} - paths: - - ~/.go_workspace/pkg/mod - - build-cli - - run: mkdir -p $TEST_RESULTS - - run-acceptance-tests: - consul-k8s-image: $CONSUL_K8S_IMAGE - additional-flags: -use-kind -kubecontext="kind-dc1" -secondary-kubecontext="kind-dc2" -consul-image=$CONSUL_IMAGE -consul-version="1.12" -envoy-image=$ENVOY_IMAGE -helm-chart-version=$HELM_CHART_VERSION -enable-transparent-proxy - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - slack/status: - channel: *slack-channel - fail_only: true - failure_message: "Acceptance tests against Kind with Kubernetes v1.25 with Consul 1.12 nightly failed. Check the logs at: ${CIRCLE_BUILD_URL}" - - acceptance-kind-1-23-consul-compat-nightly-1-13: - environment: - - TEST_RESULTS: /tmp/test-results - - CONSUL_IMAGE: "docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.13-dev" - - ENVOY_IMAGE: "envoyproxy/envoy:v1.23.1" - - CONSUL_K8S_IMAGE: "docker.mirror.hashicorp.services/hashicorp/consul-k8s-control-plane:0.49.0" - - HELM_CHART_VERSION: "0.49.0" - machine: - image: ubuntu-2004:202010-01 - resource_class: xlarge - steps: - - custom-checkout: - git-ref: "v$HELM_CHART_VERSION" - - install-prereqs - - create-kind-clusters: - version: "v1.23.0" - - restore_cache: - keys: - - consul-helm-modcache-v2-{{ checksum "acceptance/go.mod" }} - - run: - name: go mod download - working_directory: *acceptance-mod-path - command: go mod download - - save_cache: - key: consul-helm-modcache-v2-{{ checksum "acceptance/go.mod" }} - paths: - - ~/.go_workspace/pkg/mod - - build-cli - - run: mkdir -p $TEST_RESULTS - - run-acceptance-tests: - consul-k8s-image: $CONSUL_K8S_IMAGE - additional-flags: -use-kind -kubecontext="kind-dc1" -secondary-kubecontext="kind-dc2" -consul-image=$CONSUL_IMAGE -consul-version="1.13" -envoy-image=$ENVOY_IMAGE -helm-chart-version=$HELM_CHART_VERSION -enable-transparent-proxy - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - slack/status: - channel: *slack-channel - fail_only: true - failure_message: "Acceptance tests against Kind with Kubernetes v1.25 with Consul 1.13 nightly failed. Check the logs at: ${CIRCLE_BUILD_URL}" - -######################## -# WORKFLOWS -######################## -# Workflows are a set of rules for defining a collection of jobs and their run order. -# This is where the pipeline tests and builds are constructed and triggers for running these tests -# are defined -workflows: - version: 2 - test-and-build: - jobs: - # Build this one control-plane binary so that acceptance and acceptance-tproxy will run - # The rest of these CircleCI jobs have been migrated to GitHub Actions. We need to wait until - # there is support for a larger pool of runners before the acceptance tests can - # be moved - # Run acceptance tests using the docker image built for the control plane for this particular - # branch - # This is run on every PR - - build-distro: - OS: "linux" - ARCH: "amd64 arm64" - name: build-distros-linux - - dev-upload-docker: - context: consul-ci - requires: - - build-distros-linux - - acceptance: - context: consul-ci - requires: - - dev-upload-docker - - acceptance-tproxy-cni: - context: consul-ci - requires: - - dev-upload-docker - - acceptance-tproxy: - context: consul-ci - requires: - - dev-upload-docker - - - nightly-cleanup: - triggers: - - schedule: - cron: "0 12 * * *" # Run at 12 pm UTC (5 am PST) - filters: - branches: - only: - - main - jobs: - - cleanup-gcp-resources - - cleanup-azure-resources - - cleanup-eks-resources - - nightly-acceptance-tests-release: - description: | - Tests which run on a release branch nightly. These exist separate from the main - acceptance tests so that they can run at their own cadence, but - contains the same sequence of jobs. - triggers: - - schedule: - cron: "0 0 * * *" # Run at 12 am UTC (5 pm PST) - filters: - branches: - only: - - release/0.49.x - - release/1.0.x - - release/1.1.x - jobs: - - build-distro: - OS: "linux" - ARCH: "amd64 arm64" - name: build-distros-linux - - dev-upload-docker: - requires: - - build-distros-linux - # Disable until we can use UBI images. - # - acceptance-openshift - - acceptance-gke-1-25: - requires: - - dev-upload-docker - - acceptance-gke-cni-1-25: - requires: - - acceptance-gke-1-25 - - acceptance-tproxy: - requires: - - dev-upload-docker - - nightly-acceptance-tests-main: - description: | - Tests which run on the main branch nightly. These exist separate from the release - acceptance tests so that they can run at their own cadence, but - contains the same sequence of jobs. - triggers: - - schedule: - cron: "0 0 * * *" # Run at 12 am UTC (5 pm PST) - filters: - branches: - only: - - main - jobs: - - build-distro: - OS: "linux" - ARCH: "amd64 arm64" - name: build-distros-linux - - dev-upload-docker: - requires: - - build-distros-linux - # Disable until we can use UBI images. - # - acceptance-openshift - - acceptance-gke-1-25: - requires: - - dev-upload-docker - - acceptance-gke-cni-1-25: - requires: - - acceptance-gke-1-25 - - acceptance-eks-1-23: - requires: - - dev-upload-docker - - acceptance-eks-cni-1-23: - requires: - - acceptance-eks-1-23 - - acceptance-aks-1-24: - requires: - - dev-upload-docker - - acceptance-aks-cni-1-24: - requires: - - acceptance-aks-1-24 - - acceptance-tproxy: - requires: - - dev-upload-docker From 5817c28423f2ddc404454443f1ecb4685c7b92a0 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Fri, 14 Apr 2023 13:45:59 -0400 Subject: [PATCH 135/340] Update status on PRs (#2054) * Update status on PRs * Split pr and push into 2 different files so that context can be passed through --- .github/workflows/merge.yml | 31 +++++++++++++++++++++++++++++++ .github/workflows/pr.yml | 14 ++++---------- 2 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/merge.yml diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml new file mode 100644 index 0000000000..be1b392f4a --- /dev/null +++ b/.github/workflows/merge.yml @@ -0,0 +1,31 @@ +# Dispatch to the consul-k8s-workflows when a PR is created and on merges to main/release* +name: merge +on: + push: + # Sequence of patterns matched against refs/heads + branches: + # Push events on main branch + - main + # Push events to branches matching refs/heads/release/** + - "release/**" + +# these should be the only settings that you will ever need to change +env: + CONSUL_IMAGE: hashicorppreview/consul-enterprise:1.16-dev # Consul's enterprise version to use in tests. We use this consul image on release branches too + BRANCH: ${{ github.head_ref || github.ref_name }} + CONTEXT: "merge" + SHA: ${{ github.event.pull_request.head.sha || github.sha }} + +jobs: + test: + name: test + runs-on: ubuntu-latest + steps: + - uses: benc-uk/workflow-dispatch@v1.2.2 + name: test + with: + workflow: test.yml + repo: hashicorp/consul-k8s-workflows + ref: main + token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} + inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ env.SHA }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}", "consul-image":"${{ env.CONSUL_IMAGE }}" }' diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index d006c2514c..32baf472fb 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -2,19 +2,13 @@ name: pr on: pull_request: - push: - # Sequence of patterns matched against refs/heads - branches: - # Push events on main branch - - main - # Push events to branches matching refs/heads/release/** - - "release/**" # these should be the only settings that you will ever need to change env: CONSUL_IMAGE: hashicorppreview/consul-enterprise:1.16-dev # Consul's enterprise version to use in tests. We use this consul image on release branches too - BRANCH: ${{ github.head_ref || github.ref_name }} - CONTEXT: "${{ github.actor }}" + BRANCH: ${{ github.head_ref || github.ref_name }} + CONTEXT: "pr" + SHA: ${{ github.event.pull_request.head.sha || github.sha }} jobs: test: @@ -28,4 +22,4 @@ jobs: repo: hashicorp/consul-k8s-workflows ref: main token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} - inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}", "consul-image":"${{ env.CONSUL_IMAGE }}" }' + inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ env.SHA }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}", "consul-image":"${{ env.CONSUL_IMAGE }}" }' From 8d2c1931f838f0f1b09fed894926a897713e8f8e Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Mon, 17 Apr 2023 14:53:10 -0400 Subject: [PATCH 136/340] Update backport assistant to support -gh-automerge (#2047) --- .github/workflows/backport.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 0283034a66..ad9b77b0d1 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -13,10 +13,10 @@ jobs: backport: if: github.event.pull_request.merged runs-on: ubuntu-latest - container: hashicorpdev/backport-assistant:0.2.5 + container: hashicorpdev/backport-assistant:0.3.0 steps: - name: Run Backport Assistant - run: backport-assistant backport -automerge + run: backport-assistant backport -merge-method=squash -gh-automerge env: BACKPORT_LABEL_REGEXP: "backport/(?P\\d+\\.\\d+\\.x)" BACKPORT_TARGET_TEMPLATE: "release/{{.target}}" From 34fb4a2a1e2047cf254174e048126e75e75c1a34 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Mon, 17 Apr 2023 20:09:52 -0400 Subject: [PATCH 137/340] Add a cleanup cron job (#2059) * Add a cleanup cron job --- .github/workflows/nightly-cleanup.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/nightly-cleanup.yml diff --git a/.github/workflows/nightly-cleanup.yml b/.github/workflows/nightly-cleanup.yml new file mode 100644 index 0000000000..79360e497d --- /dev/null +++ b/.github/workflows/nightly-cleanup.yml @@ -0,0 +1,27 @@ +# Dispatch to the consul-k8s-workflows with a nightly cron +name: nightly-cleanup +on: + schedule: + # * is a special character in YAML so you have to quote this string + # Run nightly at 12PM UTC/8AM EST/5AM PST + - cron: '0 12 * * *' + +# these should be the only settings that you will ever need to change +env: + CONSUL_IMAGE: "not used" + BRANCH: ${{ github.ref_name }} + CONTEXT: "nightly" + +jobs: + cleanup: + name: cleanup + runs-on: ubuntu-latest + steps: + - uses: benc-uk/workflow-dispatch@v1.2.2 + name: cleanup + with: + workflow: cleanup.yml + repo: hashicorp/consul-k8s-workflows + ref: main + token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} + inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}", "consul-image":"${{ env.CONSUL_IMAGE }}" }' From 11f842cbe94b5df544c03e2441a1820397f92b9b Mon Sep 17 00:00:00 2001 From: malizz Date: Tue, 18 Apr 2023 16:12:58 -0700 Subject: [PATCH 138/340] add sameness group CRD (#2048) * draft of adding sameness group CRD * move sameness group tests to ent test file * update tests * fix lint issues * generate yaml and update helm charts * update field descriptions and validation and its test * remove unwanted files, add license comments back * rename samenessgroups to samenessgroup * fix resource names * update failing unit test --- CONTRIBUTING.md | 10 +- .../templates/connect-inject-clusterrole.yaml | 2 + ...t-inject-mutatingwebhookconfiguration.yaml | 21 + .../consul/templates/crd-proxydefaults.yaml | 14 +- .../consul/templates/crd-samenessgroups.yaml | 126 ++++++ .../templates/crd-serviceresolvers.yaml | 9 +- control-plane/PROJECT | 13 + control-plane/api/common/common.go | 1 + .../api/v1alpha1/samenessgroup_types.go | 262 ++++++++++++ .../api/v1alpha1/samenessgroup_types_test.go | 390 ++++++++++++++++++ .../api/v1alpha1/samenessgroup_webhook.go | 61 +++ .../v1alpha1/servicedefaults_types_test.go | 2 +- control-plane/api/v1alpha1/shared_types.go | 8 +- .../api/v1alpha1/zz_generated.deepcopy.go | 124 ++++++ .../consul.hashicorp.com_proxydefaults.yaml | 12 +- .../consul.hashicorp.com_samenessgroups.yaml | 121 ++++++ ...consul.hashicorp.com_serviceresolvers.yaml | 9 +- control-plane/config/rbac/role.yaml | 23 +- control-plane/config/webhook/manifests.yaml | 24 +- .../configentry_controller.go | 2 +- .../configentry_controller_ent_test.go | 382 +++++++++++++++-- .../configentry_controller_test.go | 2 +- .../exportedservices_controller.go | 2 +- .../exportedservices_controller_ent_test.go | 20 +- .../ingressgateway_controller.go | 2 +- .../mesh_controller.go | 2 +- .../proxydefaults_controller.go | 2 +- .../controllers/samenessgroups_controller.go | 41 ++ .../servicedefaults_controller.go | 2 +- .../serviceintentions_controller.go | 2 +- .../serviceresolver_controller.go | 2 +- .../servicerouter_controller.go | 2 +- .../servicesplitter_controller.go | 2 +- .../terminatinggateway_controller.go | 2 +- control-plane/go.mod | 4 + control-plane/main.go | 3 +- .../subcommand/inject-connect/command.go | 39 +- 37 files changed, 1640 insertions(+), 105 deletions(-) create mode 100644 charts/consul/templates/crd-samenessgroups.yaml create mode 100644 control-plane/api/v1alpha1/samenessgroup_types.go create mode 100644 control-plane/api/v1alpha1/samenessgroup_types_test.go create mode 100644 control-plane/api/v1alpha1/samenessgroup_webhook.go create mode 100644 control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml rename control-plane/{controller => controllers}/configentry_controller.go (99%) rename control-plane/{controller => controllers}/configentry_controller_ent_test.go (64%) rename control-plane/{controller => controllers}/configentry_controller_test.go (99%) rename control-plane/{controller => controllers}/exportedservices_controller.go (98%) rename control-plane/{controller => controllers}/exportedservices_controller_ent_test.go (95%) rename control-plane/{controller => controllers}/ingressgateway_controller.go (98%) rename control-plane/{controller => controllers}/mesh_controller.go (98%) rename control-plane/{controller => controllers}/proxydefaults_controller.go (98%) create mode 100644 control-plane/controllers/samenessgroups_controller.go rename control-plane/{controller => controllers}/servicedefaults_controller.go (98%) rename control-plane/{controller => controllers}/serviceintentions_controller.go (98%) rename control-plane/{controller => controllers}/serviceresolver_controller.go (98%) rename control-plane/{controller => controllers}/servicerouter_controller.go (98%) rename control-plane/{controller => controllers}/servicesplitter_controller.go (98%) rename control-plane/{controller => controllers}/terminatinggateway_controller.go (98%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8e634e323e..3745c80bac 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -167,7 +167,7 @@ rebase the branch on main, fixing any conflicts along the way before the code ca ```bash operator-sdk create api --group consul --version v1alpha1 --kind IngressGateway --controller --namespaced=true --make=false --resource=true ``` -1. Re-order the file so it looks like: +1. Re-order the generated ingressgateway_types.go file, so it looks like: ```go func init() { SchemeBuilder.Register(&IngressGateway{}, &IngressGatewayList{}) @@ -320,8 +320,6 @@ rebase the branch on main, fixing any conflicts along the way before the code ca ### Controller 1. Delete the file `control-plane/controllers/suite_test.go`. We don't write suite tests, just unit tests. -1. Move `control-plane/controllers/ingressgateway_controller.go` to `control-plane/controller` directory. -1. Delete the `control-plane/controllers` directory. 1. Rename `Reconciler` to `Controller`, e.g. `IngressGatewayReconciler` => `IngressGatewayController` 1. Use the existing controller files as a guide and make this file match. 1. Add your controller as a case in the tests in `configentry_controller_test.go`: @@ -395,13 +393,13 @@ rebase the branch on main, fixing any conflicts along the way before the code ca ``` ### Updating Helm chart -1. Update `charts/consul/templates/controller-mutatingwebhookconfiguration` with the webhook for this resource +1. Update `charts/consul/templates/connect-inject-mutatingwebhookconfiguration` with the webhook for this resource using the updated `control-plane/config/webhook/manifests.v1beta1.yaml` and replacing `clientConfig.service.name/namespace` with the templated strings shown below to match the other webhooks.: ```yaml - clientConfig: service: - name: {{ template "consul.fullname" . }}-controller-webhook + name: {{ template "consul.fullname" . }}-connect-injector namespace: {{ .Release.Namespace }} path: /mutate-v1alpha1-ingressgateway failurePolicy: Fail @@ -421,7 +419,7 @@ rebase the branch on main, fixing any conflicts along the way before the code ca - ingressgateways sideEffects: None ``` -1. Update `charts/consul/templates/controller-clusterrole.yaml` to allow the controller to +1. Update `charts/consul/templates/connect-inject-clusterrole.yaml` to allow the controller to manage your resource type. ### Testing A New CRD diff --git a/charts/consul/templates/connect-inject-clusterrole.yaml b/charts/consul/templates/connect-inject-clusterrole.yaml index f2e12f0ad9..e383e5ce28 100644 --- a/charts/consul/templates/connect-inject-clusterrole.yaml +++ b/charts/consul/templates/connect-inject-clusterrole.yaml @@ -24,6 +24,7 @@ rules: - serviceintentions - ingressgateways - terminatinggateways + - samenessgroups {{- if .Values.global.peering.enabled }} - peeringacceptors - peeringdialers @@ -49,6 +50,7 @@ rules: - serviceintentions/status - ingressgateways/status - terminatinggateways/status + - samenessgroups/status {{- if .Values.global.peering.enabled }} - peeringacceptors/status - peeringdialers/status diff --git a/charts/consul/templates/connect-inject-mutatingwebhookconfiguration.yaml b/charts/consul/templates/connect-inject-mutatingwebhookconfiguration.yaml index afcfd3800f..b68efdb9f8 100644 --- a/charts/consul/templates/connect-inject-mutatingwebhookconfiguration.yaml +++ b/charts/consul/templates/connect-inject-mutatingwebhookconfiguration.yaml @@ -291,5 +291,26 @@ webhooks: admissionReviewVersions: - "v1beta1" - "v1" +- admissionReviewVersions: + - v1beta1 + - v1 + clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-samenessgroup + failurePolicy: Fail + name: mutate-samenessgroup.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - samenessgroups + sideEffects: None {{- end }} {{- end }} diff --git a/charts/consul/templates/crd-proxydefaults.yaml b/charts/consul/templates/crd-proxydefaults.yaml index aaaa3228a1..6bb1234066 100644 --- a/charts/consul/templates/crd-proxydefaults.yaml +++ b/charts/consul/templates/crd-proxydefaults.yaml @@ -143,16 +143,18 @@ spec: type: object type: array type: object - failoverPolicy: - description: FailoverPolicy specifies the exact mechanism used for failover. + failoverPolicy: + description: FailoverPolicy specifies the exact mechanism used for + failover. properties: mode: - description: Mode specifies the type of failover that will be performed. - Valid values are "sequential", "" (equivalent to "sequential") and "order-by-locality". + description: Mode specifies the type of failover that will be + performed. Valid values are "sequential", "" (equivalent to + "sequential") and "order-by-locality". type: string - regions: + regions: description: The ordered list of the regions of the failover targets. - Valid values can be "us-west-1", "us-west-2", and so on. + Valid values can be "us-west-1", "us-west-2", and so on. items: type: string type: array diff --git a/charts/consul/templates/crd-samenessgroups.yaml b/charts/consul/templates/crd-samenessgroups.yaml new file mode 100644 index 0000000000..e541539481 --- /dev/null +++ b/charts/consul/templates/crd-samenessgroups.yaml @@ -0,0 +1,126 @@ +{{- if .Values.connectInject.enabled }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: samenessgroups.consul.hashicorp.com + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd +spec: + group: consul.hashicorp.com + names: + kind: SamenessGroup + listKind: SamenessGroupList + plural: samenessgroups + shortNames: + - sameness-group + singular: samenessgroup + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: SamenessGroup is the Schema for the samenessgroups API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: SamenessGroupSpec defines the desired state of SamenessGroup. + properties: + defaultForFailover: + description: 'DefaultForFailover indicates that upstream requests to members of the given sameness group will implicitly failover between members of this sameness group.' + type: boolean + includeLocal: + description: 'IncludeLocal is used to include the local partition as the first member of the sameness group.' + type: boolean + members: + description: 'Members are the partitions and peers that are part of the sameness group.' + items: + properties: + partition: + type: string + peer: + type: string + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +{{- end }} diff --git a/charts/consul/templates/crd-serviceresolvers.yaml b/charts/consul/templates/crd-serviceresolvers.yaml index 842506d642..1942b79b8f 100644 --- a/charts/consul/templates/crd-serviceresolvers.yaml +++ b/charts/consul/templates/crd-serviceresolvers.yaml @@ -80,13 +80,14 @@ spec: the current namespace is used. type: string policy: - description: FailoverPolicy specifies the exact mechanism used for failover. + description: Policy specifies the exact mechanism used for failover. properties: mode: - description: Mode specifies the type of failover that will be performed. - Valid values are "sequential", "" (equivalent to "sequential") and "order-by-locality". + description: Mode specifies the type of failover that will + be performed. Valid values are "sequential", "" (equivalent + to "sequential") and "order-by-locality". type: string - regions: + regions: description: The ordered list of the regions of the failover targets. Valid values can be "us-west-1", "us-west-2", and so on. items: diff --git a/control-plane/PROJECT b/control-plane/PROJECT index c11e857849..eb653ad34a 100644 --- a/control-plane/PROJECT +++ b/control-plane/PROJECT @@ -1,3 +1,7 @@ +# Code generated by tool. DO NOT EDIT. +# This file is used to track the info used to scaffold your project +# and allow the plugins properly work. +# More info: https://book.kubebuilder.io/reference/project-config.html domain: hashicorp.com layout: - go.kubebuilder.io/v2 @@ -77,4 +81,13 @@ resources: kind: PeeringDialer path: github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1beta1 + namespaced: true + controller: true + domain: hashicorp.com + group: consul + kind: SamenessGroup + path: github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1 + version: v1alpha1 version: "3" diff --git a/control-plane/api/common/common.go b/control-plane/api/common/common.go index 2c579ba715..4faeccada1 100644 --- a/control-plane/api/common/common.go +++ b/control-plane/api/common/common.go @@ -14,6 +14,7 @@ const ( ExportedServices string = "exportedservices" IngressGateway string = "ingressgateway" TerminatingGateway string = "terminatinggateway" + SamenessGroup string = "samenessgroup" Global string = "global" Mesh string = "mesh" diff --git a/control-plane/api/v1alpha1/samenessgroup_types.go b/control-plane/api/v1alpha1/samenessgroup_types.go new file mode 100644 index 0000000000..7f5f194150 --- /dev/null +++ b/control-plane/api/v1alpha1/samenessgroup_types.go @@ -0,0 +1,262 @@ +package v1alpha1 + +import ( + "encoding/json" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/hashicorp/consul-k8s/control-plane/api/common" + "github.com/hashicorp/consul/api" + capi "github.com/hashicorp/consul/api" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" +) + +const ( + SamenessGroupKubeKind string = "samenessgroup" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +func init() { + SchemeBuilder.Register(&SamenessGroup{}, &SamenessGroupList{}) +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// SamenessGroup is the Schema for the samenessgroups API +// +kubebuilder:printcolumn:name="Synced",type="string",JSONPath=".status.conditions[?(@.type==\"Synced\")].status",description="The sync status of the resource with Consul" +// +kubebuilder:printcolumn:name="Last Synced",type="date",JSONPath=".status.lastSyncedTime",description="The last successful synced time of the resource with Consul" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="The age of the resource" +// +kubebuilder:resource:shortName="sameness-group" +type SamenessGroup struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec SamenessGroupSpec `json:"spec,omitempty"` + Status `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// SamenessGroupList contains a list of SamenessGroup. +type SamenessGroupList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []SamenessGroup `json:"items"` +} + +// SamenessGroupSpec defines the desired state of SamenessGroup. +type SamenessGroupSpec struct { + // DefaultForFailover indicates that upstream requests to members of the given sameness group will implicitly failover between members of this sameness group. + // When DefaultForFailover is true, the local partition must be a member of the sameness group or IncludeLocal must be set to true. + DefaultForFailover bool `json:"defaultForFailover,omitempty"` + // IncludeLocal is used to include the local partition as the first member of the sameness group. + // The local partition can only be a member of a single sameness group. + IncludeLocal bool `json:"includeLocal,omitempty"` + // Members are the partitions and peers that are part of the sameness group. + // If a member of a sameness group does not exist, it will be ignored. + Members []SamenessGroupMember `json:"members,omitempty"` +} + +type SamenessGroupMember struct { + // The partitions and peers that are part of the sameness group. + // A sameness group member cannot define both peer and partition at the same time. + Partition string `json:"partition,omitempty"` + Peer string `json:"peer,omitempty"` +} + +func (in *SamenessGroup) GetObjectMeta() metav1.ObjectMeta { + return in.ObjectMeta +} + +func (in *SamenessGroup) AddFinalizer(name string) { + in.ObjectMeta.Finalizers = append(in.Finalizers(), name) +} + +func (in *SamenessGroup) RemoveFinalizer(name string) { + var newFinalizers []string + for _, oldF := range in.Finalizers() { + if oldF != name { + newFinalizers = append(newFinalizers, oldF) + } + } + in.ObjectMeta.Finalizers = newFinalizers +} + +func (in *SamenessGroup) Finalizers() []string { + return in.ObjectMeta.Finalizers +} + +func (in *SamenessGroup) ConsulKind() string { + return capi.SamenessGroup +} + +func (in *SamenessGroup) ConsulGlobalResource() bool { + return false +} + +func (in *SamenessGroup) ConsulMirroringNS() string { + return common.DefaultConsulNamespace +} + +func (in *SamenessGroup) KubeKind() string { + return SamenessGroupKubeKind +} + +func (in *SamenessGroup) ConsulName() string { + return in.ObjectMeta.Name +} + +func (in *SamenessGroup) KubernetesName() string { + return in.ObjectMeta.Name +} + +func (in *SamenessGroup) SetSyncedCondition(status corev1.ConditionStatus, reason, message string) { + in.Status.Conditions = Conditions{ + { + Type: ConditionSynced, + Status: status, + LastTransitionTime: metav1.Now(), + Reason: reason, + Message: message, + }, + } +} + +func (in *SamenessGroup) SetLastSyncedTime(time *metav1.Time) { + in.Status.LastSyncedTime = time +} + +func (in *SamenessGroup) SyncedCondition() (status corev1.ConditionStatus, reason, message string) { + cond := in.Status.GetCondition(ConditionSynced) + if cond == nil { + return corev1.ConditionUnknown, "", "" + } + return cond.Status, cond.Reason, cond.Message +} + +func (in *SamenessGroup) SyncedConditionStatus() corev1.ConditionStatus { + cond := in.Status.GetCondition(ConditionSynced) + if cond == nil { + return corev1.ConditionUnknown + } + return cond.Status +} + +func (in *SamenessGroup) ToConsul(datacenter string) api.ConfigEntry { + return &capi.SamenessGroupConfigEntry{ + Kind: in.ConsulKind(), + Name: in.ConsulName(), + DefaultForFailover: in.Spec.DefaultForFailover, + IncludeLocal: in.Spec.IncludeLocal, + Members: SamenessGroupMembers(in.Spec.Members).toConsul(), + Meta: meta(datacenter), + } +} + +func (in *SamenessGroup) MatchesConsul(candidate api.ConfigEntry) bool { + configEntry, ok := candidate.(*capi.SamenessGroupConfigEntry) + if !ok { + return false + } + return cmp.Equal(in.ToConsul(""), configEntry, cmpopts.IgnoreFields(capi.SamenessGroupConfigEntry{}, "Partition", "Meta", "ModifyIndex", "CreateIndex"), cmpopts.IgnoreUnexported(), cmpopts.EquateEmpty(), + cmp.Comparer(transparentProxyConfigComparer)) +} + +func (in *SamenessGroup) Validate(consulMeta common.ConsulMeta) error { + var allErrs field.ErrorList + path := field.NewPath("spec") + + if in == nil { + return nil + } + if in.Name == "" { + allErrs = append(allErrs, field.Invalid(path.Child("name"), in.Name, "sameness groups must have a name defined")) + } + + partition := consulMeta.Partition + includesLocal := in.Spec.IncludeLocal + + if in.ObjectMeta.Namespace != "default" && in.ObjectMeta.Namespace != "" { + allErrs = append(allErrs, field.Invalid(path.Child("name"), consulMeta.DestinationNamespace, "sameness groups must reside in the default namespace")) + } + + if len(in.Spec.Members) == 0 { + asJSON, _ := json.Marshal(in.Spec.Members) + allErrs = append(allErrs, field.Invalid(path.Child("members"), string(asJSON), "sameness groups must have at least one member")) + } + + seenMembers := make(map[SamenessGroupMember]struct{}) + for i, m := range in.Spec.Members { + if partition == m.Partition { + includesLocal = true + } + if err := m.validate(path.Child("members").Index(i)); err != nil { + allErrs = append(allErrs, err) + } + if _, ok := seenMembers[m]; ok { + asJSON, _ := json.Marshal(m) + allErrs = append(allErrs, field.Invalid(path.Child("members").Index(i), string(asJSON), "sameness group members must be unique")) + } + seenMembers[m] = struct{}{} + + } + + if !includesLocal { + allErrs = append(allErrs, field.Invalid(path.Child("members"), in.Spec.IncludeLocal, "the local partition must be a member of sameness groups")) + } + + if len(allErrs) > 0 { + return apierrors.NewInvalid( + schema.GroupKind{Group: ConsulHashicorpGroup, Kind: SamenessGroupKubeKind}, + in.KubernetesName(), allErrs) + } + + return nil +} + +// DefaultNamespaceFields has no behaviour here as sameness-groups have no namespace specific fields. +func (in *SamenessGroup) DefaultNamespaceFields(_ common.ConsulMeta) { +} + +type SamenessGroupMembers []SamenessGroupMember + +func (in SamenessGroupMembers) toConsul() []capi.SamenessGroupMember { + if in == nil { + return nil + } + + outMembers := make([]capi.SamenessGroupMember, 0, len(in)) + for _, e := range in { + consulMember := capi.SamenessGroupMember{ + Peer: e.Peer, + Partition: e.Partition, + } + outMembers = append(outMembers, consulMember) + } + return outMembers +} + +func (in *SamenessGroupMember) validate(path *field.Path) *field.Error { + asJSON, _ := json.Marshal(in) + + if in == nil { + return field.Invalid(path, string(asJSON), "sameness group member is nil") + } + if in.isEmpty() { + return field.Invalid(path, string(asJSON), "sameness group members must specify either partition or peer") + } + // We do not allow referencing peer connections in other partitions. + if in.Peer != "" && in.Partition != "" { + return field.Invalid(path, string(asJSON), "sameness group members cannot specify both partition and peer in the same entry") + } + return nil +} + +func (in *SamenessGroupMember) isEmpty() bool { + return in.Peer == "" && in.Partition == "" +} diff --git a/control-plane/api/v1alpha1/samenessgroup_types_test.go b/control-plane/api/v1alpha1/samenessgroup_types_test.go new file mode 100644 index 0000000000..0f461701cc --- /dev/null +++ b/control-plane/api/v1alpha1/samenessgroup_types_test.go @@ -0,0 +1,390 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package v1alpha1 + +import ( + "github.com/hashicorp/consul-k8s/control-plane/api/common" + capi "github.com/hashicorp/consul/api" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "testing" + "time" +) + +func TestSamenessGroups_ToConsul(t *testing.T) { + cases := map[string]struct { + input *SamenessGroup + expected *capi.SamenessGroupConfigEntry + }{ + "empty fields": { + &SamenessGroup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: SamenessGroupSpec{}, + }, + &capi.SamenessGroupConfigEntry{ + Name: "foo", + Kind: capi.SamenessGroup, + Meta: map[string]string{ + common.SourceKey: common.SourceValue, + common.DatacenterKey: "datacenter", + }, + }, + }, + "every field set": { + &SamenessGroup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: SamenessGroupSpec{ + DefaultForFailover: true, + IncludeLocal: true, + Members: []SamenessGroupMember{ + { + Peer: "peer2", + }, + { + Partition: "p2", + }, + }, + }, + }, + &capi.SamenessGroupConfigEntry{ + Name: "foo", + Kind: capi.SamenessGroup, + Meta: map[string]string{ + common.SourceKey: common.SourceValue, + common.DatacenterKey: "datacenter", + }, + DefaultForFailover: true, + IncludeLocal: true, + Members: []capi.SamenessGroupMember{ + { + Peer: "peer2", + }, + { + Partition: "p2", + }, + }, + }, + }, + } + for name, testCase := range cases { + t.Run(name, func(t *testing.T) { + output := testCase.input.ToConsul("datacenter") + require.Equal(t, testCase.expected, output) + }) + } +} + +func TestSamenessGroups_MatchesConsul(t *testing.T) { + cases := map[string]struct { + internal *SamenessGroup + consul capi.ConfigEntry + matches bool + }{ + "empty fields matches": { + &SamenessGroup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-sameness-group", + }, + Spec: SamenessGroupSpec{}, + }, + &capi.SamenessGroupConfigEntry{ + Kind: capi.SamenessGroup, + Name: "my-test-sameness-group", + CreateIndex: 1, + ModifyIndex: 2, + Meta: map[string]string{ + common.SourceKey: common.SourceValue, + common.DatacenterKey: "datacenter", + }, + }, + true, + }, + "all fields populated matches": { + &SamenessGroup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-sameness-group", + }, + Spec: SamenessGroupSpec{ + DefaultForFailover: true, + IncludeLocal: true, + Members: []SamenessGroupMember{ + { + Peer: "peer2", + }, + { + Partition: "p2", + }, + }, + }, + }, + &capi.SamenessGroupConfigEntry{ + Kind: capi.SamenessGroup, + Name: "my-test-sameness-group", + Meta: map[string]string{ + common.SourceKey: common.SourceValue, + common.DatacenterKey: "datacenter", + }, + DefaultForFailover: true, + IncludeLocal: true, + Members: []capi.SamenessGroupMember{ + { + Peer: "peer2", + }, + { + Partition: "p2", + }, + }, + }, + true, + }, + } + + for name, testCase := range cases { + t.Run(name, func(t *testing.T) { + require.Equal(t, testCase.matches, testCase.internal.MatchesConsul(testCase.consul)) + }) + } +} + +func TestSamenessGroups_Validate(t *testing.T) { + cases := map[string]struct { + input *SamenessGroup + partitionsEnabled bool + expectedErrMsg string + }{ + "valid": { + input: &SamenessGroup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-sameness-group", + }, + Spec: SamenessGroupSpec{ + DefaultForFailover: true, + IncludeLocal: true, + Members: []SamenessGroupMember{ + { + Peer: "peer2", + Partition: "", + }, + { + Peer: "", + Partition: "p2", + }, + }, + }, + }, + partitionsEnabled: true, + expectedErrMsg: "", + }, + "invalid - with peer and partition both": { + input: &SamenessGroup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-sameness-group", + }, + Spec: SamenessGroupSpec{ + DefaultForFailover: true, + IncludeLocal: true, + Members: []SamenessGroupMember{ + { + Peer: "peer2", + Partition: "p2", + }, + }, + }, + }, + partitionsEnabled: true, + expectedErrMsg: "sameness group members cannot specify both partition and peer in the same entry", + }, + "invalid - no name": { + input: &SamenessGroup{ + ObjectMeta: metav1.ObjectMeta{}, + Spec: SamenessGroupSpec{ + DefaultForFailover: true, + IncludeLocal: true, + Members: []SamenessGroupMember{ + { + Peer: "peer2", + }, + { + Partition: "p2", + }, + }, + }, + }, + partitionsEnabled: true, + expectedErrMsg: "sameness groups must have a name defined", + }, + "invalid - empty members": { + input: &SamenessGroup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-sameness-group", + }, + Spec: SamenessGroupSpec{ + DefaultForFailover: true, + IncludeLocal: true, + Members: []SamenessGroupMember{}, + }, + }, + partitionsEnabled: true, + expectedErrMsg: "sameness groups must have at least one member", + }, + "invalid - not unique members": { + input: &SamenessGroup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-sameness-group", + }, + Spec: SamenessGroupSpec{ + DefaultForFailover: true, + IncludeLocal: true, + Members: []SamenessGroupMember{ + { + Peer: "peer2", + }, + { + Peer: "peer2", + }, + }, + }, + }, + partitionsEnabled: true, + expectedErrMsg: "sameness group members must be unique", + }, + "invalid - not in default namespace": { + input: &SamenessGroup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-sameness-group", + Namespace: "non-default", + }, + Spec: SamenessGroupSpec{ + DefaultForFailover: true, + IncludeLocal: true, + Members: []SamenessGroupMember{ + { + Peer: "peer2", + }, + }, + }, + }, + partitionsEnabled: true, + expectedErrMsg: "sameness groups must reside in the default namespace", + }, + } + + for name, testCase := range cases { + t.Run(name, func(t *testing.T) { + err := testCase.input.Validate(common.ConsulMeta{}) + if testCase.expectedErrMsg != "" { + require.ErrorContains(t, err, testCase.expectedErrMsg) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestSamenessGroups_GetObjectMeta(t *testing.T) { + meta := metav1.ObjectMeta{ + Name: "name", + } + samenessGroups := &SamenessGroup{ + ObjectMeta: meta, + } + require.Equal(t, meta, samenessGroups.GetObjectMeta()) +} + +func TestSamenessGroups_AddFinalizer(t *testing.T) { + samenessGroups := &SamenessGroup{} + samenessGroups.AddFinalizer("finalizer") + require.Equal(t, []string{"finalizer"}, samenessGroups.ObjectMeta.Finalizers) +} + +func TestSamenessGroups_RemoveFinalizer(t *testing.T) { + samenessGroups := &SamenessGroup{ + ObjectMeta: metav1.ObjectMeta{ + Finalizers: []string{"f1", "f2"}, + }, + } + samenessGroups.RemoveFinalizer("f1") + require.Equal(t, []string{"f2"}, samenessGroups.ObjectMeta.Finalizers) +} + +func TestSamenessGroups_ConsulKind(t *testing.T) { + require.Equal(t, capi.SamenessGroup, (&SamenessGroup{}).ConsulKind()) +} + +func TestSamenessGroups_ConsulGlobalResource(t *testing.T) { + require.False(t, (&SamenessGroup{}).ConsulGlobalResource()) +} + +func TestSamenessGroups_ConsulMirroringNS(t *testing.T) { + +} + +func TestSamenessGroups_KubeKind(t *testing.T) { + require.Equal(t, "samenessgroup", (&SamenessGroup{}).KubeKind()) +} + +func TestSamenessGroups_ConsulName(t *testing.T) { + require.Equal(t, "foo", (&SamenessGroup{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}).ConsulName()) +} + +func TestSamenessGroups_KubernetesName(t *testing.T) { + require.Equal(t, "foo", (&SamenessGroup{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}).KubernetesName()) +} + +func TestSamenessGroups_SetSyncedCondition(t *testing.T) { + samenessGroups := &SamenessGroup{} + samenessGroups.SetSyncedCondition(corev1.ConditionTrue, "reason", "message") + + require.Equal(t, corev1.ConditionTrue, samenessGroups.Status.Conditions[0].Status) + require.Equal(t, "reason", samenessGroups.Status.Conditions[0].Reason) + require.Equal(t, "message", samenessGroups.Status.Conditions[0].Message) + now := metav1.Now() + require.True(t, samenessGroups.Status.Conditions[0].LastTransitionTime.Before(&now)) +} + +func TestSamenessGroups_SetLastSyncedTime(t *testing.T) { + samenessGroups := &SamenessGroup{} + syncedTime := metav1.NewTime(time.Now()) + samenessGroups.SetLastSyncedTime(&syncedTime) + + require.Equal(t, &syncedTime, samenessGroups.Status.LastSyncedTime) +} + +func TestSamenessGroups_GetSyncedConditionStatus(t *testing.T) { + cases := []corev1.ConditionStatus{ + corev1.ConditionUnknown, + corev1.ConditionFalse, + corev1.ConditionTrue, + } + for _, status := range cases { + t.Run(string(status), func(t *testing.T) { + samenessGroups := &SamenessGroup{ + Status: Status{ + Conditions: []Condition{{ + Type: ConditionSynced, + Status: status, + }}, + }, + } + + require.Equal(t, status, samenessGroups.SyncedConditionStatus()) + }) + } +} + +func TestSamenessGroups_SyncedConditionStatusWhenStatusNil(t *testing.T) { + require.Equal(t, corev1.ConditionUnknown, (&SamenessGroup{}).SyncedConditionStatus()) +} + +func TestSamenessGroups_SyncedConditionWhenStatusNil(t *testing.T) { + status, reason, message := (&SamenessGroup{}).SyncedCondition() + require.Equal(t, corev1.ConditionUnknown, status) + require.Equal(t, "", reason) + require.Equal(t, "", message) +} diff --git a/control-plane/api/v1alpha1/samenessgroup_webhook.go b/control-plane/api/v1alpha1/samenessgroup_webhook.go new file mode 100644 index 0000000000..6c1da5cba2 --- /dev/null +++ b/control-plane/api/v1alpha1/samenessgroup_webhook.go @@ -0,0 +1,61 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package v1alpha1 + +import ( + "context" + "net/http" + + "github.com/go-logr/logr" + "github.com/hashicorp/consul-k8s/control-plane/api/common" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// +kubebuilder:object:generate=false + +type SamenessGroupWebhook struct { + Logger logr.Logger + + // ConsulMeta contains metadata specific to the Consul installation. + ConsulMeta common.ConsulMeta + + decoder *admission.Decoder + client.Client +} + +// NOTE: The path value in the below line is the path to the webhook. +// If it is updated, run code-gen, update subcommand/controller/command.go +// and the consul-helm value for the path to the webhook. +// +// NOTE: The below line cannot be combined with any other comment. If it is it will break the code generation. +// +// +kubebuilder:webhook:verbs=create;update,path=/mutate-v1alpha1-samenessgroups,mutating=true,failurePolicy=fail,groups=consul.hashicorp.com,resources=samenessgroups,versions=v1alpha1,name=mutate-samenessgroup.consul.hashicorp.com,sideEffects=None,admissionReviewVersions=v1beta1;v1 + +func (v *SamenessGroupWebhook) Handle(ctx context.Context, req admission.Request) admission.Response { + var resource SamenessGroup + err := v.decoder.Decode(req, &resource) + if err != nil { + return admission.Errored(http.StatusBadRequest, err) + } + + return common.ValidateConfigEntry(ctx, req, v.Logger, v, &resource, v.ConsulMeta) +} + +func (v *SamenessGroupWebhook) List(ctx context.Context) ([]common.ConfigEntryResource, error) { + var resourceList SamenessGroupList + if err := v.Client.List(ctx, &resourceList); err != nil { + return nil, err + } + var entries []common.ConfigEntryResource + for _, item := range resourceList.Items { + entries = append(entries, common.ConfigEntryResource(&item)) + } + return entries, nil +} + +func (v *SamenessGroupWebhook) InjectDecoder(d *admission.Decoder) error { + v.decoder = d + return nil +} diff --git a/control-plane/api/v1alpha1/servicedefaults_types_test.go b/control-plane/api/v1alpha1/servicedefaults_types_test.go index 9f9e7ebbda..69b749decd 100644 --- a/control-plane/api/v1alpha1/servicedefaults_types_test.go +++ b/control-plane/api/v1alpha1/servicedefaults_types_test.go @@ -1296,7 +1296,7 @@ func TestServiceDefaults_ConsulName(t *testing.T) { } func TestServiceDefaults_KubernetesName(t *testing.T) { - require.Equal(t, "foo", (&ServiceDefaults{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}).ConsulName()) + require.Equal(t, "foo", (&ServiceDefaults{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}).KubernetesName()) } func TestServiceDefaults_ConsulNamespace(t *testing.T) { diff --git a/control-plane/api/v1alpha1/shared_types.go b/control-plane/api/v1alpha1/shared_types.go index fdac9a3fea..98eb3f1b20 100644 --- a/control-plane/api/v1alpha1/shared_types.go +++ b/control-plane/api/v1alpha1/shared_types.go @@ -248,9 +248,11 @@ func (in EnvoyExtension) validate(path *field.Path) *field.Error { // FailoverPolicy specifies the exact mechanism used for failover. type FailoverPolicy struct { // Mode specifies the type of failover that will be performed. Valid values are - // "default", "" (equivalent to "default") and "order-by-locality". - Mode string `json:",omitempty"` - Regions []string `json:",omitempty"` + // "sequential", "" (equivalent to "sequential") and "order-by-locality". + Mode string `json:"mode,omitempty"` + // Regions is the ordered list of the regions of the failover targets. + // Valid values can be "us-west-1", "us-west-2", and so on. + Regions []string `json:"regions,omitempty"` } func (in *FailoverPolicy) toConsul() *capi.ServiceResolverFailoverPolicy { diff --git a/control-plane/api/v1alpha1/zz_generated.deepcopy.go b/control-plane/api/v1alpha1/zz_generated.deepcopy.go index d12db29d14..f485b5c5f1 100644 --- a/control-plane/api/v1alpha1/zz_generated.deepcopy.go +++ b/control-plane/api/v1alpha1/zz_generated.deepcopy.go @@ -255,6 +255,26 @@ func (in *ExposePath) DeepCopy() *ExposePath { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FailoverPolicy) DeepCopyInto(out *FailoverPolicy) { + *out = *in + if in.Regions != nil { + in, out := &in.Regions, &out.Regions + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FailoverPolicy. +func (in *FailoverPolicy) DeepCopy() *FailoverPolicy { + if in == nil { + return nil + } + out := new(FailoverPolicy) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GatewayServiceTLSConfig) DeepCopyInto(out *GatewayServiceTLSConfig) { *out = *in @@ -1272,6 +1292,11 @@ func (in *ProxyDefaultsSpec) DeepCopyInto(out *ProxyDefaultsSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.FailoverPolicy != nil { + in, out := &in.FailoverPolicy, &out.FailoverPolicy + *out = new(FailoverPolicy) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyDefaultsSpec. @@ -1299,6 +1324,100 @@ func (in *RingHashConfig) DeepCopy() *RingHashConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SamenessGroupMember) DeepCopyInto(out *SamenessGroupMember) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SamenessGroupMember. +func (in *SamenessGroupMember) DeepCopy() *SamenessGroupMember { + if in == nil { + return nil + } + out := new(SamenessGroupMember) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SamenessGroup) DeepCopyInto(out *SamenessGroup) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SamenessGroups. +func (in *SamenessGroup) DeepCopy() *SamenessGroup { + if in == nil { + return nil + } + out := new(SamenessGroup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *SamenessGroup) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SamenessGroupList) DeepCopyInto(out *SamenessGroupList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]SamenessGroup, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SamenessGroupList. +func (in *SamenessGroupList) DeepCopy() *SamenessGroupList { + if in == nil { + return nil + } + out := new(SamenessGroupList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *SamenessGroupList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SamenessGroupSpec) DeepCopyInto(out *SamenessGroupSpec) { + *out = *in + if in.Members != nil { + in, out := &in.Members, &out.Members + *out = make([]SamenessGroupMember, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SamenessGroupSpec. +func (in *SamenessGroupSpec) DeepCopy() *SamenessGroupSpec { + if in == nil { + return nil + } + out := new(SamenessGroupSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Secret) DeepCopyInto(out *Secret) { *out = *in @@ -1594,6 +1713,11 @@ func (in *ServiceResolverFailover) DeepCopyInto(out *ServiceResolverFailover) { *out = make([]ServiceResolverFailoverTarget, len(*in)) copy(*out, *in) } + if in.Policy != nil { + in, out := &in.Policy, &out.Policy + *out = new(FailoverPolicy) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceResolverFailover. diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml index 0f90e95e16..c66b5fdd0f 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml @@ -139,14 +139,16 @@ spec: type: object type: array type: object - failoverPolicy: - description: FailoverPolicy specifies the exact mechanism used for failover. + failoverPolicy: + description: FailoverPolicy specifies the exact mechanism used for + failover. properties: mode: - description: Mode specifies the type of failover that will be performed. - Valid values are "sequential", "" (equivalent to "sequential") and "order-by-locality". + description: Mode specifies the type of failover that will be + performed. Valid values are "sequential", "" (equivalent to + "sequential") and "order-by-locality". type: string - regions: + regions: description: The ordered list of the regions of the failover targets. Valid values can be "us-west-1", "us-west-2", and so on. items: diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml new file mode 100644 index 0000000000..5efda1ffa2 --- /dev/null +++ b/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml @@ -0,0 +1,121 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: samenessgroups.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: SamenessGroup + listKind: SamenessGroupList + plural: samenessgroups + shortNames: + - sameness-group + singular: samenessgroup + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: SamenessGroup is the Schema for the samenessgroups API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: SamenessGroupSpec defines the desired state of SamenessGroup. + properties: + defaultForFailover: + description: 'DefaultForFailover indicates that upstream requests to members of the given sameness group will implicitly failover between members of this sameness group.' + type: boolean + includeLocal: + description: 'IncludeLocal is used to include the local partition as the first member of the sameness group.' + type: boolean + members: + description: 'Members are the partitions and peers that are part of the sameness group.' + items: + properties: + partition: + type: string + peer: + type: string + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml index a36784cc77..56b5e72014 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml @@ -76,13 +76,14 @@ spec: the current namespace is used. type: string policy: - description: FailoverPolicy specifies the exact mechanism used for failover. + description: Policy specifies the exact mechanism used for failover. properties: mode: - description: Mode specifies the type of failover that will be performed. - Valid values are "sequential", "" (equivalent to "sequential") and "order-by-locality". + description: Mode specifies the type of failover that will + be performed. Valid values are "sequential", "" (equivalent + to "sequential") and "order-by-locality". type: string - regions: + regions: description: The ordered list of the regions of the failover targets. Valid values can be "us-west-1", "us-west-2", and so on. items: diff --git a/control-plane/config/rbac/role.yaml b/control-plane/config/rbac/role.yaml index 245f09568f..562eb5f9f9 100644 --- a/control-plane/config/rbac/role.yaml +++ b/control-plane/config/rbac/role.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -146,6 +143,26 @@ rules: - get - patch - update +- apiGroups: + - consul.hashicorp.com + resources: + - samenessgroups + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - consul.hashicorp.com + resources: + - samenessgroups/status + verbs: + - get + - patch + - update - apiGroups: - consul.hashicorp.com resources: diff --git a/control-plane/config/webhook/manifests.yaml b/control-plane/config/webhook/manifests.yaml index d064b50acb..f96d669544 100644 --- a/control-plane/config/webhook/manifests.yaml +++ b/control-plane/config/webhook/manifests.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - --- apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration @@ -134,6 +131,27 @@ webhooks: resources: - proxydefaults sideEffects: None +- admissionReviewVersions: + - v1beta1 + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-v1alpha1-samenessgroup + failurePolicy: Fail + name: mutate-samenessgroup.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - samenessgroups + sideEffects: None - admissionReviewVersions: - v1beta1 - v1 diff --git a/control-plane/controller/configentry_controller.go b/control-plane/controllers/configentry_controller.go similarity index 99% rename from control-plane/controller/configentry_controller.go rename to control-plane/controllers/configentry_controller.go index 593fb1514f..c2c6da9071 100644 --- a/control-plane/controller/configentry_controller.go +++ b/control-plane/controllers/configentry_controller.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package controller +package controllers import ( "context" diff --git a/control-plane/controller/configentry_controller_ent_test.go b/control-plane/controllers/configentry_controller_ent_test.go similarity index 64% rename from control-plane/controller/configentry_controller_ent_test.go rename to control-plane/controllers/configentry_controller_ent_test.go index cfe9985e56..ef2aa6b7a4 100644 --- a/control-plane/controller/configentry_controller_ent_test.go +++ b/control-plane/controllers/configentry_controller_ent_test.go @@ -3,7 +3,7 @@ //go:build enterprise -package controller_test +package controllers import ( "context" @@ -15,7 +15,7 @@ import ( logrtest "github.com/go-logr/logr/testing" "github.com/hashicorp/consul-k8s/control-plane/api/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul-k8s/control-plane/controller" + "github.com/hashicorp/consul-k8s/control-plane/consul" "github.com/hashicorp/consul-k8s/control-plane/helper/test" capi "github.com/hashicorp/consul/api" "github.com/stretchr/testify/require" @@ -29,11 +29,323 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" ) -// NOTE: We're not testing each controller type here because that's done in +// NOTE: We're not testing each controller type here because that's mostly done in // the OSS tests and it would result in too many permutations. Instead -// we're only testing with the ServiceDefaults and ProxyDefaults controller which will exercise -// all the namespaces code for config entries that are namespaced and those that +// we're only testing with the ServiceDefaults and ProxyDefaults controllers which +// will exercise all the namespaces code for config entries that are namespaced and those that // exist in the global namespace. +// We also test Enterprise only features like SamenessGroups. + +func TestConfigEntryController_createsEntConfigEntry(t *testing.T) { + t.Parallel() + kubeNS := "default" + + cases := []struct { + kubeKind string + consulKind string + consulPrereqs []capi.ConfigEntry + configEntryResource common.ConfigEntryResource + reconciler func(client.Client, *consul.Config, consul.ServerConnectionManager, logr.Logger) testReconciler + compare func(t *testing.T, consul capi.ConfigEntry) + }{ + { + kubeKind: "SamenessGroup", + consulKind: capi.SamenessGroup, + configEntryResource: &v1alpha1.SamenessGroup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: kubeNS, + }, + Spec: v1alpha1.SamenessGroupSpec{ + DefaultForFailover: true, + IncludeLocal: true, + Members: []v1alpha1.SamenessGroupMember{ + { + Peer: "dc1", + Partition: "", + }, + }, + }, + }, + reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) testReconciler { + return &SamenessGroupController{ + Client: client, + Log: logger, + ConfigEntryController: &ConfigEntryController{ + ConsulClientConfig: cfg, + ConsulServerConnMgr: watcher, + DatacenterName: datacenterName, + }, + } + }, + compare: func(t *testing.T, consulEntry capi.ConfigEntry) { + resource, ok := consulEntry.(*capi.SamenessGroupConfigEntry) + require.True(t, ok, "cast error") + require.Equal(t, true, resource.DefaultForFailover) + require.Equal(t, true, resource.IncludeLocal) + require.Equal(t, "dc1", resource.Members[0].Peer) + require.Equal(t, "", resource.Members[0].Partition) + }, + }, + } + + for _, c := range cases { + t.Run(c.kubeKind, func(t *testing.T) { + req := require.New(t) + ctx := context.Background() + + s := runtime.NewScheme() + s.AddKnownTypes(v1alpha1.GroupVersion, c.configEntryResource) + fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(c.configEntryResource).Build() + + testClient := test.TestServerWithMockConnMgrWatcher(t, nil) + testClient.TestServer.WaitForServiceIntentions(t) + consulClient := testClient.APIClient + + for _, configEntry := range c.consulPrereqs { + written, _, err := consulClient.ConfigEntries().Set(configEntry, nil) + req.NoError(err) + req.True(written) + } + + r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.TestLogger{T: t}) + namespacedName := types.NamespacedName{ + Namespace: kubeNS, + Name: c.configEntryResource.KubernetesName(), + } + resp, err := r.Reconcile(ctx, ctrl.Request{ + NamespacedName: namespacedName, + }) + req.NoError(err) + req.False(resp.Requeue) + + cfg, _, err := consulClient.ConfigEntries().Get(c.consulKind, c.configEntryResource.ConsulName(), nil) + req.NoError(err) + req.Equal(c.configEntryResource.ConsulName(), cfg.GetName()) + c.compare(t, cfg) + + // Check that the status is "synced". + err = fakeClient.Get(ctx, namespacedName, c.configEntryResource) + req.NoError(err) + req.Equal(corev1.ConditionTrue, c.configEntryResource.SyncedConditionStatus()) + + // Check that the finalizer is added. + req.Contains(c.configEntryResource.Finalizers(), FinalizerName) + }) + } +} + +func TestConfigEntryController_updatesEntConfigEntry(t *testing.T) { + t.Parallel() + kubeNS := "default" + + cases := []struct { + kubeKind string + consulKind string + consulPrereqs []capi.ConfigEntry + configEntryResource common.ConfigEntryResource + reconciler func(client.Client, *consul.Config, consul.ServerConnectionManager, logr.Logger) testReconciler + updateF func(common.ConfigEntryResource) + compare func(t *testing.T, consul capi.ConfigEntry) + }{ + { + kubeKind: "SamenessGroup", + consulKind: capi.SamenessGroup, + configEntryResource: &v1alpha1.SamenessGroup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: kubeNS, + }, + Spec: v1alpha1.SamenessGroupSpec{ + DefaultForFailover: true, + IncludeLocal: true, + Members: []v1alpha1.SamenessGroupMember{ + { + Peer: "dc1", + Partition: "", + }, + }, + }, + }, + reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) testReconciler { + return &SamenessGroupController{ + Client: client, + Log: logger, + ConfigEntryController: &ConfigEntryController{ + ConsulClientConfig: cfg, + ConsulServerConnMgr: watcher, + DatacenterName: datacenterName, + }, + } + }, + updateF: func(resource common.ConfigEntryResource) { + sg := resource.(*v1alpha1.SamenessGroup) + sg.Spec.IncludeLocal = false + }, + compare: func(t *testing.T, consulEntry capi.ConfigEntry) { + resource, ok := consulEntry.(*capi.SamenessGroupConfigEntry) + require.True(t, ok, "cast error") + require.Equal(t, true, resource.DefaultForFailover) + require.Equal(t, false, resource.IncludeLocal) + require.Equal(t, "dc1", resource.Members[0].Peer) + require.Equal(t, "", resource.Members[0].Partition) + }, + }, + } + + for _, c := range cases { + t.Run(c.kubeKind, func(t *testing.T) { + req := require.New(t) + ctx := context.Background() + + s := runtime.NewScheme() + s.AddKnownTypes(v1alpha1.GroupVersion, c.configEntryResource) + fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(c.configEntryResource).Build() + + testClient := test.TestServerWithMockConnMgrWatcher(t, nil) + testClient.TestServer.WaitForServiceIntentions(t) + consulClient := testClient.APIClient + + // Create any prereqs. + for _, configEntry := range c.consulPrereqs { + written, _, err := consulClient.ConfigEntries().Set(configEntry, nil) + req.NoError(err) + req.True(written) + } + + // We haven't run reconcile yet so we must create the config entry + // in Consul ourselves. + { + written, _, err := consulClient.ConfigEntries().Set(c.configEntryResource.ToConsul(datacenterName), nil) + req.NoError(err) + req.True(written) + } + + // Now run reconcile which should update the entry in Consul. + { + namespacedName := types.NamespacedName{ + Namespace: kubeNS, + Name: c.configEntryResource.KubernetesName(), + } + // First get it so we have the latest revision number. + err := fakeClient.Get(ctx, namespacedName, c.configEntryResource) + req.NoError(err) + + // Update the entry in Kube and run reconcile. + c.updateF(c.configEntryResource) + err = fakeClient.Update(ctx, c.configEntryResource) + req.NoError(err) + r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.TestLogger{T: t}) + resp, err := r.Reconcile(ctx, ctrl.Request{ + NamespacedName: namespacedName, + }) + req.NoError(err) + req.False(resp.Requeue) + + // Now check that the object in Consul is as expected. + cfg, _, err := consulClient.ConfigEntries().Get(c.consulKind, c.configEntryResource.ConsulName(), nil) + req.NoError(err) + req.Equal(c.configEntryResource.ConsulName(), cfg.GetName()) + c.compare(t, cfg) + } + }) + } +} + +func TestConfigEntryController_deletesEntConfigEntry(t *testing.T) { + t.Parallel() + kubeNS := "default" + + cases := []struct { + kubeKind string + consulKind string + consulPrereq []capi.ConfigEntry + configEntryResourceWithDeletion common.ConfigEntryResource + reconciler func(client.Client, *consul.Config, consul.ServerConnectionManager, logr.Logger) testReconciler + }{ + { + kubeKind: "SamenessGroup", + consulKind: capi.SamenessGroup, + configEntryResourceWithDeletion: &v1alpha1.SamenessGroup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: kubeNS, + DeletionTimestamp: &metav1.Time{Time: time.Now()}, + Finalizers: []string{FinalizerName}, + }, + Spec: v1alpha1.SamenessGroupSpec{ + DefaultForFailover: true, + IncludeLocal: true, + Members: []v1alpha1.SamenessGroupMember{ + { + Peer: "dc1", + Partition: "", + }, + }, + }, + }, + reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) testReconciler { + return &SamenessGroupController{ + Client: client, + Log: logger, + ConfigEntryController: &ConfigEntryController{ + ConsulClientConfig: cfg, + ConsulServerConnMgr: watcher, + DatacenterName: datacenterName, + }, + } + }, + }, + } + + for _, c := range cases { + t.Run(c.kubeKind, func(t *testing.T) { + req := require.New(t) + + s := runtime.NewScheme() + s.AddKnownTypes(v1alpha1.GroupVersion, c.configEntryResourceWithDeletion) + fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(c.configEntryResourceWithDeletion).Build() + + testClient := test.TestServerWithMockConnMgrWatcher(t, nil) + testClient.TestServer.WaitForServiceIntentions(t) + consulClient := testClient.APIClient + + // Create any prereqs. + for _, configEntry := range c.consulPrereq { + written, _, err := consulClient.ConfigEntries().Set(configEntry, nil) + req.NoError(err) + req.True(written) + } + + // We haven't run reconcile yet so we must create the config entry + // in Consul ourselves. + { + written, _, err := consulClient.ConfigEntries().Set(c.configEntryResourceWithDeletion.ToConsul(datacenterName), nil) + req.NoError(err) + req.True(written) + } + + // Now run reconcile. It's marked for deletion so this should delete it. + { + namespacedName := types.NamespacedName{ + Namespace: kubeNS, + Name: c.configEntryResourceWithDeletion.KubernetesName(), + } + r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.TestLogger{T: t}) + resp, err := r.Reconcile(context.Background(), ctrl.Request{ + NamespacedName: namespacedName, + }) + req.NoError(err) + req.False(resp.Requeue) + + _, _, err = consulClient.ConfigEntries().Get(c.consulKind, c.configEntryResourceWithDeletion.ConsulName(), nil) + req.EqualError(err, + fmt.Sprintf("Unexpected response code: 404 (Config entry not found for %q / %q)", + c.consulKind, c.configEntryResourceWithDeletion.ConsulName())) + } + }) + } +} func TestConfigEntryController_createsConfigEntry_consulNamespaces(tt *testing.T) { tt.Parallel() @@ -88,7 +400,7 @@ func TestConfigEntryController_createsConfigEntry_consulNamespaces(tt *testing.T ConsulKind string ConsulNamespace string KubeResource common.ConfigEntryResource - GetController func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *controller.ConfigEntryController) reconcile.Reconciler + GetController func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *ConfigEntryController) reconcile.Reconciler AssertValidConfig func(entry capi.ConfigEntry) bool }{ "namespaced": { @@ -102,8 +414,8 @@ func TestConfigEntryController_createsConfigEntry_consulNamespaces(tt *testing.T Protocol: "http", }, }, - GetController: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *controller.ConfigEntryController) reconcile.Reconciler { - return &controller.ServiceDefaultsController{ + GetController: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *ConfigEntryController) reconcile.Reconciler { + return &ServiceDefaultsController{ Client: client, Log: logger, Scheme: scheme, @@ -132,8 +444,8 @@ func TestConfigEntryController_createsConfigEntry_consulNamespaces(tt *testing.T }, }, }, - GetController: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *controller.ConfigEntryController) reconcile.Reconciler { - return &controller.ProxyDefaultsController{ + GetController: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *ConfigEntryController) reconcile.Reconciler { + return &ProxyDefaultsController{ Client: client, Log: logger, Scheme: scheme, @@ -170,8 +482,8 @@ func TestConfigEntryController_createsConfigEntry_consulNamespaces(tt *testing.T }, }, }, - GetController: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *controller.ConfigEntryController) reconcile.Reconciler { - return &controller.ServiceIntentionsController{ + GetController: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *ConfigEntryController) reconcile.Reconciler { + return &ServiceIntentionsController{ Client: client, Log: logger, Scheme: scheme, @@ -206,7 +518,7 @@ func TestConfigEntryController_createsConfigEntry_consulNamespaces(tt *testing.T fakeClient, logrtest.TestLogger{T: t}, s, - &controller.ConfigEntryController{ + &ConfigEntryController{ ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, EnableConsulNamespaces: true, @@ -299,7 +611,7 @@ func TestConfigEntryController_updatesConfigEntry_consulNamespaces(tt *testing.T ConsulKind string ConsulNamespace string KubeResource common.ConfigEntryResource - GetControllerFunc func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *controller.ConfigEntryController) reconcile.Reconciler + GetControllerFunc func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *ConfigEntryController) reconcile.Reconciler AssertValidConfigFunc func(entry capi.ConfigEntry) bool WriteConfigEntryFunc func(consulClient *capi.Client, namespace string) error UpdateResourceFunc func(client client.Client, ctx context.Context, in common.ConfigEntryResource) error @@ -310,15 +622,15 @@ func TestConfigEntryController_updatesConfigEntry_consulNamespaces(tt *testing.T ObjectMeta: metav1.ObjectMeta{ Name: "foo", Namespace: c.SourceKubeNS, - Finalizers: []string{controller.FinalizerName}, + Finalizers: []string{FinalizerName}, }, Spec: v1alpha1.ServiceDefaultsSpec{ Protocol: "http", }, }, ConsulNamespace: c.ExpConsulNS, - GetControllerFunc: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *controller.ConfigEntryController) reconcile.Reconciler { - return &controller.ServiceDefaultsController{ + GetControllerFunc: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *ConfigEntryController) reconcile.Reconciler { + return &ServiceDefaultsController{ Client: client, Log: logger, Scheme: scheme, @@ -352,7 +664,7 @@ func TestConfigEntryController_updatesConfigEntry_consulNamespaces(tt *testing.T ObjectMeta: metav1.ObjectMeta{ Name: common.Global, Namespace: c.SourceKubeNS, - Finalizers: []string{controller.FinalizerName}, + Finalizers: []string{FinalizerName}, }, Spec: v1alpha1.ProxyDefaultsSpec{ MeshGateway: v1alpha1.MeshGateway{ @@ -361,8 +673,8 @@ func TestConfigEntryController_updatesConfigEntry_consulNamespaces(tt *testing.T }, }, ConsulNamespace: common.DefaultConsulNamespace, - GetControllerFunc: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *controller.ConfigEntryController) reconcile.Reconciler { - return &controller.ProxyDefaultsController{ + GetControllerFunc: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *ConfigEntryController) reconcile.Reconciler { + return &ProxyDefaultsController{ Client: client, Log: logger, Scheme: scheme, @@ -398,7 +710,7 @@ func TestConfigEntryController_updatesConfigEntry_consulNamespaces(tt *testing.T ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: c.SourceKubeNS, - Finalizers: []string{controller.FinalizerName}, + Finalizers: []string{FinalizerName}, }, Spec: v1alpha1.ServiceIntentionsSpec{ Destination: v1alpha1.IntentionDestination{ @@ -415,8 +727,8 @@ func TestConfigEntryController_updatesConfigEntry_consulNamespaces(tt *testing.T }, }, ConsulNamespace: c.ExpConsulNS, - GetControllerFunc: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *controller.ConfigEntryController) reconcile.Reconciler { - return &controller.ServiceIntentionsController{ + GetControllerFunc: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *ConfigEntryController) reconcile.Reconciler { + return &ServiceIntentionsController{ Client: client, Log: logger, Scheme: scheme, @@ -468,7 +780,7 @@ func TestConfigEntryController_updatesConfigEntry_consulNamespaces(tt *testing.T fakeClient, logrtest.TestLogger{T: t}, s, - &controller.ConfigEntryController{ + &ConfigEntryController{ ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, EnableConsulNamespaces: true, @@ -577,7 +889,7 @@ func TestConfigEntryController_deletesConfigEntry_consulNamespaces(tt *testing.T ConsulKind string ConsulNamespace string KubeResource common.ConfigEntryResource - GetControllerFunc func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *controller.ConfigEntryController) reconcile.Reconciler + GetControllerFunc func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *ConfigEntryController) reconcile.Reconciler WriteConfigEntryFunc func(consulClient *capi.Client, namespace string) error }{ "namespaced": { @@ -588,7 +900,7 @@ func TestConfigEntryController_deletesConfigEntry_consulNamespaces(tt *testing.T ObjectMeta: metav1.ObjectMeta{ Name: "foo", Namespace: c.SourceKubeNS, - Finalizers: []string{controller.FinalizerName}, + Finalizers: []string{FinalizerName}, DeletionTimestamp: &metav1.Time{Time: time.Now()}, }, Spec: v1alpha1.ServiceDefaultsSpec{ @@ -596,8 +908,8 @@ func TestConfigEntryController_deletesConfigEntry_consulNamespaces(tt *testing.T }, }, ConsulNamespace: c.ExpConsulNS, - GetControllerFunc: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *controller.ConfigEntryController) reconcile.Reconciler { - return &controller.ServiceDefaultsController{ + GetControllerFunc: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *ConfigEntryController) reconcile.Reconciler { + return &ServiceDefaultsController{ Client: client, Log: logger, Scheme: scheme, @@ -621,7 +933,7 @@ func TestConfigEntryController_deletesConfigEntry_consulNamespaces(tt *testing.T ObjectMeta: metav1.ObjectMeta{ Name: common.Global, Namespace: c.SourceKubeNS, - Finalizers: []string{controller.FinalizerName}, + Finalizers: []string{FinalizerName}, DeletionTimestamp: &metav1.Time{Time: time.Now()}, }, Spec: v1alpha1.ProxyDefaultsSpec{ @@ -631,8 +943,8 @@ func TestConfigEntryController_deletesConfigEntry_consulNamespaces(tt *testing.T }, }, ConsulNamespace: common.DefaultConsulNamespace, - GetControllerFunc: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *controller.ConfigEntryController) reconcile.Reconciler { - return &controller.ProxyDefaultsController{ + GetControllerFunc: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *ConfigEntryController) reconcile.Reconciler { + return &ProxyDefaultsController{ Client: client, Log: logger, Scheme: scheme, @@ -658,7 +970,7 @@ func TestConfigEntryController_deletesConfigEntry_consulNamespaces(tt *testing.T ObjectMeta: metav1.ObjectMeta{ Name: "foo", Namespace: c.SourceKubeNS, - Finalizers: []string{controller.FinalizerName}, + Finalizers: []string{FinalizerName}, DeletionTimestamp: &metav1.Time{Time: time.Now()}, }, Spec: v1alpha1.ServiceIntentionsSpec{ @@ -676,8 +988,8 @@ func TestConfigEntryController_deletesConfigEntry_consulNamespaces(tt *testing.T }, }, ConsulNamespace: c.ExpConsulNS, - GetControllerFunc: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *controller.ConfigEntryController) reconcile.Reconciler { - return &controller.ServiceIntentionsController{ + GetControllerFunc: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *ConfigEntryController) reconcile.Reconciler { + return &ServiceIntentionsController{ Client: client, Log: logger, Scheme: scheme, @@ -717,7 +1029,7 @@ func TestConfigEntryController_deletesConfigEntry_consulNamespaces(tt *testing.T fakeClient, logrtest.TestLogger{T: t}, s, - &controller.ConfigEntryController{ + &ConfigEntryController{ ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, EnableConsulNamespaces: true, diff --git a/control-plane/controller/configentry_controller_test.go b/control-plane/controllers/configentry_controller_test.go similarity index 99% rename from control-plane/controller/configentry_controller_test.go rename to control-plane/controllers/configentry_controller_test.go index 47f28f02d1..494715cf4f 100644 --- a/control-plane/controller/configentry_controller_test.go +++ b/control-plane/controllers/configentry_controller_test.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package controller +package controllers import ( "context" diff --git a/control-plane/controller/exportedservices_controller.go b/control-plane/controllers/exportedservices_controller.go similarity index 98% rename from control-plane/controller/exportedservices_controller.go rename to control-plane/controllers/exportedservices_controller.go index 90d7261d9a..e72b743a1f 100644 --- a/control-plane/controller/exportedservices_controller.go +++ b/control-plane/controllers/exportedservices_controller.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package controller +package controllers import ( "context" diff --git a/control-plane/controller/exportedservices_controller_ent_test.go b/control-plane/controllers/exportedservices_controller_ent_test.go similarity index 95% rename from control-plane/controller/exportedservices_controller_ent_test.go rename to control-plane/controllers/exportedservices_controller_ent_test.go index aba193bdd9..40ab5d1e1e 100644 --- a/control-plane/controller/exportedservices_controller_ent_test.go +++ b/control-plane/controllers/exportedservices_controller_ent_test.go @@ -3,7 +3,7 @@ //go:build enterprise -package controller_test +package controllers_test import ( "context" @@ -14,7 +14,7 @@ import ( logrtest "github.com/go-logr/logr/testing" "github.com/hashicorp/consul-k8s/control-plane/api/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul-k8s/control-plane/controller" + "github.com/hashicorp/consul-k8s/control-plane/controllers" "github.com/hashicorp/consul-k8s/control-plane/helper/test" capi "github.com/hashicorp/consul/api" "github.com/stretchr/testify/require" @@ -103,11 +103,11 @@ func TestExportedServicesController_createsExportedServices(tt *testing.T) { fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(exportedServices).Build() - controller := &controller.ExportedServicesController{ + controller := &controllers.ExportedServicesController{ Client: fakeClient, Log: logrtest.TestLogger{T: t}, Scheme: s, - ConfigEntryController: &controller.ConfigEntryController{ + ConfigEntryController: &controllers.ConfigEntryController{ ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, EnableConsulNamespaces: true, @@ -195,7 +195,7 @@ func TestExportedServicesController_updatesExportedServices(tt *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: c.SourceKubeNS, - Finalizers: []string{controller.FinalizerName}, + Finalizers: []string{controllers.FinalizerName}, }, Spec: v1alpha1.ExportedServicesSpec{ Services: []v1alpha1.ExportedService{ @@ -218,11 +218,11 @@ func TestExportedServicesController_updatesExportedServices(tt *testing.T) { consulClient := testClient.APIClient fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(exportedServices).Build() - controller := &controller.ExportedServicesController{ + controller := &controllers.ExportedServicesController{ Client: fakeClient, Log: logrtest.TestLogger{T: t}, Scheme: s, - ConfigEntryController: &controller.ConfigEntryController{ + ConfigEntryController: &controllers.ConfigEntryController{ ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, EnableConsulNamespaces: true, @@ -332,7 +332,7 @@ func TestExportedServicesController_deletesExportedServices(tt *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: c.SourceKubeNS, - Finalizers: []string{controller.FinalizerName}, + Finalizers: []string{controllers.FinalizerName}, DeletionTimestamp: &metav1.Time{Time: time.Now()}, }, Spec: v1alpha1.ExportedServicesSpec{ @@ -356,11 +356,11 @@ func TestExportedServicesController_deletesExportedServices(tt *testing.T) { fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(exportedServices).Build() - controller := &controller.ExportedServicesController{ + controller := &controllers.ExportedServicesController{ Client: fakeClient, Log: logrtest.TestLogger{T: t}, Scheme: s, - ConfigEntryController: &controller.ConfigEntryController{ + ConfigEntryController: &controllers.ConfigEntryController{ ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, EnableConsulNamespaces: true, diff --git a/control-plane/controller/ingressgateway_controller.go b/control-plane/controllers/ingressgateway_controller.go similarity index 98% rename from control-plane/controller/ingressgateway_controller.go rename to control-plane/controllers/ingressgateway_controller.go index fffc3c5a06..faa728dd4f 100644 --- a/control-plane/controller/ingressgateway_controller.go +++ b/control-plane/controllers/ingressgateway_controller.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package controller +package controllers import ( "context" diff --git a/control-plane/controller/mesh_controller.go b/control-plane/controllers/mesh_controller.go similarity index 98% rename from control-plane/controller/mesh_controller.go rename to control-plane/controllers/mesh_controller.go index 9f7d8cd7c8..92839d0104 100644 --- a/control-plane/controller/mesh_controller.go +++ b/control-plane/controllers/mesh_controller.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package controller +package controllers import ( "context" diff --git a/control-plane/controller/proxydefaults_controller.go b/control-plane/controllers/proxydefaults_controller.go similarity index 98% rename from control-plane/controller/proxydefaults_controller.go rename to control-plane/controllers/proxydefaults_controller.go index 7499928ea4..1415da8688 100644 --- a/control-plane/controller/proxydefaults_controller.go +++ b/control-plane/controllers/proxydefaults_controller.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package controller +package controllers import ( "context" diff --git a/control-plane/controllers/samenessgroups_controller.go b/control-plane/controllers/samenessgroups_controller.go new file mode 100644 index 0000000000..afc243f267 --- /dev/null +++ b/control-plane/controllers/samenessgroups_controller.go @@ -0,0 +1,41 @@ +package controllers + +import ( + "context" + "k8s.io/apimachinery/pkg/types" + + "github.com/go-logr/logr" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + consulv1alpha1 "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" +) + +// SamenessGroupController reconciles a SamenessGroups object. +type SamenessGroupController struct { + client.Client + Log logr.Logger + Scheme *runtime.Scheme + ConfigEntryController *ConfigEntryController +} + +//+kubebuilder:rbac:groups=consul.hashicorp.com,resources=samenessgroups,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=consul.hashicorp.com,resources=samenessgroups/status,verbs=get;update;patch + +func (r *SamenessGroupController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + return r.ConfigEntryController.ReconcileEntry(ctx, r, req, &consulv1alpha1.SamenessGroup{}) +} + +func (r *SamenessGroupController) Logger(name types.NamespacedName) logr.Logger { + return r.Log.WithValues("request", name) +} + +func (r *SamenessGroupController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { + return r.Status().Update(ctx, obj, opts...) +} + +// SetupWithManager sets up the controller with the Manager. +func (r *SamenessGroupController) SetupWithManager(mgr ctrl.Manager) error { + return setupWithManager(mgr, &consulv1alpha1.SamenessGroup{}, r) +} diff --git a/control-plane/controller/servicedefaults_controller.go b/control-plane/controllers/servicedefaults_controller.go similarity index 98% rename from control-plane/controller/servicedefaults_controller.go rename to control-plane/controllers/servicedefaults_controller.go index b96ff7e566..9c2dbe683d 100644 --- a/control-plane/controller/servicedefaults_controller.go +++ b/control-plane/controllers/servicedefaults_controller.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package controller +package controllers import ( "context" diff --git a/control-plane/controller/serviceintentions_controller.go b/control-plane/controllers/serviceintentions_controller.go similarity index 98% rename from control-plane/controller/serviceintentions_controller.go rename to control-plane/controllers/serviceintentions_controller.go index 43298a23f7..30dcb63f81 100644 --- a/control-plane/controller/serviceintentions_controller.go +++ b/control-plane/controllers/serviceintentions_controller.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package controller +package controllers import ( "context" diff --git a/control-plane/controller/serviceresolver_controller.go b/control-plane/controllers/serviceresolver_controller.go similarity index 98% rename from control-plane/controller/serviceresolver_controller.go rename to control-plane/controllers/serviceresolver_controller.go index cca014ab50..f82c4d42a2 100644 --- a/control-plane/controller/serviceresolver_controller.go +++ b/control-plane/controllers/serviceresolver_controller.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package controller +package controllers import ( "context" diff --git a/control-plane/controller/servicerouter_controller.go b/control-plane/controllers/servicerouter_controller.go similarity index 98% rename from control-plane/controller/servicerouter_controller.go rename to control-plane/controllers/servicerouter_controller.go index 6ed1e52fad..831179eee9 100644 --- a/control-plane/controller/servicerouter_controller.go +++ b/control-plane/controllers/servicerouter_controller.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package controller +package controllers import ( "context" diff --git a/control-plane/controller/servicesplitter_controller.go b/control-plane/controllers/servicesplitter_controller.go similarity index 98% rename from control-plane/controller/servicesplitter_controller.go rename to control-plane/controllers/servicesplitter_controller.go index dc5e2a8dd3..1dd89dc278 100644 --- a/control-plane/controller/servicesplitter_controller.go +++ b/control-plane/controllers/servicesplitter_controller.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package controller +package controllers import ( "context" diff --git a/control-plane/controller/terminatinggateway_controller.go b/control-plane/controllers/terminatinggateway_controller.go similarity index 98% rename from control-plane/controller/terminatinggateway_controller.go rename to control-plane/controllers/terminatinggateway_controller.go index 10af041c39..9550a2d04f 100644 --- a/control-plane/controller/terminatinggateway_controller.go +++ b/control-plane/controllers/terminatinggateway_controller.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package controller +package controllers import ( "context" diff --git a/control-plane/go.mod b/control-plane/go.mod index b139e1cd3f..23900deb46 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -25,6 +25,8 @@ require ( github.com/mitchellh/cli v1.1.0 github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/mapstructure v1.5.0 + github.com/onsi/ginkgo v1.16.4 + github.com/onsi/gomega v1.17.0 github.com/stretchr/testify v1.7.2 go.uber.org/zap v1.19.0 golang.org/x/text v0.3.8 @@ -108,6 +110,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 // indirect + github.com/nxadm/tail v1.4.8 // indirect github.com/oklog/run v1.0.0 // indirect github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c // indirect github.com/pierrec/lz4 v2.5.2+incompatible // indirect @@ -144,6 +147,7 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/resty.v1 v1.12.0 // indirect gopkg.in/square/go-jose.v2 v2.5.1 // indirect + gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.22.2 // indirect diff --git a/control-plane/main.go b/control-plane/main.go index a4ccc9630c..64ccd5d43a 100644 --- a/control-plane/main.go +++ b/control-plane/main.go @@ -7,8 +7,9 @@ import ( "log" "os" - "github.com/hashicorp/consul-k8s/control-plane/version" "github.com/mitchellh/cli" + + "github.com/hashicorp/consul-k8s/control-plane/version" ) func main() { diff --git a/control-plane/subcommand/inject-connect/command.go b/control-plane/subcommand/inject-connect/command.go index e570832ee9..05937dfe90 100644 --- a/control-plane/subcommand/inject-connect/command.go +++ b/control-plane/subcommand/inject-connect/command.go @@ -21,7 +21,7 @@ import ( "github.com/hashicorp/consul-k8s/control-plane/connect-inject/controllers/peering" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/metrics" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/webhook" - "github.com/hashicorp/consul-k8s/control-plane/controller" + "github.com/hashicorp/consul-k8s/control-plane/controllers" mutatingwebhookconfiguration "github.com/hashicorp/consul-k8s/control-plane/helper/mutating-webhook-configuration" "github.com/hashicorp/consul-k8s/control-plane/subcommand/common" "github.com/hashicorp/consul-k8s/control-plane/subcommand/flags" @@ -456,7 +456,7 @@ func (c *Command) Run(args []string) int { Prefix: c.flagK8SNSMirroringPrefix, } - configEntryReconciler := &controller.ConfigEntryController{ + configEntryReconciler := &controllers.ConfigEntryController{ ConsulClientConfig: c.consul.ConsulClientConfig(), ConsulServerConnMgr: watcher, DatacenterName: c.consul.Datacenter, @@ -466,7 +466,7 @@ func (c *Command) Run(args []string) int { NSMirroringPrefix: c.flagK8SNSMirroringPrefix, CrossNSACLPolicy: c.flagCrossNamespaceACLPolicy, } - if err = (&controller.ServiceDefaultsController{ + if err = (&controllers.ServiceDefaultsController{ ConfigEntryController: configEntryReconciler, Client: mgr.GetClient(), Log: ctrl.Log.WithName("controller").WithName(apicommon.ServiceDefaults), @@ -475,7 +475,7 @@ func (c *Command) Run(args []string) int { setupLog.Error(err, "unable to create controller", "controller", apicommon.ServiceDefaults) return 1 } - if err = (&controller.ServiceResolverController{ + if err = (&controllers.ServiceResolverController{ ConfigEntryController: configEntryReconciler, Client: mgr.GetClient(), Log: ctrl.Log.WithName("controller").WithName(apicommon.ServiceResolver), @@ -484,7 +484,7 @@ func (c *Command) Run(args []string) int { setupLog.Error(err, "unable to create controller", "controller", apicommon.ServiceResolver) return 1 } - if err = (&controller.ProxyDefaultsController{ + if err = (&controllers.ProxyDefaultsController{ ConfigEntryController: configEntryReconciler, Client: mgr.GetClient(), Log: ctrl.Log.WithName("controller").WithName(apicommon.ProxyDefaults), @@ -493,7 +493,7 @@ func (c *Command) Run(args []string) int { setupLog.Error(err, "unable to create controller", "controller", apicommon.ProxyDefaults) return 1 } - if err = (&controller.MeshController{ + if err = (&controllers.MeshController{ ConfigEntryController: configEntryReconciler, Client: mgr.GetClient(), Log: ctrl.Log.WithName("controller").WithName(apicommon.Mesh), @@ -502,7 +502,7 @@ func (c *Command) Run(args []string) int { setupLog.Error(err, "unable to create controller", "controller", apicommon.Mesh) return 1 } - if err = (&controller.ExportedServicesController{ + if err = (&controllers.ExportedServicesController{ ConfigEntryController: configEntryReconciler, Client: mgr.GetClient(), Log: ctrl.Log.WithName("controller").WithName(apicommon.ExportedServices), @@ -511,7 +511,7 @@ func (c *Command) Run(args []string) int { setupLog.Error(err, "unable to create controller", "controller", apicommon.ExportedServices) return 1 } - if err = (&controller.ServiceRouterController{ + if err = (&controllers.ServiceRouterController{ ConfigEntryController: configEntryReconciler, Client: mgr.GetClient(), Log: ctrl.Log.WithName("controller").WithName(apicommon.ServiceRouter), @@ -520,7 +520,7 @@ func (c *Command) Run(args []string) int { setupLog.Error(err, "unable to create controller", "controller", apicommon.ServiceRouter) return 1 } - if err = (&controller.ServiceSplitterController{ + if err = (&controllers.ServiceSplitterController{ ConfigEntryController: configEntryReconciler, Client: mgr.GetClient(), Log: ctrl.Log.WithName("controller").WithName(apicommon.ServiceSplitter), @@ -529,7 +529,7 @@ func (c *Command) Run(args []string) int { setupLog.Error(err, "unable to create controller", "controller", apicommon.ServiceSplitter) return 1 } - if err = (&controller.ServiceIntentionsController{ + if err = (&controllers.ServiceIntentionsController{ ConfigEntryController: configEntryReconciler, Client: mgr.GetClient(), Log: ctrl.Log.WithName("controller").WithName(apicommon.ServiceIntentions), @@ -538,7 +538,7 @@ func (c *Command) Run(args []string) int { setupLog.Error(err, "unable to create controller", "controller", apicommon.ServiceIntentions) return 1 } - if err = (&controller.IngressGatewayController{ + if err = (&controllers.IngressGatewayController{ ConfigEntryController: configEntryReconciler, Client: mgr.GetClient(), Log: ctrl.Log.WithName("controller").WithName(apicommon.IngressGateway), @@ -547,7 +547,7 @@ func (c *Command) Run(args []string) int { setupLog.Error(err, "unable to create controller", "controller", apicommon.IngressGateway) return 1 } - if err = (&controller.TerminatingGatewayController{ + if err = (&controllers.TerminatingGatewayController{ ConfigEntryController: configEntryReconciler, Client: mgr.GetClient(), Log: ctrl.Log.WithName("controller").WithName(apicommon.TerminatingGateway), @@ -556,6 +556,15 @@ func (c *Command) Run(args []string) int { setupLog.Error(err, "unable to create controller", "controller", apicommon.TerminatingGateway) return 1 } + if err = (&controllers.SamenessGroupController{ + ConfigEntryController: configEntryReconciler, + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controller").WithName(apicommon.SamenessGroup), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", apicommon.SamenessGroup) + return 1 + } if err = mgr.AddReadyzCheck("ready", webhook.ReadinessCheck{CertDir: c.flagCertDir}.Ready); err != nil { setupLog.Error(err, "unable to create readiness check", "controller", endpoints.Controller{}) @@ -706,6 +715,12 @@ func (c *Command) Run(args []string) int { Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.TerminatingGateway), ConsulMeta: consulMeta, }}) + mgr.GetWebhookServer().Register("/mutate-v1alpha1-samenessgroup", + &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.SamenessGroupWebhook{ + Client: mgr.GetClient(), + Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.SamenessGroup), + ConsulMeta: consulMeta, + }}) if c.flagEnableWebhookCAUpdate { err = c.updateWebhookCABundle(ctx) From 7145b025b8e83fca69c44d4cc77e1ad374bf5a45 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Thu, 20 Apr 2023 14:29:23 -0400 Subject: [PATCH 139/340] Supply chain updates (#2072) --- .github/workflows/build.yml | 34 +++++++++---------- .github/workflows/changelog-checker.yml | 2 +- .github/workflows/jira-issues.yaml | 12 +++---- .github/workflows/merge.yml | 2 +- .github/workflows/nightly-acceptance.yml | 2 +- .github/workflows/nightly-cleanup.yml | 2 +- .github/workflows/pr.yml | 2 +- .../workflows/weekly-acceptance-0-49-x.yml | 2 +- .github/workflows/weekly-acceptance-1-0-x.yml | 2 +- .github/workflows/weekly-acceptance-1-1-x.yml | 2 +- 10 files changed, 31 insertions(+), 31 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index de0d18d648..a00629bde2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,7 +21,7 @@ jobs: outputs: go-version: ${{ steps.get-go-version.outputs.go-version }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Determine Go version id: get-go-version # We use .go-version as our source of truth for current Go @@ -35,7 +35,7 @@ jobs: outputs: product-version: ${{ steps.get-product-version.outputs.product-version }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: get product version id: get-product-version run: | @@ -49,7 +49,7 @@ jobs: filepath: ${{ steps.generate-metadata-file.outputs.filepath }} steps: - name: "Checkout directory" - uses: actions/checkout@v3 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Generate metadata file id: generate-metadata-file uses: hashicorp/actions-generate-metadata@v1 @@ -57,7 +57,7 @@ jobs: version: ${{ needs.get-product-version.outputs.product-version }} product: ${{ env.PKG_NAME }} repositoryOwner: "hashicorp" - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: metadata.json path: ${{ steps.generate-metadata-file.outputs.filepath }} @@ -109,10 +109,10 @@ jobs: name: Go ${{ matrix.go }} ${{ matrix.goos }} ${{ matrix.goarch }} ${{ matrix.component }} build steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Setup go - uses: actions/setup-go@v3 + uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0 with: go-version: ${{ matrix.go }} @@ -134,7 +134,7 @@ jobs: zip -r -j out/${{ matrix.pkg_name }}_${{ needs.get-product-version.outputs.product-version }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip dist/ - name: Upload built binaries - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: ${{ matrix.pkg_name }}_${{ needs.get-product-version.outputs.product-version }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip path: ${{ matrix.component}}/out/${{ matrix.pkg_name }}_${{ needs.get-product-version.outputs.product-version }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip @@ -162,7 +162,7 @@ jobs: - name: Test rpm package if: ${{ matrix.goos == 'linux' && matrix.component == 'cli' && matrix.goarch == 'amd64'}} - uses: addnab/docker-run-action@v3 + uses: addnab/docker-run-action@v3 # TSCCR: no entry for repository "addnab/docker-run-action" with: image: registry.access.redhat.com/ubi8/ubi:latest options: -v ${{ github.workspace }}:/work @@ -179,7 +179,7 @@ jobs: echo "Test PASSED, expected: ${VERSION}, got: ${CONSUL_K8S_VERSION}" - name: Upload rpm package - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ matrix.goos == 'linux' && matrix.component == 'cli' && matrix.goarch == 'amd64'}} with: name: ${{ env.RPM_PACKAGE }} @@ -187,7 +187,7 @@ jobs: - name: Test debian package if: ${{ matrix.goos == 'linux' && matrix.component == 'cli' && matrix.goarch == 'amd64'}} - uses: addnab/docker-run-action@v3 + uses: addnab/docker-run-action@v3 # TSCCR: no entry for repository "addnab/docker-run-action" with: image: ubuntu:latest options: -v ${{ github.workspace }}:/work @@ -204,7 +204,7 @@ jobs: echo "Test PASSED, expected: ${VERSION}, got: ${CONSUL_K8S_VERSION}" - name: Upload debian packages - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ matrix.goos == 'linux' && matrix.component == 'cli' && matrix.goarch == 'amd64'}} with: name: ${{ env.DEB_PACKAGE }} @@ -221,8 +221,8 @@ jobs: repo: ${{ github.event.repository.name }} version: ${{ needs.get-product-version.outputs.product-version }} steps: - - uses: actions/checkout@v3 - - uses: actions/download-artifact@v3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: name: consul-cni_${{ needs.get-product-version.outputs.product-version }}_linux_${{ matrix.arch }}.zip path: control-plane/dist/cni/linux/${{ matrix.arch }} @@ -265,8 +265,8 @@ jobs: repo: ${{ github.event.repository.name }} version: ${{ needs.get-product-version.outputs.product-version }} steps: - - uses: actions/checkout@v3 - - uses: actions/download-artifact@v3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: name: consul-cni_${{ needs.get-product-version.outputs.product-version }}_linux_${{ matrix.arch }}.zip path: control-plane/dist/cni/linux/${{ matrix.arch }} @@ -307,8 +307,8 @@ jobs: repo: ${{ github.event.repository.name }} version: ${{ needs.get-product-version.outputs.product-version }} steps: - - uses: actions/checkout@v3 - - uses: actions/download-artifact@v3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: name: consul-cni_${{ needs.get-product-version.outputs.product-version }}_linux_${{ matrix.arch }}.zip path: control-plane/dist/cni/linux/${{ matrix.arch }} diff --git a/.github/workflows/changelog-checker.yml b/.github/workflows/changelog-checker.yml index 3595781825..1c41634fd3 100644 --- a/.github/workflows/changelog-checker.yml +++ b/.github/workflows/changelog-checker.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 # by default the checkout action doesn't checkout all branches diff --git a/.github/workflows/jira-issues.yaml b/.github/workflows/jira-issues.yaml index dc743e9328..bddc69c83f 100644 --- a/.github/workflows/jira-issues.yaml +++ b/.github/workflows/jira-issues.yaml @@ -15,7 +15,7 @@ jobs: name: Jira Community Issue sync steps: - name: Login - uses: atlassian/gajira-login@v3.0.0 + uses: atlassian/gajira-login@ca13f8850ea309cf44a6e4e0c49d9aa48ac3ca4c # v3 env: JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} @@ -38,7 +38,7 @@ jobs: - name: Create ticket if an issue is filed, or if PR not by a team member is opened if: github.event.action == 'opened' - uses: tomhjp/gh-action-jira-create@v0.2.0 + uses: tomhjp/gh-action-jira-create@3ed1789cad3521292e591a7cfa703215ec1348bf # v0.2.1 with: project: NET issuetype: "${{ steps.set-ticket-type.outputs.TYPE }}" @@ -58,28 +58,28 @@ jobs: - name: Search if: github.event.action != 'opened' id: search - uses: tomhjp/gh-action-jira-search@v0.2.1 + uses: tomhjp/gh-action-jira-search@04700b457f317c3e341ce90da5a3ff4ce058f2fa # v0.2.2 with: # cf[10089] is Issue Link (use JIRA API to retrieve) jql: 'issuetype = "${{ steps.set-ticket-type.outputs.TYPE }}" and cf[10089] = "${{ github.event.issue.html_url || github.event.pull_request.html_url }}"' - name: Sync comment if: github.event.action == 'created' && steps.search.outputs.issue - uses: tomhjp/gh-action-jira-comment@v0.1.0 + uses: tomhjp/gh-action-jira-comment@6eb6b9ead70221916b6badd118c24535ed220bd9 # v0.2.0 with: issue: ${{ steps.search.outputs.issue }} comment: "${{ github.actor }} ${{ github.event.review.state || 'commented' }}:\n\n${{ github.event.comment.body || github.event.review.body }}\n\n${{ github.event.comment.html_url || github.event.review.html_url }}" - name: Close ticket if: ( github.event.action == 'closed' || github.event.action == 'deleted' ) && steps.search.outputs.issue - uses: atlassian/gajira-transition@v2.0.1 + uses: atlassian/gajira-transition@4749176faf14633954d72af7a44d7f2af01cc92b # v3 with: issue: ${{ steps.search.outputs.issue }} transition: "Closed" - name: Reopen ticket if: github.event.action == 'reopened' && steps.search.outputs.issue - uses: atlassian/gajira-transition@v2.0.1 + uses: atlassian/gajira-transition@4749176faf14633954d72af7a44d7f2af01cc92b # v3 with: issue: ${{ steps.search.outputs.issue }} transition: "To Do" diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml index be1b392f4a..b6037e0af3 100644 --- a/.github/workflows/merge.yml +++ b/.github/workflows/merge.yml @@ -21,7 +21,7 @@ jobs: name: test runs-on: ubuntu-latest steps: - - uses: benc-uk/workflow-dispatch@v1.2.2 + - uses: benc-uk/workflow-dispatch@798e70c97009500150087d30d9f11c5444830385 # v1.2.2 name: test with: workflow: test.yml diff --git a/.github/workflows/nightly-acceptance.yml b/.github/workflows/nightly-acceptance.yml index b8b7f50798..6414d6a611 100644 --- a/.github/workflows/nightly-acceptance.yml +++ b/.github/workflows/nightly-acceptance.yml @@ -17,7 +17,7 @@ jobs: name: cloud runs-on: ubuntu-latest steps: - - uses: benc-uk/workflow-dispatch@v1.2.2 + - uses: benc-uk/workflow-dispatch@798e70c97009500150087d30d9f11c5444830385 # v1.2.2 name: cloud with: workflow: cloud.yml diff --git a/.github/workflows/nightly-cleanup.yml b/.github/workflows/nightly-cleanup.yml index 79360e497d..4a304549df 100644 --- a/.github/workflows/nightly-cleanup.yml +++ b/.github/workflows/nightly-cleanup.yml @@ -17,7 +17,7 @@ jobs: name: cleanup runs-on: ubuntu-latest steps: - - uses: benc-uk/workflow-dispatch@v1.2.2 + - uses: benc-uk/workflow-dispatch@798e70c97009500150087d30d9f11c5444830385 # v1.2.2 name: cleanup with: workflow: cleanup.yml diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 32baf472fb..b4b431693a 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -15,7 +15,7 @@ jobs: name: test runs-on: ubuntu-latest steps: - - uses: benc-uk/workflow-dispatch@v1.2.2 + - uses: benc-uk/workflow-dispatch@798e70c97009500150087d30d9f11c5444830385 # v1.2.2 name: test with: workflow: test.yml diff --git a/.github/workflows/weekly-acceptance-0-49-x.yml b/.github/workflows/weekly-acceptance-0-49-x.yml index 7025bcb241..adba13846a 100644 --- a/.github/workflows/weekly-acceptance-0-49-x.yml +++ b/.github/workflows/weekly-acceptance-0-49-x.yml @@ -19,7 +19,7 @@ jobs: name: cloud runs-on: ubuntu-latest steps: - - uses: benc-uk/workflow-dispatch@v1.2.2 + - uses: benc-uk/workflow-dispatch@798e70c97009500150087d30d9f11c5444830385 # v1.2.2 name: cloud with: workflow: cloud.yml diff --git a/.github/workflows/weekly-acceptance-1-0-x.yml b/.github/workflows/weekly-acceptance-1-0-x.yml index 4aa49594f3..72769f0ca1 100644 --- a/.github/workflows/weekly-acceptance-1-0-x.yml +++ b/.github/workflows/weekly-acceptance-1-0-x.yml @@ -20,7 +20,7 @@ jobs: name: cloud runs-on: ubuntu-latest steps: - - uses: benc-uk/workflow-dispatch@v1.2.2 + - uses: benc-uk/workflow-dispatch@798e70c97009500150087d30d9f11c5444830385 # v1.2.2 name: cloud with: workflow: cloud.yml diff --git a/.github/workflows/weekly-acceptance-1-1-x.yml b/.github/workflows/weekly-acceptance-1-1-x.yml index 1ffc6f8684..b77da7eff0 100644 --- a/.github/workflows/weekly-acceptance-1-1-x.yml +++ b/.github/workflows/weekly-acceptance-1-1-x.yml @@ -20,7 +20,7 @@ jobs: name: cloud runs-on: ubuntu-latest steps: - - uses: benc-uk/workflow-dispatch@v1.2.2 + - uses: benc-uk/workflow-dispatch@798e70c97009500150087d30d9f11c5444830385 # v1.2.2 name: cloud with: workflow: cloud.yml From e0df67985209482af3552ab25a63238753a71f21 Mon Sep 17 00:00:00 2001 From: Marc Billow Date: Fri, 21 Apr 2023 13:42:35 -0500 Subject: [PATCH 140/340] Fix Sync Catalog ACL Token Environment Var Name (#2068) * Fix Sync Catalog ACL Token Environment Var Name * Update ACL variable name in tests --- charts/consul/templates/sync-catalog-deployment.yaml | 2 +- charts/consul/test/unit/sync-catalog-deployment.bats | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/charts/consul/templates/sync-catalog-deployment.yaml b/charts/consul/templates/sync-catalog-deployment.yaml index f2815d9627..ec1abbfc84 100644 --- a/charts/consul/templates/sync-catalog-deployment.yaml +++ b/charts/consul/templates/sync-catalog-deployment.yaml @@ -100,7 +100,7 @@ spec: fieldRef: fieldPath: metadata.namespace {{- if (and .Values.syncCatalog.aclSyncToken.secretName .Values.syncCatalog.aclSyncToken.secretKey) }} - - name: CONSUL_HTTP_TOKEN + - name: CONSUL_ACL_TOKEN valueFrom: secretKeyRef: name: {{ .Values.syncCatalog.aclSyncToken.secretName }} diff --git a/charts/consul/test/unit/sync-catalog-deployment.bats b/charts/consul/test/unit/sync-catalog-deployment.bats index ae1fe1a854..0b397a55d0 100755 --- a/charts/consul/test/unit/sync-catalog-deployment.bats +++ b/charts/consul/test/unit/sync-catalog-deployment.bats @@ -338,7 +338,7 @@ load _helpers --set 'syncCatalog.enabled=true' \ --set 'syncCatalog.aclSyncToken.secretKey=bar' \ . | tee /dev/stderr | - yq '[.spec.template.spec.containers[0].env[].name] | any(contains("CONSUL_HTTP_TOKEN"))' | tee /dev/stderr) + yq '[.spec.template.spec.containers[0].env[].name] | any(contains("CONSUL_ACL_TOKEN"))' | tee /dev/stderr) [ "${actual}" = "false" ] } @@ -349,7 +349,7 @@ load _helpers --set 'syncCatalog.enabled=true' \ --set 'syncCatalog.aclSyncToken.secretName=foo' \ . | tee /dev/stderr | - yq '[.spec.template.spec.containers[0].env[].name] | any(contains("CONSUL_HTTP_TOKEN"))' | tee /dev/stderr) + yq '[.spec.template.spec.containers[0].env[].name] | any(contains("CONSUL_ACL_TOKEN"))' | tee /dev/stderr) [ "${actual}" = "false" ] } @@ -361,7 +361,7 @@ load _helpers --set 'syncCatalog.aclSyncToken.secretName=foo' \ --set 'syncCatalog.aclSyncToken.secretKey=bar' \ . | tee /dev/stderr | - yq '[.spec.template.spec.containers[0].env[].name] | any(contains("CONSUL_HTTP_TOKEN"))' | tee /dev/stderr) + yq '[.spec.template.spec.containers[0].env[].name] | any(contains("CONSUL_ACL_TOKEN"))' | tee /dev/stderr) [ "${actual}" = "true" ] } From 568ab03f14fdba410e8b1306105ed001b2c2c3b7 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Mon, 24 Apr 2023 13:51:34 -0400 Subject: [PATCH 141/340] Add changelog for NET 2422 (#2080) --- .changelog/2068.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/2068.txt diff --git a/.changelog/2068.txt b/.changelog/2068.txt new file mode 100644 index 0000000000..bc17061f51 --- /dev/null +++ b/.changelog/2068.txt @@ -0,0 +1,3 @@ +```release-note:bug +sync-catalog: fix issue where the sync-catalog ACL token were set with an incorrect ENV VAR. +``` \ No newline at end of file From f8eb931d53b693311da3710c142079fbcdc7cd56 Mon Sep 17 00:00:00 2001 From: malizz Date: Mon, 24 Apr 2023 11:27:03 -0700 Subject: [PATCH 142/340] add sameness group to exported services (#2075) * add sameness group to exported services * update CRDs * update deep copy * re add license line * check if sameness group is wildcard * remove experimental tag on peering fields * update error message case * update error message case in webhook test --- .../templates/crd-exportedservices.yaml | 7 +- .../consul/templates/crd-proxydefaults.yaml | 5 +- .../consul/templates/crd-samenessgroups.yaml | 17 ++- .../templates/crd-serviceintentions.yaml | 3 +- .../templates/crd-serviceresolvers.yaml | 5 +- .../api/v1alpha1/exportedservices_types.go | 40 ++++-- .../v1alpha1/exportedservices_types_test.go | 119 +++++++++++++++++- .../v1alpha1/exportedservices_webhook_test.go | 2 +- .../api/v1alpha1/serviceintentions_types.go | 2 +- .../api/v1alpha1/zz_generated.deepcopy.go | 51 +++++--- ...consul.hashicorp.com_exportedservices.yaml | 7 +- .../consul.hashicorp.com_proxydefaults.yaml | 5 +- .../consul.hashicorp.com_samenessgroups.yaml | 17 ++- ...onsul.hashicorp.com_serviceintentions.yaml | 3 +- ...consul.hashicorp.com_serviceresolvers.yaml | 5 +- control-plane/config/webhook/manifests.yaml | 2 +- control-plane/go.mod | 6 +- control-plane/go.sum | 4 +- 18 files changed, 238 insertions(+), 62 deletions(-) diff --git a/charts/consul/templates/crd-exportedservices.yaml b/charts/consul/templates/crd-exportedservices.yaml index 007990372c..073e081d0c 100644 --- a/charts/consul/templates/crd-exportedservices.yaml +++ b/charts/consul/templates/crd-exportedservices.yaml @@ -76,8 +76,11 @@ spec: the service to. type: string peer: - description: '[Experimental] Peer is the name of the peer - to export the service to.' + description: Peer is the name of the peer to export the service to. + type: string + samenessGroup: + description: SamenessGroup is the name of the sameness + group to export the service to. type: string type: object type: array diff --git a/charts/consul/templates/crd-proxydefaults.yaml b/charts/consul/templates/crd-proxydefaults.yaml index 6bb1234066..f72d1c7cea 100644 --- a/charts/consul/templates/crd-proxydefaults.yaml +++ b/charts/consul/templates/crd-proxydefaults.yaml @@ -153,8 +153,9 @@ spec: "sequential") and "order-by-locality". type: string regions: - description: The ordered list of the regions of the failover targets. - Valid values can be "us-west-1", "us-west-2", and so on. + description: Regions is the ordered list of the regions of the + failover targets. Valid values can be "us-west-1", "us-west-2", + and so on. items: type: string type: array diff --git a/charts/consul/templates/crd-samenessgroups.yaml b/charts/consul/templates/crd-samenessgroups.yaml index e541539481..60beb5662c 100644 --- a/charts/consul/templates/crd-samenessgroups.yaml +++ b/charts/consul/templates/crd-samenessgroups.yaml @@ -58,16 +58,27 @@ spec: description: SamenessGroupSpec defines the desired state of SamenessGroup. properties: defaultForFailover: - description: 'DefaultForFailover indicates that upstream requests to members of the given sameness group will implicitly failover between members of this sameness group.' + description: DefaultForFailover indicates that upstream requests to + members of the given sameness group will implicitly failover between + members of this sameness group. When DefaultForFailover is true, + the local partition must be a member of the sameness group or IncludeLocal + must be set to true. type: boolean includeLocal: - description: 'IncludeLocal is used to include the local partition as the first member of the sameness group.' + description: IncludeLocal is used to include the local partition as + the first member of the sameness group. The local partition can + only be a member of a single sameness group. type: boolean members: - description: 'Members are the partitions and peers that are part of the sameness group.' + description: Members are the partitions and peers that are part of + the sameness group. If a member of a sameness group does not exist, + it will be ignored. items: properties: partition: + description: The partitions and peers that are part of the sameness + group. A sameness group member cannot define both peer and + partition at the same time. type: string peer: type: string diff --git a/charts/consul/templates/crd-serviceintentions.yaml b/charts/consul/templates/crd-serviceintentions.yaml index cdbb5413b0..ede8ae09b0 100644 --- a/charts/consul/templates/crd-serviceintentions.yaml +++ b/charts/consul/templates/crd-serviceintentions.yaml @@ -102,8 +102,7 @@ spec: description: Partition is the Admin Partition for the Name parameter. type: string peer: - description: '[Experimental] Peer is the peer name for the Name - parameter.' + description: Peer is the peer name for the Name parameter. type: string permissions: description: Permissions is the list of all additional L7 attributes diff --git a/charts/consul/templates/crd-serviceresolvers.yaml b/charts/consul/templates/crd-serviceresolvers.yaml index 1942b79b8f..04d6dd9754 100644 --- a/charts/consul/templates/crd-serviceresolvers.yaml +++ b/charts/consul/templates/crd-serviceresolvers.yaml @@ -88,8 +88,9 @@ spec: to "sequential") and "order-by-locality". type: string regions: - description: The ordered list of the regions of the failover targets. - Valid values can be "us-west-1", "us-west-2", and so on. + description: Regions is the ordered list of the regions + of the failover targets. Valid values can be "us-west-1", + "us-west-2", and so on. items: type: string type: array diff --git a/control-plane/api/v1alpha1/exportedservices_types.go b/control-plane/api/v1alpha1/exportedservices_types.go index e4df1cee0d..06d6ce30cf 100644 --- a/control-plane/api/v1alpha1/exportedservices_types.go +++ b/control-plane/api/v1alpha1/exportedservices_types.go @@ -19,6 +19,7 @@ import ( ) const ExportedServicesKubeKind = "exportedservices" +const WildcardSpecifier = "*" func init() { SchemeBuilder.Register(&ExportedServices{}, &ExportedServicesList{}) @@ -71,8 +72,10 @@ type ExportedService struct { type ServiceConsumer struct { // Partition is the admin partition to export the service to. Partition string `json:"partition,omitempty"` - // [Experimental] Peer is the name of the peer to export the service to. + // Peer is the name of the peer to export the service to. Peer string `json:"peer,omitempty"` + // SamenessGroup is the name of the sameness group to export the service to. + SamenessGroup string `json:"samenessGroup,omitempty"` } func (in *ExportedServices) GetObjectMeta() metav1.ObjectMeta { @@ -169,8 +172,9 @@ func (in *ExportedService) toConsul() capi.ExportedService { var consumers []capi.ServiceConsumer for _, consumer := range in.Consumers { consumers = append(consumers, capi.ServiceConsumer{ - Partition: consumer.Partition, - Peer: consumer.Peer, + Partition: consumer.Partition, + Peer: consumer.Peer, + SamenessGroup: consumer.SamenessGroup, }) } return capi.ExportedService{ @@ -230,14 +234,34 @@ func (in *ExportedService) validate(path *field.Path, consulMeta common.ConsulMe } func (in *ServiceConsumer) validate(path *field.Path, consulMeta common.ConsulMeta) *field.Error { - if in.Partition != "" && in.Peer != "" { - return field.Invalid(path, *in, "both partition and peer cannot be specified.") + count := 0 + + if in.Partition != "" { + count++ + } + if in.Peer != "" { + count++ + } + if in.SamenessGroup != "" { + count++ + } + if count > 1 { + return field.Invalid(path, *in, "service consumer must define at most one of Peer, Partition, or SamenessGroup") } - if in.Partition == "" && in.Peer == "" { - return field.Invalid(path, *in, "either partition or peer must be specified.") + if count == 0 { + return field.Invalid(path, *in, "service consumer must define at least one of Peer, Partition, or SamenessGroup") } if !consulMeta.PartitionsEnabled && in.Partition != "" { - return field.Invalid(path.Child("partitions"), in.Partition, "Consul Admin Partitions need to be enabled to specify partition.") + return field.Invalid(path.Child("partition"), in.Partition, "Consul Admin Partitions need to be enabled to specify partition.") + } + if in.Partition == WildcardSpecifier { + return field.Invalid(path.Child("partition"), "", "exporting to all partitions (wildcard) is not supported") + } + if in.Peer == WildcardSpecifier { + return field.Invalid(path.Child("peer"), "", "exporting to all peers (wildcard) is not supported") + } + if in.SamenessGroup == WildcardSpecifier { + return field.Invalid(path.Child("samenessgroup"), "", "exporting to all sameness groups (wildcard) is not supported") } return nil } diff --git a/control-plane/api/v1alpha1/exportedservices_types_test.go b/control-plane/api/v1alpha1/exportedservices_types_test.go index e94eb36cbe..d759a6f270 100644 --- a/control-plane/api/v1alpha1/exportedservices_types_test.go +++ b/control-plane/api/v1alpha1/exportedservices_types_test.go @@ -59,6 +59,9 @@ func TestExportedServices_MatchesConsul(t *testing.T) { { Peer: "second-peer", }, + { + SamenessGroup: "sg1", + }, }, }, { @@ -74,6 +77,9 @@ func TestExportedServices_MatchesConsul(t *testing.T) { { Peer: "third-peer", }, + { + SamenessGroup: "sg2", + }, }, }, }, @@ -95,6 +101,9 @@ func TestExportedServices_MatchesConsul(t *testing.T) { { Peer: "second-peer", }, + { + SamenessGroup: "sg1", + }, }, }, { @@ -110,6 +119,9 @@ func TestExportedServices_MatchesConsul(t *testing.T) { { Peer: "third-peer", }, + { + SamenessGroup: "sg2", + }, }, }, }, @@ -183,6 +195,9 @@ func TestExportedServices_ToConsul(t *testing.T) { { Peer: "second-peer", }, + { + SamenessGroup: "sg2", + }, }, }, { @@ -198,6 +213,9 @@ func TestExportedServices_ToConsul(t *testing.T) { { Peer: "third-peer", }, + { + SamenessGroup: "sg3", + }, }, }, }, @@ -219,6 +237,9 @@ func TestExportedServices_ToConsul(t *testing.T) { { Peer: "second-peer", }, + { + SamenessGroup: "sg2", + }, }, }, { @@ -234,6 +255,9 @@ func TestExportedServices_ToConsul(t *testing.T) { { Peer: "third-peer", }, + { + SamenessGroup: "sg3", + }, }, }, }, @@ -278,6 +302,9 @@ func TestExportedServices_Validate(t *testing.T) { { Peer: "second-peer", }, + { + SamenessGroup: "sg2", + }, }, }, }, @@ -331,10 +358,10 @@ func TestExportedServices_Validate(t *testing.T) { namespaceEnabled: true, partitionsEnabled: true, expectedErrMsgs: []string{ - `spec.services[0].consumers[0]: Invalid value: v1alpha1.ServiceConsumer{Partition:"second", Peer:"second-peer"}: both partition and peer cannot be specified.`, + `service consumer must define at most one of Peer, Partition, or SamenessGroup`, }, }, - "neither partition nor peer name specified": { + "none of peer, partition, or sameness group defined": { input: &ExportedServices{ ObjectMeta: metav1.ObjectMeta{ Name: common.DefaultConsulPartition, @@ -354,7 +381,7 @@ func TestExportedServices_Validate(t *testing.T) { namespaceEnabled: true, partitionsEnabled: true, expectedErrMsgs: []string{ - `spec.services[0].consumers[0]: Invalid value: v1alpha1.ServiceConsumer{Partition:"", Peer:""}: either partition or peer must be specified.`, + `service consumer must define at least one of Peer, Partition, or SamenessGroup`, }, }, "partition provided when partitions are disabled": { @@ -379,7 +406,7 @@ func TestExportedServices_Validate(t *testing.T) { namespaceEnabled: true, partitionsEnabled: false, expectedErrMsgs: []string{ - `spec.services[0].consumers[0].partitions: Invalid value: "test-partition": Consul Admin Partitions need to be enabled to specify partition.`, + `spec.services[0].consumers[0].partition: Invalid value: "test-partition": Consul Admin Partitions need to be enabled to specify partition.`, }, }, "namespace provided when namespaces are disabled": { @@ -407,6 +434,81 @@ func TestExportedServices_Validate(t *testing.T) { `spec.services[0]: Invalid value: "frontend": Consul Namespaces must be enabled to specify service namespace.`, }, }, + "exporting to all partitions is not supported": { + input: &ExportedServices{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.DefaultConsulPartition, + }, + Spec: ExportedServicesSpec{ + Services: []ExportedService{ + { + Name: "service-frontend", + Namespace: "frontend", + Consumers: []ServiceConsumer{ + { + Partition: "*", + }, + }, + }, + }, + }, + }, + namespaceEnabled: true, + partitionsEnabled: true, + expectedErrMsgs: []string{ + `exporting to all partitions (wildcard) is not supported`, + }, + }, + "exporting to all peers (wildcard) is not supported": { + input: &ExportedServices{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.DefaultConsulPartition, + }, + Spec: ExportedServicesSpec{ + Services: []ExportedService{ + { + Name: "service-frontend", + Namespace: "frontend", + Consumers: []ServiceConsumer{ + { + Peer: "*", + }, + }, + }, + }, + }, + }, + namespaceEnabled: true, + partitionsEnabled: true, + expectedErrMsgs: []string{ + `exporting to all peers (wildcard) is not supported`, + }, + }, + "exporting to all sameness groups (wildcard) is not supported": { + input: &ExportedServices{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.DefaultConsulPartition, + }, + Spec: ExportedServicesSpec{ + Services: []ExportedService{ + { + Name: "service-frontend", + Namespace: "frontend", + Consumers: []ServiceConsumer{ + { + SamenessGroup: "*", + }, + }, + }, + }, + }, + }, + namespaceEnabled: true, + partitionsEnabled: true, + expectedErrMsgs: []string{ + `exporting to all sameness groups (wildcard) is not supported`, + }, + }, "multiple errors": { input: &ExportedServices{ ObjectMeta: metav1.ObjectMeta{ @@ -423,6 +525,10 @@ func TestExportedServices_Validate(t *testing.T) { Peer: "second-peer", }, {}, + { + SamenessGroup: "sg2", + Partition: "partition2", + }, }, }, }, @@ -431,8 +537,9 @@ func TestExportedServices_Validate(t *testing.T) { namespaceEnabled: true, partitionsEnabled: true, expectedErrMsgs: []string{ - `spec.services[0].consumers[0]: Invalid value: v1alpha1.ServiceConsumer{Partition:"second", Peer:"second-peer"}: both partition and peer cannot be specified.`, - `spec.services[0].consumers[1]: Invalid value: v1alpha1.ServiceConsumer{Partition:"", Peer:""}: either partition or peer must be specified.`, + `spec.services[0].consumers[0]: Invalid value: v1alpha1.ServiceConsumer{Partition:"second", Peer:"second-peer", SamenessGroup:""}: service consumer must define at most one of Peer, Partition, or SamenessGroup`, + `spec.services[0].consumers[1]: Invalid value: v1alpha1.ServiceConsumer{Partition:"", Peer:"", SamenessGroup:""}: service consumer must define at least one of Peer, Partition, or SamenessGroup`, + `spec.services[0].consumers[2]: Invalid value: v1alpha1.ServiceConsumer{Partition:"partition2", Peer:"", SamenessGroup:"sg2"}: service consumer must define at most one of Peer, Partition, or SamenessGroup`, }, }, } diff --git a/control-plane/api/v1alpha1/exportedservices_webhook_test.go b/control-plane/api/v1alpha1/exportedservices_webhook_test.go index 41476bd822..735f938825 100644 --- a/control-plane/api/v1alpha1/exportedservices_webhook_test.go +++ b/control-plane/api/v1alpha1/exportedservices_webhook_test.go @@ -123,7 +123,7 @@ func TestValidateExportedServices(t *testing.T) { Partition: "", }, expAllow: false, - expErrMessage: "exportedservices.consul.hashicorp.com \"default\" is invalid: spec.services[0].consumers[0].partitions: Invalid value: \"other\": Consul Admin Partitions need to be enabled to specify partition.", + expErrMessage: "exportedservices.consul.hashicorp.com \"default\" is invalid: spec.services[0].consumers[0].partition: Invalid value: \"other\": Consul Admin Partitions need to be enabled to specify partition.", }, "no services": { existingResources: []runtime.Object{}, diff --git a/control-plane/api/v1alpha1/serviceintentions_types.go b/control-plane/api/v1alpha1/serviceintentions_types.go index 82a5dcfdc8..a066b4e84f 100644 --- a/control-plane/api/v1alpha1/serviceintentions_types.go +++ b/control-plane/api/v1alpha1/serviceintentions_types.go @@ -81,7 +81,7 @@ type SourceIntention struct { Name string `json:"name,omitempty"` // Namespace is the namespace for the Name parameter. Namespace string `json:"namespace,omitempty"` - // [Experimental] Peer is the peer name for the Name parameter. + // Peer is the peer name for the Name parameter. Peer string `json:"peer,omitempty"` // Partition is the Admin Partition for the Name parameter. Partition string `json:"partition,omitempty"` diff --git a/control-plane/api/v1alpha1/zz_generated.deepcopy.go b/control-plane/api/v1alpha1/zz_generated.deepcopy.go index f485b5c5f1..87d4bd9594 100644 --- a/control-plane/api/v1alpha1/zz_generated.deepcopy.go +++ b/control-plane/api/v1alpha1/zz_generated.deepcopy.go @@ -1324,21 +1324,6 @@ func (in *RingHashConfig) DeepCopy() *RingHashConfig { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SamenessGroupMember) DeepCopyInto(out *SamenessGroupMember) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SamenessGroupMember. -func (in *SamenessGroupMember) DeepCopy() *SamenessGroupMember { - if in == nil { - return nil - } - out := new(SamenessGroupMember) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SamenessGroup) DeepCopyInto(out *SamenessGroup) { *out = *in @@ -1348,7 +1333,7 @@ func (in *SamenessGroup) DeepCopyInto(out *SamenessGroup) { in.Status.DeepCopyInto(&out.Status) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SamenessGroups. +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SamenessGroup. func (in *SamenessGroup) DeepCopy() *SamenessGroup { if in == nil { return nil @@ -1398,6 +1383,40 @@ func (in *SamenessGroupList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SamenessGroupMember) DeepCopyInto(out *SamenessGroupMember) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SamenessGroupMember. +func (in *SamenessGroupMember) DeepCopy() *SamenessGroupMember { + if in == nil { + return nil + } + out := new(SamenessGroupMember) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in SamenessGroupMembers) DeepCopyInto(out *SamenessGroupMembers) { + { + in := &in + *out = make(SamenessGroupMembers, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SamenessGroupMembers. +func (in SamenessGroupMembers) DeepCopy() SamenessGroupMembers { + if in == nil { + return nil + } + out := new(SamenessGroupMembers) + in.DeepCopyInto(out) + return *out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SamenessGroupSpec) DeepCopyInto(out *SamenessGroupSpec) { *out = *in diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml index 6352ac3af1..84e523f50c 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml @@ -72,8 +72,11 @@ spec: the service to. type: string peer: - description: '[Experimental] Peer is the name of the peer - to export the service to.' + description: Peer is the name of the peer to export the service to. + type: string + samenessGroup: + description: SamenessGroup is the name of the sameness + group to export the service to. type: string type: object type: array diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml index c66b5fdd0f..86409d2de0 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml @@ -149,8 +149,9 @@ spec: "sequential") and "order-by-locality". type: string regions: - description: The ordered list of the regions of the failover targets. - Valid values can be "us-west-1", "us-west-2", and so on. + description: Regions is the ordered list of the regions of the + failover targets. Valid values can be "us-west-1", "us-west-2", + and so on. items: type: string type: array diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml index 5efda1ffa2..c71a211f63 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml @@ -54,16 +54,27 @@ spec: description: SamenessGroupSpec defines the desired state of SamenessGroup. properties: defaultForFailover: - description: 'DefaultForFailover indicates that upstream requests to members of the given sameness group will implicitly failover between members of this sameness group.' + description: DefaultForFailover indicates that upstream requests to + members of the given sameness group will implicitly failover between + members of this sameness group. When DefaultForFailover is true, + the local partition must be a member of the sameness group or IncludeLocal + must be set to true. type: boolean includeLocal: - description: 'IncludeLocal is used to include the local partition as the first member of the sameness group.' + description: IncludeLocal is used to include the local partition as + the first member of the sameness group. The local partition can + only be a member of a single sameness group. type: boolean members: - description: 'Members are the partitions and peers that are part of the sameness group.' + description: Members are the partitions and peers that are part of + the sameness group. If a member of a sameness group does not exist, + it will be ignored. items: properties: partition: + description: The partitions and peers that are part of the sameness + group. A sameness group member cannot define both peer and + partition at the same time. type: string peer: type: string diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml index 8e186af1a7..3c80ea8933 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml @@ -98,8 +98,7 @@ spec: description: Partition is the Admin Partition for the Name parameter. type: string peer: - description: '[Experimental] Peer is the peer name for the Name - parameter.' + description: Peer is the peer name for the Name parameter. type: string permissions: description: Permissions is the list of all additional L7 attributes diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml index 56b5e72014..bae83ac7b9 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml @@ -84,8 +84,9 @@ spec: to "sequential") and "order-by-locality". type: string regions: - description: The ordered list of the regions of the failover targets. - Valid values can be "us-west-1", "us-west-2", and so on. + description: Regions is the ordered list of the regions + of the failover targets. Valid values can be "us-west-1", + "us-west-2", and so on. items: type: string type: array diff --git a/control-plane/config/webhook/manifests.yaml b/control-plane/config/webhook/manifests.yaml index f96d669544..663ace9d70 100644 --- a/control-plane/config/webhook/manifests.yaml +++ b/control-plane/config/webhook/manifests.yaml @@ -138,7 +138,7 @@ webhooks: service: name: webhook-service namespace: system - path: /mutate-v1alpha1-samenessgroup + path: /mutate-v1alpha1-samenessgroups failurePolicy: Fail name: mutate-samenessgroup.consul.hashicorp.com rules: diff --git a/control-plane/go.mod b/control-plane/go.mod index 23900deb46..5307056ce3 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -10,7 +10,7 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af65262de8 github.com/hashicorp/consul-server-connection-manager v0.1.0 - github.com/hashicorp/consul/api v1.10.1-0.20230331190547-fc64a702f43c + github.com/hashicorp/consul/api v1.10.1-0.20230418163148-eb9f671eafae github.com/hashicorp/consul/sdk v0.13.1 github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f github.com/hashicorp/go-hclog v1.2.2 @@ -25,8 +25,6 @@ require ( github.com/mitchellh/cli v1.1.0 github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/mapstructure v1.5.0 - github.com/onsi/ginkgo v1.16.4 - github.com/onsi/gomega v1.17.0 github.com/stretchr/testify v1.7.2 go.uber.org/zap v1.19.0 golang.org/x/text v0.3.8 @@ -110,7 +108,6 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 // indirect - github.com/nxadm/tail v1.4.8 // indirect github.com/oklog/run v1.0.0 // indirect github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c // indirect github.com/pierrec/lz4 v2.5.2+incompatible // indirect @@ -147,7 +144,6 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/resty.v1 v1.12.0 // indirect gopkg.in/square/go-jose.v2 v2.5.1 // indirect - gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.22.2 // indirect diff --git a/control-plane/go.sum b/control-plane/go.sum index d794bad47f..c045aba7bd 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -353,8 +353,8 @@ github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af6526 github.com/hashicorp/consul-server-connection-manager v0.1.0 h1:XCweGvMHzra88rYv2zxwwuUOjBUdcQmNKVrnQmt/muo= github.com/hashicorp/consul-server-connection-manager v0.1.0/go.mod h1:XVVlO+Yk7aiRpspiHZkrrFVn9BJIiOPnQIzqytPxGaU= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.10.1-0.20230331190547-fc64a702f43c h1:MfDuWW38RozPodXT2aGc5jakoYo9EjgYpZl+vR3//Wg= -github.com/hashicorp/consul/api v1.10.1-0.20230331190547-fc64a702f43c/go.mod h1:f8zVJwBcLdr1IQnfdfszjUM0xzp31Zl3bpws3pL9uFM= +github.com/hashicorp/consul/api v1.10.1-0.20230418163148-eb9f671eafae h1:lYnO52QxlfATRZ1Vo8tQV+lFns7rZ4iAbbi3JN4ZAQw= +github.com/hashicorp/consul/api v1.10.1-0.20230418163148-eb9f671eafae/go.mod h1:f8zVJwBcLdr1IQnfdfszjUM0xzp31Zl3bpws3pL9uFM= github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3usQMXZF6ziK3SzXU= github.com/hashicorp/consul/proto-public v0.1.0/go.mod h1:vs2KkuWwtjkIgA5ezp4YKPzQp4GitV+q/+PvksrA92k= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= From 969b6f91c446b6d9c1de65b56552fd0a1b1fdd19 Mon Sep 17 00:00:00 2001 From: Nathan Coleman Date: Thu, 27 Apr 2023 15:47:08 -0400 Subject: [PATCH 143/340] Adjust API gateway controller deployment appropriately when Vault configured as secrets backend (#2083) * Adjust mount based on whether Vault is enabled as secrets backend * Add changelog entry * Improve wording of changelog entry * Use Vault serverca for CONSUL_CACERT when secrets backend enabled * Add comment to Helm template explaining logic * Add unit test for CONSUL_CACERT with Vault secret path * Add unit tests for removing mounts when Vault is secrets backend --- .changelog/2083.txt | 3 ++ .../api-gateway-controller-deployment.yaml | 13 +++-- .../api-gateway-controller-deployment.bats | 47 +++++++++++++++++++ 3 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 .changelog/2083.txt diff --git a/.changelog/2083.txt b/.changelog/2083.txt new file mode 100644 index 0000000000..23c0c77592 --- /dev/null +++ b/.changelog/2083.txt @@ -0,0 +1,3 @@ +```release-note:bug +api-gateway: fix issue where the API Gateway controller is unable to start up successfully when Vault is configured as the secrets backend +``` diff --git a/charts/consul/templates/api-gateway-controller-deployment.yaml b/charts/consul/templates/api-gateway-controller-deployment.yaml index 86517d7140..8c5c2fa73e 100644 --- a/charts/consul/templates/api-gateway-controller-deployment.yaml +++ b/charts/consul/templates/api-gateway-controller-deployment.yaml @@ -65,7 +65,14 @@ spec: {{- if or (not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots)) .Values.client.enabled }} {{- if .Values.global.tls.enabled }} - name: CONSUL_CACERT + {{- /* When Vault is being used as a secrets backend, auto-encrypt must be enabled. Since clients use a separate + root CA from servers when auto-encrypt is enabled, and our controller communicates with the agent when clients are + enabled, we only use the Vault server CA if clients are disabled and our controller will be communicating w/ the server. */}} + {{- if and (not .Values.client.enabled) .Values.global.secretsBackend.vault.enabled }} + value: /vault/secrets/serverca.crt + {{- else }} value: /consul/tls/ca/tls.crt + {{- end }} {{- end }} {{- end }} - name: HOST_IP @@ -156,7 +163,7 @@ spec: - name: consul-bin mountPath: /consul-bin {{- end }} - {{- if or (not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots)) .Values.client.enabled }} + {{- if or (not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled)) .Values.client.enabled }} {{- if .Values.global.tls.enabled }} {{- if and .Values.client.enabled .Values.global.tls.enableAutoEncrypt }} - name: consul-auto-encrypt-ca-cert @@ -186,7 +193,7 @@ spec: emptyDir: { } {{- end }} {{- if .Values.global.tls.enabled }} - {{- if not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) }} + {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} - name: consul-ca-cert secret: {{- if .Values.global.tls.caCert.secretName }} @@ -253,7 +260,7 @@ spec: - mountPath: /consul/login name: consul-data readOnly: false - {{- if not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) }} + {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} {{- if .Values.global.tls.enabled }} - name: consul-ca-cert mountPath: /consul/tls/ca diff --git a/charts/consul/test/unit/api-gateway-controller-deployment.bats b/charts/consul/test/unit/api-gateway-controller-deployment.bats index 880586ab43..327802af07 100755 --- a/charts/consul/test/unit/api-gateway-controller-deployment.bats +++ b/charts/consul/test/unit/api-gateway-controller-deployment.bats @@ -1522,6 +1522,23 @@ load _helpers [ "${actual}" = "true" ] } +@test "apiGateway/Deployment: CONSUL_CACERT has correct path with Vault as secrets backend and client disabled" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/api-gateway-controller-deployment.yaml \ + --set 'apiGateway.enabled=true' \ + --set 'apiGateway.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.caCert.secretName=foo' \ + --set 'server.enabled=true' \ + --set 'client.enabled=false' \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + . | tee /dev/stderr| + yq '.spec.template.spec.containers[0].env[0].value == "/vault/secrets/serverca.crt"' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + @test "apiGateway/Deployment: CONSUL_CACERT is not set when using tls and useSystemRoots" { cd `chart_dir` local actual=$(helm template \ @@ -1555,6 +1572,21 @@ load _helpers [ "${actual}" = "" ] } +@test "apiGateway/Deployment: consul-ca-cert volume mount is not set when using Vault as a secrets backend" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/api-gateway-controller-deployment.yaml \ + --set 'apiGateway.enabled=true' \ + --set 'apiGateway.image=bar' \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.tls.enabled=true' \ + --set 'server.enabled=true' \ + --set 'global.secretsBackend.vault.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) + [ "${actual}" = "" ] +} + @test "apiGateway/Deployment: consul-ca-cert volume mount is not set on acl-init when using externalServers and useSystemRoots" { cd `chart_dir` local actual=$(helm template \ @@ -1572,6 +1604,21 @@ load _helpers [ "${actual}" = "" ] } +@test "apiGateway/Deployment: consul-ca-cert volume mount is not set on acl-init when using Vault as secrets backend" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/api-gateway-controller-deployment.yaml \ + --set 'apiGateway.enabled=true' \ + --set 'apiGateway.image=bar' \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.tls.enabled=true' \ + --set 'server.enabled=true' \ + --set 'global.secretsBackend.vault.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.initContainers[1].volumeMounts[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) + [ "${actual}" = "" ] +} + @test "apiGateway/Deployment: consul-auto-encrypt-ca-cert volume mount is set when tls.enabled, client.enabled, externalServers, useSystemRoots, and autoencrypt" { cd `chart_dir` local actual=$(helm template \ From 21731124eb8fe9a7b1a8be8e6715a3b98bfeb10c Mon Sep 17 00:00:00 2001 From: "hashicorp-tsccr[bot]" <129506189+hashicorp-tsccr[bot]@users.noreply.github.com> Date: Thu, 27 Apr 2023 17:00:42 -0400 Subject: [PATCH 144/340] Result of tsccr-helper -pin-all-workflows . (#2089) Co-authored-by: hashicorp-tsccr[bot] --- .github/workflows/build.yml | 4 ++-- .github/workflows/jira-issues.yaml | 6 +++--- .github/workflows/jira-pr.yaml | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a00629bde2..43194ea7bf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -162,7 +162,7 @@ jobs: - name: Test rpm package if: ${{ matrix.goos == 'linux' && matrix.component == 'cli' && matrix.goarch == 'amd64'}} - uses: addnab/docker-run-action@v3 # TSCCR: no entry for repository "addnab/docker-run-action" + uses: addnab/docker-run-action@v3 # TSCCR: no entry for repository "addnab/docker-run-action" with: image: registry.access.redhat.com/ubi8/ubi:latest options: -v ${{ github.workspace }}:/work @@ -187,7 +187,7 @@ jobs: - name: Test debian package if: ${{ matrix.goos == 'linux' && matrix.component == 'cli' && matrix.goarch == 'amd64'}} - uses: addnab/docker-run-action@v3 # TSCCR: no entry for repository "addnab/docker-run-action" + uses: addnab/docker-run-action@v3 # TSCCR: no entry for repository "addnab/docker-run-action" with: image: ubuntu:latest options: -v ${{ github.workspace }}:/work diff --git a/.github/workflows/jira-issues.yaml b/.github/workflows/jira-issues.yaml index bddc69c83f..700803a45f 100644 --- a/.github/workflows/jira-issues.yaml +++ b/.github/workflows/jira-issues.yaml @@ -15,7 +15,7 @@ jobs: name: Jira Community Issue sync steps: - name: Login - uses: atlassian/gajira-login@ca13f8850ea309cf44a6e4e0c49d9aa48ac3ca4c # v3 + uses: atlassian/gajira-login@45fd029b9f1d6d8926c6f04175aa80c0e42c9026 # v3.0.1 env: JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} @@ -72,14 +72,14 @@ jobs: - name: Close ticket if: ( github.event.action == 'closed' || github.event.action == 'deleted' ) && steps.search.outputs.issue - uses: atlassian/gajira-transition@4749176faf14633954d72af7a44d7f2af01cc92b # v3 + uses: atlassian/gajira-transition@38fc9cd61b03d6a53dd35fcccda172fe04b36de3 # v3.0.1 with: issue: ${{ steps.search.outputs.issue }} transition: "Closed" - name: Reopen ticket if: github.event.action == 'reopened' && steps.search.outputs.issue - uses: atlassian/gajira-transition@4749176faf14633954d72af7a44d7f2af01cc92b # v3 + uses: atlassian/gajira-transition@38fc9cd61b03d6a53dd35fcccda172fe04b36de3 # v3.0.1 with: issue: ${{ steps.search.outputs.issue }} transition: "To Do" diff --git a/.github/workflows/jira-pr.yaml b/.github/workflows/jira-pr.yaml index c07a92ee77..a285c4c176 100644 --- a/.github/workflows/jira-pr.yaml +++ b/.github/workflows/jira-pr.yaml @@ -13,7 +13,7 @@ jobs: name: Jira sync steps: - name: Login - uses: atlassian/gajira-login@v3.0.0 + uses: atlassian/gajira-login@45fd029b9f1d6d8926c6f04175aa80c0e42c9026 # v3.0.1 env: JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} @@ -52,7 +52,7 @@ jobs: - name: Create ticket if an issue is filed, or if PR not by a team member is opened if: ( github.event.action == 'opened' && steps.is-team-member.outputs.MESSAGE == 'false' ) - uses: tomhjp/gh-action-jira-create@v0.2.0 + uses: tomhjp/gh-action-jira-create@3ed1789cad3521292e591a7cfa703215ec1348bf # v0.2.1 with: project: NET issuetype: "${{ steps.set-ticket-type.outputs.TYPE }}" @@ -72,28 +72,28 @@ jobs: - name: Search if: github.event.action != 'opened' id: search - uses: tomhjp/gh-action-jira-search@v0.2.1 + uses: tomhjp/gh-action-jira-search@04700b457f317c3e341ce90da5a3ff4ce058f2fa # v0.2.2 with: # cf[10089] is Issue Link (use JIRA API to retrieve) jql: 'issuetype = "${{ steps.set-ticket-type.outputs.TYPE }}" and cf[10089] = "${{ github.event.issue.html_url || github.event.pull_request.html_url }}"' - name: Sync comment if: github.event.action == 'created' && steps.search.outputs.issue - uses: tomhjp/gh-action-jira-comment@v0.1.0 + uses: tomhjp/gh-action-jira-comment@6eb6b9ead70221916b6badd118c24535ed220bd9 # v0.2.0 with: issue: ${{ steps.search.outputs.issue }} comment: "${{ github.actor }} ${{ github.event.review.state || 'commented' }}:\n\n${{ github.event.comment.body || github.event.review.body }}\n\n${{ github.event.comment.html_url || github.event.review.html_url }}" - name: Close ticket if: ( github.event.action == 'closed' || github.event.action == 'deleted' ) && steps.search.outputs.issue - uses: atlassian/gajira-transition@v2.0.1 + uses: atlassian/gajira-transition@38fc9cd61b03d6a53dd35fcccda172fe04b36de3 # v3.0.1 with: issue: ${{ steps.search.outputs.issue }} transition: "Closed" - name: Reopen ticket if: github.event.action == 'reopened' && steps.search.outputs.issue - uses: atlassian/gajira-transition@v2.0.1 + uses: atlassian/gajira-transition@38fc9cd61b03d6a53dd35fcccda172fe04b36de3 # v3.0.1 with: issue: ${{ steps.search.outputs.issue }} transition: "To Do" From 7a006b5cfb7b8c0808e9adec472dbe76684f8199 Mon Sep 17 00:00:00 2001 From: Eric Haberkorn Date: Fri, 28 Apr 2023 09:13:51 -0400 Subject: [PATCH 145/340] set consul server locality from k8s node labels (#2093) --- .changelog/2093.txt | 3 + charts/consul/templates/_helpers.tpl | 4 +- .../consul/templates/server-clusterrole.yaml | 16 ++ .../templates/server-clusterrolebinding.yaml | 18 ++ .../consul/templates/server-statefulset.yaml | 27 ++- control-plane/commands.go | 4 + .../subcommand/fetch-server-region/command.go | 158 ++++++++++++++++++ .../fetch-server-region/command_test.go | 114 +++++++++++++ 8 files changed, 337 insertions(+), 7 deletions(-) create mode 100644 .changelog/2093.txt create mode 100644 charts/consul/templates/server-clusterrole.yaml create mode 100644 charts/consul/templates/server-clusterrolebinding.yaml create mode 100644 control-plane/subcommand/fetch-server-region/command.go create mode 100644 control-plane/subcommand/fetch-server-region/command_test.go diff --git a/.changelog/2093.txt b/.changelog/2093.txt new file mode 100644 index 0000000000..20c657e566 --- /dev/null +++ b/.changelog/2093.txt @@ -0,0 +1,3 @@ +```release-note:improvement +control-plane: set agent localities on Consul servers to the server node's `topology.kubernetes.io/region` label. +``` diff --git a/charts/consul/templates/_helpers.tpl b/charts/consul/templates/_helpers.tpl index 3552c8c209..b1feb0dbd6 100644 --- a/charts/consul/templates/_helpers.tpl +++ b/charts/consul/templates/_helpers.tpl @@ -335,7 +335,7 @@ Consul server environment variables for consul-k8s commands. {{- end }} {{- if and .Values.externalServers.enabled .Values.externalServers.skipServerWatch }} - name: CONSUL_SKIP_SERVER_WATCH - value: "true" + value: "true" {{- end }} {{- end -}} @@ -366,7 +366,7 @@ Usage: {{ template "consul.validateCloudSecretKeys" . }} */}} {{- define "consul.validateCloudSecretKeys" -}} -{{- if and .Values.global.cloud.enabled }} +{{- if and .Values.global.cloud.enabled }} {{- if or (and .Values.global.cloud.resourceId.secretName (not .Values.global.cloud.resourceId.secretKey)) (and .Values.global.cloud.resourceId.secretKey (not .Values.global.cloud.resourceId.secretName)) }} {{fail "When either global.cloud.resourceId.secretName or global.cloud.resourceId.secretKey is defined, both must be set."}} {{- end }} diff --git a/charts/consul/templates/server-clusterrole.yaml b/charts/consul/templates/server-clusterrole.yaml new file mode 100644 index 0000000000..c22f562264 --- /dev/null +++ b/charts/consul/templates/server-clusterrole.yaml @@ -0,0 +1,16 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "consul.fullname" . }}-server + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server +rules: +- apiGroups: [""] + resources: ["nodes"] + verbs: + - get diff --git a/charts/consul/templates/server-clusterrolebinding.yaml b/charts/consul/templates/server-clusterrolebinding.yaml new file mode 100644 index 0000000000..854fda870e --- /dev/null +++ b/charts/consul/templates/server-clusterrolebinding.yaml @@ -0,0 +1,18 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "consul.fullname" . }}-server + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "consul.fullname" . }}-server +subjects: +- kind: ServiceAccount + name: {{ template "consul.fullname" . }}-server + namespace: {{ .Release.Namespace }} diff --git a/charts/consul/templates/server-statefulset.yaml b/charts/consul/templates/server-statefulset.yaml index 8b73306fd7..aa9198f127 100644 --- a/charts/consul/templates/server-statefulset.yaml +++ b/charts/consul/templates/server-statefulset.yaml @@ -217,6 +217,22 @@ spec: {{- if .Values.server.priorityClassName }} priorityClassName: {{ .Values.server.priorityClassName | quote }} {{- end }} + initContainers: + - name: locality-init + image: {{ .Values.global.imageK8S }} + env: + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + command: + - "/bin/sh" + - "-ec" + - | + consul-k8s-control-plane fetch-server-region -node-name "$NODE_NAME" -output-file /consul/extra-config/locality.json + volumeMounts: + - name: extra-config + mountPath: /consul/extra-config containers: - name: consul image: "{{ default .Values.global.image .Values.server.image }}" @@ -291,9 +307,9 @@ spec: {{- end }} {{- if .Values.global.cloud.enabled}} # These are mounted as secrets so that the consul server agent can use them. - # - the hcp-go-sdk in consul agent will already look for HCP_CLIENT_ID, HCP_CLIENT_SECRET, HCP_AUTH_URL, + # - the hcp-go-sdk in consul agent will already look for HCP_CLIENT_ID, HCP_CLIENT_SECRET, HCP_AUTH_URL, # HCP_SCADA_ADDRESS, and HCP_API_HOST. so nothing more needs to be done. - # - HCP_RESOURCE_ID is created for use in the + # - HCP_RESOURCE_ID is created for use in the # `-hcl="cloud { resource_id = \"${HCP_RESOURCE_ID}\" }"` logic in the command below. {{- if .Values.global.cloud.clientId.secretName }} - name: HCP_CLIENT_ID @@ -328,7 +344,7 @@ spec: valueFrom: secretKeyRef: name: {{ .Values.global.cloud.apiHost.secretName }} - key: {{ .Values.global.cloud.apiHost.secretKey }} + key: {{ .Values.global.cloud.apiHost.secretKey }} {{- end}} {{- if .Values.global.cloud.scadaAddress.secretName }} - name: HCP_SCADA_ADDRESS @@ -336,7 +352,7 @@ spec: secretKeyRef: name: {{ .Values.global.cloud.scadaAddress.secretName }} key: {{ .Values.global.cloud.scadaAddress.secretKey }} - {{- end}} + {{- end}} {{- end }} {{- include "consul.extraEnvironmentVars" .Values.server | nindent 12 }} command: @@ -375,7 +391,8 @@ spec: -config-dir=/consul/userconfig/{{ .name }} \ {{- end }} {{- end }} - -config-file=/consul/extra-config/extra-from-values.json + -config-file=/consul/extra-config/extra-from-values.json \ + -config-file=/consul/extra-config/locality.json {{- if and .Values.global.cloud.enabled .Values.global.cloud.resourceId.secretName }} -hcl="cloud { resource_id = \"${HCP_RESOURCE_ID}\" }" {{- end }} diff --git a/control-plane/commands.go b/control-plane/commands.go index 4b7cbed362..0da29c938b 100644 --- a/control-plane/commands.go +++ b/control-plane/commands.go @@ -11,6 +11,7 @@ import ( cmdConsulLogout "github.com/hashicorp/consul-k8s/control-plane/subcommand/consul-logout" cmdCreateFederationSecret "github.com/hashicorp/consul-k8s/control-plane/subcommand/create-federation-secret" cmdDeleteCompletedJob "github.com/hashicorp/consul-k8s/control-plane/subcommand/delete-completed-job" + cmdFetchServerRegion "github.com/hashicorp/consul-k8s/control-plane/subcommand/fetch-server-region" cmdGetConsulClientCA "github.com/hashicorp/consul-k8s/control-plane/subcommand/get-consul-client-ca" cmdGossipEncryptionAutogenerate "github.com/hashicorp/consul-k8s/control-plane/subcommand/gossip-encryption-autogenerate" cmdInjectConnect "github.com/hashicorp/consul-k8s/control-plane/subcommand/inject-connect" @@ -90,6 +91,9 @@ func init() { "install-cni": func() (cli.Command, error) { return &cmdInstallCNI.Command{UI: ui}, nil }, + "fetch-server-region": func() (cli.Command, error) { + return &cmdFetchServerRegion.Command{UI: ui}, nil + }, } } diff --git a/control-plane/subcommand/fetch-server-region/command.go b/control-plane/subcommand/fetch-server-region/command.go new file mode 100644 index 0000000000..248ce971e7 --- /dev/null +++ b/control-plane/subcommand/fetch-server-region/command.go @@ -0,0 +1,158 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fetchserverregion + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "os" + "sync" + + "github.com/hashicorp/consul-k8s/control-plane/subcommand/common" + "github.com/hashicorp/consul-k8s/control-plane/subcommand/flags" + "github.com/hashicorp/go-hclog" + "github.com/mitchellh/cli" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" +) + +// The consul-logout command issues a Consul logout API request to delete an ACL token. +type Command struct { + UI cli.Ui + + flagLogLevel string + flagLogJSON bool + flagNodeName string + flagOutputFile string + + flagSet *flag.FlagSet + k8s *flags.K8SFlags + + once sync.Once + help string + logger hclog.Logger + + // for testing + clientset kubernetes.Interface +} + +type Locality struct { + Region string `json:"region"` +} + +type Config struct { + Locality Locality `json:"locality"` +} + +func (c *Command) init() { + c.flagSet = flag.NewFlagSet("", flag.ContinueOnError) + c.flagSet.StringVar(&c.flagLogLevel, "log-level", "info", + "Log verbosity level. Supported values (in order of detail) are \"trace\", "+ + "\"debug\", \"info\", \"warn\", and \"error\".") + c.flagSet.BoolVar(&c.flagLogJSON, "log-json", false, + "Enable or disable JSON output format for logging.") + c.flagSet.StringVar(&c.flagNodeName, "node-name", "", + "Specifies the node name that will be used.") + c.flagSet.StringVar(&c.flagOutputFile, "output-file", "", + "The file path for writing the locality portion of a Consul agent configuration to.") + + c.k8s = &flags.K8SFlags{} + flags.Merge(c.flagSet, c.k8s.Flags()) + + c.help = flags.Usage(help, c.flagSet) + +} + +func (c *Command) Run(args []string) int { + var err error + c.once.Do(c.init) + + if err := c.flagSet.Parse(args); err != nil { + return 1 + } + + if c.logger == nil { + c.logger, err = common.Logger(c.flagLogLevel, c.flagLogJSON) + if err != nil { + c.UI.Error(err.Error()) + return 1 + } + } + + if c.flagNodeName == "" { + c.UI.Error("-node-name is required") + return 1 + } + + if c.flagOutputFile == "" { + c.UI.Error("-output-file is required") + return 1 + } + + if c.clientset == nil { + config, err := rest.InClusterConfig() + if err != nil { + // This just allows us to test it locally. + kubeconfig := clientcmd.RecommendedHomeFile + config, err = clientcmd.BuildConfigFromFlags("", kubeconfig) + if err != nil { + c.UI.Error(err.Error()) + return 1 + } + } + + c.clientset, err = kubernetes.NewForConfig(config) + if err != nil { + c.UI.Error(err.Error()) + return 1 + } + } + + config := c.fetchLocalityConfig() + + jsonData, err := json.Marshal(config) + if err != nil { + c.UI.Error(err.Error()) + return 1 + } + + err = os.WriteFile(c.flagOutputFile, jsonData, 0644) + if err != nil { + c.UI.Error(fmt.Sprintf("Error writing locality file: %s", err)) + return 1 + } + + return 0 +} + +func (c *Command) fetchLocalityConfig() Config { + var cfg Config + node, err := c.clientset.CoreV1().Nodes().Get(context.Background(), c.flagNodeName, metav1.GetOptions{}) + if err != nil { + return cfg + } + + cfg.Locality.Region = node.Labels[corev1.LabelTopologyRegion] + + return cfg +} + +func (c *Command) Synopsis() string { return synopsis } +func (c *Command) Help() string { + c.once.Do(c.init) + return c.help +} + +const synopsis = "Fetch the cloud region for a Consul server from the Kubernetes node's region label." +const help = ` +Usage: consul-k8s-control-plane fetch-server-region [options] + + Fetch the region for a Consul server. + Not intended for stand-alone use. +` diff --git a/control-plane/subcommand/fetch-server-region/command_test.go b/control-plane/subcommand/fetch-server-region/command_test.go new file mode 100644 index 0000000000..a64dc9be95 --- /dev/null +++ b/control-plane/subcommand/fetch-server-region/command_test.go @@ -0,0 +1,114 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fetchserverregion + +import ( + "os" + "testing" + + "github.com/mitchellh/cli" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/fake" +) + +func TestRun_FlagValidation(t *testing.T) { + t.Parallel() + + ui := cli.NewMockUi() + cmd := Command{ + UI: ui, + } + + cases := map[string]struct { + args []string + err string + }{ + "missing node name": { + args: []string{}, + err: "-node-name is required", + }, + "missing output-file": { + args: []string{"-node-name", "n1"}, + err: "-output-file is required", + }, + } + + for n, c := range cases { + c := c + t.Run(n, func(t *testing.T) { + responseCode := cmd.Run(c.args) + require.Equal(t, 1, responseCode, ui.ErrorWriter.String()) + require.Contains(t, ui.ErrorWriter.String(), c.err) + }) + } +} + +func TestRun(t *testing.T) { + t.Parallel() + + cases := map[string]struct { + region string + expected string + missingNode bool + }{ + "no region": { + expected: `{"locality":{"region":""}}`, + }, + "region": { + region: "us-east-1", + expected: `{"locality":{"region":"us-east-1"}}`, + }, + "missing node": { + region: "us-east-1", + missingNode: true, + expected: `{"locality":{"region":""}}`, + }, + } + + for n, c := range cases { + c := c + t.Run(n, func(t *testing.T) { + outputFile, err := os.CreateTemp("", "ca") + require.NoError(t, err) + t.Cleanup(func() { + os.RemoveAll(outputFile.Name()) + }) + + var objs []runtime.Object + if !c.missingNode { + objs = append(objs, &v1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-node", + Labels: map[string]string{ + corev1.LabelTopologyRegion: c.region, + }, + }, + }) + } + + k8s := fake.NewSimpleClientset(objs...) + ui := cli.NewMockUi() + cmd := Command{ + UI: ui, + clientset: k8s, + } + + responseCode := cmd.Run([]string{ + "-node-name", + "my-node", + "-output-file", + outputFile.Name(), + }) + require.Equal(t, 0, responseCode, ui.ErrorWriter.String()) + require.NoError(t, err) + cfg, err := os.ReadFile(outputFile.Name()) + require.NoError(t, err) + require.Equal(t, c.expected, string(cfg)) + }) + } +} From 28f396ac12fe04131e4380d4d968a688ef8b3494 Mon Sep 17 00:00:00 2001 From: malizz Date: Fri, 28 Apr 2023 11:44:16 -0700 Subject: [PATCH 146/340] add sameness group to service resolver, update manifests (#2086) * add sameness group to service resolver, update manifests * get the latest api and update acceptance tests * get the latest api in acceptanc tests * update validation code, remove dynamic validations, update tests * check nil pointer * go get latest api * revert acceptance changes --- acceptance/go.mod | 2 +- acceptance/go.sum | 2 +- .../bases/crds-oss/serviceresolver.yaml | 2 +- .../templates/crd-serviceresolvers.yaml | 8 + .../api/v1alpha1/serviceresolver_types.go | 213 ++++++- .../v1alpha1/serviceresolver_types_test.go | 585 +++++++++++++++++- ...consul.hashicorp.com_serviceresolvers.yaml | 8 + control-plane/go.mod | 10 +- control-plane/go.sum | 22 +- 9 files changed, 792 insertions(+), 60 deletions(-) diff --git a/acceptance/go.mod b/acceptance/go.mod index 66fa6c4b44..503ca5333c 100644 --- a/acceptance/go.mod +++ b/acceptance/go.mod @@ -101,4 +101,4 @@ require ( k8s.io/utils v0.0.0-20220812165043-ad590609e2e5 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect sigs.k8s.io/yaml v1.2.0 // indirect -) +) \ No newline at end of file diff --git a/acceptance/go.sum b/acceptance/go.sum index 44969f22ae..3301c3a7da 100644 --- a/acceptance/go.sum +++ b/acceptance/go.sum @@ -1213,4 +1213,4 @@ sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3 sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/crds-oss/serviceresolver.yaml b/acceptance/tests/fixtures/bases/crds-oss/serviceresolver.yaml index a71da92e35..fc236966d6 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/serviceresolver.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/serviceresolver.yaml @@ -7,4 +7,4 @@ metadata: name: resolver spec: redirect: - service: bar + service: bar \ No newline at end of file diff --git a/charts/consul/templates/crd-serviceresolvers.yaml b/charts/consul/templates/crd-serviceresolvers.yaml index 04d6dd9754..2a6f7923b8 100644 --- a/charts/consul/templates/crd-serviceresolvers.yaml +++ b/charts/consul/templates/crd-serviceresolvers.yaml @@ -95,6 +95,10 @@ spec: type: string type: array type: object + samenessGroup: + description: SamenessGroup is the name of the sameness group + to try during failover. + type: string service: description: Service is the service to resolve instead of the default as the failover group of instances during failover. @@ -248,6 +252,10 @@ spec: description: Peer is the name of the cluster peer to resolve the service from instead of the current one. type: string + samenessGroup: + description: SamenessGroup is the name of the sameness group to + resolve the service from instead of the current one. + type: string service: description: Service is a service to resolve instead of the current service. diff --git a/control-plane/api/v1alpha1/serviceresolver_types.go b/control-plane/api/v1alpha1/serviceresolver_types.go index 48483550ba..75aa44f6b9 100644 --- a/control-plane/api/v1alpha1/serviceresolver_types.go +++ b/control-plane/api/v1alpha1/serviceresolver_types.go @@ -5,16 +5,19 @@ package v1alpha1 import ( "encoding/json" + "regexp" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "github.com/hashicorp/consul-k8s/control-plane/api/common" - capi "github.com/hashicorp/consul/api" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" + + "github.com/hashicorp/consul-k8s/control-plane/api/common" + capi "github.com/hashicorp/consul/api" + "github.com/hashicorp/go-bexpr" ) const ServiceResolverKubeKind string = "serviceresolver" @@ -97,6 +100,8 @@ type ServiceResolverRedirect struct { // Peer is the name of the cluster peer to resolve the service from instead // of the current one. Peer string `json:"peer,omitempty"` + // SamenessGroup is the name of the sameness group to resolve the service from instead of the current one. + SamenessGroup string `json:"samenessGroup,omitempty"` } type ServiceResolverSubsetMap map[string]ServiceResolverSubset @@ -133,6 +138,8 @@ type ServiceResolverFailover struct { Targets []ServiceResolverFailoverTarget `json:"targets,omitempty"` // Policy specifies the exact mechanism used for failover. Policy *FailoverPolicy `json:"policy,omitempty"` + // SamenessGroup is the name of the sameness group to try during failover. + SamenessGroup string `json:"samenessGroup,omitempty"` } type ServiceResolverFailoverTarget struct { @@ -322,14 +329,17 @@ func (in *ServiceResolver) Validate(consulMeta common.ConsulMeta) error { var errs field.ErrorList path := field.NewPath("spec") - for k, v := range in.Spec.Failover { - if err := v.validate(path.Child("failover").Key(k)); err != nil { - errs = append(errs, err) - } + for subset, f := range in.Spec.Failover { + errs = append(errs, f.validate(path.Child("failover").Key(subset), consulMeta)...) + } + if len(in.Spec.Failover) > 0 && in.Spec.Redirect != nil { + asJSON, _ := json.Marshal(in) + errs = append(errs, field.Invalid(path, string(asJSON), "service resolver redirect and failover cannot both be set")) } + errs = append(errs, in.Spec.Redirect.validate(path.Child("redirect"), consulMeta)...) + errs = append(errs, in.Spec.Subsets.validate(path.Child("subsets"))...) errs = append(errs, in.Spec.LoadBalancer.validate(path.Child("loadBalancer"))...) - errs = append(errs, in.validateEnterprise(consulMeta)...) if len(errs) > 0 { @@ -356,6 +366,31 @@ func (in ServiceResolverSubsetMap) toConsul() map[string]capi.ServiceResolverSub return m } +func (in ServiceResolverSubsetMap) validate(path *field.Path) field.ErrorList { + var errs field.ErrorList + if len(in) == 0 { + return nil + } + validServiceSubset := regexp.MustCompile(`^[a-z0-9]([a-z0-9-]*[a-z0-9])?$`) + + for name, subset := range in { + indexPath := path.Key(name) + + if name == "" { + errs = append(errs, field.Invalid(indexPath, name, "subset defined with empty name")) + } + if !validServiceSubset.MatchString(name) { + errs = append(errs, field.Invalid(indexPath, name, "subset name must begin or end with lower case alphanumeric characters, and contain lower case alphanumeric characters or '-' in between")) + } + if subset.Filter != "" { + if _, err := bexpr.CreateEvaluator(subset.Filter, nil); err != nil { + errs = append(errs, field.Invalid(indexPath.Child("filter"), subset.Filter, "filter for subset is not a valid expression")) + } + } + } + return errs +} + func (in ServiceResolverSubset) toConsul() capi.ServiceResolverSubset { return capi.ServiceResolverSubset{ Filter: in.Filter, @@ -374,7 +409,74 @@ func (in *ServiceResolverRedirect) toConsul() *capi.ServiceResolverRedirect { Datacenter: in.Datacenter, Partition: in.Partition, Peer: in.Peer, + SamenessGroup: in.SamenessGroup, + } +} + +func (in *ServiceResolverRedirect) validate(path *field.Path, consulMeta common.ConsulMeta) field.ErrorList { + var errs field.ErrorList + if in == nil { + return nil + } + + asJSON, _ := json.Marshal(in) + if in.isEmpty() { + errs = append(errs, field.Invalid(path, "{}", + "service resolver redirect cannot be empty")) + } + + if consulMeta.Partition != "default" && in.Datacenter != "" { + errs = append(errs, field.Invalid(path.Child("datacenter"), in.Datacenter, + "cross-datacenter redirect is only supported in the default partition")) + } + if consulMeta.Partition != in.Partition && in.Datacenter != "" { + errs = append(errs, field.Invalid(path.Child("partition"), in.Partition, + "cross-datacenter and cross-partition redirect is not supported")) + } + + switch { + case in.SamenessGroup != "" && in.ServiceSubset != "": + errs = append(errs, field.Invalid(path, string(asJSON), + "samenessGroup cannot be set with serviceSubset")) + case in.SamenessGroup != "" && in.Partition != "": + errs = append(errs, field.Invalid(path, string(asJSON), + "partition cannot be set with samenessGroup")) + case in.SamenessGroup != "" && in.Datacenter != "": + errs = append(errs, field.Invalid(path, string(asJSON), + "samenessGroup cannot be set with datacenter")) + case in.Peer != "" && in.ServiceSubset != "": + errs = append(errs, field.Invalid(path, string(asJSON), + "peer cannot be set with serviceSubset")) + case in.Peer != "" && in.Partition != "": + errs = append(errs, field.Invalid(path, string(asJSON), + "partition cannot be set with peer")) + case in.Peer != "" && in.Datacenter != "": + errs = append(errs, field.Invalid(path, string(asJSON), + "peer cannot be set with datacenter")) + case in.Service == "": + if in.ServiceSubset != "" { + errs = append(errs, field.Invalid(path, string(asJSON), + "serviceSubset defined without service")) + } + if in.Namespace != "" { + errs = append(errs, field.Invalid(path, string(asJSON), + "namespace defined without service")) + } + if in.Partition != "" { + errs = append(errs, field.Invalid(path, string(asJSON), + "partition defined without service")) + } + if in.Peer != "" { + errs = append(errs, field.Invalid(path, string(asJSON), + "peer defined without service")) + } } + + return errs +} + +func (in *ServiceResolverRedirect) isEmpty() bool { + return in.Service == "" && in.ServiceSubset == "" && in.Namespace == "" && in.Partition == "" && in.Datacenter == "" && in.Peer == "" && in.SamenessGroup == "" } func (in ServiceResolverFailoverMap) toConsul() map[string]capi.ServiceResolverFailover { @@ -383,27 +485,38 @@ func (in ServiceResolverFailoverMap) toConsul() map[string]capi.ServiceResolverF } m := make(map[string]capi.ServiceResolverFailover) for k, v := range in { - m[k] = v.toConsul() + if f := v.toConsul(); f != nil { + m[k] = *f + } } return m } -func (in ServiceResolverFailover) toConsul() capi.ServiceResolverFailover { +func (in *ServiceResolverFailover) toConsul() *capi.ServiceResolverFailover { + if in == nil { + return nil + } var targets []capi.ServiceResolverFailoverTarget for _, target := range in.Targets { targets = append(targets, target.toConsul()) } - return capi.ServiceResolverFailover{ + var policy *capi.ServiceResolverFailoverPolicy + if in.Policy != nil { + policy = &capi.ServiceResolverFailoverPolicy{ + Mode: in.Policy.Mode, + Regions: in.Policy.Regions, + } + } + + return &capi.ServiceResolverFailover{ Service: in.Service, ServiceSubset: in.ServiceSubset, Namespace: in.Namespace, Datacenters: in.Datacenters, Targets: targets, - Policy: &capi.ServiceResolverFailoverPolicy{ - Mode: in.Policy.Mode, - Regions: in.Policy.Regions, - }, + Policy: policy, + SamenessGroup: in.SamenessGroup, } } @@ -513,17 +626,79 @@ func (in *ServiceResolver) validateEnterprise(consulMeta common.ConsulMeta) fiel } func (in *ServiceResolverFailover) isEmpty() bool { - return in.Service == "" && in.ServiceSubset == "" && in.Namespace == "" && len(in.Datacenters) == 0 && len(in.Targets) == 0 && in.Policy == nil + return in.Service == "" && in.ServiceSubset == "" && in.Namespace == "" && len(in.Datacenters) == 0 && len(in.Targets) == 0 && in.Policy == nil && in.SamenessGroup == "" } -func (in *ServiceResolverFailover) validate(path *field.Path) *field.Error { +func (in *ServiceResolverFailover) validate(path *field.Path, consulMeta common.ConsulMeta) field.ErrorList { + var errs field.ErrorList if in.isEmpty() { // NOTE: We're passing "{}" here as our value because we know that the // error is we have an empty object. - return field.Invalid(path, "{}", - "service, serviceSubset, namespace, datacenters, policy, and targets cannot all be empty at once") + errs = append(errs, field.Invalid(path, "{}", + "service, serviceSubset, namespace, datacenters, policy, and targets cannot all be empty at once")) } - return nil + + if consulMeta.Partition != "default" && len(in.Datacenters) != 0 { + errs = append(errs, field.Invalid(path.Child("datacenters"), in.Datacenters, + "cross-datacenter failover is only supported in the default partition")) + } + + errs = append(errs, in.Policy.validate(path.Child("policy"))...) + + asJSON, _ := json.Marshal(in) + if in.SamenessGroup != "" { + switch { + case len(in.Datacenters) > 0: + errs = append(errs, field.Invalid(path, string(asJSON), + "samenessGroup cannot be set with datacenters")) + case in.ServiceSubset != "": + errs = append(errs, field.Invalid(path, string(asJSON), + "samenessGroup cannot be set with serviceSubset")) + case len(in.Targets) > 0: + errs = append(errs, field.Invalid(path, string(asJSON), + "samenessGroup cannot be set with targets")) + } + } + + if len(in.Datacenters) != 0 && len(in.Targets) != 0 { + errs = append(errs, field.Invalid(path, string(asJSON), + "targets cannot be set with datacenters")) + } + + if in.ServiceSubset != "" && len(in.Targets) != 0 { + errs = append(errs, field.Invalid(path, string(asJSON), + "targets cannot be set with serviceSubset")) + } + + if in.Service != "" && len(in.Targets) != 0 { + errs = append(errs, field.Invalid(path, string(asJSON), + "targets cannot be set with service")) + } + + for i, target := range in.Targets { + asJSON, _ := json.Marshal(target) + switch { + case target.Peer != "" && target.ServiceSubset != "": + errs = append(errs, field.Invalid(path.Child("targets").Index(i), string(asJSON), + "target.peer cannot be set with target.serviceSubset")) + case target.Peer != "" && target.Partition != "": + errs = append(errs, field.Invalid(path.Child("targets").Index(i), string(asJSON), + "target.partition cannot be set with target.peer")) + case target.Peer != "" && target.Datacenter != "": + errs = append(errs, field.Invalid(path.Child("targets").Index(i), string(asJSON), + "target.peer cannot be set with target.datacenter")) + case target.Partition != "" && target.Datacenter != "": + errs = append(errs, field.Invalid(path.Child("targets").Index(i), string(asJSON), + "target.partition cannot be set with target.datacenter")) + } + } + + for i, dc := range in.Datacenters { + if dc == "" { + errs = append(errs, field.Invalid(path.Child("datacenters").Index(i), "", "found empty datacenter")) + } + } + return errs } func (in *LoadBalancer) validate(path *field.Path) field.ErrorList { diff --git a/control-plane/api/v1alpha1/serviceresolver_types_test.go b/control-plane/api/v1alpha1/serviceresolver_types_test.go index c82394784d..d09f0809c8 100644 --- a/control-plane/api/v1alpha1/serviceresolver_types_test.go +++ b/control-plane/api/v1alpha1/serviceresolver_types_test.go @@ -4,6 +4,7 @@ package v1alpha1 import ( + "strings" "testing" "time" @@ -12,6 +13,7 @@ import ( "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/validation/field" ) func TestServiceResolver_MatchesConsul(t *testing.T) { @@ -74,6 +76,7 @@ func TestServiceResolver_MatchesConsul(t *testing.T) { Mode: "sequential", Regions: []string{"us-west-2"}, }, + SamenessGroup: "sg2", }, "failover2": { Service: "failover2", @@ -84,6 +87,7 @@ func TestServiceResolver_MatchesConsul(t *testing.T) { Mode: "", Regions: []string{"us-west-1"}, }, + SamenessGroup: "sg3", }, "failover3": { Targets: []ServiceResolverFailoverTarget{ @@ -153,6 +157,7 @@ func TestServiceResolver_MatchesConsul(t *testing.T) { Mode: "sequential", Regions: []string{"us-west-2"}, }, + SamenessGroup: "sg2", }, "failover2": { Service: "failover2", @@ -163,6 +168,7 @@ func TestServiceResolver_MatchesConsul(t *testing.T) { Mode: "", Regions: []string{"us-west-1"}, }, + SamenessGroup: "sg3", }, "failover3": { Targets: []capi.ServiceResolverFailoverTarget{ @@ -281,6 +287,7 @@ func TestServiceResolver_ToConsul(t *testing.T) { Mode: "sequential", Regions: []string{"us-west-2"}, }, + SamenessGroup: "sg2", }, "failover2": { Service: "failover2", @@ -291,6 +298,7 @@ func TestServiceResolver_ToConsul(t *testing.T) { Mode: "", Regions: []string{"us-west-1"}, }, + SamenessGroup: "sg3", }, "failover3": { Targets: []ServiceResolverFailoverTarget{ @@ -360,6 +368,7 @@ func TestServiceResolver_ToConsul(t *testing.T) { Mode: "sequential", Regions: []string{"us-west-2"}, }, + SamenessGroup: "sg2", }, "failover2": { Service: "failover2", @@ -370,6 +379,7 @@ func TestServiceResolver_ToConsul(t *testing.T) { Mode: "", Regions: []string{"us-west-1"}, }, + SamenessGroup: "sg3", }, "failover3": { Targets: []capi.ServiceResolverFailoverTarget{ @@ -543,16 +553,15 @@ func TestServiceResolver_Validate(t *testing.T) { Name: "foo", }, Spec: ServiceResolverSpec{ - Redirect: &ServiceResolverRedirect{ - Service: "bar", - Namespace: "namespace-a", - }, Failover: map[string]ServiceResolverFailover{ - "failA": { + "v1": { Service: "baz", Namespace: "namespace-b", }, }, + Subsets: map[string]ServiceResolverSubset{ + "v1": {Filter: "Service.Meta.version == v1"}, + }, }, }, namespacesEnabled: true, @@ -568,10 +577,8 @@ func TestServiceResolver_Validate(t *testing.T) { Redirect: &ServiceResolverRedirect{ Service: "bar", }, - Failover: map[string]ServiceResolverFailover{ - "failA": { - Service: "baz", - }, + Subsets: map[string]ServiceResolverSubset{ + "v1": {Filter: "Service.Meta.version == v1"}, }, }, }, @@ -585,17 +592,15 @@ func TestServiceResolver_Validate(t *testing.T) { Name: "foo", }, Spec: ServiceResolverSpec{ - Redirect: &ServiceResolverRedirect{ - Service: "bar", - Namespace: "namespace-a", - Partition: "other", - }, Failover: map[string]ServiceResolverFailover{ - "failA": { + "v1": { Service: "baz", Namespace: "namespace-b", }, }, + Subsets: map[string]ServiceResolverSubset{ + "v1": {Filter: "Service.Meta.version == v1"}, + }, }, }, namespacesEnabled: true, @@ -611,11 +616,6 @@ func TestServiceResolver_Validate(t *testing.T) { Redirect: &ServiceResolverRedirect{ Service: "bar", }, - Failover: map[string]ServiceResolverFailover{ - "failA": { - Service: "baz", - }, - }, }, }, namespacesEnabled: false, @@ -629,13 +629,13 @@ func TestServiceResolver_Validate(t *testing.T) { }, Spec: ServiceResolverSpec{ Failover: map[string]ServiceResolverFailover{ - "failA": { + "v1": { Service: "", ServiceSubset: "", Namespace: "", Datacenters: nil, }, - "failB": { + "v2": { Service: "", ServiceSubset: "", Namespace: "", @@ -646,10 +646,32 @@ func TestServiceResolver_Validate(t *testing.T) { }, namespacesEnabled: false, expectedErrMsgs: []string{ - "spec.failover[failA]: Invalid value: \"{}\": service, serviceSubset, namespace, datacenters, policy, and targets cannot all be empty at once", - "spec.failover[failB]: Invalid value: \"{}\": service, serviceSubset, namespace, datacenters, policy, and targets cannot all be empty at once", + "spec.failover[v1]: Invalid value: \"{}\": service, serviceSubset, namespace, datacenters, policy, and targets cannot all be empty at once", + "spec.failover[v2]: Invalid value: \"{}\": service, serviceSubset, namespace, datacenters, policy, and targets cannot all be empty at once", }, }, + "service resolver redirect and failover cannot both be set": { + input: &ServiceResolver{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: ServiceResolverSpec{ + Redirect: &ServiceResolverRedirect{ + Service: "bar", + Namespace: "namespace-a", + }, + Failover: map[string]ServiceResolverFailover{ + "failA": { + Service: "baz", + Namespace: "namespace-b", + }, + }, + }, + }, + namespacesEnabled: true, + partitionsEnabled: false, + expectedErrMsgs: []string{"service resolver redirect and failover cannot both be set"}, + }, "hashPolicy.field invalid": { input: &ServiceResolver{ ObjectMeta: metav1.ObjectMeta{ @@ -723,11 +745,19 @@ func TestServiceResolver_Validate(t *testing.T) { }, }, }, + Subsets: map[string]ServiceResolverSubset{ + "": { + Filter: "random string", + }, + }, }, }, namespacesEnabled: false, expectedErrMsgs: []string{ - `serviceresolver.consul.hashicorp.com "foo" is invalid: spec.loadBalancer.hashPolicies[0]: Invalid value: "{\"field\":\"header\",\"sourceIP\":true}": cannot set both field and sourceIP`, + `spec.loadBalancer.hashPolicies[0]: Invalid value: "{\"field\":\"header\",\"sourceIP\":true}": cannot set both field and sourceIP`, + `subset defined with empty name`, + `subset name must begin or end with lower case alphanumeric characters, and contain lower case alphanumeric characters or '-' in between`, + `filter for subset is not a valid expression`, }, }, "hashPolicy nothing set is valid": { @@ -778,6 +808,7 @@ func TestServiceResolver_Validate(t *testing.T) { }, Spec: ServiceResolverSpec{ Redirect: &ServiceResolverRedirect{ + Service: "bar", Namespace: "namespace-a", }, }, @@ -794,6 +825,7 @@ func TestServiceResolver_Validate(t *testing.T) { }, Spec: ServiceResolverSpec{ Redirect: &ServiceResolverRedirect{ + Service: "bar", Namespace: "namespace-a", Partition: "other", }, @@ -812,14 +844,19 @@ func TestServiceResolver_Validate(t *testing.T) { }, Spec: ServiceResolverSpec{ Failover: map[string]ServiceResolverFailover{ - "failA": { + "v1": { Namespace: "namespace-a", }, }, + Subsets: map[string]ServiceResolverSubset{ + "v1": { + Filter: "Service.Meta.version == v1", + }, + }, }, }, expectedErrMsgs: []string{ - "serviceresolver.consul.hashicorp.com \"foo\" is invalid: spec.failover[failA].namespace: Invalid value: \"namespace-a\": Consul Enterprise namespaces must be enabled to set failover.namespace", + "serviceresolver.consul.hashicorp.com \"foo\" is invalid: spec.failover[v1].namespace: Invalid value: \"namespace-a\": Consul Enterprise namespaces must be enabled to set failover.namespace", }, namespacesEnabled: false, }, @@ -860,3 +897,497 @@ func TestServiceResolver_Validate(t *testing.T) { }) } } + +func TestServiceResolverRedirect_ToConsul(t *testing.T) { + cases := map[string]struct { + Ours *ServiceResolverRedirect + Exp *capi.ServiceResolverRedirect + }{ + "nil": { + Ours: nil, + Exp: nil, + }, + "empty fields": { + Ours: &ServiceResolverRedirect{}, + Exp: &capi.ServiceResolverRedirect{}, + }, + "every field set": { + Ours: &ServiceResolverRedirect{ + Service: "foo", + ServiceSubset: "v1", + Namespace: "ns1", + Datacenter: "dc1", + Partition: "default", + Peer: "peer1", + SamenessGroup: "sg1", + }, + Exp: &capi.ServiceResolverRedirect{ + Service: "foo", + ServiceSubset: "v1", + Namespace: "ns1", + Datacenter: "dc1", + Partition: "default", + Peer: "peer1", + SamenessGroup: "sg1", + }, + }, + } + for name, c := range cases { + t.Run(name, func(t *testing.T) { + actual := c.Ours.toConsul() + require.Equal(t, c.Exp, actual) + }) + } +} + +func TestServiceResolverRedirect_Validate(t *testing.T) { + cases := map[string]struct { + input *ServiceResolverRedirect + consulMeta common.ConsulMeta + expectedErrMsgs []string + }{ + "empty redirect": { + input: &ServiceResolverRedirect{}, + consulMeta: common.ConsulMeta{}, + expectedErrMsgs: []string{ + "service resolver redirect cannot be empty", + }, + }, + "cross-datacenter redirect is only supported in the default partition": { + input: &ServiceResolverRedirect{ + Datacenter: "dc2", + Partition: "p2", + Service: "foo", + }, + consulMeta: common.ConsulMeta{ + Partition: "p2", + PartitionsEnabled: true, + }, + expectedErrMsgs: []string{ + "cross-datacenter redirect is only supported in the default partition", + }, + }, + "cross-datacenter and cross-partition redirect is not supported": { + input: &ServiceResolverRedirect{ + Partition: "p1", + Datacenter: "dc2", + Service: "foo", + }, + consulMeta: common.ConsulMeta{ + Partition: "default", + PartitionsEnabled: true, + }, + expectedErrMsgs: []string{ + "cross-datacenter and cross-partition redirect is not supported", + }, + }, + "samenessGroup cannot be set with serviceSubset": { + input: &ServiceResolverRedirect{ + Service: "foo", + ServiceSubset: "v1", + SamenessGroup: "sg2", + }, + expectedErrMsgs: []string{ + "samenessGroup cannot be set with serviceSubset", + }, + }, + "samenessGroup cannot be set with partition": { + input: &ServiceResolverRedirect{ + Partition: "default", + Service: "foo", + SamenessGroup: "sg2", + }, + consulMeta: common.ConsulMeta{ + Partition: "default", + PartitionsEnabled: true, + }, + expectedErrMsgs: []string{ + "partition cannot be set with samenessGroup", + }, + }, + "samenessGroup cannot be set with datacenter": { + input: &ServiceResolverRedirect{ + Datacenter: "dc2", + Service: "foo", + SamenessGroup: "sg2", + }, + consulMeta: common.ConsulMeta{ + Partition: "default", + PartitionsEnabled: true, + }, + expectedErrMsgs: []string{ + "cross-datacenter and cross-partition redirect is not supported", + "samenessGroup cannot be set with datacenter", + }, + }, + "peer cannot be set with serviceSubset": { + input: &ServiceResolverRedirect{ + Peer: "p2", + Service: "foo", + ServiceSubset: "v1", + }, + consulMeta: common.ConsulMeta{ + Partition: "default", + PartitionsEnabled: true, + }, + expectedErrMsgs: []string{ + "peer cannot be set with serviceSubset", + }, + }, + "partition cannot be set with peer": { + input: &ServiceResolverRedirect{ + Partition: "default", + Peer: "p2", + Service: "foo", + }, + consulMeta: common.ConsulMeta{ + Partition: "default", + PartitionsEnabled: true, + }, + expectedErrMsgs: []string{ + "partition cannot be set with peer", + }, + }, + "peer cannot be set with datacenter": { + input: &ServiceResolverRedirect{ + Peer: "p2", + Service: "foo", + Datacenter: "dc2", + }, + consulMeta: common.ConsulMeta{ + Partition: "default", + PartitionsEnabled: true, + }, + expectedErrMsgs: []string{ + "peer cannot be set with datacenter", + "cross-datacenter and cross-partition redirect is not supported", + }, + }, + "serviceSubset defined without service": { + input: &ServiceResolverRedirect{ + ServiceSubset: "v1", + }, + consulMeta: common.ConsulMeta{ + PartitionsEnabled: true, + }, + expectedErrMsgs: []string{ + "serviceSubset defined without service", + }, + }, + "namespace defined without service": { + input: &ServiceResolverRedirect{ + Namespace: "ns1", + }, + consulMeta: common.ConsulMeta{ + PartitionsEnabled: true, + }, + expectedErrMsgs: []string{ + "namespace defined without service", + }, + }, + "partition defined without service": { + input: &ServiceResolverRedirect{ + Partition: "default", + }, + consulMeta: common.ConsulMeta{ + Partition: "default", + PartitionsEnabled: true, + }, + expectedErrMsgs: []string{ + "partition defined without service", + }, + }, + "peer defined without service": { + input: &ServiceResolverRedirect{ + Peer: "p2", + }, + consulMeta: common.ConsulMeta{ + PartitionsEnabled: true, + }, + expectedErrMsgs: []string{ + "peer defined without service", + }, + }, + } + + path := field.NewPath("spec.redirect") + for name, testCase := range cases { + t.Run(name, func(t *testing.T) { + errList := testCase.input.validate(path, testCase.consulMeta) + compareErrorLists(t, testCase.expectedErrMsgs, errList) + }) + } +} + +func compareErrorLists(t *testing.T, expectedErrMsgs []string, errList field.ErrorList) { + if len(expectedErrMsgs) != 0 { + require.Equal(t, len(expectedErrMsgs), len(errList)) + for _, m := range expectedErrMsgs { + found := false + for _, e := range errList { + errMsg := e.ErrorBody() + if strings.Contains(errMsg, m) { + found = true + break + } + } + require.Equal(t, true, found) + } + } else { + require.Equal(t, 0, len(errList)) + } +} + +func TestServiceResolverFailover_ToConsul(t *testing.T) { + cases := map[string]struct { + Ours *ServiceResolverFailover + Exp *capi.ServiceResolverFailover + }{ + "nil": { + Ours: nil, + Exp: nil, + }, + "empty fields": { + Ours: &ServiceResolverFailover{}, + Exp: &capi.ServiceResolverFailover{}, + }, + "every field set": { + Ours: &ServiceResolverFailover{ + Service: "foo", + ServiceSubset: "v1", + Namespace: "ns1", + Datacenters: []string{"dc1"}, + Targets: []ServiceResolverFailoverTarget{ + { + Peer: "p2", + }, + }, + Policy: &FailoverPolicy{ + Mode: "sequential", + Regions: []string{"us-west-2"}, + }, + SamenessGroup: "sg1", + }, + Exp: &capi.ServiceResolverFailover{ + Service: "foo", + ServiceSubset: "v1", + Namespace: "ns1", + Datacenters: []string{"dc1"}, + Targets: []capi.ServiceResolverFailoverTarget{ + { + Peer: "p2", + }, + }, + Policy: &capi.ServiceResolverFailoverPolicy{ + Mode: "sequential", + Regions: []string{"us-west-2"}, + }, + SamenessGroup: "sg1", + }, + }, + } + for name, c := range cases { + t.Run(name, func(t *testing.T) { + actual := c.Ours.toConsul() + require.Equal(t, c.Exp, actual) + }) + } +} + +func TestServiceResolverFailover_Validate(t *testing.T) { + cases := map[string]struct { + input *ServiceResolverFailover + consulMeta common.ConsulMeta + expectedErrMsgs []string + }{ + "empty failover": { + input: &ServiceResolverFailover{}, + consulMeta: common.ConsulMeta{}, + expectedErrMsgs: []string{ + "service, serviceSubset, namespace, datacenters, policy, and targets cannot all be empty at once", + }, + }, + "cross-datacenter failover is only supported in the default partition": { + input: &ServiceResolverFailover{ + Datacenters: []string{"dc2"}, + Service: "foo", + }, + consulMeta: common.ConsulMeta{ + Partition: "p2", + PartitionsEnabled: true, + }, + expectedErrMsgs: []string{ + "cross-datacenter failover is only supported in the default partition", + }, + }, + "samenessGroup cannot be set with datacenters": { + input: &ServiceResolverFailover{ + Service: "foo", + Datacenters: []string{"dc2"}, + SamenessGroup: "sg2", + }, + consulMeta: common.ConsulMeta{ + Partition: "default", + PartitionsEnabled: true, + }, + expectedErrMsgs: []string{ + "samenessGroup cannot be set with datacenters", + }, + }, + "samenessGroup cannot be set with serviceSubset": { + input: &ServiceResolverFailover{ + ServiceSubset: "v1", + Service: "foo", + SamenessGroup: "sg2", + }, + consulMeta: common.ConsulMeta{ + Partition: "default", + PartitionsEnabled: true, + }, + expectedErrMsgs: []string{ + "samenessGroup cannot be set with serviceSubset", + }, + }, + "samenessGroup cannot be set with targets": { + input: &ServiceResolverFailover{ + Targets: []ServiceResolverFailoverTarget{ + { + Peer: "p2", + }, + }, + SamenessGroup: "sg2", + }, + consulMeta: common.ConsulMeta{ + Partition: "default", + PartitionsEnabled: true, + }, + expectedErrMsgs: []string{ + "samenessGroup cannot be set with targets", + }, + }, + "targets cannot be set with datacenters": { + input: &ServiceResolverFailover{ + Targets: []ServiceResolverFailoverTarget{ + { + Peer: "p2", + }, + }, + Datacenters: []string{"dc1"}, + }, + consulMeta: common.ConsulMeta{ + Partition: "default", + PartitionsEnabled: true, + }, + expectedErrMsgs: []string{ + "targets cannot be set with datacenters", + }, + }, + "targets cannot be set with serviceSubset or service": { + input: &ServiceResolverFailover{ + Targets: []ServiceResolverFailoverTarget{ + { + Peer: "p2", + }, + }, + ServiceSubset: "v1", + Service: "foo", + }, + consulMeta: common.ConsulMeta{ + Partition: "default", + PartitionsEnabled: true, + }, + expectedErrMsgs: []string{ + "targets cannot be set with serviceSubset", + "targets cannot be set with service", + }, + }, + "target.peer cannot be set with target.serviceSubset": { + input: &ServiceResolverFailover{ + Targets: []ServiceResolverFailoverTarget{ + { + Peer: "p2", + ServiceSubset: "v1", + }, + }, + }, + consulMeta: common.ConsulMeta{ + Partition: "default", + PartitionsEnabled: true, + }, + expectedErrMsgs: []string{ + "target.peer cannot be set with target.serviceSubset", + }, + }, + "target.partition cannot be set with target.peer": { + input: &ServiceResolverFailover{ + Targets: []ServiceResolverFailoverTarget{ + { + Peer: "p2", + Partition: "partition2", + }, + }, + }, + consulMeta: common.ConsulMeta{ + Partition: "default", + PartitionsEnabled: true, + }, + expectedErrMsgs: []string{ + "target.partition cannot be set with target.peer", + }, + }, + "target.peer cannot be set with target.datacenter": { + input: &ServiceResolverFailover{ + Targets: []ServiceResolverFailoverTarget{ + { + Peer: "p2", + Datacenter: "dc2", + }, + }, + }, + consulMeta: common.ConsulMeta{ + Partition: "default", + PartitionsEnabled: true, + }, + expectedErrMsgs: []string{ + "target.peer cannot be set with target.datacenter", + }, + }, + "target.partition cannot be set with target.datacenter": { + input: &ServiceResolverFailover{ + Targets: []ServiceResolverFailoverTarget{ + { + Partition: "p2", + Datacenter: "dc2", + }, + }, + }, + consulMeta: common.ConsulMeta{ + Partition: "default", + PartitionsEnabled: true, + }, + expectedErrMsgs: []string{ + "target.partition cannot be set with target.datacenter", + }, + }, + "found empty datacenter": { + input: &ServiceResolverFailover{ + Datacenters: []string{""}, + }, + consulMeta: common.ConsulMeta{ + Partition: "default", + PartitionsEnabled: true, + }, + expectedErrMsgs: []string{ + "found empty datacenter", + }, + }, + } + + path := field.NewPath("spec.redirect") + for name, testCase := range cases { + t.Run(name, func(t *testing.T) { + errList := testCase.input.validate(path, testCase.consulMeta) + compareErrorLists(t, testCase.expectedErrMsgs, errList) + }) + } +} diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml index bae83ac7b9..69084724a9 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml @@ -91,6 +91,10 @@ spec: type: string type: array type: object + samenessGroup: + description: SamenessGroup is the name of the sameness group + to try during failover. + type: string service: description: Service is the service to resolve instead of the default as the failover group of instances during failover. @@ -244,6 +248,10 @@ spec: description: Peer is the name of the cluster peer to resolve the service from instead of the current one. type: string + samenessGroup: + description: SamenessGroup is the name of the sameness group to + resolve the service from instead of the current one. + type: string service: description: Service is a service to resolve instead of the current service. diff --git a/control-plane/go.mod b/control-plane/go.mod index 5307056ce3..06ac31d7eb 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -6,12 +6,13 @@ require ( github.com/deckarep/golang-set v1.7.1 github.com/fsnotify/fsnotify v1.5.4 github.com/go-logr/logr v0.4.0 - github.com/google/go-cmp v0.5.7 + github.com/google/go-cmp v0.5.8 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af65262de8 github.com/hashicorp/consul-server-connection-manager v0.1.0 - github.com/hashicorp/consul/api v1.10.1-0.20230418163148-eb9f671eafae + github.com/hashicorp/consul/api v1.10.1-0.20230427155444-391ed069c461 github.com/hashicorp/consul/sdk v0.13.1 + github.com/hashicorp/go-bexpr v0.1.11 github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f github.com/hashicorp/go-hclog v1.2.2 github.com/hashicorp/go-multierror v1.1.1 @@ -104,6 +105,7 @@ require ( github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/go-testing-interface v1.0.0 // indirect + github.com/mitchellh/pointerstructure v1.2.1 // indirect github.com/mitchellh/reflectwalk v1.0.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -130,12 +132,12 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.6.0 // indirect golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect + golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect + golang.org/x/sys v0.1.0 // indirect golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/api v0.43.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect diff --git a/control-plane/go.sum b/control-plane/go.sum index c045aba7bd..2ad5716991 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -299,8 +299,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/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.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -353,8 +353,10 @@ github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af6526 github.com/hashicorp/consul-server-connection-manager v0.1.0 h1:XCweGvMHzra88rYv2zxwwuUOjBUdcQmNKVrnQmt/muo= github.com/hashicorp/consul-server-connection-manager v0.1.0/go.mod h1:XVVlO+Yk7aiRpspiHZkrrFVn9BJIiOPnQIzqytPxGaU= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.10.1-0.20230418163148-eb9f671eafae h1:lYnO52QxlfATRZ1Vo8tQV+lFns7rZ4iAbbi3JN4ZAQw= -github.com/hashicorp/consul/api v1.10.1-0.20230418163148-eb9f671eafae/go.mod h1:f8zVJwBcLdr1IQnfdfszjUM0xzp31Zl3bpws3pL9uFM= +github.com/hashicorp/consul/api v1.10.1-0.20230424202255-e47f3216e51b h1:6JQAVFzqHzB51SP55BOLoDsw6sVD2WGkNSOoxI1hVZ4= +github.com/hashicorp/consul/api v1.10.1-0.20230424202255-e47f3216e51b/go.mod h1:tXfrC6o0yFTgAW46xd5Ic8STHc9oIBcRVBcwhX5KNCQ= +github.com/hashicorp/consul/api v1.10.1-0.20230427155444-391ed069c461 h1:cbsTR88ShbvcRMqLU8K0atm4GmRr8UH4x4jX4e12RYE= +github.com/hashicorp/consul/api v1.10.1-0.20230427155444-391ed069c461/go.mod h1:tXfrC6o0yFTgAW46xd5Ic8STHc9oIBcRVBcwhX5KNCQ= github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3usQMXZF6ziK3SzXU= github.com/hashicorp/consul/proto-public v0.1.0/go.mod h1:vs2KkuWwtjkIgA5ezp4YKPzQp4GitV+q/+PvksrA92k= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= @@ -363,6 +365,8 @@ github.com/hashicorp/consul/sdk v0.13.1/go.mod h1:SW/mM4LbKfqmMvcFu8v+eiQQ7oitXE github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-bexpr v0.1.11 h1:6DqdA/KBjurGby9yTY0bmkathya0lfwF2SeuubCI7dY= +github.com/hashicorp/go-bexpr v0.1.11/go.mod h1:f03lAo0duBlDIUMGCuad8oLcgejw4m7U+N8T+6Kz1AE= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= @@ -530,6 +534,8 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/pointerstructure v1.2.1 h1:ZhBBeX8tSlRpu/FFhXH4RC4OJzFlqsQhoHZAz4x7TIw= +github.com/mitchellh/pointerstructure v1.2.1/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= @@ -765,6 +771,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -943,8 +951,9 @@ golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1027,11 +1036,10 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= 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= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= From 8c22f833001f2067376fbc65202cac3f74b6391d Mon Sep 17 00:00:00 2001 From: malizz Date: Fri, 28 Apr 2023 14:55:58 -0700 Subject: [PATCH 147/340] add sameness group to source intention (#2097) * add sameness group to source intention * add more test coverage * add comment on metaValueMaxLength variable * fix comment lint issue --- .../templates/crd-exportedservices.yaml | 3 +- .../templates/crd-serviceintentions.yaml | 4 + .../api/v1alpha1/serviceintentions_types.go | 75 +++++--- .../v1alpha1/serviceintentions_types_test.go | 175 +++++++++++++++++- control-plane/api/v1alpha1/shared_types.go | 3 + ...consul.hashicorp.com_exportedservices.yaml | 3 +- ...onsul.hashicorp.com_serviceintentions.yaml | 4 + control-plane/go.sum | 2 - 8 files changed, 239 insertions(+), 30 deletions(-) diff --git a/charts/consul/templates/crd-exportedservices.yaml b/charts/consul/templates/crd-exportedservices.yaml index 073e081d0c..591500cb12 100644 --- a/charts/consul/templates/crd-exportedservices.yaml +++ b/charts/consul/templates/crd-exportedservices.yaml @@ -76,7 +76,8 @@ spec: the service to. type: string peer: - description: Peer is the name of the peer to export the service to. + description: Peer is the name of the peer to export the + service to. type: string samenessGroup: description: SamenessGroup is the name of the sameness diff --git a/charts/consul/templates/crd-serviceintentions.yaml b/charts/consul/templates/crd-serviceintentions.yaml index ede8ae09b0..67998f776c 100644 --- a/charts/consul/templates/crd-serviceintentions.yaml +++ b/charts/consul/templates/crd-serviceintentions.yaml @@ -185,6 +185,10 @@ spec: type: object type: object type: array + samenessGroup: + description: SamenessGroup is the name of the sameness group, + if applicable. + type: string type: object type: array type: object diff --git a/control-plane/api/v1alpha1/serviceintentions_types.go b/control-plane/api/v1alpha1/serviceintentions_types.go index a066b4e84f..fd18ecd3fe 100644 --- a/control-plane/api/v1alpha1/serviceintentions_types.go +++ b/control-plane/api/v1alpha1/serviceintentions_types.go @@ -5,6 +5,7 @@ package v1alpha1 import ( "encoding/json" + "fmt" "net/http" "strings" @@ -85,6 +86,8 @@ type SourceIntention struct { Peer string `json:"peer,omitempty"` // Partition is the Admin Partition for the Name parameter. Partition string `json:"partition,omitempty"` + // SamenessGroup is the name of the sameness group, if applicable. + SamenessGroup string `json:"samenessGroup,omitempty"` // Action is required for an L4 intention, and should be set to one of // "allow" or "deny" for the action that should be taken if this intention matches a request. Action IntentionAction `json:"action,omitempty"` @@ -272,10 +275,10 @@ func (in *ServiceIntentions) Validate(consulMeta common.ConsulMeta) error { } else { errs = append(errs, source.Permissions.validate(path.Child("sources").Index(i))...) } + errs = append(errs, source.validate(path.Child("sources").Index(i), consulMeta.PartitionsEnabled)...) } errs = append(errs, in.validateNamespaces(consulMeta.NamespacesEnabled)...) - errs = append(errs, in.validateSourcePeerAndPartitions(consulMeta.PartitionsEnabled)...) if len(errs) > 0 { return apierrors.NewInvalid( @@ -285,6 +288,46 @@ func (in *ServiceIntentions) Validate(consulMeta common.ConsulMeta) error { return nil } +func (in *SourceIntention) validate(path *field.Path, partitionsEnabled bool) field.ErrorList { + var errs field.ErrorList + + if in.Name == "" { + errs = append(errs, field.Required(path.Child("name"), "name is required.")) + } + + if strings.Contains(in.Partition, WildcardSpecifier) { + errs = append(errs, field.Invalid(path.Child("partition"), in.Partition, "partition cannot use or contain wildcard '*'")) + } + if strings.Contains(in.Peer, WildcardSpecifier) { + errs = append(errs, field.Invalid(path.Child("peer"), in.Peer, "peer cannot use or contain wildcard '*'")) + } + if strings.Contains(in.SamenessGroup, WildcardSpecifier) { + errs = append(errs, field.Invalid(path.Child("samenessgroup"), in.SamenessGroup, "samenessgroup cannot use or contain wildcard '*'")) + } + + if in.Partition != "" && !partitionsEnabled { + errs = append(errs, field.Invalid(path.Child("partition"), in.Partition, `Consul Enterprise Admin Partitions must be enabled to set source.partition`)) + } + + if in.Peer != "" && in.Partition != "" { + errs = append(errs, field.Invalid(path, *in, "cannot set peer and partition at the same time.")) + } + + if in.SamenessGroup != "" && in.Partition != "" { + errs = append(errs, field.Invalid(path, *in, "cannot set samenessgroup and partition at the same time.")) + } + + if in.SamenessGroup != "" && in.Peer != "" { + errs = append(errs, field.Invalid(path, *in, "cannot set samenessgroup and peer at the same time.")) + } + + if len(in.Description) > metaValueMaxLength { + errs = append(errs, field.Invalid(path, "", fmt.Sprintf("description exceeds maximum length %d", metaValueMaxLength))) + } + + return errs +} + // DefaultNamespaceFields sets the namespace field on spec.destination to their default values if namespaces are enabled. func (in *ServiceIntentions) DefaultNamespaceFields(consulMeta common.ConsulMeta) { // If namespaces are enabled we want to set the destination namespace field to it's @@ -313,13 +356,14 @@ func (in *SourceIntention) toConsul() *capi.SourceIntention { return nil } return &capi.SourceIntention{ - Name: in.Name, - Namespace: in.Namespace, - Partition: in.Partition, - Peer: in.Peer, - Action: in.Action.toConsul(), - Permissions: in.Permissions.toConsul(), - Description: in.Description, + Name: in.Name, + Namespace: in.Namespace, + Partition: in.Partition, + Peer: in.Peer, + SamenessGroup: in.SamenessGroup, + Action: in.Action.toConsul(), + Permissions: in.Permissions.toConsul(), + Description: in.Description, } } @@ -461,21 +505,6 @@ func (in *ServiceIntentions) validateNamespaces(namespacesEnabled bool) field.Er return errs } -func (in *ServiceIntentions) validateSourcePeerAndPartitions(partitionsEnabled bool) field.ErrorList { - var errs field.ErrorList - path := field.NewPath("spec") - for i, source := range in.Spec.Sources { - if source.Partition != "" && !partitionsEnabled { - errs = append(errs, field.Invalid(path.Child("sources").Index(i).Child("partition"), source.Partition, `Consul Enterprise Admin Partitions must be enabled to set source.partition`)) - } - - if source.Peer != "" && source.Partition != "" { - errs = append(errs, field.Invalid(path.Child("sources").Index(i), source, `Both source.peer and source.partition cannot be set.`)) - } - } - return errs -} - func (in IntentionAction) validate(path *field.Path) *field.Error { actions := []string{"allow", "deny"} if !sliceContains(actions, string(in)) { diff --git a/control-plane/api/v1alpha1/serviceintentions_types_test.go b/control-plane/api/v1alpha1/serviceintentions_types_test.go index 99b391039c..df0100e75a 100644 --- a/control-plane/api/v1alpha1/serviceintentions_types_test.go +++ b/control-plane/api/v1alpha1/serviceintentions_types_test.go @@ -4,6 +4,7 @@ package v1alpha1 import ( + "strings" "testing" "time" @@ -269,6 +270,13 @@ func TestServiceIntentions_ToConsul(t *testing.T) { Action: "deny", Description: "disallow access from namespace not-test", }, + { + Name: "*", + Namespace: "ns1", + SamenessGroup: "sg2", + Action: "deny", + Description: "disallow access from namespace ns1", + }, { Name: "svc-2", Namespace: "bar", @@ -322,6 +330,13 @@ func TestServiceIntentions_ToConsul(t *testing.T) { Action: "deny", Description: "disallow access from namespace not-test", }, + { + Name: "*", + Namespace: "ns1", + SamenessGroup: "sg2", + Action: "deny", + Description: "disallow access from namespace ns1", + }, { Name: "svc-2", Namespace: "bar", @@ -602,6 +617,8 @@ func TestServiceIntentions_DefaultNamespaceFields(t *testing.T) { } func TestServiceIntentions_Validate(t *testing.T) { + longDescription := strings.Repeat("x", metaValueMaxLength+1) + cases := map[string]struct { input *ServiceIntentions namespacesEnabled bool @@ -1124,6 +1141,54 @@ func TestServiceIntentions_Validate(t *testing.T) { `serviceintentions.consul.hashicorp.com "does-not-matter" is invalid: spec.sources[0]: Invalid value: "{\"name\":\"svc-2\",\"namespace\":\"bar\",\"action\":\"deny\",\"permissions\":[{\"action\":\"allow\",\"http\":{\"pathExact\":\"/bar\"}}]}": action and permissions are mutually exclusive and only one of them can be specified`, }, }, + "name not specified": { + input: &ServiceIntentions{ + ObjectMeta: metav1.ObjectMeta{ + Name: "does-not-matter", + }, + Spec: ServiceIntentionsSpec{ + Destination: IntentionDestination{ + Name: "dest-service", + Namespace: "namespace", + }, + Sources: SourceIntentions{ + { + Namespace: "bar", + Action: "deny", + }, + }, + }, + }, + namespacesEnabled: true, + expectedErrMsgs: []string{ + `serviceintentions.consul.hashicorp.com "does-not-matter" is invalid: spec.sources[0].name: Required value: name is required.`, + }, + }, + "description is too long": { + input: &ServiceIntentions{ + ObjectMeta: metav1.ObjectMeta{ + Name: "does-not-matter", + }, + Spec: ServiceIntentionsSpec{ + Destination: IntentionDestination{ + Name: "dest-service", + Namespace: "namespace", + }, + Sources: SourceIntentions{ + { + Name: "foo", + Namespace: "bar", + Action: "deny", + Description: longDescription, + }, + }, + }, + }, + namespacesEnabled: true, + expectedErrMsgs: []string{ + `serviceintentions.consul.hashicorp.com "does-not-matter" is invalid: spec.sources[0]: Invalid value: "": description exceeds maximum length 512`, + }, + }, "namespaces disabled: destination namespace specified": { input: &ServiceIntentions{ ObjectMeta: metav1.ObjectMeta{ @@ -1343,7 +1408,71 @@ func TestServiceIntentions_Validate(t *testing.T) { namespacesEnabled: true, partitionsEnabled: true, expectedErrMsgs: []string{ - `spec.sources[0]: Invalid value: v1alpha1.SourceIntention{Name:"web", Namespace:"namespace-b", Peer:"peer-other", Partition:"partition-other", Action:"allow", Permissions:v1alpha1.IntentionPermissions(nil), Description:""}: Both source.peer and source.partition cannot be set.`, + `cannot set peer and partition at the same time.`, + }, + }, + "single source samenessgroup and partition specified": { + input: &ServiceIntentions{ + ObjectMeta: metav1.ObjectMeta{ + Name: "does-not-matter", + }, + Spec: ServiceIntentionsSpec{ + Destination: IntentionDestination{ + Name: "dest-service", + Namespace: "namespace-a", + }, + Sources: SourceIntentions{ + { + Name: "web", + Action: "allow", + Namespace: "namespace-b", + Partition: "partition-other", + SamenessGroup: "sg2", + }, + { + Name: "db", + Action: "deny", + Namespace: "namespace-c", + }, + }, + }, + }, + namespacesEnabled: true, + partitionsEnabled: true, + expectedErrMsgs: []string{ + `cannot set samenessgroup and partition at the same time.`, + }, + }, + "single source samenessgroup and peer specified": { + input: &ServiceIntentions{ + ObjectMeta: metav1.ObjectMeta{ + Name: "does-not-matter", + }, + Spec: ServiceIntentionsSpec{ + Destination: IntentionDestination{ + Name: "dest-service", + Namespace: "namespace-a", + }, + Sources: SourceIntentions{ + { + Name: "web", + Action: "allow", + Namespace: "namespace-b", + Peer: "p2", + SamenessGroup: "sg2", + }, + { + Name: "db", + Action: "deny", + Namespace: "namespace-c", + }, + }, + }, + }, + namespacesEnabled: true, + partitionsEnabled: true, + expectedErrMsgs: []string{ + `cannot set samenessgroup and peer at the same time.`, }, }, "multiple source peer and partition specified": { @@ -1377,8 +1506,48 @@ func TestServiceIntentions_Validate(t *testing.T) { namespacesEnabled: true, partitionsEnabled: true, expectedErrMsgs: []string{ - `spec.sources[0]: Invalid value: v1alpha1.SourceIntention{Name:"web", Namespace:"namespace-b", Peer:"peer-other", Partition:"partition-other", Action:"allow", Permissions:v1alpha1.IntentionPermissions(nil), Description:""}: Both source.peer and source.partition cannot be set.`, - `spec.sources[1]: Invalid value: v1alpha1.SourceIntention{Name:"db", Namespace:"namespace-c", Peer:"peer-2", Partition:"partition-2", Action:"deny", Permissions:v1alpha1.IntentionPermissions(nil), Description:""}: Both source.peer and source.partition cannot be set.`, + `spec.sources[0]: Invalid value: v1alpha1.SourceIntention{Name:"web", Namespace:"namespace-b", Peer:"peer-other", Partition:"partition-other", SamenessGroup:"", Action:"allow", Permissions:v1alpha1.IntentionPermissions(nil), Description:""}: cannot set peer and partition at the same time.`, + `spec.sources[1]: Invalid value: v1alpha1.SourceIntention{Name:"db", Namespace:"namespace-c", Peer:"peer-2", Partition:"partition-2", SamenessGroup:"", Action:"deny", Permissions:v1alpha1.IntentionPermissions(nil), Description:""}: cannot set peer and partition at the same time.`, + }, + }, + "multiple errors: wildcard peer and partition and samenessgroup specified": { + input: &ServiceIntentions{ + ObjectMeta: metav1.ObjectMeta{ + Name: "does-not-matter", + }, + Spec: ServiceIntentionsSpec{ + Destination: IntentionDestination{ + Name: "dest-service", + Namespace: "namespace-a", + }, + Sources: SourceIntentions{ + { + Name: "web", + Action: "allow", + Namespace: "namespace-b", + Partition: "*", + }, + { + Name: "db", + Action: "deny", + Namespace: "namespace-c", + Peer: "*", + }, + { + Name: "db2", + Action: "deny", + Namespace: "namespace-d", + SamenessGroup: "*", + }, + }, + }, + }, + namespacesEnabled: true, + partitionsEnabled: true, + expectedErrMsgs: []string{ + `partition cannot use or contain wildcard '*'`, + `peer cannot use or contain wildcard '*'`, + `samenessgroup cannot use or contain wildcard '*'`, }, }, } diff --git a/control-plane/api/v1alpha1/shared_types.go b/control-plane/api/v1alpha1/shared_types.go index 98eb3f1b20..9aa5e519d4 100644 --- a/control-plane/api/v1alpha1/shared_types.go +++ b/control-plane/api/v1alpha1/shared_types.go @@ -16,6 +16,9 @@ import ( // This file contains structs that are shared between multiple config entries. +// metaValueMaxLength is the maximum allowed string length of a metadata value. +const metaValueMaxLength = 512 + type MeshGatewayMode string // Expose describes HTTP paths to expose through Envoy outside of Connect. diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml index 84e523f50c..0b6b969856 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml @@ -72,7 +72,8 @@ spec: the service to. type: string peer: - description: Peer is the name of the peer to export the service to. + description: Peer is the name of the peer to export the + service to. type: string samenessGroup: description: SamenessGroup is the name of the sameness diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml index 3c80ea8933..35930a0e8a 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml @@ -181,6 +181,10 @@ spec: type: object type: object type: array + samenessGroup: + description: SamenessGroup is the name of the sameness group, + if applicable. + type: string type: object type: array type: object diff --git a/control-plane/go.sum b/control-plane/go.sum index 2ad5716991..66d96bdb84 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -353,8 +353,6 @@ github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af6526 github.com/hashicorp/consul-server-connection-manager v0.1.0 h1:XCweGvMHzra88rYv2zxwwuUOjBUdcQmNKVrnQmt/muo= github.com/hashicorp/consul-server-connection-manager v0.1.0/go.mod h1:XVVlO+Yk7aiRpspiHZkrrFVn9BJIiOPnQIzqytPxGaU= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.10.1-0.20230424202255-e47f3216e51b h1:6JQAVFzqHzB51SP55BOLoDsw6sVD2WGkNSOoxI1hVZ4= -github.com/hashicorp/consul/api v1.10.1-0.20230424202255-e47f3216e51b/go.mod h1:tXfrC6o0yFTgAW46xd5Ic8STHc9oIBcRVBcwhX5KNCQ= github.com/hashicorp/consul/api v1.10.1-0.20230427155444-391ed069c461 h1:cbsTR88ShbvcRMqLU8K0atm4GmRr8UH4x4jX4e12RYE= github.com/hashicorp/consul/api v1.10.1-0.20230427155444-391ed069c461/go.mod h1:tXfrC6o0yFTgAW46xd5Ic8STHc9oIBcRVBcwhX5KNCQ= github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3usQMXZF6ziK3SzXU= From bc693e6ca6b731e5b80fc888c642805bd4eba6ec Mon Sep 17 00:00:00 2001 From: Dan Stough Date: Thu, 4 May 2023 14:55:14 -0400 Subject: [PATCH 148/340] security: update Go version to 1.20.4 (#2102) --- .changelog/2102.txt | 12 ++++++++++++ .go-version | 2 +- acceptance/go.mod | 10 +++++----- acceptance/go.sum | 17 +++++++++-------- cli/go.mod | 8 ++++---- cli/go.sum | 18 ++++++++---------- control-plane/cni/go.mod | 8 ++++---- control-plane/cni/go.sum | 15 ++++++++------- control-plane/go.mod | 8 ++++---- control-plane/go.sum | 16 ++++++++-------- 10 files changed, 63 insertions(+), 51 deletions(-) create mode 100644 .changelog/2102.txt diff --git a/.changelog/2102.txt b/.changelog/2102.txt new file mode 100644 index 0000000000..59d120f747 --- /dev/null +++ b/.changelog/2102.txt @@ -0,0 +1,12 @@ +```release-note:security +Upgrade to use Go 1.20.4. +This resolves vulnerabilities [CVE-2023-24537](https://github.com/advisories/GHSA-9f7g-gqwh-jpf5)(`go/scanner`), +[CVE-2023-24538](https://github.com/advisories/GHSA-v4m2-x4rp-hv22)(`html/template`), +[CVE-2023-24534](https://github.com/advisories/GHSA-8v5j-pwr7-w5f8)(`net/textproto`) and +[CVE-2023-24536](https://github.com/advisories/GHSA-9f7g-gqwh-jpf5)(`mime/multipart`). +Also, `golang.org/x/net` has been updated to v0.7.0 to resolve CVEs [CVE-2022-41721 +](https://github.com/advisories/GHSA-fxg5-wq6x-vr4w +), [CVE-2022-27664](https://github.com/advisories/GHSA-69cg-p879-7622) and [CVE-2022-41723 +](https://github.com/advisories/GHSA-vvpx-j8f3-3w6h +.) +``` diff --git a/.go-version b/.go-version index 0044d6cb96..0bd54efd31 100644 --- a/.go-version +++ b/.go-version @@ -1 +1 @@ -1.20.1 +1.20.4 diff --git a/acceptance/go.mod b/acceptance/go.mod index 503ca5333c..0693467102 100644 --- a/acceptance/go.mod +++ b/acceptance/go.mod @@ -83,11 +83,11 @@ require ( github.com/urfave/cli v1.22.2 // indirect go.uber.org/atomic v1.7.0 // indirect golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect - golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect + golang.org/x/net v0.7.0 // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect - golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect - golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/term v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect @@ -101,4 +101,4 @@ require ( k8s.io/utils v0.0.0-20220812165043-ad590609e2e5 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect sigs.k8s.io/yaml v1.2.0 // indirect -) \ No newline at end of file +) diff --git a/acceptance/go.sum b/acceptance/go.sum index 3301c3a7da..a2c392ed4e 100644 --- a/acceptance/go.sum +++ b/acceptance/go.sum @@ -803,8 +803,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 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= @@ -902,13 +902,14 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM= -golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -918,8 +919,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1213,4 +1214,4 @@ sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3 sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= \ No newline at end of file +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/cli/go.mod b/cli/go.mod index adc0309466..9633fd5dd2 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -17,7 +17,7 @@ require ( github.com/olekukonko/tablewriter v0.0.5 github.com/posener/complete v1.2.3 github.com/stretchr/testify v1.8.0 - golang.org/x/text v0.5.0 + golang.org/x/text v0.7.0 helm.sh/helm/v3 v3.9.4 k8s.io/api v0.25.0 k8s.io/apiextensions-apiserver v0.25.0 @@ -165,11 +165,11 @@ require ( go.mongodb.org/mongo-driver v1.11.1 // indirect go.starlark.net v0.0.0-20230128213706-3f75dec8e403 // indirect golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect - golang.org/x/net v0.4.0 // indirect + golang.org/x/net v0.7.0 // indirect golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 // indirect golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect - golang.org/x/sys v0.3.0 // indirect - golang.org/x/term v0.3.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/term v0.5.0 // indirect golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737 // indirect diff --git a/cli/go.sum b/cli/go.sum index 6ba77d4281..b5ee614f52 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -443,8 +443,6 @@ github.com/hashicorp/consul/envoyextensions v0.1.2 h1:PvPqJ/td3UpOeIKQl5ycFPUy46 github.com/hashicorp/consul/envoyextensions v0.1.2/go.mod h1:N94DQQkgITiA40zuTQ/UdPOLAAWobgHfVT5u7wxE/aU= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.13.1 h1:EygWVWWMczTzXGpO93awkHFzfUka6hLYJ0qhETd+6lY= -github.com/hashicorp/consul/troubleshoot v0.0.0-20230217154305-8dab825c3640 h1:P81kThpSzUW2oERDMrLsiZE3OuilLo3/EQhtVQW5M+8= -github.com/hashicorp/consul/troubleshoot v0.0.0-20230217154305-8dab825c3640/go.mod h1:rskvju2tK8XvHYTAILHjO7lpV1/uViHs3Q3mg9Rkwlg= github.com/hashicorp/consul/troubleshoot v0.1.2 h1:c6uMTSt/qTMhK3e18nl4xW4j7JcANdQNHOEYhoXH1P8= github.com/hashicorp/consul/troubleshoot v0.1.2/go.mod h1:q35QOtN7K5kFLPm2SXHBDD+PzsuBekcqTZuuoOTzbWA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -967,8 +965,8 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 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= @@ -1089,14 +1087,14 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1106,8 +1104,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/control-plane/cni/go.mod b/control-plane/cni/go.mod index aabc1ceaf0..b594015392 100644 --- a/control-plane/cni/go.mod +++ b/control-plane/cni/go.mod @@ -30,11 +30,11 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/net v0.0.0-20211216030914-fe4d6282115f // indirect + golang.org/x/net v0.7.0 // indirect golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect - golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect - golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/term v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.27.1 // indirect diff --git a/control-plane/cni/go.sum b/control-plane/cni/go.sum index ec1e322cf3..845baf6231 100644 --- a/control-plane/cni/go.sum +++ b/control-plane/cni/go.sum @@ -287,8 +287,8 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM= -golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 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= @@ -339,20 +339,21 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/control-plane/go.mod b/control-plane/go.mod index 06ac31d7eb..4cf30f5816 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -28,7 +28,7 @@ require ( github.com/mitchellh/mapstructure v1.5.0 github.com/stretchr/testify v1.7.2 go.uber.org/zap v1.19.0 - golang.org/x/text v0.3.8 + golang.org/x/text v0.7.0 golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 gomodules.xyz/jsonpatch/v2 v2.2.0 k8s.io/api v0.22.2 @@ -133,11 +133,11 @@ require ( go.uber.org/multierr v1.6.0 // indirect golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect - golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect + golang.org/x/net v0.7.0 // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.1.0 // indirect - golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/term v0.5.0 // indirect google.golang.org/api v0.43.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect diff --git a/control-plane/go.sum b/control-plane/go.sum index 66d96bdb84..f49bc4b2d5 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -847,8 +847,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 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= @@ -950,13 +950,13 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM= -golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -966,8 +966,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From c86f3d5e3738a8f38e4a5c0c6577a1aa61e34b04 Mon Sep 17 00:00:00 2001 From: malizz Date: Thu, 4 May 2023 14:33:01 -0700 Subject: [PATCH 149/340] Spatel/net 1646 add max ejection percent and base ejection time (#2064) * Add MaxEjectionPercent and BaseEjectionTime to servicedefaults * test with sister branch in consul repo * missed one * fix tag names * fix json tags and duration type * update test * generate yaml files and fix imports --------- Co-authored-by: Semir Patel --- .../bases/crds-oss/servicedefaults.yaml | 2 ++ .../consul/templates/crd-servicedefaults.yaml | 28 +++++++++++++++ .../api/v1alpha1/servicedefaults_types.go | 11 +++++- .../v1alpha1/servicedefaults_types_test.go | 36 +++++++++++++++++++ .../api/v1alpha1/zz_generated.deepcopy.go | 11 ++++++ .../consul.hashicorp.com_servicedefaults.yaml | 28 +++++++++++++++ 6 files changed, 115 insertions(+), 1 deletion(-) diff --git a/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml b/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml index d930439c67..1a6818f7fe 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml @@ -22,6 +22,8 @@ spec: interval: 1s maxFailures: 10 enforcing_consecutive_5xx: 60 + maxEjectionPercent: 100 + baseEjectionTime: 20s - name: "bar" limits: maxConnections: 5 diff --git a/charts/consul/templates/crd-servicedefaults.yaml b/charts/consul/templates/crd-servicedefaults.yaml index 5c6ecc7476..320ef31508 100644 --- a/charts/consul/templates/crd-servicedefaults.yaml +++ b/charts/consul/templates/crd-servicedefaults.yaml @@ -274,6 +274,13 @@ spec: upstream proxy instances will be monitored for removal from the load balancing pool. properties: + baseEjectionTime: + description: The base time that a host is ejected for. + The real time is equal to the base time multiplied by + the number of times the host has been ejected and is + capped by max_ejection_time (Default 300s). Defaults + to 30000ms or 30s. + type: string enforcing_consecutive_5xx: description: EnforcingConsecutive5xx is the % chance that a host will be actually ejected when an outlier status @@ -285,6 +292,13 @@ spec: description: Interval between health check analysis sweeps. Each sweep may remove hosts or return hosts to the pool. type: string + maxEjectionPercent: + description: The maximum % of an upstream cluster that + can be ejected due to outlier detection. Defaults to + 10% but will eject at least one host regardless of the + value. + format: int32 + type: integer maxFailures: description: MaxFailures is the count of consecutive failures that results in a host being removed from the pool. @@ -377,6 +391,13 @@ spec: how upstream proxy instances will be monitored for removal from the load balancing pool. properties: + baseEjectionTime: + description: The base time that a host is ejected for. + The real time is equal to the base time multiplied + by the number of times the host has been ejected and + is capped by max_ejection_time (Default 300s). Defaults + to 30000ms or 30s. + type: string enforcing_consecutive_5xx: description: EnforcingConsecutive5xx is the % chance that a host will be actually ejected when an outlier @@ -389,6 +410,13 @@ spec: sweeps. Each sweep may remove hosts or return hosts to the pool. type: string + maxEjectionPercent: + description: The maximum % of an upstream cluster that + can be ejected due to outlier detection. Defaults + to 10% but will eject at least one host regardless + of the value. + format: int32 + type: integer maxFailures: description: MaxFailures is the count of consecutive failures that results in a host being removed from diff --git a/control-plane/api/v1alpha1/servicedefaults_types.go b/control-plane/api/v1alpha1/servicedefaults_types.go index 304bce2db6..13455c4c8f 100644 --- a/control-plane/api/v1alpha1/servicedefaults_types.go +++ b/control-plane/api/v1alpha1/servicedefaults_types.go @@ -10,7 +10,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - capi "github.com/hashicorp/consul/api" "github.com/miekg/dns" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -19,6 +18,7 @@ import ( "k8s.io/apimachinery/pkg/util/validation/field" "github.com/hashicorp/consul-k8s/control-plane/api/common" + capi "github.com/hashicorp/consul/api" ) const ( @@ -184,6 +184,13 @@ type PassiveHealthCheck struct { // when an outlier status is detected through consecutive 5xx. // This setting can be used to disable ejection or to ramp it up slowly. EnforcingConsecutive5xx *uint32 `json:"enforcing_consecutive_5xx,omitempty"` + // The maximum % of an upstream cluster that can be ejected due to outlier detection. + // Defaults to 10% but will eject at least one host regardless of the value. + MaxEjectionPercent *uint32 `json:"maxEjectionPercent,omitempty"` + // The base time that a host is ejected for. The real time is equal to the base time + // multiplied by the number of times the host has been ejected and is capped by + // max_ejection_time (Default 300s). Defaults to 30000ms or 30s. + BaseEjectionTime *metav1.Duration `json:"baseEjectionTime,omitempty"` } type ServiceDefaultsDestination struct { @@ -448,6 +455,8 @@ func (in *PassiveHealthCheck) toConsul() *capi.PassiveHealthCheck { Interval: in.Interval.Duration, MaxFailures: in.MaxFailures, EnforcingConsecutive5xx: in.EnforcingConsecutive5xx, + MaxEjectionPercent: in.MaxEjectionPercent, + BaseEjectionTime: &in.BaseEjectionTime.Duration, } } diff --git a/control-plane/api/v1alpha1/servicedefaults_types_test.go b/control-plane/api/v1alpha1/servicedefaults_types_test.go index 69b749decd..3201529e4e 100644 --- a/control-plane/api/v1alpha1/servicedefaults_types_test.go +++ b/control-plane/api/v1alpha1/servicedefaults_types_test.go @@ -90,6 +90,10 @@ func TestServiceDefaults_ToConsul(t *testing.T) { }, MaxFailures: uint32(20), EnforcingConsecutive5xx: pointer.Uint32(100), + MaxEjectionPercent: pointer.Uint32(10), + BaseEjectionTime: &metav1.Duration{ + Duration: 10 * time.Second, + }, }, MeshGateway: MeshGateway{ Mode: "local", @@ -115,6 +119,10 @@ func TestServiceDefaults_ToConsul(t *testing.T) { }, MaxFailures: uint32(10), EnforcingConsecutive5xx: pointer.Uint32(60), + MaxEjectionPercent: pointer.Uint32(20), + BaseEjectionTime: &metav1.Duration{ + Duration: 20 * time.Second, + }, }, MeshGateway: MeshGateway{ Mode: "remote", @@ -139,6 +147,10 @@ func TestServiceDefaults_ToConsul(t *testing.T) { }, MaxFailures: uint32(10), EnforcingConsecutive5xx: pointer.Uint32(60), + MaxEjectionPercent: pointer.Uint32(30), + BaseEjectionTime: &metav1.Duration{ + Duration: 30 * time.Second, + }, }, MeshGateway: MeshGateway{ Mode: "remote", @@ -215,6 +227,8 @@ func TestServiceDefaults_ToConsul(t *testing.T) { Interval: 2 * time.Second, MaxFailures: uint32(20), EnforcingConsecutive5xx: pointer.Uint32(100), + MaxEjectionPercent: pointer.Uint32(10), + BaseEjectionTime: pointer.Duration(10 * time.Second), }, MeshGateway: capi.MeshGatewayConfig{ Mode: "local", @@ -238,6 +252,8 @@ func TestServiceDefaults_ToConsul(t *testing.T) { Interval: 2 * time.Second, MaxFailures: uint32(10), EnforcingConsecutive5xx: pointer.Uint32(60), + MaxEjectionPercent: pointer.Uint32(20), + BaseEjectionTime: pointer.Duration(20 * time.Second), }, MeshGateway: capi.MeshGatewayConfig{ Mode: "remote", @@ -260,6 +276,8 @@ func TestServiceDefaults_ToConsul(t *testing.T) { Interval: 2 * time.Second, MaxFailures: uint32(10), EnforcingConsecutive5xx: pointer.Uint32(60), + MaxEjectionPercent: pointer.Uint32(30), + BaseEjectionTime: pointer.Duration(30 * time.Second), }, MeshGateway: capi.MeshGatewayConfig{ Mode: "remote", @@ -386,6 +404,10 @@ func TestServiceDefaults_MatchesConsul(t *testing.T) { }, MaxFailures: uint32(20), EnforcingConsecutive5xx: pointer.Uint32(100), + MaxEjectionPercent: pointer.Uint32(10), + BaseEjectionTime: &metav1.Duration{ + Duration: 10 * time.Second, + }, }, MeshGateway: MeshGateway{ Mode: "local", @@ -410,6 +432,10 @@ func TestServiceDefaults_MatchesConsul(t *testing.T) { }, MaxFailures: uint32(10), EnforcingConsecutive5xx: pointer.Uint32(60), + MaxEjectionPercent: pointer.Uint32(20), + BaseEjectionTime: &metav1.Duration{ + Duration: 20 * time.Second, + }, }, MeshGateway: MeshGateway{ Mode: "remote", @@ -433,6 +459,10 @@ func TestServiceDefaults_MatchesConsul(t *testing.T) { }, MaxFailures: uint32(10), EnforcingConsecutive5xx: pointer.Uint32(60), + MaxEjectionPercent: pointer.Uint32(30), + BaseEjectionTime: &metav1.Duration{ + Duration: 30 * time.Second, + }, }, MeshGateway: MeshGateway{ Mode: "remote", @@ -504,6 +534,8 @@ func TestServiceDefaults_MatchesConsul(t *testing.T) { Interval: 2 * time.Second, MaxFailures: uint32(20), EnforcingConsecutive5xx: pointer.Uint32(100), + MaxEjectionPercent: pointer.Uint32(10), + BaseEjectionTime: pointer.Duration(10 * time.Second), }, MeshGateway: capi.MeshGatewayConfig{ Mode: "local", @@ -526,6 +558,8 @@ func TestServiceDefaults_MatchesConsul(t *testing.T) { Interval: 2 * time.Second, MaxFailures: uint32(10), EnforcingConsecutive5xx: pointer.Uint32(60), + MaxEjectionPercent: pointer.Uint32(20), + BaseEjectionTime: pointer.Duration(20 * time.Second), }, MeshGateway: capi.MeshGatewayConfig{ Mode: "remote", @@ -547,6 +581,8 @@ func TestServiceDefaults_MatchesConsul(t *testing.T) { Interval: 2 * time.Second, MaxFailures: uint32(10), EnforcingConsecutive5xx: pointer.Uint32(60), + MaxEjectionPercent: pointer.Uint32(30), + BaseEjectionTime: pointer.Duration(30 * time.Second), }, MeshGateway: capi.MeshGatewayConfig{ Mode: "remote", diff --git a/control-plane/api/v1alpha1/zz_generated.deepcopy.go b/control-plane/api/v1alpha1/zz_generated.deepcopy.go index 87d4bd9594..cedd78a1e5 100644 --- a/control-plane/api/v1alpha1/zz_generated.deepcopy.go +++ b/control-plane/api/v1alpha1/zz_generated.deepcopy.go @@ -7,6 +7,7 @@ package v1alpha1 import ( "encoding/json" + "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -924,6 +925,16 @@ func (in *PassiveHealthCheck) DeepCopyInto(out *PassiveHealthCheck) { *out = new(uint32) **out = **in } + if in.MaxEjectionPercent != nil { + in, out := &in.MaxEjectionPercent, &out.MaxEjectionPercent + *out = new(uint32) + **out = **in + } + if in.BaseEjectionTime != nil { + in, out := &in.BaseEjectionTime, &out.BaseEjectionTime + *out = new(v1.Duration) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PassiveHealthCheck. diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml index 7744a8fe7a..8009b3816b 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml @@ -270,6 +270,13 @@ spec: upstream proxy instances will be monitored for removal from the load balancing pool. properties: + baseEjectionTime: + description: The base time that a host is ejected for. + The real time is equal to the base time multiplied by + the number of times the host has been ejected and is + capped by max_ejection_time (Default 300s). Defaults + to 30000ms or 30s. + type: string enforcing_consecutive_5xx: description: EnforcingConsecutive5xx is the % chance that a host will be actually ejected when an outlier status @@ -281,6 +288,13 @@ spec: description: Interval between health check analysis sweeps. Each sweep may remove hosts or return hosts to the pool. type: string + maxEjectionPercent: + description: The maximum % of an upstream cluster that + can be ejected due to outlier detection. Defaults to + 10% but will eject at least one host regardless of the + value. + format: int32 + type: integer maxFailures: description: MaxFailures is the count of consecutive failures that results in a host being removed from the pool. @@ -373,6 +387,13 @@ spec: how upstream proxy instances will be monitored for removal from the load balancing pool. properties: + baseEjectionTime: + description: The base time that a host is ejected for. + The real time is equal to the base time multiplied + by the number of times the host has been ejected and + is capped by max_ejection_time (Default 300s). Defaults + to 30000ms or 30s. + type: string enforcing_consecutive_5xx: description: EnforcingConsecutive5xx is the % chance that a host will be actually ejected when an outlier @@ -385,6 +406,13 @@ spec: sweeps. Each sweep may remove hosts or return hosts to the pool. type: string + maxEjectionPercent: + description: The maximum % of an upstream cluster that + can be ejected due to outlier detection. Defaults + to 10% but will eject at least one host regardless + of the value. + format: int32 + type: integer maxFailures: description: MaxFailures is the count of consecutive failures that results in a host being removed from From d7bf922f53af63071e946c77537b1122e5cace49 Mon Sep 17 00:00:00 2001 From: Dan Stough Date: Mon, 8 May 2023 10:14:04 -0400 Subject: [PATCH 150/340] chore(ci): fix changelog action for non-main base branches (#2105) --- .github/workflows/changelog-checker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/changelog-checker.yml b/.github/workflows/changelog-checker.yml index 1c41634fd3..ab76073047 100644 --- a/.github/workflows/changelog-checker.yml +++ b/.github/workflows/changelog-checker.yml @@ -36,7 +36,7 @@ jobs: changelog_file_path=".changelog/[_0-9]*.txt" fi - changelog_files=$(git --no-pager diff --name-only HEAD "$(git merge-base HEAD "origin/main")" | egrep ${changelog_file_path}) + changelog_files=$(git --no-pager diff --name-only HEAD "$(git merge-base HEAD "origin/${{ github.event.pull_request.base.ref }}")" | egrep -e "${changelog_file_path}")) # If we do not find a file in .changelog/, we fail the check if [ -z "$changelog_files" ]; then From cb7fd51e95520d65d9a6f3da61a90130cddbdb55 Mon Sep 17 00:00:00 2001 From: Dan Stough Date: Mon, 8 May 2023 10:59:57 -0400 Subject: [PATCH 151/340] chore(ci): fix backport assistant not finding new branches (#2113) --- .github/workflows/backport.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index ad9b77b0d1..7f39b4e5a0 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -13,7 +13,7 @@ jobs: backport: if: github.event.pull_request.merged runs-on: ubuntu-latest - container: hashicorpdev/backport-assistant:0.3.0 + container: hashicorpdev/backport-assistant:0.3.3 steps: - name: Run Backport Assistant run: backport-assistant backport -merge-method=squash -gh-automerge From 4a2bd440ac99a8597e90e74a84986b9a6daabd3f Mon Sep 17 00:00:00 2001 From: Sujata Roy <61177855+20sr20@users.noreply.github.com> Date: Wed, 10 May 2023 11:52:40 -0700 Subject: [PATCH 152/340] Customizing Vault Version for WanFed Test (#2043) * Customizing Vault Version for WanFed Test * Modified * Changed according to the review comments * Removed the commented line * Vault server version type changed to String * changed back to VaultServerVersion type * Changing "VaultServerVersion" to type "String" --- acceptance/framework/config/config.go | 3 +++ acceptance/framework/flags/flags.go | 27 +++++++++++++-------- acceptance/framework/vault/vault_cluster.go | 9 ++++++- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/acceptance/framework/config/config.go b/acceptance/framework/config/config.go index 310955e8f8..c771e49653 100644 --- a/acceptance/framework/config/config.go +++ b/acceptance/framework/config/config.go @@ -52,6 +52,9 @@ type TestConfig struct { ConsulVersion *version.Version EnvoyImage string + VaultHelmChartVersion string + VaultServerVersion string + NoCleanupOnFailure bool DebugDirectory string diff --git a/acceptance/framework/flags/flags.go b/acceptance/framework/flags/flags.go index b8c315044b..ea58fda058 100644 --- a/acceptance/framework/flags/flags.go +++ b/acceptance/framework/flags/flags.go @@ -34,11 +34,13 @@ type TestFlags struct { flagEnableTransparentProxy bool - flagHelmChartVersion string - flagConsulImage string - flagConsulK8sImage string - flagConsulVersion string - flagEnvoyImage string + flagHelmChartVersion string + flagConsulImage string + flagConsulK8sImage string + flagConsulVersion string + flagEnvoyImage string + flagVaultHelmChartVersion string + flagVaultServerVersion string flagNoCleanupOnFailure bool @@ -72,6 +74,8 @@ func (t *TestFlags) init() { flag.StringVar(&t.flagConsulVersion, "consul-version", "", "The consul version used for all tests.") flag.StringVar(&t.flagHelmChartVersion, "helm-chart-version", config.HelmChartPath, "The helm chart used for all tests.") flag.StringVar(&t.flagEnvoyImage, "envoy-image", "", "The Envoy image to use for all tests.") + flag.StringVar(&t.flagVaultServerVersion, "vault-server-version", "", "The vault serverversion used for all tests.") + flag.StringVar(&t.flagVaultHelmChartVersion, "vault-helm-chart-version", "", "The Vault helm chart used for all tests.") flag.BoolVar(&t.flagEnableMultiCluster, "enable-multi-cluster", false, "If true, the tests that require multiple Kubernetes clusters will be run. "+ @@ -142,6 +146,7 @@ func (t *TestFlags) TestConfigFromFlags() *config.TestConfig { // if the Version is empty consulVersion will be nil consulVersion, _ := version.NewVersion(t.flagConsulVersion) + //vaultserverVersion, _ := version.NewVersion(t.flagVaultServerVersion) return &config.TestConfig{ Kubeconfig: t.flagKubeconfig, @@ -166,11 +171,13 @@ func (t *TestFlags) TestConfigFromFlags() *config.TestConfig { DisablePeering: t.flagDisablePeering, - HelmChartVersion: t.flagHelmChartVersion, - ConsulImage: t.flagConsulImage, - ConsulK8SImage: t.flagConsulK8sImage, - ConsulVersion: consulVersion, - EnvoyImage: t.flagEnvoyImage, + HelmChartVersion: t.flagHelmChartVersion, + ConsulImage: t.flagConsulImage, + ConsulK8SImage: t.flagConsulK8sImage, + ConsulVersion: consulVersion, + EnvoyImage: t.flagEnvoyImage, + VaultHelmChartVersion: t.flagVaultHelmChartVersion, + VaultServerVersion: t.flagVaultServerVersion, NoCleanupOnFailure: t.flagNoCleanupOnFailure, DebugDirectory: tempDir, diff --git a/acceptance/framework/vault/vault_cluster.go b/acceptance/framework/vault/vault_cluster.go index 26da56b646..e0030490c9 100644 --- a/acceptance/framework/vault/vault_cluster.go +++ b/acceptance/framework/vault/vault_cluster.go @@ -59,13 +59,20 @@ func NewVaultCluster(t *testing.T, ctx environment.TestContext, cfg *config.Test if cfg.EnablePodSecurityPolicies { values["global.psp.enable"] = "true" } + if cfg.VaultServerVersion != "" { + values["server.image.tag"] = cfg.VaultServerVersion + } + vaultHelmChartVersion := defaultVaultHelmChartVersion + if cfg.VaultHelmChartVersion != "" { + vaultHelmChartVersion = cfg.VaultHelmChartVersion + } helpers.MergeMaps(values, helmValues) vaultHelmOpts := &helm.Options{ SetValues: values, KubectlOptions: kopts, Logger: logger, - Version: defaultVaultHelmChartVersion, + Version: vaultHelmChartVersion, } helm.AddRepo(t, vaultHelmOpts, "hashicorp", "https://helm.releases.hashicorp.com") From ccb51c163bf7834fdac3c61e153b9f6ad481ab27 Mon Sep 17 00:00:00 2001 From: Hans Hasselberg Date: Thu, 11 May 2023 11:57:09 +0200 Subject: [PATCH 153/340] add config read command (#2078) * add config read command * add tests * lint * update docs * add changelog * fix linting errors * PR feedback --- .changelog/2078.txt | 3 + cli/cmd/config/command.go | 26 ++++ cli/cmd/config/read/command.go | 199 ++++++++++++++++++++++++++++ cli/cmd/config/read/command_test.go | 149 +++++++++++++++++++++ cli/commands.go | 12 ++ 5 files changed, 389 insertions(+) create mode 100644 .changelog/2078.txt create mode 100644 cli/cmd/config/command.go create mode 100644 cli/cmd/config/read/command.go create mode 100644 cli/cmd/config/read/command_test.go diff --git a/.changelog/2078.txt b/.changelog/2078.txt new file mode 100644 index 0000000000..2206de1128 --- /dev/null +++ b/.changelog/2078.txt @@ -0,0 +1,3 @@ +```release-note:improvement +cli: Add `consul-k8s config read` command that returns the helm configuration in yaml format. +``` diff --git a/cli/cmd/config/command.go b/cli/cmd/config/command.go new file mode 100644 index 0000000000..5e44677ff6 --- /dev/null +++ b/cli/cmd/config/command.go @@ -0,0 +1,26 @@ +package config + +import ( + "fmt" + + "github.com/hashicorp/consul-k8s/cli/common" + "github.com/mitchellh/cli" +) + +// ConfigCommand provides a synopsis for the config subcommands (e.g. read). +type ConfigCommand struct { + *common.BaseCommand +} + +// Run prints out information about the subcommands. +func (c *ConfigCommand) Run([]string) int { + return cli.RunResultHelp +} + +func (c *ConfigCommand) Help() string { + return fmt.Sprintf("%s\n\nUsage: consul-k8s config ", c.Synopsis()) +} + +func (c *ConfigCommand) Synopsis() string { + return "Operate on configuration" +} diff --git a/cli/cmd/config/read/command.go b/cli/cmd/config/read/command.go new file mode 100644 index 0000000000..e2258bd013 --- /dev/null +++ b/cli/cmd/config/read/command.go @@ -0,0 +1,199 @@ +package read + +import ( + "errors" + "fmt" + "sync" + + "github.com/posener/complete" + + "github.com/hashicorp/consul-k8s/cli/common" + "github.com/hashicorp/consul-k8s/cli/common/flag" + "github.com/hashicorp/consul-k8s/cli/common/terminal" + "github.com/hashicorp/consul-k8s/cli/helm" + "helm.sh/helm/v3/pkg/action" + helmCLI "helm.sh/helm/v3/pkg/cli" + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/yaml" +) + +const ( + flagNameKubeConfig = "kubeconfig" + flagNameKubeContext = "context" +) + +type ReadCommand struct { + *common.BaseCommand + + helmActionsRunner helm.HelmActionsRunner + + kubernetes kubernetes.Interface + + set *flag.Sets + + flagKubeConfig string + flagKubeContext string + + once sync.Once + help string +} + +func (c *ReadCommand) init() { + c.set = flag.NewSets() + + f := c.set.NewSet("Global Options") + f.StringVar(&flag.StringVar{ + Name: "kubeconfig", + Aliases: []string{"c"}, + Target: &c.flagKubeConfig, + Default: "", + Usage: "Path to kubeconfig file.", + }) + f.StringVar(&flag.StringVar{ + Name: "context", + Target: &c.flagKubeContext, + Default: "", + Usage: "Kubernetes context to use.", + }) + + c.help = c.set.Help() +} + +// Run checks the status of a Consul installation on Kubernetes. +func (c *ReadCommand) Run(args []string) int { + c.once.Do(c.init) + if c.helmActionsRunner == nil { + c.helmActionsRunner = &helm.ActionRunner{} + } + + c.Log.ResetNamed("config read") + defer common.CloseWithError(c.BaseCommand) + + if err := c.set.Parse(args); err != nil { + c.UI.Output(err.Error()) + return 1 + } + + if err := c.validateFlags(); err != nil { + c.UI.Output(err.Error()) + return 1 + } + + // helmCLI.New() will create a settings object which is used by the Helm Go SDK calls. + settings := helmCLI.New() + if c.flagKubeConfig != "" { + settings.KubeConfig = c.flagKubeConfig + } + if c.flagKubeContext != "" { + settings.KubeContext = c.flagKubeContext + } + + if err := c.setupKubeClient(settings); err != nil { + c.UI.Output(err.Error(), terminal.WithErrorStyle()) + return 1 + } + + // Setup logger to stream Helm library logs. + var uiLogger = func(s string, args ...interface{}) { + logMsg := fmt.Sprintf(s, args...) + c.UI.Output(logMsg, terminal.WithLibraryStyle()) + } + + _, releaseName, namespace, err := c.helmActionsRunner.CheckForInstallations(&helm.CheckForInstallationsOptions{ + Settings: settings, + ReleaseName: common.DefaultReleaseName, + DebugLog: uiLogger, + }) + if err != nil { + c.UI.Output(err.Error(), terminal.WithErrorStyle()) + return 1 + } + + if err := c.checkHelmInstallation(settings, uiLogger, releaseName, namespace); err != nil { + c.UI.Output(err.Error(), terminal.WithErrorStyle()) + return 1 + } + + return 0 +} + +// validateFlags checks the command line flags and values for errors. +func (c *ReadCommand) validateFlags() error { + if len(c.set.Args()) > 0 { + return errors.New("should have no non-flag arguments") + } + return nil +} + +// AutocompleteFlags returns a mapping of supported flags and autocomplete +// options for this command. The map key for the Flags map should be the +// complete flag such as "-foo" or "--foo". +func (c *ReadCommand) AutocompleteFlags() complete.Flags { + return complete.Flags{ + fmt.Sprintf("-%s", flagNameKubeConfig): complete.PredictFiles("*"), + fmt.Sprintf("-%s", flagNameKubeContext): complete.PredictNothing, + } +} + +// AutocompleteArgs returns the argument predictor for this command. +// Since argument completion is not supported, this will return +// complete.PredictNothing. +func (c *ReadCommand) AutocompleteArgs() complete.Predictor { + return complete.PredictNothing +} + +// checkHelmInstallation uses the helm Go SDK to depict the status of a named release. This function then prints +// the version of the release, it's status (unknown, deployed, uninstalled, ...), and the overwritten values. +func (c *ReadCommand) checkHelmInstallation(settings *helmCLI.EnvSettings, uiLogger action.DebugLog, releaseName, namespace string) error { + // Need a specific action config to call helm status, where namespace comes from the previous call to list. + statusConfig := new(action.Configuration) + statusConfig, err := helm.InitActionConfig(statusConfig, namespace, settings, uiLogger) + if err != nil { + return err + } + + statuser := action.NewStatus(statusConfig) + rel, err := c.helmActionsRunner.GetStatus(statuser, releaseName) + if err != nil { + return fmt.Errorf("couldn't check for installations: %s", err) + } + + valuesYaml, err := yaml.Marshal(rel.Config) + if err != nil { + return err + } + c.UI.Output(string(valuesYaml)) + + return nil +} + +// setupKubeClient to use for non Helm SDK calls to the Kubernetes API The Helm SDK will use +// settings.RESTClientGetter for its calls as well, so this will use a consistent method to +// target the right cluster for both Helm SDK and non Helm SDK calls. +func (c *ReadCommand) setupKubeClient(settings *helmCLI.EnvSettings) error { + if c.kubernetes == nil { + restConfig, err := settings.RESTClientGetter().ToRESTConfig() + if err != nil { + c.UI.Output("Error retrieving Kubernetes authentication: %v", err, terminal.WithErrorStyle()) + return err + } + c.kubernetes, err = kubernetes.NewForConfig(restConfig) + if err != nil { + c.UI.Output("Error initializing Kubernetes client: %v", err, terminal.WithErrorStyle()) + return err + } + } + + return nil +} + +// Help returns a description of the command and how it is used. +func (c *ReadCommand) Help() string { + c.once.Do(c.init) + return c.Synopsis() + "\n\nUsage: consul-k8s config read [flags]\n\n" + c.help +} + +// Synopsis returns a one-line command summary. +func (c *ReadCommand) Synopsis() string { + return "Returns the helm config of a Consul installation on Kubernetes." +} diff --git a/cli/cmd/config/read/command_test.go b/cli/cmd/config/read/command_test.go new file mode 100644 index 0000000000..a3716cf3c1 --- /dev/null +++ b/cli/cmd/config/read/command_test.go @@ -0,0 +1,149 @@ +package read + +import ( + "bytes" + "context" + "errors" + "flag" + "fmt" + "io" + "os" + "testing" + + "github.com/hashicorp/consul-k8s/cli/common" + cmnFlag "github.com/hashicorp/consul-k8s/cli/common/flag" + "github.com/hashicorp/consul-k8s/cli/common/terminal" + "github.com/hashicorp/consul-k8s/cli/helm" + "github.com/hashicorp/go-hclog" + "github.com/posener/complete" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/chart" + helmRelease "helm.sh/helm/v3/pkg/release" + helmTime "helm.sh/helm/v3/pkg/time" + "k8s.io/client-go/kubernetes/fake" +) + +func TestConfigRead(t *testing.T) { + nowTime := helmTime.Now() + cases := map[string]struct { + messages []string + helmActionsRunner *helm.MockActionRunner + expectedReturnCode int + }{ + "empty config": { + messages: []string{"\n"}, + + helmActionsRunner: &helm.MockActionRunner{ + GetStatusFunc: func(status *action.Status, name string) (*helmRelease.Release, error) { + return &helmRelease.Release{ + Name: "consul", Namespace: "consul", + Info: &helmRelease.Info{LastDeployed: nowTime, Status: "READY"}, + Chart: &chart.Chart{Metadata: &chart.Metadata{Version: "1.0.0"}}, + Config: make(map[string]interface{})}, nil + }, + }, + expectedReturnCode: 0, + }, + "error": { + messages: []string{"error", "\n"}, + + helmActionsRunner: &helm.MockActionRunner{ + GetStatusFunc: func(status *action.Status, name string) (*helmRelease.Release, error) { + return nil, errors.New("error") + }, + }, + expectedReturnCode: 1, + }, + "some config": { + messages: []string{"global: \"true\"", "\n"}, + + helmActionsRunner: &helm.MockActionRunner{ + GetStatusFunc: func(status *action.Status, name string) (*helmRelease.Release, error) { + return &helmRelease.Release{ + Name: "consul", Namespace: "consul", + Info: &helmRelease.Info{LastDeployed: nowTime, Status: "READY"}, + Chart: &chart.Chart{ + Metadata: &chart.Metadata{ + Version: "1.0.0", + }, + }, + Config: map[string]interface{}{"global": "true"}, + }, nil + }, + }, + expectedReturnCode: 0, + }, + } + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + buf := new(bytes.Buffer) + c := getInitializedCommand(t, buf) + c.kubernetes = fake.NewSimpleClientset() + c.helmActionsRunner = tc.helmActionsRunner + returnCode := c.Run([]string{}) + require.Equal(t, tc.expectedReturnCode, returnCode) + output := buf.String() + for _, msg := range tc.messages { + require.Contains(t, output, msg) + } + }) + } +} + +func TestTaskCreateCommand_AutocompleteFlags(t *testing.T) { + t.Parallel() + cmd := getInitializedCommand(t, nil) + + predictor := cmd.AutocompleteFlags() + + // Test that we get the expected number of predictions + args := complete.Args{Last: "-"} + res := predictor.Predict(args) + + // Grab the list of flags from the Flag object + flags := make([]string, 0) + cmd.set.VisitSets(func(name string, set *cmnFlag.Set) { + set.VisitAll(func(flag *flag.Flag) { + flags = append(flags, fmt.Sprintf("-%s", flag.Name)) + }) + }) + + // Verify that there is a prediction for each flag associated with the command + assert.Equal(t, len(flags), len(res)) + assert.ElementsMatch(t, flags, res, "flags and predictions didn't match, make sure to add "+ + "new flags to the command AutoCompleteFlags function") +} + +func TestTaskCreateCommand_AutocompleteArgs(t *testing.T) { + cmd := getInitializedCommand(t, nil) + c := cmd.AutocompleteArgs() + assert.Equal(t, complete.PredictNothing, c) +} + +// getInitializedCommand sets up a command struct for tests. +func getInitializedCommand(t *testing.T, buf io.Writer) *ReadCommand { + t.Helper() + log := hclog.New(&hclog.LoggerOptions{ + Name: "cli", + Level: hclog.Info, + Output: os.Stdout, + }) + var ui terminal.UI + if buf != nil { + ui = terminal.NewUI(context.Background(), buf) + } else { + ui = terminal.NewBasicUI(context.Background()) + } + baseCommand := &common.BaseCommand{ + Log: log, + UI: ui, + } + + c := &ReadCommand{ + BaseCommand: baseCommand, + } + c.init() + return c +} diff --git a/cli/commands.go b/cli/commands.go index 1f62d4eafd..1c58e75cf8 100644 --- a/cli/commands.go +++ b/cli/commands.go @@ -6,6 +6,8 @@ package main import ( "context" + "github.com/hashicorp/consul-k8s/cli/cmd/config" + config_read "github.com/hashicorp/consul-k8s/cli/cmd/config/read" "github.com/hashicorp/consul-k8s/cli/cmd/install" "github.com/hashicorp/consul-k8s/cli/cmd/proxy" "github.com/hashicorp/consul-k8s/cli/cmd/proxy/list" @@ -79,6 +81,16 @@ func initializeCommands(ctx context.Context, log hclog.Logger) (*common.BaseComm BaseCommand: baseCommand, }, nil }, + "config": func() (cli.Command, error) { + return &config.ConfigCommand{ + BaseCommand: baseCommand, + }, nil + }, + "config read": func() (cli.Command, error) { + return &config_read.ReadCommand{ + BaseCommand: baseCommand, + }, nil + }, "troubleshoot": func() (cli.Command, error) { return &troubleshoot.TroubleshootCommand{ BaseCommand: baseCommand, From bd16ab83383dbec5f8db680f39d85edd66b25b62 Mon Sep 17 00:00:00 2001 From: Paul Glass Date: Thu, 11 May 2023 09:39:18 -0500 Subject: [PATCH 154/340] Update CRDs for Permissive mTLS (#2100) * Add mutualTLSMode to service-defaults and proxy-defaults * Add allowEnablingPermisiveMutualTLS to mesh config entry --- .changelog/2100.txt | 3 ++ charts/consul/templates/crd-meshes.yaml | 5 ++++ .../consul/templates/crd-proxydefaults.yaml | 12 ++++++++ .../consul/templates/crd-servicedefaults.yaml | 12 ++++++++ control-plane/api/v1alpha1/mesh_types.go | 14 +++++---- control-plane/api/v1alpha1/mesh_types_test.go | 4 +++ .../api/v1alpha1/proxydefaults_types.go | 15 ++++++++++ .../api/v1alpha1/proxydefaults_types_test.go | 15 ++++++++++ .../api/v1alpha1/servicedefaults_types.go | 15 ++++++++++ .../v1alpha1/servicedefaults_types_test.go | 16 ++++++++++ control-plane/api/v1alpha1/shared_types.go | 29 +++++++++++++++++++ .../bases/consul.hashicorp.com_meshes.yaml | 5 ++++ .../consul.hashicorp.com_proxydefaults.yaml | 12 ++++++++ .../consul.hashicorp.com_servicedefaults.yaml | 12 ++++++++ 14 files changed, 164 insertions(+), 5 deletions(-) create mode 100644 .changelog/2100.txt diff --git a/.changelog/2100.txt b/.changelog/2100.txt new file mode 100644 index 0000000000..4fece0991c --- /dev/null +++ b/.changelog/2100.txt @@ -0,0 +1,3 @@ +```release-note:feature +crd: Add `mutualTLSMode` to the ProxyDefaults and ServiceDefaults CRDs and `allowEnablingPermissiveMutualTLS` to the Mesh CRD to support configuring permissive mutual TLS. +``` diff --git a/charts/consul/templates/crd-meshes.yaml b/charts/consul/templates/crd-meshes.yaml index 2e33eb9653..0710d41280 100644 --- a/charts/consul/templates/crd-meshes.yaml +++ b/charts/consul/templates/crd-meshes.yaml @@ -55,6 +55,11 @@ spec: spec: description: MeshSpec defines the desired state of Mesh. properties: + allowEnablingPermissiveMutualTLS: + description: AllowEnablingPermissiveMutualTLS must be true in order + to allow setting MutualTLSMode=permissive in either service-defaults + or proxy-defaults. + type: boolean http: description: HTTP defines the HTTP configuration for the service mesh. properties: diff --git a/charts/consul/templates/crd-proxydefaults.yaml b/charts/consul/templates/crd-proxydefaults.yaml index f72d1c7cea..362672c1c1 100644 --- a/charts/consul/templates/crd-proxydefaults.yaml +++ b/charts/consul/templates/crd-proxydefaults.yaml @@ -180,6 +180,18 @@ spec: CRD and should be set using annotations on the services that are part of the mesh.' type: string + mutualTLSMode: + description: 'MutualTLSMode controls whether mutual TLS is required + for all incoming connections when transparent proxy is enabled. + This can be set to "permissive" or "strict". "strict" is the default + which requires mutual TLS for incoming connections. In the insecure + "permissive" mode, connections to the sidecar proxy public listener + port require mutual TLS, but connections to the service port do + not require mutual TLS and are proxied to the application unmodified. + Note: Intentions are not enforced for non-mTLS connections. To keep + your services secure, we recommend using "strict" mode whenever + possible and enabling "permissive" mode only when necessary.' + type: string transparentProxy: description: 'TransparentProxy controls configuration specific to proxies in transparent mode. Note: This cannot be set using the diff --git a/charts/consul/templates/crd-servicedefaults.yaml b/charts/consul/templates/crd-servicedefaults.yaml index 320ef31508..36900d1bda 100644 --- a/charts/consul/templates/crd-servicedefaults.yaml +++ b/charts/consul/templates/crd-servicedefaults.yaml @@ -171,6 +171,18 @@ spec: CRD and should be set using annotations on the services that are part of the mesh.' type: string + mutualTLSMode: + description: 'MutualTLSMode controls whether mutual TLS is required + for all incoming connections when transparent proxy is enabled. + This can be set to "permissive" or "strict". "strict" is the default + which requires mutual TLS for incoming connections. In the insecure + "permissive" mode, connections to the sidecar proxy public listener + port require mutual TLS, but connections to the service port do + not require mutual TLS and are proxied to the application unmodified. + Note: Intentions are not enforced for non-mTLS connections. To keep + your services secure, we recommend using "strict" mode whenever + possible and enabling "permissive" mode only when necessary.' + type: string protocol: description: Protocol sets the protocol of the service. This is used by Connect proxies for things like observability features and to diff --git a/control-plane/api/v1alpha1/mesh_types.go b/control-plane/api/v1alpha1/mesh_types.go index 9a2df631f2..162132a47a 100644 --- a/control-plane/api/v1alpha1/mesh_types.go +++ b/control-plane/api/v1alpha1/mesh_types.go @@ -51,6 +51,9 @@ type MeshList struct { type MeshSpec struct { // TransparentProxy controls the configuration specific to proxies in "transparent" mode. Added in v1.10.0. TransparentProxy TransparentProxyMeshConfig `json:"transparentProxy,omitempty"` + // AllowEnablingPermissiveMutualTLS must be true in order to allow setting + // MutualTLSMode=permissive in either service-defaults or proxy-defaults. + AllowEnablingPermissiveMutualTLS bool `json:"allowEnablingPermissiveMutualTLS,omitempty"` // TLS defines the TLS configuration for the service mesh. TLS *MeshTLSConfig `json:"tls,omitempty"` // HTTP defines the HTTP configuration for the service mesh. @@ -192,11 +195,12 @@ func (in *Mesh) SetLastSyncedTime(time *metav1.Time) { func (in *Mesh) ToConsul(datacenter string) capi.ConfigEntry { return &capi.MeshConfigEntry{ - TransparentProxy: in.Spec.TransparentProxy.toConsul(), - TLS: in.Spec.TLS.toConsul(), - HTTP: in.Spec.HTTP.toConsul(), - Peering: in.Spec.Peering.toConsul(), - Meta: meta(datacenter), + TransparentProxy: in.Spec.TransparentProxy.toConsul(), + AllowEnablingPermissiveMutualTLS: in.Spec.AllowEnablingPermissiveMutualTLS, + TLS: in.Spec.TLS.toConsul(), + HTTP: in.Spec.HTTP.toConsul(), + Peering: in.Spec.Peering.toConsul(), + Meta: meta(datacenter), } } diff --git a/control-plane/api/v1alpha1/mesh_types_test.go b/control-plane/api/v1alpha1/mesh_types_test.go index e20ce19d47..f2ea714f60 100644 --- a/control-plane/api/v1alpha1/mesh_types_test.go +++ b/control-plane/api/v1alpha1/mesh_types_test.go @@ -48,6 +48,7 @@ func TestMesh_MatchesConsul(t *testing.T) { TransparentProxy: TransparentProxyMeshConfig{ MeshDestinationsOnly: true, }, + AllowEnablingPermissiveMutualTLS: true, TLS: &MeshTLSConfig{ Incoming: &MeshDirectionalTLSConfig{ TLSMinVersion: "TLSv1_0", @@ -72,6 +73,7 @@ func TestMesh_MatchesConsul(t *testing.T) { TransparentProxy: capi.TransparentProxyMeshConfig{ MeshDestinationsOnly: true, }, + AllowEnablingPermissiveMutualTLS: true, TLS: &capi.MeshTLSConfig{ Incoming: &capi.MeshDirectionalTLSConfig{ TLSMinVersion: "TLSv1_0", @@ -148,6 +150,7 @@ func TestMesh_ToConsul(t *testing.T) { TransparentProxy: TransparentProxyMeshConfig{ MeshDestinationsOnly: true, }, + AllowEnablingPermissiveMutualTLS: true, TLS: &MeshTLSConfig{ Incoming: &MeshDirectionalTLSConfig{ TLSMinVersion: "TLSv1_0", @@ -172,6 +175,7 @@ func TestMesh_ToConsul(t *testing.T) { TransparentProxy: capi.TransparentProxyMeshConfig{ MeshDestinationsOnly: true, }, + AllowEnablingPermissiveMutualTLS: true, TLS: &capi.MeshTLSConfig{ Incoming: &capi.MeshDirectionalTLSConfig{ TLSMinVersion: "TLSv1_0", diff --git a/control-plane/api/v1alpha1/proxydefaults_types.go b/control-plane/api/v1alpha1/proxydefaults_types.go index f83b12ecfe..1100cd107a 100644 --- a/control-plane/api/v1alpha1/proxydefaults_types.go +++ b/control-plane/api/v1alpha1/proxydefaults_types.go @@ -67,6 +67,17 @@ type ProxyDefaultsSpec struct { // Note: This cannot be set using the CRD and should be set using annotations on the // services that are part of the mesh. TransparentProxy *TransparentProxy `json:"transparentProxy,omitempty"` + // MutualTLSMode controls whether mutual TLS is required for all incoming + // connections when transparent proxy is enabled. This can be set to + // "permissive" or "strict". "strict" is the default which requires mutual + // TLS for incoming connections. In the insecure "permissive" mode, + // connections to the sidecar proxy public listener port require mutual + // TLS, but connections to the service port do not require mutual TLS and + // are proxied to the application unmodified. Note: Intentions are not + // enforced for non-mTLS connections. To keep your services secure, we + // recommend using "strict" mode whenever possible and enabling + // "permissive" mode only when necessary. + MutualTLSMode MutualTLSMode `json:"mutualTLSMode,omitempty"` // Config is an arbitrary map of configuration values used by Connect proxies. // Any values that your proxy allows can be configured globally here. // Supports JSON config values. See https://www.consul.io/docs/connect/proxies/envoy#configuration-formatting @@ -174,6 +185,7 @@ func (in *ProxyDefaults) ToConsul(datacenter string) capi.ConfigEntry { Expose: in.Spec.Expose.toConsul(), Config: consulConfig, TransparentProxy: in.Spec.TransparentProxy.toConsul(), + MutualTLSMode: in.Spec.MutualTLSMode.toConsul(), AccessLogs: in.Spec.AccessLogs.toConsul(), EnvoyExtensions: in.Spec.EnvoyExtensions.toConsul(), FailoverPolicy: in.Spec.FailoverPolicy.toConsul(), @@ -201,6 +213,9 @@ func (in *ProxyDefaults) Validate(_ common.ConsulMeta) error { if err := in.Spec.TransparentProxy.validate(path.Child("transparentProxy")); err != nil { allErrs = append(allErrs, err) } + if err := in.Spec.MutualTLSMode.validate(); err != nil { + allErrs = append(allErrs, field.Invalid(path.Child("mutualTLSMode"), in.Spec.MutualTLSMode, err.Error())) + } if err := in.Spec.Mode.validate(path.Child("mode")); err != nil { allErrs = append(allErrs, err) } diff --git a/control-plane/api/v1alpha1/proxydefaults_types_test.go b/control-plane/api/v1alpha1/proxydefaults_types_test.go index 16642ad277..07f894f322 100644 --- a/control-plane/api/v1alpha1/proxydefaults_types_test.go +++ b/control-plane/api/v1alpha1/proxydefaults_types_test.go @@ -74,6 +74,7 @@ func TestProxyDefaults_MatchesConsul(t *testing.T) { OutboundListenerPort: 1000, DialedDirectly: true, }, + MutualTLSMode: MutualTLSModePermissive, AccessLogs: &AccessLogs{ Enabled: true, DisableListenerLogs: true, @@ -129,6 +130,7 @@ func TestProxyDefaults_MatchesConsul(t *testing.T) { OutboundListenerPort: 1000, DialedDirectly: true, }, + MutualTLSMode: capi.MutualTLSModePermissive, AccessLogs: &capi.AccessLogsConfig{ Enabled: true, DisableListenerLogs: true, @@ -292,6 +294,7 @@ func TestProxyDefaults_ToConsul(t *testing.T) { OutboundListenerPort: 1000, DialedDirectly: true, }, + MutualTLSMode: MutualTLSModeStrict, AccessLogs: &AccessLogs{ Enabled: true, DisableListenerLogs: true, @@ -348,6 +351,7 @@ func TestProxyDefaults_ToConsul(t *testing.T) { OutboundListenerPort: 1000, DialedDirectly: true, }, + MutualTLSMode: capi.MutualTLSModeStrict, AccessLogs: &capi.AccessLogsConfig{ Enabled: true, DisableListenerLogs: true, @@ -497,6 +501,17 @@ func TestProxyDefaults_Validate(t *testing.T) { }, expectedErrMsg: "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.mode: Invalid value: \"transparent\": use the annotation `consul.hashicorp.com/transparent-proxy` to configure the Transparent Proxy Mode", }, + "mutualTLSMode": { + input: &ProxyDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "global", + }, + Spec: ProxyDefaultsSpec{ + MutualTLSMode: MutualTLSMode("asdf"), + }, + }, + expectedErrMsg: `proxydefaults.consul.hashicorp.com "global" is invalid: spec.mutualTLSMode: Invalid value: "asdf": Must be one of "", "strict", or "permissive".`, + }, "accessLogs.type": { input: &ProxyDefaults{ ObjectMeta: metav1.ObjectMeta{ diff --git a/control-plane/api/v1alpha1/servicedefaults_types.go b/control-plane/api/v1alpha1/servicedefaults_types.go index 13455c4c8f..cfb8865bdf 100644 --- a/control-plane/api/v1alpha1/servicedefaults_types.go +++ b/control-plane/api/v1alpha1/servicedefaults_types.go @@ -73,6 +73,17 @@ type ServiceDefaultsSpec struct { // Note: This cannot be set using the CRD and should be set using annotations on the // services that are part of the mesh. TransparentProxy *TransparentProxy `json:"transparentProxy,omitempty"` + // MutualTLSMode controls whether mutual TLS is required for all incoming + // connections when transparent proxy is enabled. This can be set to + // "permissive" or "strict". "strict" is the default which requires mutual + // TLS for incoming connections. In the insecure "permissive" mode, + // connections to the sidecar proxy public listener port require mutual + // TLS, but connections to the service port do not require mutual TLS and + // are proxied to the application unmodified. Note: Intentions are not + // enforced for non-mTLS connections. To keep your services secure, we + // recommend using "strict" mode whenever possible and enabling + // "permissive" mode only when necessary. + MutualTLSMode MutualTLSMode `json:"mutualTLSMode,omitempty"` // MeshGateway controls the default mesh gateway configuration for this service. MeshGateway MeshGateway `json:"meshGateway,omitempty"` // Expose controls the default expose path configuration for Envoy. @@ -286,6 +297,7 @@ func (in *ServiceDefaults) ToConsul(datacenter string) capi.ConfigEntry { Expose: in.Spec.Expose.toConsul(), ExternalSNI: in.Spec.ExternalSNI, TransparentProxy: in.Spec.TransparentProxy.toConsul(), + MutualTLSMode: in.Spec.MutualTLSMode.toConsul(), UpstreamConfig: in.Spec.UpstreamConfig.toConsul(), Destination: in.Spec.Destination.toConsul(), Meta: meta(datacenter), @@ -313,6 +325,9 @@ func (in *ServiceDefaults) Validate(consulMeta common.ConsulMeta) error { if err := in.Spec.TransparentProxy.validate(path.Child("transparentProxy")); err != nil { allErrs = append(allErrs, err) } + if err := in.Spec.MutualTLSMode.validate(); err != nil { + allErrs = append(allErrs, field.Invalid(path.Child("mutualTLSMode"), in.Spec.MutualTLSMode, err.Error())) + } if err := in.Spec.Mode.validate(path.Child("mode")); err != nil { allErrs = append(allErrs, err) } diff --git a/control-plane/api/v1alpha1/servicedefaults_types_test.go b/control-plane/api/v1alpha1/servicedefaults_types_test.go index 3201529e4e..0a5a59e4d1 100644 --- a/control-plane/api/v1alpha1/servicedefaults_types_test.go +++ b/control-plane/api/v1alpha1/servicedefaults_types_test.go @@ -70,6 +70,7 @@ func TestServiceDefaults_ToConsul(t *testing.T) { OutboundListenerPort: 1000, DialedDirectly: true, }, + MutualTLSMode: MutualTLSModePermissive, UpstreamConfig: &Upstreams{ Defaults: &Upstream{ Name: "upstream-default", @@ -209,6 +210,7 @@ func TestServiceDefaults_ToConsul(t *testing.T) { OutboundListenerPort: 1000, DialedDirectly: true, }, + MutualTLSMode: capi.MutualTLSModePermissive, UpstreamConfig: &capi.UpstreamConfiguration{ Defaults: &capi.UpstreamConfig{ Name: "upstream-default", @@ -385,6 +387,7 @@ func TestServiceDefaults_MatchesConsul(t *testing.T) { OutboundListenerPort: 1000, DialedDirectly: true, }, + MutualTLSMode: MutualTLSModeStrict, UpstreamConfig: &Upstreams{ Defaults: &Upstream{ Name: "upstream-default", @@ -517,6 +520,7 @@ func TestServiceDefaults_MatchesConsul(t *testing.T) { OutboundListenerPort: 1000, DialedDirectly: true, }, + MutualTLSMode: capi.MutualTLSModeStrict, UpstreamConfig: &capi.UpstreamConfiguration{ Defaults: &capi.UpstreamConfig{ Name: "upstream-default", @@ -716,6 +720,7 @@ func TestServiceDefaults_Validate(t *testing.T) { MeshGateway: MeshGateway{ Mode: "remote", }, + MutualTLSMode: MutualTLSModePermissive, Expose: Expose{ Checks: false, Paths: []ExposePath{ @@ -851,6 +856,17 @@ func TestServiceDefaults_Validate(t *testing.T) { }, expectedErrMsg: "servicedefaults.consul.hashicorp.com \"my-service\" is invalid: spec.transparentProxy.outboundListenerPort: Invalid value: 1000: use the annotation `consul.hashicorp.com/transparent-proxy-outbound-listener-port` to configure the Outbound Listener Port", }, + "mutualTLSMode": { + input: &ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-service", + }, + Spec: ServiceDefaultsSpec{ + MutualTLSMode: MutualTLSMode("asdf"), + }, + }, + expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.mutualTLSMode: Invalid value: "asdf": Must be one of "", "strict", or "permissive".`, + }, "mode": { input: &ServiceDefaults{ ObjectMeta: metav1.ObjectMeta{ diff --git a/control-plane/api/v1alpha1/shared_types.go b/control-plane/api/v1alpha1/shared_types.go index 9aa5e519d4..aa19c339da 100644 --- a/control-plane/api/v1alpha1/shared_types.go +++ b/control-plane/api/v1alpha1/shared_types.go @@ -58,6 +58,35 @@ type TransparentProxy struct { DialedDirectly bool `json:"dialedDirectly,omitempty"` } +type MutualTLSMode string + +const ( + // MutualTLSModeDefault represents no specific mode and should + // be used to indicate that a different layer of the configuration + // chain should take precedence. + MutualTLSModeDefault MutualTLSMode = "" + + // MutualTLSModeStrict requires mTLS for incoming traffic. + MutualTLSModeStrict MutualTLSMode = "strict" + + // MutualTLSModePermissive allows incoming non-mTLS traffic. + MutualTLSModePermissive MutualTLSMode = "permissive" +) + +func (m MutualTLSMode) validate() error { + switch m { + case MutualTLSModeDefault, MutualTLSModeStrict, MutualTLSModePermissive: + return nil + } + return fmt.Errorf("Must be one of %q, %q, or %q.", + MutualTLSModeDefault, MutualTLSModeStrict, MutualTLSModePermissive, + ) +} + +func (m MutualTLSMode) toConsul() capi.MutualTLSMode { + return capi.MutualTLSMode(m) +} + // MeshGateway controls how Mesh Gateways are used for upstream Connect // services. type MeshGateway struct { diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml index 4850ad152e..adbb12bba6 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml @@ -51,6 +51,11 @@ spec: spec: description: MeshSpec defines the desired state of Mesh. properties: + allowEnablingPermissiveMutualTLS: + description: AllowEnablingPermissiveMutualTLS must be true in order + to allow setting MutualTLSMode=permissive in either service-defaults + or proxy-defaults. + type: boolean http: description: HTTP defines the HTTP configuration for the service mesh. properties: diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml index 86409d2de0..7084980bf0 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml @@ -176,6 +176,18 @@ spec: CRD and should be set using annotations on the services that are part of the mesh.' type: string + mutualTLSMode: + description: 'MutualTLSMode controls whether mutual TLS is required + for all incoming connections when transparent proxy is enabled. + This can be set to "permissive" or "strict". "strict" is the default + which requires mutual TLS for incoming connections. In the insecure + "permissive" mode, connections to the sidecar proxy public listener + port require mutual TLS, but connections to the service port do + not require mutual TLS and are proxied to the application unmodified. + Note: Intentions are not enforced for non-mTLS connections. To keep + your services secure, we recommend using "strict" mode whenever + possible and enabling "permissive" mode only when necessary.' + type: string transparentProxy: description: 'TransparentProxy controls configuration specific to proxies in transparent mode. Note: This cannot be set using the diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml index 8009b3816b..ed4dbff857 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml @@ -167,6 +167,18 @@ spec: CRD and should be set using annotations on the services that are part of the mesh.' type: string + mutualTLSMode: + description: 'MutualTLSMode controls whether mutual TLS is required + for all incoming connections when transparent proxy is enabled. + This can be set to "permissive" or "strict". "strict" is the default + which requires mutual TLS for incoming connections. In the insecure + "permissive" mode, connections to the sidecar proxy public listener + port require mutual TLS, but connections to the service port do + not require mutual TLS and are proxied to the application unmodified. + Note: Intentions are not enforced for non-mTLS connections. To keep + your services secure, we recommend using "strict" mode whenever + possible and enabling "permissive" mode only when necessary.' + type: string protocol: description: Protocol sets the protocol of the service. This is used by Connect proxies for things like observability features and to From d159fc546a684e38e2b15c52177909bb816f991d Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 12 May 2023 12:43:50 -0700 Subject: [PATCH 155/340] helm: add HOST_IP to mesh-gateway (#1808) * add HOST_IP to mesh-gateway --- .changelog/1808.txt | 3 +++ charts/consul/templates/mesh-gateway-deployment.yaml | 4 ++++ 2 files changed, 7 insertions(+) create mode 100644 .changelog/1808.txt diff --git a/.changelog/1808.txt b/.changelog/1808.txt new file mode 100644 index 0000000000..a860c635d9 --- /dev/null +++ b/.changelog/1808.txt @@ -0,0 +1,3 @@ +```release-note:bug +helm: add missing `$HOST_IP` environment variable to to mesh gateway deployments. +``` diff --git a/charts/consul/templates/mesh-gateway-deployment.yaml b/charts/consul/templates/mesh-gateway-deployment.yaml index 2b2bdc8c2a..449d6ae492 100644 --- a/charts/consul/templates/mesh-gateway-deployment.yaml +++ b/charts/consul/templates/mesh-gateway-deployment.yaml @@ -203,6 +203,10 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP - name: POD_NAME valueFrom: fieldRef: From 8a10701ab26c0b26b580b7772b1938ab315d03c4 Mon Sep 17 00:00:00 2001 From: Dan Stough Date: Fri, 12 May 2023 15:51:02 -0400 Subject: [PATCH 156/340] chore(ci): fix typo in changelog checker (#2127) --- .github/workflows/changelog-checker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/changelog-checker.yml b/.github/workflows/changelog-checker.yml index ab76073047..1eea4196e7 100644 --- a/.github/workflows/changelog-checker.yml +++ b/.github/workflows/changelog-checker.yml @@ -36,7 +36,7 @@ jobs: changelog_file_path=".changelog/[_0-9]*.txt" fi - changelog_files=$(git --no-pager diff --name-only HEAD "$(git merge-base HEAD "origin/${{ github.event.pull_request.base.ref }}")" | egrep -e "${changelog_file_path}")) + changelog_files=$(git --no-pager diff --name-only HEAD "$(git merge-base HEAD "origin/${{ github.event.pull_request.base.ref }}")" | egrep -e "${changelog_file_path}") # If we do not find a file in .changelog/, we fail the check if [ -z "$changelog_files" ]; then From eeab0760f829bb5facb752119a428b73867764e9 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Mon, 15 May 2023 15:31:41 -0400 Subject: [PATCH 157/340] Add support for syncing Ingress hostname to the Consul Catalog (#2098) * Add support for syncing Ingress hostname to the Consul Catalog * fix changelog-checker syntax error --- .changelog/2098.txt | 3 + .../templates/sync-catalog-clusterrole.yaml | 55 ++- .../templates/sync-catalog-deployment.yaml | 264 +++++------ .../test/unit/sync-catalog-deployment.bats | 48 ++ charts/consul/values.yaml | 13 + control-plane/catalog/to-consul/resource.go | 207 ++++++++- .../catalog/to-consul/resource_test.go | 412 +++++++++++++++--- .../subcommand/sync-catalog/command.go | 11 + 8 files changed, 795 insertions(+), 218 deletions(-) create mode 100644 .changelog/2098.txt diff --git a/.changelog/2098.txt b/.changelog/2098.txt new file mode 100644 index 0000000000..f7c56e65a3 --- /dev/null +++ b/.changelog/2098.txt @@ -0,0 +1,3 @@ +```release-note:feature +sync-catalog: add ability to sync hostname from a Kubernetes Ingress resource to the Consul Catalog during service registration. +``` \ No newline at end of file diff --git a/charts/consul/templates/sync-catalog-clusterrole.yaml b/charts/consul/templates/sync-catalog-clusterrole.yaml index 0b0837c0df..585b5ad171 100644 --- a/charts/consul/templates/sync-catalog-clusterrole.yaml +++ b/charts/consul/templates/sync-catalog-clusterrole.yaml @@ -11,31 +11,38 @@ metadata: release: {{ .Release.Name }} component: sync-catalog rules: - - apiGroups: [""] - resources: - - services - - endpoints - verbs: - - get - - list - - watch +- apiGroups: [ "" ] + resources: + - services + - endpoints + verbs: + - get + - list + - watch {{- if .Values.syncCatalog.toK8S }} - - update - - patch - - delete - - create + - update + - patch + - delete + - create {{- end }} - - apiGroups: [""] - resources: - - nodes - verbs: - - get +- apiGroups: [ "" ] + resources: + - nodes + verbs: + - get {{- if .Values.global.enablePodSecurityPolicies }} - - apiGroups: ["policy"] - resources: ["podsecuritypolicies"] - verbs: - - use - resourceNames: - - {{ template "consul.fullname" . }}-sync-catalog -{{- end }} +- apiGroups: [ "policy" ] + resources: [ "podsecuritypolicies" ] + verbs: + - use + resourceNames: + - {{ template "consul.fullname" . }}-sync-catalog {{- end }} +- apiGroups: [ "networking.k8s.io" ] + resources: + - ingresses + verbs: + - get + - list + - watch +{{- end }} \ No newline at end of file diff --git a/charts/consul/templates/sync-catalog-deployment.yaml b/charts/consul/templates/sync-catalog-deployment.yaml index ec1abbfc84..e88adea533 100644 --- a/charts/consul/templates/sync-catalog-deployment.yaml +++ b/charts/consul/templates/sync-catalog-deployment.yaml @@ -75,136 +75,142 @@ spec: {{- end }} {{- end }} containers: - - name: sync-catalog - image: "{{ default .Values.global.imageK8S .Values.syncCatalog.image }}" - env: - {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 12 }} - {{- if .Values.global.acls.manageSystemACLs }} - - name: CONSUL_LOGIN_AUTH_METHOD - {{- if and .Values.global.federation.enabled .Values.global.federation.primaryDatacenter .Values.global.enableConsulNamespaces }} - value: {{ template "consul.fullname" . }}-k8s-component-auth-method-{{ .Values.global.datacenter }} - {{- else }} - value: {{ template "consul.fullname" . }}-k8s-component-auth-method - {{- end }} - - name: CONSUL_LOGIN_DATACENTER - {{- if and .Values.global.federation.enabled .Values.global.federation.primaryDatacenter .Values.global.enableConsulNamespaces }} - value: {{ .Values.global.federation.primaryDatacenter }} - {{- else }} - value: {{ .Values.global.datacenter }} - {{- end }} - - name: CONSUL_LOGIN_META - value: "component=sync-catalog,pod=$(NAMESPACE)/$(POD_NAME)" - {{- end }} - - name: NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - {{- if (and .Values.syncCatalog.aclSyncToken.secretName .Values.syncCatalog.aclSyncToken.secretKey) }} - - name: CONSUL_ACL_TOKEN - valueFrom: - secretKeyRef: - name: {{ .Values.syncCatalog.aclSyncToken.secretName }} - key: {{ .Values.syncCatalog.aclSyncToken.secretKey }} - {{- end }} - volumeMounts: - {{- if .Values.global.tls.enabled }} - {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} - - name: consul-ca-cert - mountPath: /consul/tls/ca - readOnly: true - {{- end }} - {{- end }} - command: - - "/bin/sh" - - "-ec" - - | - consul-k8s-control-plane sync-catalog \ - -log-level={{ default .Values.global.logLevel .Values.syncCatalog.logLevel }} \ - -log-json={{ .Values.global.logJSON }} \ - -k8s-default-sync={{ .Values.syncCatalog.default }} \ - {{- if (not .Values.syncCatalog.toConsul) }} - -to-consul=false \ - {{- end }} - {{- if (not .Values.syncCatalog.toK8S) }} - -to-k8s=false \ - {{- end }} - -consul-domain={{ .Values.global.domain }} \ - {{- if .Values.syncCatalog.k8sPrefix }} - -k8s-service-prefix="{{ .Values.syncCatalog.k8sPrefix}}" \ - {{- end }} - {{- if .Values.syncCatalog.k8sSourceNamespace }} - -k8s-source-namespace="{{ .Values.syncCatalog.k8sSourceNamespace}}" \ - {{- end }} - {{- range $value := .Values.syncCatalog.k8sAllowNamespaces }} - -allow-k8s-namespace="{{ $value }}" \ - {{- end }} - {{- range $value := .Values.syncCatalog.k8sDenyNamespaces }} - -deny-k8s-namespace="{{ $value }}" \ - {{- end }} - -k8s-write-namespace=${NAMESPACE} \ - {{- if (not .Values.syncCatalog.syncClusterIPServices) }} - -sync-clusterip-services=false \ - {{- end }} - {{- if .Values.syncCatalog.nodePortSyncType }} - -node-port-sync-type={{ .Values.syncCatalog.nodePortSyncType }} \ - {{- end }} - {{- if .Values.syncCatalog.consulWriteInterval }} - -consul-write-interval={{ .Values.syncCatalog.consulWriteInterval }} \ - {{- end }} - {{- if .Values.syncCatalog.k8sTag }} - -consul-k8s-tag={{ .Values.syncCatalog.k8sTag }} \ - {{- end }} - {{- if .Values.syncCatalog.consulNodeName }} - -consul-node-name={{ .Values.syncCatalog.consulNodeName }} \ - {{- end }} - {{- if .Values.global.adminPartitions.enabled }} - -partition={{ .Values.global.adminPartitions.name }} \ - {{- end }} - {{- if .Values.syncCatalog.consulPrefix}} - -consul-service-prefix="{{ .Values.syncCatalog.consulPrefix}}" \ - {{- end}} - {{- if .Values.syncCatalog.addK8SNamespaceSuffix}} - -add-k8s-namespace-suffix \ - {{- end}} - {{- if .Values.global.enableConsulNamespaces }} - -enable-namespaces=true \ - {{- if .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} - -consul-destination-namespace={{ .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} \ - {{- end }} - {{- if .Values.syncCatalog.consulNamespaces.mirroringK8S }} - -enable-k8s-namespace-mirroring=true \ - {{- if .Values.syncCatalog.consulNamespaces.mirroringK8SPrefix }} - -k8s-namespace-mirroring-prefix={{ .Values.syncCatalog.consulNamespaces.mirroringK8SPrefix }} \ - {{- end }} - {{- end }} - {{- if .Values.global.acls.manageSystemACLs }} - -consul-cross-namespace-acl-policy=cross-namespace-policy \ - {{- end }} - {{- end }} - livenessProbe: - httpGet: - path: /health/ready - port: 8080 - scheme: HTTP - failureThreshold: 3 - initialDelaySeconds: 30 - periodSeconds: 5 - successThreshold: 1 - timeoutSeconds: 5 - readinessProbe: - httpGet: - path: /health/ready - port: 8080 - scheme: HTTP - failureThreshold: 5 - initialDelaySeconds: 10 - periodSeconds: 5 - successThreshold: 1 - timeoutSeconds: 5 - {{- with .Values.syncCatalog.resources }} - resources: - {{- toYaml . | nindent 12 }} + - name: sync-catalog + image: "{{ default .Values.global.imageK8S .Values.syncCatalog.image }}" + env: + {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 8 }} + {{- if .Values.global.acls.manageSystemACLs }} + - name: CONSUL_LOGIN_AUTH_METHOD + {{- if and .Values.global.federation.enabled .Values.global.federation.primaryDatacenter .Values.global.enableConsulNamespaces }} + value: {{ template "consul.fullname" . }}-k8s-component-auth-method-{{ .Values.global.datacenter }} + {{- else }} + value: {{ template "consul.fullname" . }}-k8s-component-auth-method + {{- end }} + - name: CONSUL_LOGIN_DATACENTER + {{- if and .Values.global.federation.enabled .Values.global.federation.primaryDatacenter .Values.global.enableConsulNamespaces }} + value: {{ .Values.global.federation.primaryDatacenter }} + {{- else }} + value: {{ .Values.global.datacenter }} {{- end }} + - name: CONSUL_LOGIN_META + value: "component=sync-catalog,pod=$(NAMESPACE)/$(POD_NAME)" + {{- end }} + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{- if (and .Values.syncCatalog.aclSyncToken.secretName .Values.syncCatalog.aclSyncToken.secretKey) }} + - name: CONSUL_ACL_TOKEN + valueFrom: + secretKeyRef: + name: {{ .Values.syncCatalog.aclSyncToken.secretName }} + key: {{ .Values.syncCatalog.aclSyncToken.secretKey }} + {{- end }} + volumeMounts: + {{- if .Values.global.tls.enabled }} + {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- end }} + command: + - "/bin/sh" + - "-ec" + - | + consul-k8s-control-plane sync-catalog \ + -log-level={{ default .Values.global.logLevel .Values.syncCatalog.logLevel }} \ + -log-json={{ .Values.global.logJSON }} \ + -k8s-default-sync={{ .Values.syncCatalog.default }} \ + {{- if (not .Values.syncCatalog.toConsul) }} + -to-consul=false \ + {{- end }} + {{- if (not .Values.syncCatalog.toK8S) }} + -to-k8s=false \ + {{- end }} + -consul-domain={{ .Values.global.domain }} \ + {{- if .Values.syncCatalog.k8sPrefix }} + -k8s-service-prefix="{{ .Values.syncCatalog.k8sPrefix}}" \ + {{- end }} + {{- if .Values.syncCatalog.k8sSourceNamespace }} + -k8s-source-namespace="{{ .Values.syncCatalog.k8sSourceNamespace}}" \ + {{- end }} + {{- range $value := .Values.syncCatalog.k8sAllowNamespaces }} + -allow-k8s-namespace="{{ $value }}" \ + {{- end }} + {{- range $value := .Values.syncCatalog.k8sDenyNamespaces }} + -deny-k8s-namespace="{{ $value }}" \ + {{- end }} + -k8s-write-namespace=${NAMESPACE} \ + {{- if (not .Values.syncCatalog.syncClusterIPServices) }} + -sync-clusterip-services=false \ + {{- end }} + {{- if .Values.syncCatalog.nodePortSyncType }} + -node-port-sync-type={{ .Values.syncCatalog.nodePortSyncType }} \ + {{- end }} + {{- if .Values.syncCatalog.consulWriteInterval }} + -consul-write-interval={{ .Values.syncCatalog.consulWriteInterval }} \ + {{- end }} + {{- if .Values.syncCatalog.k8sTag }} + -consul-k8s-tag={{ .Values.syncCatalog.k8sTag }} \ + {{- end }} + {{- if .Values.syncCatalog.consulNodeName }} + -consul-node-name={{ .Values.syncCatalog.consulNodeName }} \ + {{- end }} + {{- if .Values.global.adminPartitions.enabled }} + -partition={{ .Values.global.adminPartitions.name }} \ + {{- end }} + {{- if .Values.syncCatalog.consulPrefix}} + -consul-service-prefix="{{ .Values.syncCatalog.consulPrefix}}" \ + {{- end}} + {{- if .Values.syncCatalog.addK8SNamespaceSuffix}} + -add-k8s-namespace-suffix \ + {{- end}} + {{- if .Values.global.enableConsulNamespaces }} + -enable-namespaces=true \ + {{- if .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} + -consul-destination-namespace={{ .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} \ + {{- end }} + {{- if .Values.syncCatalog.consulNamespaces.mirroringK8S }} + -enable-k8s-namespace-mirroring=true \ + {{- if .Values.syncCatalog.consulNamespaces.mirroringK8SPrefix }} + -k8s-namespace-mirroring-prefix={{ .Values.syncCatalog.consulNamespaces.mirroringK8SPrefix }} \ + {{- end }} + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + -consul-cross-namespace-acl-policy=cross-namespace-policy \ + {{- end }} + {{- end }} + {{- if .Values.syncCatalog.ingress.enabled }} + -enable-ingress=true \ + {{- if .Values.syncCatalog.ingress.loadBalancerIPs }} + -loadBalancer-ips=true \ + {{- end }} + {{- end }} + livenessProbe: + httpGet: + path: /health/ready + port: 8080 + scheme: HTTP + failureThreshold: 3 + initialDelaySeconds: 30 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 5 + readinessProbe: + httpGet: + path: /health/ready + port: 8080 + scheme: HTTP + failureThreshold: 5 + initialDelaySeconds: 10 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 5 + {{- with .Values.syncCatalog.resources }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} {{- if .Values.syncCatalog.priorityClassName }} priorityClassName: {{ .Values.syncCatalog.priorityClassName | quote }} {{- end }} diff --git a/charts/consul/test/unit/sync-catalog-deployment.bats b/charts/consul/test/unit/sync-catalog-deployment.bats index 0b397a55d0..318b4d3d3c 100755 --- a/charts/consul/test/unit/sync-catalog-deployment.bats +++ b/charts/consul/test/unit/sync-catalog-deployment.bats @@ -365,6 +365,54 @@ load _helpers [ "${actual}" = "true" ] } +#-------------------------------------------------------------------- +# sync ingress + +@test "syncCatalog/Deployment: enable ingress sync flag not passed when disabled" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/sync-catalog-deployment.yaml \ + --set 'syncCatalog.ingress.enabled=false' \ + --set 'syncCatalog.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command | any(contains("-enable-ingress=true"))' | tee /dev/stderr) + [ "${actual}" = "false" ] +} +@test "syncCatalog/Deployment: enable ingress sync flag passed when enabled" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/sync-catalog-deployment.yaml \ + --set 'syncCatalog.enabled=true' \ + --set 'syncCatalog.ingress.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command | any(contains("-enable-ingress=true"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "syncCatalog/Deployment: enable loadbalancer IP sync flag not passed when syncIngress disabled" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/sync-catalog-deployment.yaml \ + --set 'syncCatalog.enabled=true' \ + --set 'syncCatalog.ingress.enabled=false' \ + --set 'syncCatalog.ingress.loadBalancerIPs=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command | any(contains("-loadBalancer-ips=true"))' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "syncCatalog/Deployment: enable loadbalancer IP sync flag passed when enabled with ingress sync" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/sync-catalog-deployment.yaml \ + --set 'syncCatalog.enabled=true' \ + --set 'syncCatalog.ingress.enabled=true' \ + --set 'syncCatalog.ingress.loadBalancerIPs=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command | any(contains("-loadBalancer-ips=true"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + #-------------------------------------------------------------------- # affinity diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 73b805680b..9aac754ed4 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -1780,6 +1780,19 @@ syncCatalog: # Set this to false to skip syncing ClusterIP services. syncClusterIPServices: true + ingress: + # Syncs the hostname from a Kubernetes Ingress resource to service registrations + # when a rule matched a service. Currently only supports host based routing and + # not path based routing. The only supported path on an ingress rule is "/". + # Set this to false to skip syncing Ingress services. + # + # Currently, port 80 is synced if there is not TLS entry for the hostname. Syncs the port + # 443 if there is a TLS entry that matches the hostname. + enabled: false + # Requires syncIngress to be `true`. syncs the LoadBalancer IP from a Kubernetes Ingress + # resource instead of the hostname to service registrations when a rule matched a service. + loadBalancerIPs: false + # Configures the type of syncing that happens for NodePort # services. The valid options are: ExternalOnly, InternalOnly, ExternalFirst. # diff --git a/control-plane/catalog/to-consul/resource.go b/control-plane/catalog/to-consul/resource.go index e25257c8f8..6a4e913d80 100644 --- a/control-plane/catalog/to-consul/resource.go +++ b/control-plane/catalog/to-consul/resource.go @@ -17,6 +17,7 @@ import ( consulapi "github.com/hashicorp/consul/api" "github.com/hashicorp/go-hclog" corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/watch" @@ -146,12 +147,33 @@ type ServiceResource struct { // of each service. endpointsMap map[string]*corev1.Endpoints + // EnableIngress enables syncing of the hostname from an Ingress resource + // to the service registration if an Ingress rule matches the service. + EnableIngress bool + + // SyncLoadBalancerIPs enables syncing the IP of the Ingress LoadBalancer + // if we do not want to sync the hostname from the Ingress resource. + SyncLoadBalancerIPs bool + + // ingressServiceMap uses the same keys as serviceMap but maps to the ingress + // of each service if it exists. + ingressServiceMap map[string]map[string]string + + // serviceHostnameMap maps the name of a service to the hostName and port that + // is provided by the Ingress resource for the service. + serviceHostnameMap map[string]serviceAddress + // consulMap holds the services in Consul that we've registered from kube. // It's populated via Consul's API and lets us diff what is actually in // Consul vs. what we expect to be there. consulMap map[string][]*consulapi.CatalogRegistration } +type serviceAddress struct { + hostName string + port int32 +} + // Informer implements the controller.Resource interface. func (t *ServiceResource) Informer() cache.SharedIndexInformer { // Watch all k8s namespaces. Events will be filtered out as appropriate @@ -256,9 +278,21 @@ func (t *ServiceResource) doDelete(key string) { // Run implements the controller.Backgrounder interface. func (t *ServiceResource) Run(ch <-chan struct{}) { t.Log.Info("starting runner for endpoints") + // Register a controller for Endpoints which subsequently registers a + // controller for the Ingress resource. (&controller.Controller{ - Log: t.Log.Named("controller/endpoints"), - Resource: &serviceEndpointsResource{Service: t, Ctx: t.Ctx}, + Resource: &serviceEndpointsResource{ + Service: t, + Ctx: t.Ctx, + Log: t.Log.Named("controller/endpoints"), + Resource: &serviceIngressResource{ + Service: t, + Ctx: t.Ctx, + SyncLoadBalancerIPs: t.SyncLoadBalancerIPs, + EnableIngress: t.EnableIngress, + }, + }, + Log: t.Log.Named("controller/service"), }).Run(ch) } @@ -647,12 +681,21 @@ func (t *ServiceResource) registerServiceInstance( } } for _, subsetAddr := range subset.Addresses { - addr := subsetAddr.IP - if addr == "" && useHostname { - addr = subsetAddr.Hostname - } - if addr == "" { - continue + var addr string + // Use the address and port from the Ingress resource if + // ingress-sync is enabled and the service has an ingress + // resource that references it. + if t.EnableIngress && t.isIngressService(key) { + addr = t.serviceHostnameMap[key].hostName + epPort = int(t.serviceHostnameMap[key].port) + } else { + addr = subsetAddr.IP + if addr == "" && useHostname { + addr = subsetAddr.Hostname + } + if addr == "" { + continue + } } // Its not clear whether K8S guarantees ready addresses to @@ -719,8 +762,19 @@ func (t *ServiceResource) sync() { // a background watcher on endpoints that is used by the ServiceResource // to keep track of changing endpoints for registered services. type serviceEndpointsResource struct { - Service *ServiceResource - Ctx context.Context + Service *ServiceResource + Ctx context.Context + Log hclog.Logger + Resource controller.Resource +} + +// Run implements the controller.Backgrounder interface. +func (t *serviceEndpointsResource) Run(ch <-chan struct{}) { + t.Log.Info("starting runner for ingress") + (&controller.Controller{ + Log: t.Log.Named("controller/ingress"), + Resource: t.Resource, + }).Run(ch) } func (t *serviceEndpointsResource) Informer() cache.SharedIndexInformer { @@ -796,6 +850,134 @@ func (t *serviceEndpointsResource) Delete(key string, _ interface{}) error { return nil } +// serviceIngressResource implements controller.Resource and starts +// a background watcher on ingress resources that is used by the ServiceResource +// to keep track of changing ingress for registered services. +type serviceIngressResource struct { + Service *ServiceResource + Resource controller.Resource + Ctx context.Context + EnableIngress bool + SyncLoadBalancerIPs bool +} + +func (t *serviceIngressResource) Informer() cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { + return t.Service.Client.NetworkingV1(). + Ingresses(metav1.NamespaceAll). + List(t.Ctx, options) + }, + + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + return t.Service.Client.NetworkingV1(). + Ingresses(metav1.NamespaceAll). + Watch(t.Ctx, options) + }, + }, + &networkingv1.Ingress{}, + 0, + cache.Indexers{}, + ) +} + +func (t *serviceIngressResource) Upsert(key string, raw interface{}) error { + if !t.EnableIngress { + return nil + } + svc := t.Service + ingress, ok := raw.(*networkingv1.Ingress) + if !ok { + svc.Log.Warn("upsert got invalid type", "raw", raw) + return nil + } + + svc.serviceLock.Lock() + defer svc.serviceLock.Unlock() + + for _, rule := range ingress.Spec.Rules { + var svcName string + var hostName string + var svcPort int32 + for _, path := range rule.HTTP.Paths { + if path.Path == "/" { + svcName = path.Backend.Service.Name + svcPort = 80 + } else { + continue + } + } + if svcName == "" { + continue + } + if t.SyncLoadBalancerIPs { + if ingress.Status.LoadBalancer.Ingress[0].IP == "" { + continue + } + hostName = ingress.Status.LoadBalancer.Ingress[0].IP + } else { + hostName = rule.Host + } + for _, ingressTLS := range ingress.Spec.TLS { + for _, host := range ingressTLS.Hosts { + if rule.Host == host { + svcPort = 443 + } + } + } + + if svc.serviceHostnameMap == nil { + svc.serviceHostnameMap = make(map[string]serviceAddress) + } + // Maintain a list of the service name to the hostname from the Ingress resource. + svc.serviceHostnameMap[fmt.Sprintf("%s/%s", ingress.Namespace, svcName)] = serviceAddress{ + hostName: hostName, + port: svcPort, + } + if svc.ingressServiceMap == nil { + svc.ingressServiceMap = make(map[string]map[string]string) + } + if svc.ingressServiceMap[key] == nil { + svc.ingressServiceMap[key] = make(map[string]string) + } + // Maintain a list of all the service names that map to an Ingress resource. + svc.ingressServiceMap[key][fmt.Sprintf("%s/%s", ingress.Namespace, svcName)] = "" + } + + // Update the registration for each matched service and trigger a sync + for svcName := range svc.ingressServiceMap[key] { + svc.Log.Info(fmt.Sprintf("generating registrations for %s", svcName)) + svc.generateRegistrations(svcName) + } + svc.sync() + svc.Log.Info("upsert ingress", "key", key) + + return nil +} + +func (t *serviceIngressResource) Delete(key string, _ interface{}) error { + if !t.EnableIngress { + return nil + } + t.Service.serviceLock.Lock() + defer t.Service.serviceLock.Unlock() + + // This is a bit of an optimization. We only want to force a resync + // if we were tracking this ingress to begin with and that ingress + // had associated registrations. + if _, ok := t.Service.ingressServiceMap[key]; ok { + for svcName := range t.Service.ingressServiceMap[key] { + delete(t.Service.serviceHostnameMap, svcName) + } + delete(t.Service.ingressServiceMap, key) + t.Service.sync() + } + + t.Service.Log.Info("delete ingress", "key", key) + return nil +} + func (t *ServiceResource) addPrefixAndK8SNamespace(name, namespace string) string { if t.ConsulServicePrefix != "" { name = fmt.Sprintf("%s%s", t.ConsulServicePrefix, name) @@ -808,6 +990,11 @@ func (t *ServiceResource) addPrefixAndK8SNamespace(name, namespace string) strin return name } +// isIngressService return if a service has an Ingress resource that references it. +func (t *ServiceResource) isIngressService(key string) bool { + return t.serviceHostnameMap != nil && t.serviceHostnameMap[key].hostName != "" +} + // consulHealthCheckID deterministically generates a health check ID based on service ID and Kubernetes namespace. func consulHealthCheckID(k8sNS string, serviceID string) string { return fmt.Sprintf("%s/%s", k8sNS, serviceID) diff --git a/control-plane/catalog/to-consul/resource_test.go b/control-plane/catalog/to-consul/resource_test.go index d75f03d692..3475729950 100644 --- a/control-plane/catalog/to-consul/resource_test.go +++ b/control-plane/catalog/to-consul/resource_test.go @@ -13,7 +13,8 @@ import ( "github.com/hashicorp/consul/sdk/testutil/retry" "github.com/hashicorp/go-hclog" "github.com/stretchr/testify/require" - apiv1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/client-go/kubernetes" @@ -445,7 +446,7 @@ func TestServiceResource_lbMultiEndpoint(t *testing.T) { svc := lbService("foo", metav1.NamespaceDefault, "1.2.3.4") svc.Status.LoadBalancer.Ingress = append( svc.Status.LoadBalancer.Ingress, - apiv1.LoadBalancerIngress{IP: "2.3.4.5"}, + corev1.LoadBalancerIngress{IP: "2.3.4.5"}, ) _, err := client.CoreV1().Services(metav1.NamespaceDefault).Create(context.Background(), svc, metav1.CreateOptions{}) require.NoError(t, err) @@ -504,7 +505,7 @@ func TestServiceResource_lbPort(t *testing.T) { // Insert an LB service svc := lbService("foo", metav1.NamespaceDefault, "1.2.3.4") - svc.Spec.Ports = []apiv1.ServicePort{ + svc.Spec.Ports = []corev1.ServicePort{ {Name: "http", Port: 80, TargetPort: intstr.FromInt(8080)}, {Name: "rpc", Port: 8500, TargetPort: intstr.FromInt(2000)}, } @@ -537,7 +538,7 @@ func TestServiceResource_lbAnnotatedPort(t *testing.T) { // Insert an LB service svc := lbService("foo", metav1.NamespaceDefault, "1.2.3.4") svc.Annotations[annotationServicePort] = "rpc" - svc.Spec.Ports = []apiv1.ServicePort{ + svc.Spec.Ports = []corev1.ServicePort{ {Name: "http", Port: 80, TargetPort: intstr.FromInt(8080)}, {Name: "rpc", Port: 8500, TargetPort: intstr.FromInt(2000)}, } @@ -628,17 +629,17 @@ func TestServiceResource_lbRegisterEndpoints(t *testing.T) { // Insert the endpoints _, err := client.CoreV1().Endpoints(metav1.NamespaceDefault).Create( context.Background(), - &apiv1.Endpoints{ + &corev1.Endpoints{ ObjectMeta: metav1.ObjectMeta{ Name: "foo", }, - Subsets: []apiv1.EndpointSubset{ + Subsets: []corev1.EndpointSubset{ { - Addresses: []apiv1.EndpointAddress{ + Addresses: []corev1.EndpointAddress{ {NodeName: &node1.Name, IP: "8.8.8.8"}, }, - Ports: []apiv1.EndpointPort{ + Ports: []corev1.EndpointPort{ {Name: "http", Port: 8080}, {Name: "rpc", Port: 2000}, }, @@ -763,17 +764,17 @@ func TestServiceResource_nodePort_singleEndpoint(t *testing.T) { // Insert the endpoints _, err := client.CoreV1().Endpoints(metav1.NamespaceDefault).Create( context.Background(), - &apiv1.Endpoints{ + &corev1.Endpoints{ ObjectMeta: metav1.ObjectMeta{ Name: "foo", }, - Subsets: []apiv1.EndpointSubset{ + Subsets: []corev1.EndpointSubset{ { - Addresses: []apiv1.EndpointAddress{ + Addresses: []corev1.EndpointAddress{ {NodeName: &node1.Name, IP: "1.2.3.4"}, }, - Ports: []apiv1.EndpointPort{ + Ports: []corev1.EndpointPort{ {Name: "http", Port: 8080}, {Name: "rpc", Port: 2000}, }, @@ -860,7 +861,7 @@ func TestServiceResource_nodePortUnnamedPort(t *testing.T) { // Insert the service svc := nodePortService("foo", metav1.NamespaceDefault) // Override service ports - svc.Spec.Ports = []apiv1.ServicePort{ + svc.Spec.Ports = []corev1.ServicePort{ {Port: 80, TargetPort: intstr.FromInt(8080), NodePort: 30000}, {Port: 8500, TargetPort: intstr.FromInt(2000), NodePort: 30001}, } @@ -940,9 +941,9 @@ func TestServiceResource_nodePort_externalFirstSync(t *testing.T) { node1, _ := createNodes(t, client) - node1.Status = apiv1.NodeStatus{ - Addresses: []apiv1.NodeAddress{ - {Type: apiv1.NodeInternalIP, Address: "4.5.6.7"}, + node1.Status = corev1.NodeStatus{ + Addresses: []corev1.NodeAddress{ + {Type: corev1.NodeInternalIP, Address: "4.5.6.7"}, }, } _, err := client.CoreV1().Nodes().UpdateStatus(context.Background(), node1, metav1.UpdateOptions{}) @@ -1173,7 +1174,7 @@ func TestServiceResource_clusterIPUnnamedPorts(t *testing.T) { // Insert the service svc := clusterIPService("foo", metav1.NamespaceDefault) - svc.Spec.Ports = []apiv1.ServicePort{ + svc.Spec.Ports = []corev1.ServicePort{ {Port: 80, TargetPort: intstr.FromInt(8080)}, {Port: 8500, TargetPort: intstr.FromInt(2000)}, } @@ -1281,7 +1282,7 @@ func TestServiceResource_clusterIPTargetPortNamed(t *testing.T) { // Insert the service svc := clusterIPService("foo", metav1.NamespaceDefault) svc.Annotations[annotationServicePort] = "rpc" - svc.Spec.Ports = []apiv1.ServicePort{ + svc.Spec.Ports = []corev1.ServicePort{ {Port: 80, TargetPort: intstr.FromString("httpPort"), Name: "http"}, {Port: 8500, TargetPort: intstr.FromString("rpcPort"), Name: "rpc"}, } @@ -1531,22 +1532,323 @@ func TestServiceResource_MirroredPrefixNamespace(t *testing.T) { }) } +// Test k8s namespace suffix is not appended +// when the service name annotation is provided. +func TestServiceResource_addIngress(t *testing.T) { + t.Parallel() + + cases := map[string]struct { + enableIngress bool + syncIngressIP bool + ingress *networkingv1.Ingress + expectIngressSync bool + expectedAddress string + expectedPort int + }{ + "enable ingress on port 80": { + enableIngress: true, + ingress: &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress", + }, + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ + { + Hosts: []string{"test.other.consul"}, + SecretName: "test-other-tls-secret", + }, + }, + Rules: []networkingv1.IngressRule{ + { + Host: "test.host.consul", + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "test-service", + Port: networkingv1.ServiceBackendPort{ + Number: 8080, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + expectIngressSync: true, + expectedAddress: "test.host.consul", + expectedPort: 80, + }, + "enable ingress on port 443": { + enableIngress: true, + ingress: &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress", + }, + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ + { + Hosts: []string{"test.host.consul"}, + SecretName: "test-tls-secret", + }, + }, + Rules: []networkingv1.IngressRule{ + { + Host: "test.host.consul", + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "test-service", + Port: networkingv1.ServiceBackendPort{ + Number: 8080, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + expectIngressSync: true, + expectedAddress: "test.host.consul", + expectedPort: 443, + }, + "enable ingress on port 80 with loadbalancer IP": { + enableIngress: true, + syncIngressIP: true, + ingress: &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress", + }, + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ + { + Hosts: []string{"test.other.consul"}, + SecretName: "test-other-tls-secret", + }, + }, + Rules: []networkingv1.IngressRule{ + { + Host: "test.host.consul", + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "test-service", + Port: networkingv1.ServiceBackendPort{ + Number: 8080, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Status: networkingv1.IngressStatus{ + LoadBalancer: corev1.LoadBalancerStatus{ + Ingress: []corev1.LoadBalancerIngress{{IP: "1.2.3.4"}}, + }, + }, + }, + expectIngressSync: true, + expectedAddress: "1.2.3.4", + expectedPort: 80, + }, + "enable ingress on port 443 with loadbalancer IP": { + enableIngress: true, + syncIngressIP: true, + ingress: &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress", + }, + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ + { + Hosts: []string{"test.host.consul"}, + SecretName: "test-tls-secret", + }, + }, + Rules: []networkingv1.IngressRule{ + { + Host: "test.host.consul", + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "test-service", + Port: networkingv1.ServiceBackendPort{ + Number: 8080, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Status: networkingv1.IngressStatus{ + LoadBalancer: corev1.LoadBalancerStatus{ + Ingress: []corev1.LoadBalancerIngress{{IP: "1.2.3.4"}}, + }, + }, + }, + expectIngressSync: true, + expectedAddress: "1.2.3.4", + expectedPort: 443, + }, + "ingress disabled": { + enableIngress: false, + ingress: &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress", + }, + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ + { + Host: "test.host.consul", + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "test-service", + Port: networkingv1.ServiceBackendPort{ + Number: 8080, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + expectIngressSync: false, + expectedAddress: "1.1.1.1", + expectedPort: 8080, + }, + "ignores ingress if host != /": { + enableIngress: true, + ingress: &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress", + }, + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ + { + Host: "test.host.consul", + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/foo", + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "test-service", + Port: networkingv1.ServiceBackendPort{ + Number: 8080, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + expectIngressSync: false, + expectedAddress: "1.1.1.1", + expectedPort: 8080, + }, + } + + for name, test := range cases { + t.Run(name, func(t *testing.T) { + client := fake.NewSimpleClientset() + syncer := newTestSyncer() + serviceResource := defaultServiceResource(client, syncer) + serviceResource.ClusterIPSync = true + serviceResource.EnableIngress = test.enableIngress + serviceResource.SyncLoadBalancerIPs = test.syncIngressIP + + // Start the controller + closer := controller.TestControllerRun(&serviceResource) + defer closer() + + // Create the service + _, err := client.CoreV1().Services(metav1.NamespaceDefault).Create(context.Background(), clusterIPService("test-service", metav1.NamespaceDefault), metav1.CreateOptions{}) + require.NoError(t, err) + // Create the ingress + _, err = client.NetworkingV1().Ingresses(metav1.NamespaceDefault).Create(context.Background(), test.ingress, metav1.CreateOptions{}) + require.NoError(t, err) + createEndpoints(t, client, "test-service", metav1.NamespaceDefault) + // Verify that the service name annotation is preferred + retry.Run(t, func(r *retry.R) { + syncer.Lock() + defer syncer.Unlock() + actual := syncer.Registrations + if test.expectIngressSync { + require.Len(r, actual, 1) + require.Equal(r, test.expectedAddress, actual[0].Service.Address) + require.Equal(r, test.expectedPort, actual[0].Service.Port) + } else { + require.Len(r, actual, 2) + require.Equal(r, test.expectedAddress, actual[0].Service.Address) + require.Equal(r, test.expectedPort, actual[0].Service.Port) + } + + }) + }) + } +} + // lbService returns a Kubernetes service of type LoadBalancer. -func lbService(name, namespace, lbIP string) *apiv1.Service { - return &apiv1.Service{ +func lbService(name, namespace, lbIP string) *corev1.Service { + return &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, Annotations: map[string]string{}, }, - Spec: apiv1.ServiceSpec{ - Type: apiv1.ServiceTypeLoadBalancer, + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeLoadBalancer, }, - Status: apiv1.ServiceStatus{ - LoadBalancer: apiv1.LoadBalancerStatus{ - Ingress: []apiv1.LoadBalancerIngress{ + Status: corev1.ServiceStatus{ + LoadBalancer: corev1.LoadBalancerStatus{ + Ingress: []corev1.LoadBalancerIngress{ { IP: lbIP, }, @@ -1557,16 +1859,16 @@ func lbService(name, namespace, lbIP string) *apiv1.Service { } // nodePortService returns a Kubernetes service of type NodePort. -func nodePortService(name, namespace string) *apiv1.Service { - return &apiv1.Service{ +func nodePortService(name, namespace string) *corev1.Service { + return &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, - Spec: apiv1.ServiceSpec{ - Type: apiv1.ServiceTypeNodePort, - Ports: []apiv1.ServicePort{ + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeNodePort, + Ports: []corev1.ServicePort{ {Name: "http", Port: 80, TargetPort: intstr.FromInt(8080), NodePort: 30000}, {Name: "rpc", Port: 8500, TargetPort: intstr.FromInt(2000), NodePort: 30001}, }, @@ -1575,17 +1877,17 @@ func nodePortService(name, namespace string) *apiv1.Service { } // clusterIPService returns a Kubernetes service of type ClusterIP. -func clusterIPService(name, namespace string) *apiv1.Service { - return &apiv1.Service{ +func clusterIPService(name, namespace string) *corev1.Service { + return &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, Annotations: map[string]string{}, }, - Spec: apiv1.ServiceSpec{ - Type: apiv1.ServiceTypeClusterIP, - Ports: []apiv1.ServicePort{ + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeClusterIP, + Ports: []corev1.ServicePort{ {Name: "http", Port: 80, TargetPort: intstr.FromInt(8080)}, {Name: "rpc", Port: 8500, TargetPort: intstr.FromInt(2000)}, }, @@ -1594,34 +1896,34 @@ func clusterIPService(name, namespace string) *apiv1.Service { } // createNodes calls the fake k8s client to create two Kubernetes nodes and returns them. -func createNodes(t *testing.T, client *fake.Clientset) (*apiv1.Node, *apiv1.Node) { +func createNodes(t *testing.T, client *fake.Clientset) (*corev1.Node, *corev1.Node) { // Insert the nodes - node1 := &apiv1.Node{ + node1 := &corev1.Node{ ObjectMeta: metav1.ObjectMeta{ Name: nodeName1, }, - Status: apiv1.NodeStatus{ - Addresses: []apiv1.NodeAddress{ - {Type: apiv1.NodeExternalIP, Address: "1.2.3.4"}, - {Type: apiv1.NodeInternalIP, Address: "4.5.6.7"}, - {Type: apiv1.NodeInternalIP, Address: "7.8.9.10"}, + Status: corev1.NodeStatus{ + Addresses: []corev1.NodeAddress{ + {Type: corev1.NodeExternalIP, Address: "1.2.3.4"}, + {Type: corev1.NodeInternalIP, Address: "4.5.6.7"}, + {Type: corev1.NodeInternalIP, Address: "7.8.9.10"}, }, }, } _, err := client.CoreV1().Nodes().Create(context.Background(), node1, metav1.CreateOptions{}) require.NoError(t, err) - node2 := &apiv1.Node{ + node2 := &corev1.Node{ ObjectMeta: metav1.ObjectMeta{ Name: nodeName2, }, - Status: apiv1.NodeStatus{ - Addresses: []apiv1.NodeAddress{ - {Type: apiv1.NodeExternalIP, Address: "2.3.4.5"}, - {Type: apiv1.NodeInternalIP, Address: "3.4.5.6"}, - {Type: apiv1.NodeInternalIP, Address: "6.7.8.9"}, + Status: corev1.NodeStatus{ + Addresses: []corev1.NodeAddress{ + {Type: corev1.NodeExternalIP, Address: "2.3.4.5"}, + {Type: corev1.NodeInternalIP, Address: "3.4.5.6"}, + {Type: corev1.NodeInternalIP, Address: "6.7.8.9"}, }, }, } @@ -1635,31 +1937,31 @@ func createNodes(t *testing.T, client *fake.Clientset) (*apiv1.Node, *apiv1.Node func createEndpoints(t *testing.T, client *fake.Clientset, serviceName string, namespace string) { node1 := nodeName1 node2 := nodeName2 - targetRef := apiv1.ObjectReference{Kind: "pod", Name: "foobar"} + targetRef := corev1.ObjectReference{Kind: "pod", Name: "foobar"} _, err := client.CoreV1().Endpoints(namespace).Create( context.Background(), - &apiv1.Endpoints{ + &corev1.Endpoints{ ObjectMeta: metav1.ObjectMeta{ Name: serviceName, Namespace: namespace, }, - Subsets: []apiv1.EndpointSubset{ + Subsets: []corev1.EndpointSubset{ { - Addresses: []apiv1.EndpointAddress{ + Addresses: []corev1.EndpointAddress{ {NodeName: &node1, IP: "1.1.1.1", TargetRef: &targetRef}, }, - Ports: []apiv1.EndpointPort{ + Ports: []corev1.EndpointPort{ {Name: "http", Port: 8080}, {Name: "rpc", Port: 2000}, }, }, { - Addresses: []apiv1.EndpointAddress{ + Addresses: []corev1.EndpointAddress{ {NodeName: &node2, IP: "2.2.2.2"}, }, - Ports: []apiv1.EndpointPort{ + Ports: []corev1.EndpointPort{ {Name: "http", Port: 8080}, {Name: "rpc", Port: 2000}, }, diff --git a/control-plane/subcommand/sync-catalog/command.go b/control-plane/subcommand/sync-catalog/command.go index 1530cf9d53..2dadf6e039 100644 --- a/control-plane/subcommand/sync-catalog/command.go +++ b/control-plane/subcommand/sync-catalog/command.go @@ -67,6 +67,10 @@ type Command struct { flagK8SNSMirroringPrefix string // Prefix added to Consul namespaces created when mirroring flagCrossNamespaceACLPolicy string // The name of the ACL policy to add to every created namespace if ACLs are enabled + // Flags to support Kubernetes Ingress resources + flagEnableIngress bool // Register services using the hostname from an ingress resource + flagLoadBalancerIPs bool // Use the load balancer IP of an ingress resource instead of the hostname + clientset kubernetes.Interface // ready indicates whether this controller is ready to sync services. This will be changed to true once the @@ -151,6 +155,11 @@ func (c *Command) init() { "[Enterprise Only] Name of the ACL policy to attach to all created Consul namespaces to allow service "+ "discovery across Consul namespaces. Only necessary if ACLs are enabled.") + c.flags.BoolVar(&c.flagEnableIngress, "enable-ingress", false, + "[Enterprise Only] Enables namespaces, in either a single Consul namespace or mirrored.") + c.flags.BoolVar(&c.flagLoadBalancerIPs, "loadBalancer-ips", false, + "[Enterprise Only] Enables namespaces, in either a single Consul namespace or mirrored.") + c.consul = &flags.ConsulFlags{} c.k8s = &flags.K8SFlags{} flags.Merge(c.flags, c.consul.Flags()) @@ -294,6 +303,8 @@ func (c *Command) Run(args []string) int { EnableK8SNSMirroring: c.flagEnableK8SNSMirroring, K8SNSMirroringPrefix: c.flagK8SNSMirroringPrefix, ConsulNodeName: c.flagConsulNodeName, + EnableIngress: c.flagEnableIngress, + SyncLoadBalancerIPs: c.flagLoadBalancerIPs, }, } From 02cab6ccbd2a9a18f8b83f939c03f8a6c0b5df11 Mon Sep 17 00:00:00 2001 From: Connor Date: Tue, 16 May 2023 15:46:34 -0500 Subject: [PATCH 158/340] Add telemetry collector deployment to consul-k8s (#2134) * Create values.yaml section for telemetry-collector * Initial telemetry-collector validation and bats test * Add nodeSelector * Add connect-init initContainer * Add consul-dataplane container * Conditionally add ca-cert volume * Include vault annotations * Prune tests to pertinent test cases * Move consul server env vars * Check ca mount for dataplane container * Check correct env var * Set default resources * Set initContainer and tolerations * Support priorityClassName * Support setting initContainer resources * Fix replicas unit test * Turn off tproxy and remove unneeded security context * Set -tls-disabled if global.tls.enabled=false * Set -ca-certs correct if tls is enabled * Set external server args * Set partition flag tests * Label bats tests, remove duplicate flags * Bats tests for service, add metricsserver port * Support annotations and imagePullSecret on serviceAccount * Create configmap for custom configuration * Add configmap to deployment * Fix test names * Remove unneeded cloud validation. fixup comment * Comment values.yaml changes * Switch from sidecar auth method to component auth method * changelog * Add PodSecurityPolicy for consul-telemetry-collector * Rename init container + add comment * Remove logLevel bats tests as it is unsupported right now * Remove auth-method special cases * Replace LOGIN_DATACENTER login with LOGIN_NAMESPACE * Remove unneeded LOGIN_DATACENTER test --- .changelog/2134.txt | 3 + charts/consul/templates/_helpers.tpl | 36 + .../telemetry-collector-configmap.yaml | 18 + .../telemetry-collector-deployment.yaml | 367 +++++++ ...telemetry-collector-podsecuritypolicy.yaml | 40 + .../templates/telemetry-collector-role.yaml | 21 + .../telemetry-collector-rolebinding.yaml | 21 + .../telemetry-collector-service.yaml | 24 + .../telemetry-collector-serviceaccount.yaml | 23 + .../unit/telemetry-collector-configmap.bats | 29 + .../unit/telemetry-collector-deployment.bats | 979 ++++++++++++++++++ ...telemetry-collector-podsecuritypolicy.bats | 21 + .../test/unit/telemetry-collector-role.bats | 21 + .../unit/telemetry-collector-rolebinding.bats | 21 + .../unit/telemetry-collector-service.bats | 72 ++ .../telemetry-collector-serviceaccount.bats | 69 ++ charts/consul/values.yaml | 112 +- 17 files changed, 1865 insertions(+), 12 deletions(-) create mode 100644 .changelog/2134.txt create mode 100644 charts/consul/templates/telemetry-collector-configmap.yaml create mode 100644 charts/consul/templates/telemetry-collector-deployment.yaml create mode 100644 charts/consul/templates/telemetry-collector-podsecuritypolicy.yaml create mode 100644 charts/consul/templates/telemetry-collector-role.yaml create mode 100644 charts/consul/templates/telemetry-collector-rolebinding.yaml create mode 100644 charts/consul/templates/telemetry-collector-service.yaml create mode 100644 charts/consul/templates/telemetry-collector-serviceaccount.yaml create mode 100644 charts/consul/test/unit/telemetry-collector-configmap.bats create mode 100755 charts/consul/test/unit/telemetry-collector-deployment.bats create mode 100644 charts/consul/test/unit/telemetry-collector-podsecuritypolicy.bats create mode 100644 charts/consul/test/unit/telemetry-collector-role.bats create mode 100644 charts/consul/test/unit/telemetry-collector-rolebinding.bats create mode 100755 charts/consul/test/unit/telemetry-collector-service.bats create mode 100644 charts/consul/test/unit/telemetry-collector-serviceaccount.bats diff --git a/.changelog/2134.txt b/.changelog/2134.txt new file mode 100644 index 0000000000..980e7ba666 --- /dev/null +++ b/.changelog/2134.txt @@ -0,0 +1,3 @@ +```release-note:feature +Add support for consul-telemetry-collector to forward envoy metrics to an otelhttp compatible receiver or HCP +``` \ No newline at end of file diff --git a/charts/consul/templates/_helpers.tpl b/charts/consul/templates/_helpers.tpl index b1feb0dbd6..1b866888c0 100644 --- a/charts/consul/templates/_helpers.tpl +++ b/charts/consul/templates/_helpers.tpl @@ -387,3 +387,39 @@ Usage: {{ template "consul.validateCloudSecretKeys" . }} {{- end }} {{- end }} {{- end -}} + + +{{/* +Fails if temeletryCollector.clientId or telemetryCollector.clientSecret exist and one of other secrets is nil or empty. +- telemetryCollector.cloud.clientId.secretName +- telemetryCollector.cloud.clientSecret.secretName +- global.cloud.resourceId.secretName + +Usage: {{ template "consul.validateTelemetryCollectorCloud" . }} + +*/}} +{{- define "consul.validateTelemetryCollectorCloud" -}} +{{- if (and .Values.telemetryCollector.cloud.clientId.secretName (or (not .Values.global.cloud.resourceId.secretName) (not .Values.telemetryCollector.cloud.clientSecret.secretName))) }} +{{fail "When telemetryCollector.cloud.clientId.secretName is set, global.cloud.resourceId.secretName, telemetryCollector.cloud.clientSecret.secretName must also be set."}} +{{- end }} +{{- if (and .Values.telemetryCollector.cloud.clientSecret.secretName (or (not .Values.global.cloud.resourceId.secretName) (not .Values.telemetryCollector.cloud.clientSecret.secretName))) }} +{{fail "When telemetryCollector.cloud.clientSecret.secretName is set, global.cloud.resourceId.secretName,telemetryCollector.cloud.clientId.secretName must also be set."}} +{{- end }} +{{- end }} + +{{/**/}} + +{{- define "consul.validateTelemetryCollectorCloudSecretKeys" -}} +{{- if or (and .Values.telemetryCollector.cloud.clientId.secretName (not .Values.telemetryCollector.cloud.clientId.secretKey)) (and .Values.telemetryCollector.cloud.clientId.secretKey (not .Values.telemetryCollector.cloud.clientId.secretName)) }} +{{fail "When either telemetryCollector.cloud.clientId.secretName or telemetryCollector.cloud.clientId.secretKey is defined, both must be set."}} +{{- end }} +{{- if or (and .Values.telemetryCollector.cloud.clientSecret.secretName (not .Values.telemetryCollector.cloud.clientSecret.secretKey)) (and .Values.telemetryCollector.cloud.clientSecret.secretKey (not .Values.telemetryCollector.cloud.clientSecret.secretName)) }} +{{fail "When either telemetryCollector.cloud.clientSecret.secretName or telemetryCollector.cloud.clientSecret.secretKey is defined, both must be set."}} +{{- end }} +{{- if or (and .Values.telemetryCollector.cloud.clientSecret.secretName .Values.telemetryCollector.cloud.clientSecret.secretKey .Values.telemetryCollector.cloud.clientId.secretName .Values.telemetryCollector.cloud.clientId.secretKey (not .Values.global.cloud.resourceId.secretName)) }} +{{fail "When telemetryCollector has clientId and clientSecret global.cloud.resourceId.secretName must be set"}} +{{- end }} +{{- if or (and .Values.telemetryCollector.cloud.clientSecret.secretName .Values.telemetryCollector.cloud.clientSecret.secretKey .Values.telemetryCollector.cloud.clientId.secretName .Values.telemetryCollector.cloud.clientId.secretKey (not .Values.global.cloud.resourceId.secretKey)) }} +{{fail "When telemetryCollector has clientId and clientSecret .global.cloud.resourceId.secretKey must be set"}} +{{- end }} +{{- end -}} \ No newline at end of file diff --git a/charts/consul/templates/telemetry-collector-configmap.yaml b/charts/consul/templates/telemetry-collector-configmap.yaml new file mode 100644 index 0000000000..0bf5b8753c --- /dev/null +++ b/charts/consul/templates/telemetry-collector-configmap.yaml @@ -0,0 +1,18 @@ +{{- if (and .Values.telemetryCollector.enabled .Values.telemetryCollector.customExporterConfig) }} +# Immutable ConfigMap which saves the partition name. Attempting to update this configmap +# with a new Admin Partition name will cause the helm upgrade to fail +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "consul.fullname" . }}-telemetry-collector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: consul-telemetry-collector +data: + config.json: |- + {{ tpl .Values.telemetryCollector.customExporterConfig . | trimAll "\"" | indent 4 }} +{{- end }} diff --git a/charts/consul/templates/telemetry-collector-deployment.yaml b/charts/consul/templates/telemetry-collector-deployment.yaml new file mode 100644 index 0000000000..a073128f95 --- /dev/null +++ b/charts/consul/templates/telemetry-collector-deployment.yaml @@ -0,0 +1,367 @@ +{{- if .Values.telemetryCollector.enabled }} +{{- if not .Values.telemetryCollector.image}}{{ fail "telemetryCollector.image must be set to enable consul-telemetry-collector" }}{{ end }} +{{- if not .Values.connectInject.enabled }}{{ fail "connectInject.enabled must be true" }}{{ end -}} +{{- if and .Values.global.adminPartitions.enabled (not .Values.global.enableConsulNamespaces) }}{{ fail "global.enableConsulNamespaces must be true if global.adminPartitions.enabled=true" }}{{ end }} +{{ template "consul.validateCloudSecretKeys" . }} +{{ template "consul.validateTelemetryCollectorCloud" . }} +{{ template "consul.validateTelemetryCollectorCloudSecretKeys" . }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "consul.fullname" . }}-telemetry-collector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: consul-telemetry-collector + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.telemetryCollector.replicas }} + selector: + matchLabels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: consul-telemetry-collector + template: + metadata: + annotations: + "consul.hashicorp.com/connect-inject": "false" + # This annotation tells the endpoints controller that this pod was injected even though it wasn't. The + # endpoints controller would then sync the endpoint into Consul + "consul.hashicorp.com/connect-inject-status": "injected" + # We aren't using tproxy and we don't have an original pod. This would be simpler if we made a path similar + # to gateways + "consul.hashicorp.com/transparent-proxy": "false" + "consul.hashicorp.com/transparent-proxy-overwrite-probes": "false" + "consul.hashicorp.com/connect-k8s-version": {{ $.Chart.Version }} + {{- if .Values.telemetryCollector.customExporterConfig }} + # configmap checksum + "consul.hashicorp.com/config-checksum": {{ include (print $.Template.BasePath "/telemetry-collector-configmap.yaml") . | sha256sum }} + {{- end }} + # vault annotations + {{- if (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) }} + "vault.hashicorp.com/agent-init-first": "true" + "vault.hashicorp.com/agent-inject": "true" + "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.consulCARole }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ .Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" . }} + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": "{{ .Values.global.secretsBackend.vault.ca.secretName }}" + "vault.hashicorp.com/ca-cert": "/vault/custom/{{ .Values.global.secretsBackend.vault.ca.secretKey }}" + {{- end }} + {{- if .Values.global.secretsBackend.vault.agentAnnotations }} + {{ tpl .Values.global.secretsBackend.vault.agentAnnotations . | nindent 8 | trim }} + {{- end }} + {{- end }} + + labels: + consul.hashicorp.com/connect-inject-managed-by: consul-k8s-endpoints-controller + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: consul-telemetry-collector + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} + spec: + serviceAccountName: {{ template "consul.fullname" . }}-telemetry-collector + initContainers: + # We're manually managing this init container instead of using the connect injector so that we don't run into + # any race conditions on the connect-injector deployment or upgrade + - name: consul-connect-init + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + {{- if .Values.global.acls.manageSystemACLs }} + - name: CONSUL_LOGIN_AUTH_METHOD + value: {{ template "consul.fullname" . }}-k8s-auth-method + - name: CONSUL_LOGIN_META + value: "component=consul-telemetry-collector,pod=$(NAMESPACE)/$(POD_NAME)" + {{- end }} + - name: CONSUL_NODE_NAME + value: $(NODE_NAME)-virtual + {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 10 }} + {{- if .Values.global.enableConsulNamespaces }} + - name: CONSUL_NAMESPACE + value: {{ .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} + {{- if .Values.syncCatalog.consulNamespaces.mirroringK8S }} + - name: CONSUL_LOGIN_NAMESPACE + value: "default" + {{- else }} + - name: CONSUL_LOGIN_NAMESPACE + value: {{ .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} + {{- end }} + {{- end }} + command: + - /bin/sh + - -ec + - |- + consul-k8s-control-plane connect-init -pod-name=${POD_NAME} -pod-namespace=${POD_NAMESPACE} \ + -log-level={{ default .Values.global.logLevel }} \ + -log-json={{ .Values.global.logJSON }} \ + -service-account-name="consul-telemetry-collector" \ + -service-name="" \ + -proxy-id-file="/consul/connect-inject/proxyid" + + image: {{ .Values.global.imageK8S }} + imagePullPolicy: IfNotPresent + {{- if .Values.telemetryCollector.initContainer.resources }} + resources: + {{- toYaml .Values.telemetryCollector.initContainer.resources | nindent 12 }} + {{- else }} + resources: + limits: + cpu: 50m + memory: 150Mi + requests: + cpu: 50m + memory: 25Mi + {{- end }} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /consul/connect-inject + name: consul-connect-inject-data + {{- if .Values.global.tls.enabled }} + {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- end }} + containers: + - name: consul-telemetry-collector + image: {{ .Values.telemetryCollector.image }} + imagePullPolicy: Always + ports: + - containerPort: 9090 + name: metrics + protocol: TCP + - containerPort: 9356 + name: metricsserver + protocol: TCP + env: + # These are mounted as secrets so that the telemetry-collector can use them when cloud is enabled. + # - the hcp-go-sdk in consul agent will already look for HCP_CLIENT_ID, HCP_CLIENT_SECRET, HCP_AUTH_URL, + # HCP_SCADA_ADDRESS, and HCP_API_HOST. so nothing more needs to be done. + # - HCP_RESOURCE_ID is created for use in the global cloud section but we will share it here + {{- if .Values.telemetryCollector.cloud.clientId.secretName }} + - name: HCP_CLIENT_ID + valueFrom: + secretKeyRef: + name: {{ .Values.telemetryCollector.cloud.clientId.secretName }} + key: {{ .Values.telemetryCollector.cloud.clientId.secretKey }} + {{- end }} + {{- if .Values.telemetryCollector.cloud.clientSecret.secretName }} + - name: HCP_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: {{ .Values.telemetryCollector.cloud.clientSecret.secretName }} + key: {{ .Values.telemetryCollector.cloud.clientSecret.secretKey }} + {{- end}} + {{- if .Values.global.cloud.resourceId.secretName }} + - name: HCP_RESOURCE_ID + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.resourceId.secretName }} + key: {{ .Values.global.cloud.resourceId.secretKey }} + {{- end }} + {{- if .Values.global.cloud.authUrl.secretName }} + - name: HCP_AUTH_URL + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.authUrl.secretName }} + key: {{ .Values.global.cloud.authUrl.secretKey }} + {{- end}} + {{- if .Values.global.cloud.apiHost.secretName }} + - name: HCP_API_HOST + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.apiHost.secretName }} + key: {{ .Values.global.cloud.apiHost.secretKey }} + {{- end}} + {{- if .Values.global.cloud.scadaAddress.secretName }} + - name: HCP_SCADA_ADDRESS + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.scadaAddress.secretName }} + key: {{ .Values.global.cloud.scadaAddress.secretKey }} + {{- end}} + + command: + - "/bin/sh" + - "-ec" + - | + consul-telemetry-collector agent + {{- if .Values.telemetryCollector.customExporterConfig }} + args: + - -config-file-path /consul/config/config.json + {{ end }} + {{- if .Values.telemetryCollector.customExporterConfig }} + volumeMounts: + - name: config + mountPath: /consul/config + {{- end }} + resources: + {{- if .Values.telemetryCollector.resources }} + {{- toYaml .Values.telemetryCollector.resources | nindent 12 }} + {{- end }} + # consul-dataplane container + - name: consul-dataplane + image: "{{ .Values.global.imageConsulDataplane }}" + imagePullPolicy: IfNotPresent + command: + - consul-dataplane + args: + # addresses + {{- if .Values.externalServers.enabled }} + - -addresses={{ .Values.externalServers.hosts | first }} + {{- else }} + - -addresses={{ template "consul.fullname" . }}-server.{{ .Release.Namespace }}.svc + {{- end }} + # grpc + {{- if .Values.externalServers.enabled }} + - -grpc-port={{ .Values.externalServers.grpcPort }} + {{- else }} + - -grpc-port=8502 + {{- end }} + - -proxy-service-id-path=/consul/connect-inject/proxyid + # tls + {{- if .Values.global.tls.enabled }} + {{- if (not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots)) }} + {{- if .Values.global.secretsBackend.vault.enabled }} + - -ca-certs=/vault/secrets/serverca.crt + {{- else }} + - -ca-certs=/consul/tls/ca/tls.crt + {{- end }} + {{- end }} + {{- if and .Values.externalServers.enabled .Values.externalServers.tlsServerName }} + - -tls-server-name={{.Values.externalServers.tlsServerName }} + {{- else if .Values.global.cloud.enabled }} + - -tls-server-name=server.{{ .Values.global.datacenter}}.{{ .Values.global.domain}} + {{- end }} + {{- else }} + - -tls-disabled + {{- end }} + # credentials + {{- if .Values.global.acls.manageSystemACLs }} + - -credential-type=login + - -login-bearer-token-path=/var/run/secrets/kubernetes.io/serviceaccount/token + - -login-auth-method={{ template "consul.fullname" . }}-k8s-auth-method + {{- if .Values.global.enableConsulNamespaces }} + {{- if .Values.syncCatalog.consulNamespaces.mirroringK8S }} + - -login-namespace="default" + {{- else }} + - -login-namespace={{ .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} + {{- end }} + {{- end }} + {{- if .Values.global.adminPartitions.enabled }} + - foo + - -login-partition={{ .Values.global.adminPartitions.name }} + {{- end }} + {{- end }} + {{- if .Values.global.enableConsulNamespaces }} + - -service-namespace={{ .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} + {{- end }} + {{- if .Values.global.adminPartitions.enabled }} + - -service-partition={{ .Values.global.adminPartitions.name }} + {{- end }} + {{- if .Values.global.metrics.enabled }} + - -telemetry-prom-scrape-path=/metrics + {{- end }} + - -log-level={{ default .Values.global.logLevel }} + - -log-json={{ .Values.global.logJSON }} + - -envoy-concurrency=2 + {{- if and .Values.externalServers.enabled .Values.externalServers.skipServerWatch }} + - -server-watch-disabled=true + {{- end }} + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: DP_CREDENTIAL_LOGIN_META1 + value: pod=$(NAMESPACE)/$(POD_NAME) + - name: DP_CREDENTIAL_LOGIN_META2 + value: component=consul-telemetry-collector + - name: DP_SERVICE_NODE_NAME + value: $(NODE_NAME)-virtual + - name: TMPDIR + value: /consul/connect-inject + readinessProbe: + failureThreshold: 3 + initialDelaySeconds: 1 + periodSeconds: 10 + successThreshold: 1 + tcpSocket: + port: 20000 + timeoutSeconds: 1 + securityContext: + readOnlyRootFilesystem: true + runAsGroup: 5995 + runAsNonRoot: true + runAsUser: 5995 + # dataplane volume mounts + volumeMounts: + - mountPath: /consul/connect-inject + name: consul-connect-inject-data + {{- if .Values.global.tls.enabled }} + {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- end }} + + {{- if .Values.telemetryCollector.nodeSelector }} + nodeSelector: + {{ tpl .Values.telemetryCollector.nodeSelector . | indent 8 | trim }} + {{- end }} + {{- if .Values.telemetryCollector.priorityClassName }} + priorityClassName: {{ .Values.telemetryCollector.priorityClassName }} + {{- end }} + volumes: + - emptyDir: + medium: Memory + name: consul-connect-inject-data + {{- if .Values.global.tls.enabled }} + {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + {{- end }} + - name: config + configMap: + name: {{ template "consul.fullname" . }}-telemetry-collector +{{- end }} diff --git a/charts/consul/templates/telemetry-collector-podsecuritypolicy.yaml b/charts/consul/templates/telemetry-collector-podsecuritypolicy.yaml new file mode 100644 index 0000000000..286a92d0bd --- /dev/null +++ b/charts/consul/templates/telemetry-collector-podsecuritypolicy.yaml @@ -0,0 +1,40 @@ +{{- if and .Values.global.enablePodSecurityPolicies .Values.telemetryCollector.enabled }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-telemetry-collector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: telemetry-collector +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/consul/templates/telemetry-collector-role.yaml b/charts/consul/templates/telemetry-collector-role.yaml new file mode 100644 index 0000000000..f89373252d --- /dev/null +++ b/charts/consul/templates/telemetry-collector-role.yaml @@ -0,0 +1,21 @@ +{{- if and .Values.global.enablePodSecurityPolicies .Values.telemetryCollector.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-telemetry-collector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: consul-telemetry-collector +rules: + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" . }}-telemetry-collector + verbs: + - use +{{- end }} + diff --git a/charts/consul/templates/telemetry-collector-rolebinding.yaml b/charts/consul/templates/telemetry-collector-rolebinding.yaml new file mode 100644 index 0000000000..1f9a896997 --- /dev/null +++ b/charts/consul/templates/telemetry-collector-rolebinding.yaml @@ -0,0 +1,21 @@ +{{- if and .Values.global.enablePodSecurityPolicies .Values.telemetryCollector.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-telemetry-collector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: consul-telemetry-collector +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-telemetry-collector +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" . }}-telemetry-collector +{{- end }} + diff --git a/charts/consul/templates/telemetry-collector-service.yaml b/charts/consul/templates/telemetry-collector-service.yaml new file mode 100644 index 0000000000..f7fc3f09d9 --- /dev/null +++ b/charts/consul/templates/telemetry-collector-service.yaml @@ -0,0 +1,24 @@ +{{- if .Values.telemetryCollector.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "consul.fullname" . }}-telemetry-collector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: consul-telemetry-collector + {{ if .Values.telemetryCollector.service.annotations }} + annotations: + {{ tpl .Values.telemetryCollector.service.annotations . | nindent 4 | trim }} + {{- end }} +spec: + type: ClusterIP + ports: + - port: 9356 + targetPort: 9356 + selector: + app: consul + component: consul-telemetry-collector +{{- end }} \ No newline at end of file diff --git a/charts/consul/templates/telemetry-collector-serviceaccount.yaml b/charts/consul/templates/telemetry-collector-serviceaccount.yaml new file mode 100644 index 0000000000..f2b6e88171 --- /dev/null +++ b/charts/consul/templates/telemetry-collector-serviceaccount.yaml @@ -0,0 +1,23 @@ +{{- if .Values.telemetryCollector.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-telemetry-collector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: consul-telemetry-collector + {{- if .Values.telemetryCollector.serviceAccount.annotations }} + annotations: + {{ tpl .Values.telemetryCollector.serviceAccount.annotations . | nindent 4 | trim }} + {{- end }} +automountServiceAccountToken: true +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/consul/test/unit/telemetry-collector-configmap.bats b/charts/consul/test/unit/telemetry-collector-configmap.bats new file mode 100644 index 0000000000..c866f0d3e1 --- /dev/null +++ b/charts/consul/test/unit/telemetry-collector-configmap.bats @@ -0,0 +1,29 @@ +#!/usr/bin/env bats + +load _helpers + +@test "telemetryCollector/ConfigMap: disabled by default" { + cd `chart_dir` + assert_empty helm template \ + -s templates/telemetry-collector-configmap.yaml \ + . +} + +@test "telemetryCollector/ConfigMap: enabled with telemetryCollector enabled and config has content" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-configmap.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.customExporterConfig="{}"' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "telemetryCollector/ConfigMap: disabled with telemetryCollector enabled and config is empty content" { + cd `chart_dir` + assert_empty helm template \ + -s templates/telemetry-collector-configmap.yaml \ + --set 'telemetryCollector.enabled=true' \ + . +} \ No newline at end of file diff --git a/charts/consul/test/unit/telemetry-collector-deployment.bats b/charts/consul/test/unit/telemetry-collector-deployment.bats new file mode 100755 index 0000000000..8161e90e5d --- /dev/null +++ b/charts/consul/test/unit/telemetry-collector-deployment.bats @@ -0,0 +1,979 @@ +#!/usr/bin/env bats + +load _helpers + +@test "telemetryCollector/Deployment: disabled by default" { + cd `chart_dir` + assert_empty helm template \ + -s templates/telemetry-collector-deployment.yaml \ + . +} + +@test "telemetryCollector/Deployment: fails if no image is set" { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=null' \ + . + [ "$status" -eq 1 ] + [[ "$output" =~ "telemetryCollector.image must be set to enable consul-telemetry-collector" ]] +} + +@test "telemetryCollector/Deployment: disable with telemetry-collector.enabled" { + cd `chart_dir` + assert_empty helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=false' \ + . +} + +@test "telemetryCollector/Deployment: disable with global.enabled" { + cd `chart_dir` + assert_empty helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'global.enabled=false' \ + . +} + +@test "telemetryCollector/Deployment: container image overrides" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].image' | tee /dev/stderr) + [ "${actual}" = "\"bar\"" ] +} + +#-------------------------------------------------------------------- +# nodeSelector + +@test "telemetryCollector/Deployment: nodeSelector is not set by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + . | tee /dev/stderr | + yq '.spec.template.spec.nodeSelector' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "telemetryCollector/Deployment: specified nodeSelector" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'telemetryCollector.nodeSelector=testing' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.nodeSelector' | tee /dev/stderr) + [ "${actual}" = "testing" ] +} + +#-------------------------------------------------------------------- +# global.tls.enabled + +@test "telemetryCollector/Deployment: Adds tls-ca-cert volume when global.tls.enabled is true" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.tls.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.volumes[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) + [ "${actual}" != "" ] +} + +@test "telemetryCollector/Deployment: Adds tls-ca-cert volumeMounts when global.tls.enabled is true" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.tls.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[1].volumeMounts[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) + [ "${actual}" != "" ] +} + +@test "telemetryCollector/Deployment: can overwrite CA secret with the provided one" { + cd `chart_dir` + local ca_cert_volume=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.caCert.secretName=foo-ca-cert' \ + --set 'global.tls.caCert.secretKey=key' \ + --set 'global.tls.caKey.secretName=foo-ca-key' \ + --set 'global.tls.caKey.secretKey=key' \ + . | tee /dev/stderr | + yq '.spec.template.spec.volumes[] | select(.name=="consul-ca-cert")' | tee /dev/stderr) + + # check that the provided ca cert secret is attached as a volume + local actual + actual=$(echo $ca_cert_volume | jq -r '.secret.secretName' | tee /dev/stderr) + [ "${actual}" = "foo-ca-cert" ] + + # check that the volume uses the provided secret key + actual=$(echo $ca_cert_volume | jq -r '.secret.items[0].key' | tee /dev/stderr) + [ "${actual}" = "key" ] +} + +#-------------------------------------------------------------------- +# global.tls.enableAutoEncrypt + +@test "telemetryCollector/Deployment: consul-ca-cert volumeMount is added when TLS with auto-encrypt is enabled without clients" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'client.enabled=false' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[1].volumeMounts[] | select(.name == "consul-ca-cert") | length > 0' | tee + /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "telemetryCollector/Deployment: consul-ca-cert volume is not added if externalServers.enabled=true and externalServers.useSystemRoots=true" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'externalServers.enabled=true' \ + --set 'externalServers.hosts[0]=foo.com' \ + --set 'externalServers.useSystemRoots=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.volumes[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) + [ "${actual}" = "" ] +} + +#-------------------------------------------------------------------- +# resources + +@test "telemetryCollector/Deployment: resources has default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources' | tee /dev/stderr) + + [ $(echo "${actual}" | yq -r '.requests.memory') = "512Mi" ] + [ $(echo "${actual}" | yq -r '.requests.cpu') = "1000m" ] + [ $(echo "${actual}" | yq -r '.limits.memory') = "512Mi" ] + [ $(echo "${actual}" | yq -r '.limits.cpu') = "1000m" ] +} + +@test "telemetryCollector/Deployment: resources can be overridden" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'telemetryCollector.resources.foo=bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} + +#-------------------------------------------------------------------- +# init container resources + +@test "telemetryCollector/Deployment: init container has default resources" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.initContainers[0].resources' | tee /dev/stderr) + + [ $(echo "${actual}" | yq -r '.requests.memory') = "25Mi" ] + [ $(echo "${actual}" | yq -r '.requests.cpu') = "50m" ] + [ $(echo "${actual}" | yq -r '.limits.memory') = "150Mi" ] + [ $(echo "${actual}" | yq -r '.limits.cpu') = "50m" ] +} + +@test "telemetryCollector/Deployment: init container resources can be set" { + cd `chart_dir` + local object=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'telemetryCollector.initContainer.resources.requests.memory=memory' \ + --set 'telemetryCollector.initContainer.resources.requests.cpu=cpu' \ + --set 'telemetryCollector.initContainer.resources.limits.memory=memory2' \ + --set 'telemetryCollector.initContainer.resources.limits.cpu=cpu2' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.initContainers[0].resources' | tee /dev/stderr) + + local actual=$(echo $object | yq -r '.requests.memory' | tee /dev/stderr) + [ "${actual}" = "memory" ] + + local actual=$(echo $object | yq -r '.requests.cpu' | tee /dev/stderr) + [ "${actual}" = "cpu" ] + + local actual=$(echo $object | yq -r '.limits.memory' | tee /dev/stderr) + [ "${actual}" = "memory2" ] + + local actual=$(echo $object | yq -r '.limits.cpu' | tee /dev/stderr) + [ "${actual}" = "cpu2" ] +} + +#-------------------------------------------------------------------- +# priorityClassName + +@test "telemetryCollector/Deployment: no priorityClassName by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.priorityClassName' | tee /dev/stderr) + + [ "${actual}" = "null" ] +} + +@test "telemetryCollector/Deployment: can set a priorityClassName" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'telemetryCollector.priorityClassName=name' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.priorityClassName' | tee /dev/stderr) + + [ "${actual}" = "name" ] +} + +#-------------------------------------------------------------------- +# replicas + +@test "telemetryCollector/Deployment: replicas defaults to 1" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + . | tee /dev/stderr | + yq '.spec.replicas' | tee /dev/stderr) + + [ "${actual}" = "1" ] +} + +@test "telemetryCollector/Deployment: replicas can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'telemetryCollector.replicas=3' \ + . | tee /dev/stderr | + yq '.spec.replicas' | tee /dev/stderr) + + [ "${actual}" = "3" ] +} + +#-------------------------------------------------------------------- +# Vault + +@test "telemetryCollector/Deployment: vault CA is not configured by default" { + cd `chart_dir` + local object=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.tls.caCert.secretName=foo' \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.consulServerRole=test' \ + --set 'global.secretsBackend.vault.consulCARole=test' \ + . | tee /dev/stderr | + yq -r '.spec.template' | tee /dev/stderr) + + local actual=$(echo $object | yq -r '.metadata.annotations | has("vault.hashicorp.com/agent-extra-secret")') + [ "${actual}" = "false" ] + local actual=$(echo $object | yq -r '.metadata.annotations | has("vault.hashicorp.com/ca-cert")') + [ "${actual}" = "false" ] +} + +@test "telemetryCollector/Deployment: vault CA is not configured when secretName is set but secretKey is not" { + cd `chart_dir` + local object=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.tls.caCert.secretName=foo' \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.consulServerRole=test' \ + --set 'global.secretsBackend.vault.consulCARole=test' \ + --set 'global.secretsBackend.vault.ca.secretName=ca' \ + . | tee /dev/stderr | + yq -r '.spec.template' | tee /dev/stderr) + + local actual=$(echo $object | yq -r '.metadata.annotations | has("vault.hashicorp.com/agent-extra-secret")') + [ "${actual}" = "false" ] + local actual=$(echo $object | yq -r '.metadata.annotations | has("vault.hashicorp.com/ca-cert")') + [ "${actual}" = "false" ] +} + +@test "telemetryCollector/Deployment: vault CA is not configured when secretKey is set but secretName is not" { + cd `chart_dir` + local object=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.tls.caCert.secretName=foo' \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.consulServerRole=test' \ + --set 'global.secretsBackend.vault.consulCARole=test' \ + --set 'global.secretsBackend.vault.ca.secretKey=tls.crt' \ + . | tee /dev/stderr | + yq -r '.spec.template' | tee /dev/stderr) + + local actual=$(echo $object | yq -r '.metadata.annotations | has("vault.hashicorp.com/agent-extra-secret")') + [ "${actual}" = "false" ] + local actual=$(echo $object | yq -r '.metadata.annotations | has("vault.hashicorp.com/ca-cert")') + [ "${actual}" = "false" ] +} + +@test "telemetryCollector/Deployment: vault CA is configured when both secretName and secretKey are set" { + cd `chart_dir` + local object=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.tls.caCert.secretName=foo' \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.consulServerRole=test' \ + --set 'global.secretsBackend.vault.consulCARole=test' \ + --set 'global.secretsBackend.vault.ca.secretName=ca' \ + --set 'global.secretsBackend.vault.ca.secretKey=tls.crt' \ + . | tee /dev/stderr | + yq -r '.spec.template' | tee /dev/stderr) + + local actual=$(echo $object | yq -r '.metadata.annotations."vault.hashicorp.com/agent-extra-secret"') + [ "${actual}" = "ca" ] + local actual=$(echo $object | yq -r '.metadata.annotations."vault.hashicorp.com/ca-cert"') + [ "${actual}" = "/vault/custom/tls.crt" ] +} + +@test "telemetryCollector/Deployment: vault tls annotations are set when tls is enabled" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.consulServerRole=bar' \ + --set 'global.secretsBackend.vault.consulCARole=test' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'server.serverCert.secretName=pki_int/issue/test' \ + --set 'global.tls.caCert.secretName=pki_int/cert/ca' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata' | tee /dev/stderr) + + local actual="$(echo $cmd | + yq -r '.annotations["vault.hashicorp.com/agent-inject-template-serverca.crt"]' | tee /dev/stderr)" + local expected=$'{{- with secret \"pki_int/cert/ca\" -}}\n{{- .Data.certificate -}}\n{{- end -}}' + [ "${actual}" = "${expected}" ] + + local actual="$(echo $cmd | + yq -r '.annotations["vault.hashicorp.com/agent-inject-secret-serverca.crt"]' | tee /dev/stderr)" + [ "${actual}" = "pki_int/cert/ca" ] + + local actual="$(echo $cmd | + yq -r '.annotations["vault.hashicorp.com/agent-init-first"]' | tee /dev/stderr)" + [ "${actual}" = "true" ] + + local actual="$(echo $cmd | + yq -r '.annotations["vault.hashicorp.com/agent-inject"]' | tee /dev/stderr)" + [ "${actual}" = "true" ] + + local actual="$(echo $cmd | + yq -r '.annotations["vault.hashicorp.com/role"]' | tee /dev/stderr)" + [ "${actual}" = "test" ] +} + +@test "telemetryCollector/Deployment: vault agent annotations can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.tls.caCert.secretName=foo' \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=test' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --set 'global.secretsBackend.vault.consulCARole=test' \ + --set 'global.secretsBackend.vault.agentAnnotations=foo: bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} + +#-------------------------------------------------------------------- +# telemetryCollector.cloud + +@test "telemetryCollector/Deployment: success with all cloud bits set" { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.cloud.enabled=true' \ + --set 'global.cloud.clientSecret.secretName=client-secret-name' \ + --set 'global.cloud.clientSecret.secretKey=client-secret-key' \ + --set 'global.cloud.clientId.secretName=client-id-name' \ + --set 'global.cloud.clientId.secretKey=client-id-key' \ + --set 'global.cloud.resourceId.secretName=client-resource-id-name' \ + --set 'global.cloud.resourceId.secretKey=client-resource-id-key' \ + . +} + +@test "telemetryCollector/Deployment: fails when telemetryCollector.cloud.clientId is set and global.cloud.resourceId is not set or global.cloud.clientSecret.secretName is not set" { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.datacenter=dc-foo' \ + --set 'global.domain=bar' \ + --set 'global.cloud.enabled=true' \ + --set 'global.cloud.clientSecret.secretName=client-id-name' \ + --set 'global.cloud.clientSecret.secretKey=client-id-key' \ + --set 'global.cloud.resourceId.secretName=client-resource-id-name' \ + --set 'global.cloud.resourceId.secretKey=client-resource-id-key' \ + . + [ "$status" -eq 1 ] + [[ "$output" =~ "When global.cloud.enabled is true, global.cloud.resourceId.secretName, global.cloud.clientId.secretName, and global.cloud.clientSecret.secretName must also be set." ]] +} + +@test "telemetryCollector/Deployment: fails when global.cloud.enabled is true and global.cloud.clientSecret.secretName is not set but global.cloud.clientId.secretName and global.cloud.resourceId.secretName is set" { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.datacenter=dc-foo' \ + --set 'global.domain=bar' \ + --set 'global.cloud.enabled=true' \ + --set 'global.cloud.clientId.secretName=client-id-name' \ + --set 'global.cloud.clientId.secretKey=client-id-key' \ + --set 'global.cloud.resourceId.secretName=resource-id-name' \ + --set 'global.cloud.resourceId.secretKey=resource-id-key' \ + . + [ "$status" -eq 1 ] + [[ "$output" =~ "When global.cloud.enabled is true, global.cloud.resourceId.secretName, global.cloud.clientId.secretName, and global.cloud.clientSecret.secretName must also be set." ]] +} + +@test "telemetryCollector/Deployment: fails when global.cloud.enabled is true and global.cloud.resourceId.secretName is not set but global.cloud.clientId.secretName and global.cloud.clientSecret.secretName is set" { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.datacenter=dc-foo' \ + --set 'global.domain=bar' \ + --set 'global.cloud.enabled=true' \ + --set 'global.cloud.clientId.secretName=client-id-name' \ + --set 'global.cloud.clientId.secretKey=client-id-key' \ + --set 'global.cloud.clientSecret.secretName=client-secret-id-name' \ + --set 'global.cloud.clientSecret.secretKey=client-secret-id-key' \ + . + [ "$status" -eq 1 ] + [[ "$output" =~ "When global.cloud.enabled is true, global.cloud.resourceId.secretName, global.cloud.clientId.secretName, and global.cloud.clientSecret.secretName must also be set." ]] +} + +@test "telemetryCollector/Deployment: fails when global.cloud.resourceId.secretName is set but global.cloud.resourceId.secretKey is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.datacenter=dc-foo' \ + --set 'global.domain=bar' \ + --set 'global.cloud.enabled=true' \ + --set 'global.cloud.clientId.secretName=client-id-name' \ + --set 'global.cloud.clientId.secretKey=client-id-key' \ + --set 'global.cloud.clientSecret.secretName=client-secret-id-name' \ + --set 'global.cloud.clientSecret.secretKey=client-secret-id-key' \ + --set 'global.cloud.resourceId.secretName=resource-id-name' \ + . + [ "$status" -eq 1 ] + [[ "$output" =~ "When either global.cloud.resourceId.secretName or global.cloud.resourceId.secretKey is defined, both must be set." ]] +} + +@test "telemetryCollector/Deployment: fails when global.cloud.authURL.secretName is set but global.cloud.authURL.secretKey is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.datacenter=dc-foo' \ + --set 'global.domain=bar' \ + --set 'global.cloud.enabled=true' \ + --set 'global.cloud.clientId.secretName=client-id-name' \ + --set 'global.cloud.clientId.secretKey=client-id-key' \ + --set 'global.cloud.clientSecret.secretName=client-secret-id-name' \ + --set 'global.cloud.clientSecret.secretKey=client-secret-id-key' \ + --set 'global.cloud.resourceId.secretName=resource-id-name' \ + --set 'global.cloud.resourceId.secretKey=resource-id-key' \ + --set 'global.cloud.authUrl.secretName=auth-url-name' \ + . + [ "$status" -eq 1 ] + + [[ "$output" =~ "When either global.cloud.authUrl.secretName or global.cloud.authUrl.secretKey is defined, both must be set." ]] +} + +@test "telemetryCollector/Deployment: fails when global.cloud.authURL.secretKey is set but global.cloud.authURL.secretName is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.datacenter=dc-foo' \ + --set 'global.domain=bar' \ + --set 'global.cloud.enabled=true' \ + --set 'global.cloud.clientId.secretName=client-id-name' \ + --set 'global.cloud.clientId.secretKey=client-id-key' \ + --set 'global.cloud.clientSecret.secretName=client-secret-id-name' \ + --set 'global.cloud.clientSecret.secretKey=client-secret-id-key' \ + --set 'global.cloud.resourceId.secretName=resource-id-name' \ + --set 'global.cloud.resourceId.secretKey=resource-id-key' \ + --set 'global.cloud.authUrl.secretKey=auth-url-key' \ + . + [ "$status" -eq 1 ] + + [[ "$output" =~ "When either global.cloud.authUrl.secretName or global.cloud.authUrl.secretKey is defined, both must be set." ]] +} + +@test "telemetryCollector/Deployment: fails when global.cloud.apiHost.secretName is set but global.cloud.apiHost.secretKey is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.datacenter=dc-foo' \ + --set 'global.domain=bar' \ + --set 'global.cloud.enabled=true' \ + --set 'global.cloud.clientId.secretName=client-id-name' \ + --set 'global.cloud.clientId.secretKey=client-id-key' \ + --set 'global.cloud.clientSecret.secretName=client-secret-id-name' \ + --set 'global.cloud.clientSecret.secretKey=client-secret-id-key' \ + --set 'global.cloud.resourceId.secretName=resource-id-name' \ + --set 'global.cloud.resourceId.secretKey=resource-id-key' \ + --set 'global.cloud.apiHost.secretName=auth-url-name' \ + . + [ "$status" -eq 1 ] + + [[ "$output" =~ "When either global.cloud.apiHost.secretName or global.cloud.apiHost.secretKey is defined, both must be set." ]] +} + +@test "telemetryCollector/Deployment: fails when global.cloud.apiHost.secretKey is set but global.cloud.apiHost.secretName is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.datacenter=dc-foo' \ + --set 'global.domain=bar' \ + --set 'global.cloud.enabled=true' \ + --set 'global.cloud.clientId.secretName=client-id-name' \ + --set 'global.cloud.clientId.secretKey=client-id-key' \ + --set 'global.cloud.clientSecret.secretName=client-secret-id-name' \ + --set 'global.cloud.clientSecret.secretKey=client-secret-id-key' \ + --set 'global.cloud.resourceId.secretName=resource-id-name' \ + --set 'global.cloud.resourceId.secretKey=resource-id-key' \ + --set 'global.cloud.apiHost.secretKey=auth-url-key' \ + . + [ "$status" -eq 1 ] + + [[ "$output" =~ "When either global.cloud.apiHost.secretName or global.cloud.apiHost.secretKey is defined, both must be set." ]] +} + +@test "telemetryCollector/Deployment: fails when global.cloud.scadaAddress.secretName is set but global.cloud.scadaAddress.secretKey is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.datacenter=dc-foo' \ + --set 'global.domain=bar' \ + --set 'global.cloud.enabled=true' \ + --set 'global.cloud.clientId.secretName=client-id-name' \ + --set 'global.cloud.clientId.secretKey=client-id-key' \ + --set 'global.cloud.clientSecret.secretName=client-secret-id-name' \ + --set 'global.cloud.clientSecret.secretKey=client-secret-id-key' \ + --set 'global.cloud.resourceId.secretName=resource-id-name' \ + --set 'global.cloud.resourceId.secretKey=resource-id-key' \ + --set 'global.cloud.scadaAddress.secretName=scada-address-name' \ + . + [ "$status" -eq 1 ] + + [[ "$output" =~ "When either global.cloud.scadaAddress.secretName or global.cloud.scadaAddress.secretKey is defined, both must be set." ]] +} + +@test "telemetryCollector/Deployment: fails when global.cloud.scadaAddress.secretKey is set but global.cloud.scadaAddress.secretName is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.datacenter=dc-foo' \ + --set 'global.domain=bar' \ + --set 'global.cloud.enabled=true' \ + --set 'global.cloud.clientId.secretName=client-id-name' \ + --set 'global.cloud.clientId.secretKey=client-id-key' \ + --set 'global.cloud.clientSecret.secretName=client-secret-id-name' \ + --set 'global.cloud.clientSecret.secretKey=client-secret-id-key' \ + --set 'global.cloud.resourceId.secretName=resource-id-name' \ + --set 'global.cloud.resourceId.secretKey=resource-id-key' \ + --set 'global.cloud.scadaAddress.secretKey=scada-address-key' \ + . + [ "$status" -eq 1 ] + + [[ "$output" =~ "When either global.cloud.scadaAddress.secretName or global.cloud.scadaAddress.secretKey is defined, both must be set." ]] +} + +@test "telemetryCollector/Deployment: fails when telemetryCollector.cloud.clientId.secretName is set but telemetryCollector.cloud.clientId.secretKey is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'telemetryCollector.cloud.clientId.secretName=client-id-name' \ + --set 'telemetryCollector.clientSecret.secretName=client-secret-id-name' \ + --set 'telemetryCollector.clientSecret.secretKey=client-secret-id-key' \ + --set 'global.resourceId.secretName=resource-id-name' \ + --set 'global.resourceId.secretKey=resource-id-key' \ + . + [ "$status" -eq 1 ] + + [[ "$output" =~ "When telemetryCollector.cloud.clientId.secretName is set, global.cloud.resourceId.secretName, telemetryCollector.cloud.clientSecret.secretName must also be set." ]] +} + +@test "telemetryCollector/Deployment: fails when telemetryCollector.cloud.clientId.secretKey is set but telemetryCollector.cloud.clientId.secretName is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'telemetryCollector.cloud.clientId.secretName=client-id-name' \ + --set 'telemetryCollector.cloud.clientId.secretKey=client-id-key' \ + --set 'telemetryCollector.clientSecret.secretName=client-secret-id-name' \ + --set 'global.resourceId.secretName=resource-id-name' \ + --set 'global.resourceId.secretKey=resource-id-key' \ + . + [ "$status" -eq 1 ] + + [[ "$output" =~ "When telemetryCollector.cloud.clientId.secretName is set, global.cloud.resourceId.secretName, telemetryCollector.cloud.clientSecret.secretName must also be set." ]] +} + +@test "telemetryCollector/Deployment: fails when telemetryCollector.cloud.clientSecret.secretName is set but telemetryCollector.cloud.clientId.secretName is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'telemetryCollector.cloud.clientId.secretName=client-id-name' \ + --set 'telemetryCollector.cloud.clientId.secretKey=client-id-key' \ + --set 'telemetryCollector.clientSecret.secretName=client-secret-id-name' \ + --set 'telemetryCollector.clientSecret.secretKey=client-secret-key-name' \ + . + [ "$status" -eq 1 ] + + [[ "$output" =~ "When telemetryCollector.cloud.clientId.secretName is set, global.cloud.resourceId.secretName, telemetryCollector.cloud.clientSecret.secretName must also be set." ]] +} + +@test "telemetryCollector/Deployment: fails when telemetryCollector.cloud.clientId.secretName is set but telemetry.cloud.clientId.secretKey is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'telemetryCollector.cloud.clientId.secretName=client-id-name' \ + --set 'telemetryCollector.cloud.clientSecret.secretName=client-secret-name' \ + --set 'global.cloud.resourceId.secretName=resource-id-name' \ + . + [ "$status" -eq 1 ] + + [[ "$output" =~ "When either telemetryCollector.cloud.clientId.secretName or telemetryCollector.cloud.clientId.secretKey is defined, both must be set." ]] +} + +@test "telemetryCollector/Deployment: fails when telemetryCollector.cloud.clientSecret.secretName is set but telemetry.cloud.clientSecret.secretKey is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'telemetryCollector.cloud.clientId.secretName=client-id-name' \ + --set 'telemetryCollector.cloud.clientId.secretKey=client-id-key' \ + --set 'telemetryCollector.cloud.clientSecret.secretName=client-secret-name' \ + --set 'global.cloud.resourceId.secretName=resource-id-name' \ + . + [ "$status" -eq 1 ] + + [[ "$output" =~ "When either telemetryCollector.cloud.clientSecret.secretName or telemetryCollector.cloud.clientSecret.secretKey is defined, both must be set." ]] +} + +@test "telemetryCollector/Deployment: fails when telemetryCollector.cloud.clientId and telemetryCollector.cloud.clientSecret is set but global.cloud.resourceId.secretKey is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'telemetryCollector.cloud.clientId.secretName=client-id-name' \ + --set 'telemetryCollector.cloud.clientId.secretKey=client-id-key' \ + --set 'telemetryCollector.cloud.clientSecret.secretName=client-secret-name' \ + --set 'telemetryCollector.cloud.clientSecret.secretKey=client-secret-key' \ + --set 'global.cloud.resourceId.secretName=resource-id-name' \ + . + [ "$status" -eq 1 ] + + [[ "$output" =~ "When telemetryCollector has clientId and clientSecret .global.cloud.resourceId.secretKey must be set" ]] +} + +#-------------------------------------------------------------------- +# global.tls.enabled + +@test "telemetryCollector/Deployment: sets -tls-disabled args when when not using TLS." { + cd `chart_dir` + + local flags=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=false' \ + . | yq -r .spec.template.spec.containers[1].args) + + local actual=$(echo $flags | yq -r '. | any(contains("-tls-disabled"))') + [ "${actual}" = 'true' ] + +} + +@test "telemetryCollector/Deployment: -ca-certs set correctly when using TLS." { + cd `chart_dir` + local flags=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[1].args' | tee /dev/stderr) + + local actual=$(echo $flags | yq -r '. | any(contains("-ca-certs=/consul/tls/ca/tls.crt"))' | tee /dev/stderr) + [ "${actual}" = 'true' ] +} + +#-------------------------------------------------------------------- +# External Server + +@test "telemetryCollector/Deployment: sets external server args when global.tls.enabled and externalServers.enabled" { + cd `chart_dir` + local flags=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'externalServers.enabled=true' \ + --set 'externalServers.hosts[0]=external-consul.host' \ + --set 'externalServers.httpsPort=8501' \ + --set 'externalServers.tlsServerName=foo.tls.server' \ + --set 'externalServers.useSystemRoots=true' \ + --set 'server.enabled=false' \ + --set 'client.enabled=false' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[1].args' | tee /dev/stderr) + + local actual=$(echo $flags | yq -r '. | any(contains("-ca-certs=/consul/tls/ca/tls.crt"))' | tee /dev/stderr) + [ "${actual}" = 'false' ] + + local actual=$(echo $flags | yq -r '. | any(contains("-tls-server-name=foo.tls.server"))' | tee /dev/stderr) + [ "${actual}" = 'true' ] + + local actual=$(echo $flags | jq -r '. | any(contains("-addresses=external-consul.host"))' | tee /dev/stderr) + [ "${actual}" = 'true' ] +} + +#-------------------------------------------------------------------- +# Admin Partitions + +@test "telemetryCollector/Deployment: partition flags are set when using admin partitions" { + cd `chart_dir` + local flags=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.enableConsulNamespaces=true' \ + --set 'global.adminPartitions.enabled=true' \ + --set 'global.adminPartitions.name=hashi' \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[1].args' | tee /dev/stderr) + + local actual=$(echo $flags | jq -r '. | any(contains("-login-partition=hashi"))' | tee /dev/stderr) + [ "${actual}" = 'true' ] + + local actual=$(echo $flags | jq -r '. | any(contains("-service-partition=hashi"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "telemetryCollector/Deployment: consul-ca-cert volume mount is not set when using externalServers and useSystemRoots" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.tls.enabled=true' \ + --set 'server.enabled=false' \ + --set 'externalServers.hosts[0]=external-consul.host' \ + --set 'externalServers.enabled=true' \ + --set 'externalServers.useSystemRoots=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) + [ "${actual}" = "" ] +} + +@test "telemetryCollector/Deployment: config volume mount is set when config exists" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'telemetryCollector.customExporterConfig="foo"' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "config") | .name' | tee /dev/stderr) + [ "${actual}" = "config" ] +} + +@test "telemetryCollector/Deployment: config flag is set when config exists" { + cd `chart_dir` + local flags=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'telemetryCollector.customExporterConfig="foo"' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].args') + + local actual=$(echo $flags | yq -r '. | any(contains("-config-file-path /consul/config/config.json"))') + [ "${actual}" = "true" ] +} + +@test "telemetryCollector/Deployment: consul-ca-cert volume mount is not set on acl-init when using externalServers and useSystemRoots" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.tls.enabled=true' \ + --set 'server.enabled=false' \ + --set 'externalServers.hosts[0]=external-consul.host' \ + --set 'externalServers.enabled=true' \ + --set 'externalServers.useSystemRoots=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.initContainers[1].volumeMounts[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) + [ "${actual}" = "" ] +} + +#-------------------------------------------------------------------- +# extraLabels + +@test "telemetryCollector/Deployment: no extra labels defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.labels | del(."app") | del(."chart") | del(."release") | del(."component") | del(."consul.hashicorp.com/connect-inject-managed-by")' \ + | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "telemetryCollector/Deployment: extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.extraLabels.foo=bar' \ + . | tee /dev/stderr) + local actualBar=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + [ "${actualBar}" = "bar" ] + local actualTemplateBar=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + [ "${actualTemplateBar}" = "bar" ] +} + +@test "telemetryCollector/Deployment: multiple global extra labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.extraLabels.foo=bar' \ + --set 'global.extraLabels.baz=qux' \ + . | tee /dev/stderr) + local actualFoo=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + local actualBaz=$(echo "${actual}" | yq -r '.metadata.labels.baz' | tee /dev/stderr) + [ "${actualFoo}" = "bar" ] + [ "${actualBaz}" = "qux" ] + local actualTemplateFoo=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + local actualTemplateBaz=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.baz' | tee /dev/stderr) + [ "${actualTemplateFoo}" = "bar" ] + [ "${actualTemplateBaz}" = "qux" ] +} diff --git a/charts/consul/test/unit/telemetry-collector-podsecuritypolicy.bats b/charts/consul/test/unit/telemetry-collector-podsecuritypolicy.bats new file mode 100644 index 0000000000..90c494eca5 --- /dev/null +++ b/charts/consul/test/unit/telemetry-collector-podsecuritypolicy.bats @@ -0,0 +1,21 @@ +#!/usr/bin/env bats + +load _helpers + +@test "telemetryCollector/PodSecurityPolicy: disabled by default" { + cd `chart_dir` + assert_empty helm template \ + -s templates/telemetry-collector-podsecuritypolicy.yaml \ + . +} + +@test "telemetryCollector/PodSecurityPolicy: enabled with telemetryCollector and global.enablePodSecurityPolicies=true" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-podsecuritypolicy.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'global.enablePodSecurityPolicies=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} \ No newline at end of file diff --git a/charts/consul/test/unit/telemetry-collector-role.bats b/charts/consul/test/unit/telemetry-collector-role.bats new file mode 100644 index 0000000000..8fa209ef4b --- /dev/null +++ b/charts/consul/test/unit/telemetry-collector-role.bats @@ -0,0 +1,21 @@ +#!/usr/bin/env bats + +load _helpers + +@test "telemetryCollector/Role: disabled by default" { + cd `chart_dir` + assert_empty helm template \ + -s templates/telemetry-collector-role.yaml \ + . +} + +@test "telemetryCollector/Role: enabled with telemetryCollector and global.enablePodSecurityPolicies=true" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-role.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'global.enablePodSecurityPolicies=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} \ No newline at end of file diff --git a/charts/consul/test/unit/telemetry-collector-rolebinding.bats b/charts/consul/test/unit/telemetry-collector-rolebinding.bats new file mode 100644 index 0000000000..454c2569fb --- /dev/null +++ b/charts/consul/test/unit/telemetry-collector-rolebinding.bats @@ -0,0 +1,21 @@ +#!/usr/bin/env bats + +load _helpers + +@test "telemetryCollector/RoleBinding: disabled by default" { + cd `chart_dir` + assert_empty helm template \ + -s templates/telemetry-collector-rolebinding.yaml \ + . +} + +@test "telemetryCollector/RoleBinding: enabled with telemetryCollector and global.enablePodSecurityPolicies=true" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-rolebinding.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'global.enablePodSecurityPolicies=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} \ No newline at end of file diff --git a/charts/consul/test/unit/telemetry-collector-service.bats b/charts/consul/test/unit/telemetry-collector-service.bats new file mode 100755 index 0000000000..0ee6512c05 --- /dev/null +++ b/charts/consul/test/unit/telemetry-collector-service.bats @@ -0,0 +1,72 @@ +#!/usr/bin/env bats + +load _helpers + +@test "telemetryCollector/Service: disabled by default" { + cd `chart_dir` + assert_empty helm template \ + -s templates/telemetry-collector-service.yaml \ + . +} + +@test "telemetryCollector/Service: enabled by default with telemetryCollector, connectInject enabled" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-service.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "telemetryCollector/Service: enabled with telemetryCollector.enabled=true" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-service.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +#-------------------------------------------------------------------- +# annotations + +@test "telemetryCollector/Service: no annotations by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-service.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "telemetryCollector/Service: can set annotations" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-service.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'telemetryCollector.service.annotations=key: value' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations.key' | tee /dev/stderr) + [ "${actual}" = "value" ] +} + +#-------------------------------------------------------------------- +# port + +@test "telemetryCollector/Service: has default port" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-service.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].port' | tee /dev/stderr) + [ "${actual}" = "9356" ] +} \ No newline at end of file diff --git a/charts/consul/test/unit/telemetry-collector-serviceaccount.bats b/charts/consul/test/unit/telemetry-collector-serviceaccount.bats new file mode 100644 index 0000000000..211238f72f --- /dev/null +++ b/charts/consul/test/unit/telemetry-collector-serviceaccount.bats @@ -0,0 +1,69 @@ +#!/usr/bin/env bats + +load _helpers + +@test "telemetryCollector/ServiceAccount: disabled by default" { + cd `chart_dir` + assert_empty helm template \ + -s templates/telemetry-collector-serviceaccount.yaml \ + . +} + +@test "telemetryCollector/ServiceAccount: enabled with telemetryCollector, connectInject enabled" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-serviceaccount.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +#-------------------------------------------------------------------- +# global.imagePullSecrets + +@test "telemetryCollector/ServiceAccount: can set image pull secrets" { + cd `chart_dir` + local object=$(helm template \ + -s templates/telemetry-collector-serviceaccount.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'global.imagePullSecrets[0].name=my-secret' \ + --set 'global.imagePullSecrets[1].name=my-secret2' \ + . | tee /dev/stderr) + + local actual=$(echo "$object" | + yq -r '.imagePullSecrets[0].name' | tee /dev/stderr) + [ "${actual}" = "my-secret" ] + + local actual=$(echo "$object" | + yq -r '.imagePullSecrets[1].name' | tee /dev/stderr) + [ "${actual}" = "my-secret2" ] +} + +#-------------------------------------------------------------------- +# telemetryCollector.serviceAccount.annotations + +@test "telemetryCollector/ServiceAccount: no annotations by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-serviceaccount.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq '.metadata.annotations | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "telemetryCollector/ServiceAccount: annotations when enabled" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-serviceaccount.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set "telemetryCollector.serviceAccount.annotations=foo: bar" \ + . | tee /dev/stderr | + yq -r '.metadata.annotations.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 9aac754ed4..edf36a41f0 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -550,6 +550,11 @@ global: # @type: boolean enableGatewayMetrics: true + # Configures the Helm chart’s components to forward envoy metrics for the Consul service mesh to the + # consul-telemetry-collector. This includes gateway metrics and sidecar metrics. + # @type: boolean + enableTelemetryCollector: true + # The name (and tag) of the consul-dataplane Docker image used for the # connect-injected sidecar proxies and mesh, terminating, and ingress gateways. # @default: hashicorp/consul-dataplane: @@ -950,14 +955,14 @@ server: topologyKey: kubernetes.io/hostname # Toleration settings for server pods. This - # should be a multi-line string matching the - # [Tolerations](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) + # should be a multi-line string matching the + # [Tolerations](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) # array in a Pod spec. tolerations: "" # Pod topology spread constraints for server pods. - # This should be a multi-line YAML string matching the - # [`topologySpreadConstraints`](https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) + # This should be a multi-line YAML string matching the + # [`topologySpreadConstraints`](https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) # array in a Pod Spec. # # This requires K8S >= 1.18 (beta) or 1.19 (stable). @@ -1076,7 +1081,7 @@ server: # @type: map extraEnvironmentVars: { } - # [Enterprise Only] Values for setting up and running + # [Enterprise Only] Values for setting up and running # [snapshot agents](https://developer.hashicorp.com/consul/commands/snapshot/agent) # within the Consul clusters. They run as a sidecar with Consul servers. snapshotAgent: @@ -1963,7 +1968,7 @@ connectInject: logLevel: null # Set the namespace to install the CNI plugin into. Overrides global namespace settings for CNI resources. - # Ex: "kube-system" + # Ex: "kube-system" # @type: string namespace: null @@ -2146,7 +2151,7 @@ connectInject: # @type: string annotations: null - # The resource settings for connect inject pods. The defaults, are optimized for getting started worklows on developer deployments. The settings should be tweaked for production deployments. + # The resource settings for connect inject pods. The defaults, are optimized for getting started worklows on developer deployments. The settings should be tweaked for production deployments. # @type: map resources: requests: @@ -2344,8 +2349,8 @@ connectInject: cpu: null # The resource settings for the Connect injected init container. If null, the resources - # won't be set for the initContainer. The defaults are optimized for developer instances of - # Kubernetes, however they should be tweaked with the recommended defaults as shown below to speed up service registration times. + # won't be set for the initContainer. The defaults are optimized for developer instances of + # Kubernetes, however they should be tweaked with the recommended defaults as shown below to speed up service registration times. # @type: map initContainer: resources: @@ -2534,7 +2539,7 @@ meshGateway: tolerations: null # Pod topology spread constraints for mesh gateway pods. - # This should be a multi-line YAML string matching the + # This should be a multi-line YAML string matching the # [`topologySpreadConstraints`](https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) # array in a Pod Spec. # @@ -2681,7 +2686,7 @@ ingressGateways: tolerations: null # Pod topology spread constraints for ingress gateway pods. - # This should be a multi-line YAML string matching the + # This should be a multi-line YAML string matching the # [`topologySpreadConstraints`](https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) # array in a Pod Spec. # @@ -2918,7 +2923,7 @@ apiGateway: # @type: string nodeSelector: null - # Toleration settings for gateway pods created with the managed gateway class. + # Toleration settings for gateway pods created with the managed gateway class. # This should be a multi-line string matching the # [Tolerations](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) array in a Pod spec. # @@ -3082,3 +3087,86 @@ prometheus: # is only useful when running helm template. tests: enabled: true + +telemetryCollector: + # Enables the consul-telemetry-collector deployment + # @type: boolean + enabled: false + + # The name of the Docker image (including any tag) for the containers running + # the consul-telemetry-collector + # @type: string + image: "hashicorp/consul-telemetry-collector:0.0.1" + + # The resource settings for consul-telemetry-collector pods. + # @recurse: false + # @type: map + resources: + requests: + memory: "512Mi" + cpu: "1000m" + limits: + memory: "512Mi" + cpu: "1000m" + + # This value sets the number of consul-telemetry-collector replicas to deploy. + replicas: 1 + + # This value defines additional configuration for the telemetry collector. It should be formatted as a multi-line + # json blob string + # + # ```yaml + # customExporterConfig: | + # {"http_collector_endpoint": "other-otel-collector"} + # ``` + # + # @type: string + customExporterConfig: null + + service: + # This value defines additional annotations for the server service account. This should be formatted as a multi-line + # string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + serviceAccount: + # This value defines additional annotations for the telemetry-collector's service account. This should be formatted + # as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + cloud: + clientId: + secretName: null + secretKey: null + clientSecret: + secretName: null + secretKey: null + + initContainer: + # The resource settings for consul-telemetry-collector initContainer. + # @recurse: false + # @type: map + resources: {} + + # Optional YAML string to specify a nodeSelector config. + # @type: string + nodeSelector: null + + # Optional priorityClassName. + # @type: string + priorityClassName: "" From 5bd6c607b8ce23caea0782345b66668f1846a9fb Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Wed, 17 May 2023 09:50:41 -0400 Subject: [PATCH 159/340] NET-2619 - save ClusterIPs to manual vips table (#2124) --- .changelog/2124.txt | 3 + .../endpoints/endpoints_controller.go | 29 ++- .../endpoints/endpoints_controller_test.go | 51 ++++ .../controllers/configentry_controller.go | 61 +++++ .../configentry_controller_test.go | 233 ++++++++++++++++++ control-plane/go.mod | 6 +- control-plane/go.sum | 12 +- 7 files changed, 385 insertions(+), 10 deletions(-) create mode 100644 .changelog/2124.txt diff --git a/.changelog/2124.txt b/.changelog/2124.txt new file mode 100644 index 0000000000..b65c23db2e --- /dev/null +++ b/.changelog/2124.txt @@ -0,0 +1,3 @@ +``release-note:improvement +control-plane: Transparent proxy enhancements for failover and virtual Services +``` diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go index f408a21ea1..13f75f1156 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go @@ -1,6 +1,5 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 - package endpoints import ( @@ -295,6 +294,14 @@ func (r *Controller) registerServicesAndHealthCheck(apiClient *api.Client, pod c return err } + // Add manual ip to the VIP table + r.Log.Info("adding manual ip to virtual ip table in Consul", "name", serviceRegistration.Service.Service, + "id", serviceRegistration.ID) + err = assignServiceVirtualIP(r.Context, apiClient, serviceRegistration.Service) + if err != nil { + r.Log.Error(err, "failed to add ip to virtual ip table", "name", serviceRegistration.Service.Service) + } + // Register the proxy service instance with Consul. r.Log.Info("registering proxy service with Consul", "name", proxyServiceRegistration.Service.Service) _, err = apiClient.Catalog().Register(proxyServiceRegistration, nil) @@ -1258,6 +1265,26 @@ func (r *Controller) appendNodeMeta(registration *api.CatalogRegistration) { } } +// assignServiceVirtualIPs manually assigns the ClusterIP to the virtual IP table so that transparent proxy routing works. +func assignServiceVirtualIP(ctx context.Context, apiClient *api.Client, svc *api.AgentService) error { + ip := svc.TaggedAddresses[clusterIPTaggedAddressName].Address + if ip == "" { + return nil + } + + _, _, err := apiClient.Internal().AssignServiceVirtualIP(ctx, svc.Service, []string{ip}, &api.WriteOptions{Namespace: svc.Namespace, Partition: svc.Partition}) + if err != nil { + // Maintain backwards compatibility with older versions of Consul that do not support the VIP improvements. Tproxy + // will not work 100% correctly but the mesh will still work + if strings.Contains(err.Error(), "404") { + return fmt.Errorf("failed to add ip for service %s to virtual ip table. Please upgrade Consul to version 1.16 or higher", svc.Service) + } else { + return err + } + } + return nil +} + // hasBeenInjected checks the value of the status annotation and returns true if the Pod has been injected. func hasBeenInjected(pod corev1.Pod) bool { if anno, ok := pod.Annotations[constants.KeyInjectStatus]; ok && anno == constants.Injected { diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go index 7ea04a4e52..e9ae243a12 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go @@ -6414,3 +6414,54 @@ func createGatewayPod(name, ip string, annotations map[string]string) *corev1.Po } return pod } + +func TestReconcileAssignServiceVirtualIP(t *testing.T) { + t.Parallel() + ctx := context.Background() + cases := []struct { + name string + service *api.AgentService + expectErr bool + }{ + { + name: "valid service", + service: &api.AgentService{ + ID: "", + Service: "foo", + Port: 80, + Address: "1.2.3.4", + TaggedAddresses: map[string]api.ServiceAddress{ + "virtual": { + Address: "1.2.3.4", + Port: 80, + }, + }, + Meta: map[string]string{constants.MetaKeyKubeNS: "default"}, + }, + expectErr: false, + }, + { + name: "service missing IP should not error", + service: &api.AgentService{ + ID: "", + Service: "bar", + Meta: map[string]string{constants.MetaKeyKubeNS: "default"}, + }, + expectErr: false, + }, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + + // Create test consulServer server. + testClient := test.TestServerWithMockConnMgrWatcher(t, nil) + apiClient := testClient.APIClient + err := assignServiceVirtualIP(ctx, apiClient, c.service) + if err != nil { + require.True(t, c.expectErr) + } else { + require.False(t, c.expectErr) + } + }) + } +} diff --git a/control-plane/controllers/configentry_controller.go b/control-plane/controllers/configentry_controller.go index c2c6da9071..f35d2e88e7 100644 --- a/control-plane/controllers/configentry_controller.go +++ b/control-plane/controllers/configentry_controller.go @@ -199,6 +199,7 @@ func (r *ConfigEntryController) ReconcileEntry(ctx context.Context, crdCtrl Cont return r.syncFailed(ctx, logger, crdCtrl, configEntry, ConsulAgentError, fmt.Errorf("writing config entry to consul: %w", err)) } + logger.Info("config entry created", "request-time", writeMeta.RequestTime) return r.syncSuccessful(ctx, crdCtrl, configEntry) } @@ -267,6 +268,15 @@ func (r *ConfigEntryController) ReconcileEntry(ctx context.Context, crdCtrl Cont return r.syncSuccessful(ctx, crdCtrl, configEntry) } + // For resolvers and splitters, we need to set the ClusterIP of the matching service to Consul so that transparent + // proxy works correctly. Do not fail the reconcile if assigning the virtual IP returns an error. + if needsVirtualIPAssignment(configEntry) { + err = assignServiceVirtualIP(ctx, logger, consulClient, crdCtrl, req.NamespacedName, configEntry, r.DatacenterName) + if err != nil { + logger.Error(err, "failed assigning service virtual ip") + } + } + return ctrl.Result{}, nil } @@ -381,6 +391,57 @@ func (r *ConfigEntryController) nonMatchingMigrationError(kubeEntry common.Confi return fmt.Errorf("migration failed: Kubernetes resource does not match existing Consul config entry: consul=%s, kube=%s", consulJSON, kubeJSON) } +// needsVirtualIPAssignment checks to see if a configEntry type needs to be assigned a virtual IP. +func needsVirtualIPAssignment(configEntry common.ConfigEntryResource) bool { + kubeKind := configEntry.KubeKind() + if kubeKind == common.ServiceResolver || kubeKind == common.ServiceRouter || kubeKind == common.ServiceSplitter { + return true + } + return false +} + +// assignServiceVirtualIPs manually sends the ClusterIP for a matching service for ServiceRouter or ServiceSplitter +// CRDs to Consul so that it can be added to the virtual IP table. The assignment is skipped if the matching service +// does not exist or if an older version of Consul is being used. Endpoints Controller, on service registration, also +// manually sends a ClusterIP when a service is created. This increases the chance of a real IP ending up in the +// discovery chain. +func assignServiceVirtualIP(ctx context.Context, logger logr.Logger, consulClient *capi.Client, crdCtrl Controller, namespacedName types.NamespacedName, configEntry common.ConfigEntryResource, datacenter string) error { + service := corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: configEntry.KubernetesName(), + Namespace: namespacedName.Namespace, + }, + } + if err := crdCtrl.Get(ctx, namespacedName, &service); err != nil { + // It is non-fatal if the service does not exist. The ClusterIP will get added when the service is registered in + // the endpoints controller + if k8serr.IsNotFound(err) { + return nil + } + // Something is really wrong with the service + return err + } + + wo := &capi.WriteOptions{ + Namespace: configEntry.ToConsul(datacenter).GetNamespace(), + Partition: configEntry.ToConsul(datacenter).GetPartition(), + } + + logger.Info("adding manual ip to virtual ip table in Consul", "name", service.Name) + _, _, err := consulClient.Internal().AssignServiceVirtualIP(ctx, configEntry.KubernetesName(), []string{service.Spec.ClusterIP}, wo) + if err != nil { + // Maintain backwards compatibility with older versions of Consul that do not support the manual VIP improvements. With the older version, the mesh + // will still work. + if isNotFoundErr(err) { + logger.Error(err, "failed to add ip to virtual ip table. Please upgrade Consul to version 1.16 or higher", "name", service.Name) + return nil + } else { + return err + } + } + return nil +} + func isNotFoundErr(err error) bool { return err != nil && strings.Contains(err.Error(), "404") } diff --git a/control-plane/controllers/configentry_controller_test.go b/control-plane/controllers/configentry_controller_test.go index 494715cf4f..d548f78069 100644 --- a/control-plane/controllers/configentry_controller_test.go +++ b/control-plane/controllers/configentry_controller_test.go @@ -22,6 +22,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -1891,3 +1892,235 @@ func TestConfigEntryController_Migration(t *testing.T) { }) } } + +func TestConfigEntryControllers_assignServiceVirtualIP(t *testing.T) { + t.Parallel() + kubeNS := "default" + + cases := []struct { + name string + kubeKind string + consulKind string + consulPrereqs []capi.ConfigEntry + configEntryResource common.ConfigEntryResource + service corev1.Service + reconciler func(client.Client, *consul.Config, consul.ServerConnectionManager, logr.Logger) Controller + expectErr bool + }{ + { + name: "ServiceResolver no error and vip should be assigned", + kubeKind: "ServiceResolver", + consulKind: capi.ServiceRouter, + configEntryResource: &v1alpha1.ServiceRouter{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: kubeNS, + }, + Spec: v1alpha1.ServiceRouterSpec{ + Routes: []v1alpha1.ServiceRoute{ + { + Match: &v1alpha1.ServiceRouteMatch{ + HTTP: &v1alpha1.ServiceRouteHTTPMatch{ + PathPrefix: "/admin", + }, + }, + }, + }, + }, + }, + service: corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: kubeNS, + }, + Spec: corev1.ServiceSpec{ + ClusterIP: "10.0.0.1", + Ports: []corev1.ServicePort{ + { + Port: 8081, + }, + }, + }, + }, + reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) Controller { + return &ServiceRouterController{ + Client: client, + Log: logger, + ConfigEntryController: &ConfigEntryController{ + ConsulClientConfig: cfg, + ConsulServerConnMgr: watcher, + DatacenterName: datacenterName, + }, + } + }, + expectErr: false, + }, + { + name: "ServiceRouter no error and vip should be assigned", + kubeKind: "ServiceRouter", + consulKind: capi.ServiceRouter, + consulPrereqs: []capi.ConfigEntry{ + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "bar", + Protocol: "http", + }, + }, + configEntryResource: &v1alpha1.ServiceRouter{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar", + Namespace: kubeNS, + }, + Spec: v1alpha1.ServiceRouterSpec{ + Routes: []v1alpha1.ServiceRoute{ + { + Match: &v1alpha1.ServiceRouteMatch{ + HTTP: &v1alpha1.ServiceRouteHTTPMatch{ + PathPrefix: "/admin", + }, + }, + }, + }, + }, + }, + service: corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar", + Namespace: kubeNS, + }, + Spec: corev1.ServiceSpec{ + ClusterIP: "10.0.0.2", + Ports: []corev1.ServicePort{ + { + Port: 8081, + }, + }, + }, + }, + reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) Controller { + return &ServiceRouterController{ + Client: client, + Log: logger, + ConfigEntryController: &ConfigEntryController{ + ConsulClientConfig: cfg, + ConsulServerConnMgr: watcher, + DatacenterName: datacenterName, + }, + } + }, + expectErr: false, + }, + { + name: "ServiceRouter should fail because service does not have a valid IP address", + kubeKind: "ServiceRouter", + consulKind: capi.ServiceRouter, + consulPrereqs: []capi.ConfigEntry{ + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "bar", + Protocol: "http", + }, + }, + configEntryResource: &v1alpha1.ServiceRouter{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar", + Namespace: kubeNS, + }, + Spec: v1alpha1.ServiceRouterSpec{ + Routes: []v1alpha1.ServiceRoute{ + { + Match: &v1alpha1.ServiceRouteMatch{ + HTTP: &v1alpha1.ServiceRouteHTTPMatch{ + PathPrefix: "/admin", + }, + }, + }, + }, + }, + }, + service: corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar", + Namespace: kubeNS, + }, + }, + reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) Controller { + return &ServiceRouterController{ + Client: client, + Log: logger, + ConfigEntryController: &ConfigEntryController{ + ConsulClientConfig: cfg, + ConsulServerConnMgr: watcher, + DatacenterName: datacenterName, + }, + } + }, + expectErr: true, + }, + { + name: "ServiceSplitter no error because a matching service does not exist", + kubeKind: "ServiceSplitter", + consulKind: capi.ServiceSplitter, + consulPrereqs: []capi.ConfigEntry{ + &capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "foo", + Protocol: "http", + }, + }, + configEntryResource: &v1alpha1.ServiceSplitter{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: kubeNS, + }, + Spec: v1alpha1.ServiceSplitterSpec{ + Splits: []v1alpha1.ServiceSplit{ + { + Weight: 100, + }, + }, + }, + }, + reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) Controller { + return &ServiceSplitterController{ + Client: client, + Log: logger, + ConfigEntryController: &ConfigEntryController{ + ConsulClientConfig: cfg, + ConsulServerConnMgr: watcher, + DatacenterName: datacenterName, + }, + } + }, + expectErr: false, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + ctx := context.Background() + + s := runtime.NewScheme() + s.AddKnownTypes(v1alpha1.GroupVersion, c.configEntryResource) + s.AddKnownTypes(schema.GroupVersion{Group: "", Version: "v1"}, &corev1.Service{}) + fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(&c.service, c.configEntryResource).Build() + + testClient := test.TestServerWithMockConnMgrWatcher(t, nil) + testClient.TestServer.WaitForLeader(t) + consulClient := testClient.APIClient + + ctrl := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.TestLogger{T: t}) + namespacedName := types.NamespacedName{ + Namespace: kubeNS, + Name: c.configEntryResource.KubernetesName(), + } + + err := assignServiceVirtualIP(ctx, ctrl.Logger(namespacedName), consulClient, ctrl, namespacedName, c.configEntryResource, "dc1") + if err != nil { + require.True(t, c.expectErr) + } else { + require.False(t, c.expectErr) + } + }) + } +} diff --git a/control-plane/go.mod b/control-plane/go.mod index 4cf30f5816..6a565a080d 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -8,9 +8,9 @@ require ( github.com/go-logr/logr v0.4.0 github.com/google/go-cmp v0.5.8 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 - github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af65262de8 - github.com/hashicorp/consul-server-connection-manager v0.1.0 - github.com/hashicorp/consul/api v1.10.1-0.20230427155444-391ed069c461 + github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83383d + github.com/hashicorp/consul-server-connection-manager v0.1.2 + github.com/hashicorp/consul/api v1.10.1-0.20230512003852-bd0eb07ed3ca github.com/hashicorp/consul/sdk v0.13.1 github.com/hashicorp/go-bexpr v0.1.11 github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f diff --git a/control-plane/go.sum b/control-plane/go.sum index f49bc4b2d5..9d5bf5e90c 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -348,13 +348,13 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af65262de8 h1:TQY0oKtLV15UNYWeSkTxi4McBIyLecsEtbc/VfxvbYA= -github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af65262de8/go.mod h1:aw35GB76URgbtxaSSMxbOetbG7YEHHPkIX3/SkTBaWc= -github.com/hashicorp/consul-server-connection-manager v0.1.0 h1:XCweGvMHzra88rYv2zxwwuUOjBUdcQmNKVrnQmt/muo= -github.com/hashicorp/consul-server-connection-manager v0.1.0/go.mod h1:XVVlO+Yk7aiRpspiHZkrrFVn9BJIiOPnQIzqytPxGaU= +github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83383d h1:RJ1MZ8JKnfgKQ1kR3IBQAMpOpzXrdseZAYN/QR//MFM= +github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83383d/go.mod h1:IHIHMzkoMwlv6rLsgwcoFBVYupR7/1pKEOHBMjD4L0k= +github.com/hashicorp/consul-server-connection-manager v0.1.2 h1:tNVQHUPuMbd+cMdD8kd+qkZUYpmLmrHMAV/49f4L53I= +github.com/hashicorp/consul-server-connection-manager v0.1.2/go.mod h1:NzQoVi1KcxGI2SangsDue8+ZPuXZWs+6BKAKrDNyg+w= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.10.1-0.20230427155444-391ed069c461 h1:cbsTR88ShbvcRMqLU8K0atm4GmRr8UH4x4jX4e12RYE= -github.com/hashicorp/consul/api v1.10.1-0.20230427155444-391ed069c461/go.mod h1:tXfrC6o0yFTgAW46xd5Ic8STHc9oIBcRVBcwhX5KNCQ= +github.com/hashicorp/consul/api v1.10.1-0.20230512003852-bd0eb07ed3ca h1:5UPVYOlJg/HBEJ2q82rkkQ3ZLzeMnF5MOpGcw2kh+XU= +github.com/hashicorp/consul/api v1.10.1-0.20230512003852-bd0eb07ed3ca/go.mod h1:tXfrC6o0yFTgAW46xd5Ic8STHc9oIBcRVBcwhX5KNCQ= github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3usQMXZF6ziK3SzXU= github.com/hashicorp/consul/proto-public v0.1.0/go.mod h1:vs2KkuWwtjkIgA5ezp4YKPzQp4GitV+q/+PvksrA92k= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= From 7b09da1fb76e428745682ea31c3d914516e44c16 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Wed, 17 May 2023 18:26:56 -0400 Subject: [PATCH 160/340] Get the consul version from values.yaml (#2146) --- Makefile | 5 +++++ control-plane/build-support/scripts/consul-version.sh | 8 ++++++++ 2 files changed, 13 insertions(+) create mode 100755 control-plane/build-support/scripts/consul-version.sh diff --git a/Makefile b/Makefile index daee6b693b..ea94b303e9 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ VERSION = $(shell ./control-plane/build-support/scripts/version.sh control-plane/version/version.go) +CONSUL_VERSION = $(shell ./control-plane/build-support/scripts/consul-version.sh charts/consul/values.yaml) # ===========> Helm Targets @@ -147,6 +148,10 @@ ci.aws-acceptance-test-cleanup: ## Deletes AWS resources left behind after faile version: @echo $(VERSION) +consul-version: + @echo $(CONSUL_VERSION) + + # ===========> Release Targets prepare-release: ## Sets the versions, updates changelog to prepare this repository to release diff --git a/control-plane/build-support/scripts/consul-version.sh b/control-plane/build-support/scripts/consul-version.sh new file mode 100755 index 0000000000..634b5db07d --- /dev/null +++ b/control-plane/build-support/scripts/consul-version.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 +FILE=$1 +VERSION=$(yq .global.image $FILE) + +# echo full string "hashicorp/consul:1.15.1" | remove first and last characters | cut everything before ':' +echo "${VERSION}" | sed 's/^.//;s/.$//' | cut -d ':' -f2- From 85c28bd4d76c6f8539060fdd8e1d8a90716becb4 Mon Sep 17 00:00:00 2001 From: "hashicorp-copywrite[bot]" <110428419+hashicorp-copywrite[bot]@users.noreply.github.com> Date: Thu, 18 May 2023 10:28:38 -0400 Subject: [PATCH 161/340] [COMPLIANCE] Add Copyright and License Headers (#2079) Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com> --- cli/cmd/config/command.go | 3 +++ cli/cmd/config/read/command.go | 3 +++ cli/cmd/config/read/command_test.go | 3 +++ control-plane/api/v1alpha1/samenessgroup_types.go | 3 +++ control-plane/config/rbac/role.yaml | 3 +++ control-plane/config/webhook/manifests.yaml | 3 +++ control-plane/controllers/samenessgroups_controller.go | 3 +++ 7 files changed, 21 insertions(+) diff --git a/cli/cmd/config/command.go b/cli/cmd/config/command.go index 5e44677ff6..302b054bd9 100644 --- a/cli/cmd/config/command.go +++ b/cli/cmd/config/command.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package config import ( diff --git a/cli/cmd/config/read/command.go b/cli/cmd/config/read/command.go index e2258bd013..0565294bd0 100644 --- a/cli/cmd/config/read/command.go +++ b/cli/cmd/config/read/command.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package read import ( diff --git a/cli/cmd/config/read/command_test.go b/cli/cmd/config/read/command_test.go index a3716cf3c1..07275f872a 100644 --- a/cli/cmd/config/read/command_test.go +++ b/cli/cmd/config/read/command_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package read import ( diff --git a/control-plane/api/v1alpha1/samenessgroup_types.go b/control-plane/api/v1alpha1/samenessgroup_types.go index 7f5f194150..86b02445f6 100644 --- a/control-plane/api/v1alpha1/samenessgroup_types.go +++ b/control-plane/api/v1alpha1/samenessgroup_types.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/config/rbac/role.yaml b/control-plane/config/rbac/role.yaml index 562eb5f9f9..bd2ce10a25 100644 --- a/control-plane/config/rbac/role.yaml +++ b/control-plane/config/rbac/role.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/control-plane/config/webhook/manifests.yaml b/control-plane/config/webhook/manifests.yaml index 663ace9d70..305e3bbe67 100644 --- a/control-plane/config/webhook/manifests.yaml +++ b/control-plane/config/webhook/manifests.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + --- apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration diff --git a/control-plane/controllers/samenessgroups_controller.go b/control-plane/controllers/samenessgroups_controller.go index afc243f267..1d768cc968 100644 --- a/control-plane/controllers/samenessgroups_controller.go +++ b/control-plane/controllers/samenessgroups_controller.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package controllers import ( From 889b689a8a4f3e8d0e01eb809b535d4a8b00bc5e Mon Sep 17 00:00:00 2001 From: John Maguire Date: Tue, 23 May 2023 15:36:39 -0400 Subject: [PATCH 162/340] Update go-discover (#2157) * update go-discove so we're not pulling in a version of tencent cloud that no longer exists * Update go discover to latest --- control-plane/go.mod | 19 ++++---- control-plane/go.sum | 108 +++++++++++++++++-------------------------- 2 files changed, 53 insertions(+), 74 deletions(-) diff --git a/control-plane/go.mod b/control-plane/go.mod index 6a565a080d..da581c221f 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -13,7 +13,7 @@ require ( github.com/hashicorp/consul/api v1.10.1-0.20230512003852-bd0eb07ed3ca github.com/hashicorp/consul/sdk v0.13.1 github.com/hashicorp/go-bexpr v0.1.11 - github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f + github.com/hashicorp/go-discover v0.0.0-20230519164032-214571b6a530 github.com/hashicorp/go-hclog v1.2.2 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-netaddrs v0.1.0 @@ -22,7 +22,7 @@ require ( github.com/hashicorp/serf v0.10.1 github.com/hashicorp/vault/api v1.8.3 github.com/kr/text v0.2.0 - github.com/miekg/dns v1.1.41 + github.com/miekg/dns v1.1.50 github.com/mitchellh/cli v1.1.0 github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/mapstructure v1.5.0 @@ -54,7 +54,7 @@ require ( github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/armon/go-radix v1.0.0 // indirect - github.com/aws/aws-sdk-go v1.25.41 // indirect + github.com/aws/aws-sdk-go v1.44.262 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect github.com/cenkalti/backoff/v3 v3.0.0 // indirect @@ -96,12 +96,12 @@ require ( github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443 // indirect github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect github.com/imdario/mergo v0.3.12 // indirect - github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/linode/linodego v0.7.1 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/go-testing-interface v1.0.0 // indirect @@ -126,18 +126,21 @@ require ( github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.2.0 // indirect - github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible // indirect + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.658 // indirect + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.658 // indirect github.com/vmware/govmomi v0.18.0 // indirect go.opencensus.io v0.23.0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.6.0 // indirect golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect + golang.org/x/mod v0.7.0 // indirect golang.org/x/net v0.7.0 // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect + golang.org/x/sync v0.1.0 // indirect golang.org/x/sys v0.5.0 // indirect golang.org/x/term v0.5.0 // indirect + golang.org/x/tools v0.3.0 // indirect google.golang.org/api v0.43.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect diff --git a/control-plane/go.sum b/control-plane/go.sum index 9d5bf5e90c..8dd50a5843 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -44,11 +44,9 @@ github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest/autorest v0.11.0/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= github.com/Azure/go-autorest/autorest v0.11.18 h1:90Y4srNYrwOtAgVo3ndrQkTYn6kf1Eg/AjTFJ8Is2aM= github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q= github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= @@ -56,11 +54,8 @@ github.com/Azure/go-autorest/autorest/azure/auth v0.5.0 h1:nSMjYIe24eBYasAIxt859 github.com/Azure/go-autorest/autorest/azure/auth v0.5.0/go.mod h1:QRTvSZQpxqm8mSErhnbI+tANIBAKP7B+UIE2z4ypUO0= github.com/Azure/go-autorest/autorest/azure/cli v0.4.0 h1:Ml+UCrnlKD+cJmSzrZ/RDcDw86NjkRUpnFh7V5JUhzU= github.com/Azure/go-autorest/autorest/azure/cli v0.4.0/go.mod h1:JljT387FplPzBA31vUcvsetLKF3pec5bdAxjVU4kI2s= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= @@ -68,11 +63,9 @@ github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+X github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= github.com/Azure/go-autorest/autorest/validation v0.3.0 h1:3I9AAI63HfcLtphd9g39ruUwRI+Ca+z/f36KHPFRUss= github.com/Azure/go-autorest/autorest/validation v0.3.0/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -81,9 +74,7 @@ github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3 github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14= github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw= @@ -102,8 +93,8 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/aws/aws-sdk-go v1.25.41 h1:/hj7nZ0586wFqpwjNpzWiUTwtaMgxAZNZKHay80MdXw= -github.com/aws/aws-sdk-go v1.25.41/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.44.262 h1:gyXpcJptWoNkK+DiAiaBltlreoWKQXjAIh6FRh60F+I= +github.com/aws/aws-sdk-go v1.44.262/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -169,14 +160,11 @@ github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661 h1:lrWnAyy/F72 github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/digitalocean/godo v1.7.5/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= github.com/digitalocean/godo v1.10.0 h1:uW1/FcvZE/hoixnJcnlmIUvTVNdZCLjRLzmDtRi1xXY= github.com/digitalocean/godo v1.10.0/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= -github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= @@ -191,7 +179,6 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= -github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -210,7 +197,6 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4 github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -227,14 +213,10 @@ github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/zapr v0.4.0 h1:uc1uML3hRYL9/ZZPdgHS/n8Nzo+eaYL/Efxkkamf7OM= github.com/go-logr/zapr v0.4.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -247,7 +229,6 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -262,7 +243,6 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -301,7 +281,6 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -331,9 +310,6 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= @@ -369,8 +345,8 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f h1:7WFMVeuJQp6BkzuTv9O52pzwtEFVUJubKYN+zez8eTI= -github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f/go.mod h1:D4eo8/CN92vm9/9UDG+ldX1/fMFa4kpl8qzyTolus8o= +github.com/hashicorp/go-discover v0.0.0-20230519164032-214571b6a530 h1:WUwSDou+memX/pb6xnjA0PfAqEEJtdWSrK00kl8ySK8= +github.com/hashicorp/go-discover v0.0.0-20230519164032-214571b6a530/go.mod h1:RH2Jr1/cCsZ1nRLmAOC65hp/gRehf55SsUIYV2+NAxI= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v1.2.2 h1:ihRI7YFwcZdiSD7SIenIhHfQH3OuDvWerAUBZbeQS3M= github.com/hashicorp/go-hclog v1.2.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= @@ -419,7 +395,6 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= github.com/hashicorp/mdns v1.0.4 h1:sY0CMhFmjIPDMlTB+HfymFHCaYLhgifZ0QhjaYKD/UQ= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= @@ -447,20 +422,19 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da h1:FjHUJJ7oBW4G/9j1KzlHaXL09LyMVM9rupS39lncbXk= -github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA= github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f h1:ENpDacvnr8faw5ugQmEF1QYk+f/Y9lXFvuYmRxykago= github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f/go.mod h1:KDSfL7qe5ZfQqvlDMkVjCztbmcpp/c8M77vhQP8ZPvk= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -490,7 +464,6 @@ github.com/linode/linodego v0.7.1 h1:4WZmMpSA2NRwlPZcc0+4Gyn7rr99Evk9bnr0B3gXRKE github.com/linode/linodego v0.7.1/go.mod h1:ga11n3ivecUrPCHN0rANxKmfWBJVkOXfLMZinAbj2sY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= @@ -498,22 +471,25 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= +github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0 h1:tEElEatulEHDeedTxwckzyYMA5c86fbmNIUL1hBIiTg= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= @@ -563,7 +539,6 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn github.com/olekukonko/tablewriter v0.0.0-20180130162743-b8a9be070da4/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= @@ -571,7 +546,6 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv github.com/onsi/ginkgo/v2 v2.1.3 h1:e/3Cwtogj0HA+25nMP1jCMDIf8RtRYbGwGGuBIFztkc= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= @@ -643,7 +617,6 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUt github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= @@ -685,8 +658,10 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible h1:8uRvJleFpqLsO77WaAh2UrasMOzd8MxXrNj20e7El+Q= -github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.658 h1:q208plt7F8Pj3b1w8D3XDb/vTgHsn/JlEwDCSe+lvyo= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.658/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.658 h1:wF/3PTojsx9/J8CaeiTy0zXxvwrcuu282R4g7fDlgCQ= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.658/go.mod h1:LLex9maWMIQzOFF/mYz5rEsTUwKKcmAbGRehSNRWqgQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= @@ -701,6 +676,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= @@ -753,10 +729,10 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -797,7 +773,9 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -819,7 +797,6 @@ golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -847,6 +824,9 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -872,9 +852,10 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -900,7 +881,6 @@ golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -943,21 +923,27 @@ golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -966,6 +952,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -977,7 +965,6 @@ golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0k golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -1034,7 +1021,10 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= +golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= 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= @@ -1154,7 +1144,6 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= 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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1163,7 +1152,6 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8X gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -1199,35 +1187,26 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78= k8s.io/api v0.22.2 h1:M8ZzAD0V6725Fjg53fKeTJxGsJvRbk4TEm/fexHMtfw= k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8= k8s.io/apiextensions-apiserver v0.22.2 h1:zK7qI8Ery7j2CaN23UCFaC1hj7dMiI87n01+nKuewd4= k8s.io/apiextensions-apiserver v0.22.2/go.mod h1:2E0Ve/isxNl7tWLSUDgi6+cmwHi5fQRdwGVCxbC+KFA= -k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= k8s.io/apimachinery v0.22.2 h1:ejz6y/zNma8clPVfNDLnPbleBo6MpoFy/HBiBqCouVk= k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= k8s.io/apiserver v0.22.2/go.mod h1:vrpMmbyjWrgdyOvZTSpsusQq5iigKNWv9o9KlDAbBHI= -k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU= k8s.io/client-go v0.22.2 h1:DaSQgs02aCC1QcwUdkKZWOeaVsQjYvWv8ZazcZ6JcHc= k8s.io/client-go v0.22.2/go.mod h1:sAlhrkVDf50ZHx6z4K0S40wISNTarf1r800F+RlCF6U= k8s.io/code-generator v0.22.2/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o= k8s.io/component-base v0.22.2 h1:vNIvE0AIrLhjX8drH0BgCNJcR4QZxMXcJzBsDplDx9M= k8s.io/component-base v0.22.2/go.mod h1:5Br2QhI9OTe79p+TzPe9JKNQYvEKbq9rTJDWllunGug= -k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM= k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20220812165043-ad590609e2e5 h1:XmRqFcQlCy/lKRZ39j+RVpokYNroHPqV3mcBRfnhT5o= k8s.io/utils v0.0.0-20220812165043-ad590609e2e5/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= @@ -1237,11 +1216,8 @@ rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/controller-runtime v0.10.2 h1:jW8qiY+yMnnPx6O9hu63tgcwaKzd1yLYui+mpvClOOc= sigs.k8s.io/controller-runtime v0.10.2/go.mod h1:CQp8eyUQZ/Q7PJvnIrB6/hgfTC1kBkGylwsLgOQi1WY= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno= sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= From d5b460195b900572aa8e359fecfc8873a8421500 Mon Sep 17 00:00:00 2001 From: John Murret Date: Tue, 23 May 2023 16:11:31 -0600 Subject: [PATCH 163/340] add helm chart values to configure global server side rate limiting (#2170) * add helm chart values to configure global server side rate limiting * add changelog. * update server checksum for configmap * fix the other 2 checksums --- .changelog/2170.txt | 2 + .../templates/server-config-configmap.yaml | 9 ++ .../test/unit/server-config-configmap.bats | 92 +++++++++++++++++++ .../consul/test/unit/server-statefulset.bats | 8 +- charts/consul/values.yaml | 37 ++++++++ 5 files changed, 144 insertions(+), 4 deletions(-) create mode 100644 .changelog/2170.txt diff --git a/.changelog/2170.txt b/.changelog/2170.txt new file mode 100644 index 0000000000..6d10ae1097 --- /dev/null +++ b/.changelog/2170.txt @@ -0,0 +1,2 @@ +```release-note:feature +Add support for configuring global level server rate limiting. diff --git a/charts/consul/templates/server-config-configmap.yaml b/charts/consul/templates/server-config-configmap.yaml index f7dd85f166..1ad04a42b5 100644 --- a/charts/consul/templates/server-config-configmap.yaml +++ b/charts/consul/templates/server-config-configmap.yaml @@ -1,4 +1,6 @@ {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (not (or (eq .Values.server.limits.requestLimits.mode "disabled") (eq .Values.server.limits.requestLimits.mode "permissive") (eq .Values.server.limits.requestLimits.mode "enforce"))) }}{{fail "server.limits.requestLimits.mode must be one of the following values: disabled, permissive, and enforce." }}{{ end -}} + # StatefulSet to run the actual Consul server cluster. apiVersion: v1 kind: ConfigMap @@ -26,6 +28,13 @@ data: "datacenter": "{{ .Values.global.datacenter }}", "data_dir": "/consul/data", "domain": "{{ .Values.global.domain }}", + "limits": { + "request_limits": { + "mode": "{{ .Values.server.limits.requestLimits.mode }}", + "read_rate": {{ .Values.server.limits.requestLimits.readRate }}, + "write_rate": {{ .Values.server.limits.requestLimits.writeRate }} + } + }, "ports": { {{- if not .Values.global.tls.enabled }} "grpc": 8502, diff --git a/charts/consul/test/unit/server-config-configmap.bats b/charts/consul/test/unit/server-config-configmap.bats index 5e849d5a4f..2c8a83f4ca 100755 --- a/charts/consul/test/unit/server-config-configmap.bats +++ b/charts/consul/test/unit/server-config-configmap.bats @@ -965,3 +965,95 @@ load _helpers [ "${actual}" = "true" ] } + +#-------------------------------------------------------------------- +# server.limits.requestLimits + +@test "server/ConfigMap: server.limits.requestLimits.mode is disabled by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + . | tee /dev/stderr | + yq -r '.data["server.json"]' | jq -r .limits.request_limits.mode | tee /dev/stderr) + + [ "${actual}" = "disabled" ] +} + +@test "server/ConfigMap: server.limits.requestLimits.mode accepts disabled, permissive, and enforce" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'server.limits.requestLimits.mode=disabled' \ + . | tee /dev/stderr | + yq -r '.data["server.json"]' | jq -r .limits.request_limits.mode | tee /dev/stderr) + + [ "${actual}" = "disabled" ] + + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'server.limits.requestLimits.mode=permissive' \ + . | tee /dev/stderr | + yq -r '.data["server.json"]' | jq -r .limits.request_limits.mode | tee /dev/stderr) + + [ "${actual}" = "permissive" ] + + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'server.limits.requestLimits.mode=enforce' \ + . | tee /dev/stderr | + yq -r '.data["server.json"]' | jq -r .limits.request_limits.mode | tee /dev/stderr) + + [ "${actual}" = "enforce" ] +} + +@test "server/ConfigMap: server.limits.requestLimits.mode errors with value other than disabled, permissive, and enforce" { + cd `chart_dir` + run helm template \ + -s templates/server-config-configmap.yaml \ + --set 'server.limits.requestLimits.mode=notvalid' \ + . + [ "$status" -eq 1 ] + [[ "$output" =~ "server.limits.requestLimits.mode must be one of the following values: disabled, permissive, and enforce" ]] +} + +@test "server/ConfigMap: server.limits.request_limits.read_rate is -1 by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + . | tee /dev/stderr | + yq -r '.data["server.json"]' | jq -r .limits.request_limits.read_rate | tee /dev/stderr) + + [ "${actual}" = "-1" ] +} + +@test "server/ConfigMap: server.limits.request_limits.read_rate is set properly when specified " { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'server.limits.requestLimits.readRate=100' \ + . | tee /dev/stderr | + yq -r '.data["server.json"]' | jq -r .limits.request_limits.read_rate | tee /dev/stderr) + + [ "${actual}" = "100" ] +} + +@test "server/ConfigMap: server.limits.request_limits.write_rate is -1 by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + . | tee /dev/stderr | + yq -r '.data["server.json"]' | jq -r .limits.request_limits.write_rate | tee /dev/stderr) + + [ "${actual}" = "-1" ] +} + +@test "server/ConfigMap: server.limits.request_limits.write_rate is set properly when specified " { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'server.limits.requestLimits.writeRate=100' \ + . | tee /dev/stderr | + yq -r '.data["server.json"]' | jq -r .limits.request_limits.write_rate | tee /dev/stderr) + + [ "${actual}" = "100" ] +} diff --git a/charts/consul/test/unit/server-statefulset.bats b/charts/consul/test/unit/server-statefulset.bats index 2d21cf7c1e..f0a8b0112b 100755 --- a/charts/consul/test/unit/server-statefulset.bats +++ b/charts/consul/test/unit/server-statefulset.bats @@ -686,7 +686,7 @@ load _helpers -s templates/server-statefulset.yaml \ . | tee /dev/stderr | yq -r '.spec.template.metadata.annotations."consul.hashicorp.com/config-checksum"' | tee /dev/stderr) - [ "${actual}" = 251dd23c6cc44bf8362acddc24c78440b6a65c4618785d027fae526958af5dde ] + [ "${actual}" = 3714bf1fbca840dcd10b5aeb40c6ef35e349bd534727abe80b831c77f88da7da ] } @test "server/StatefulSet: adds config-checksum annotation when extraConfig is provided" { @@ -696,7 +696,7 @@ load _helpers --set 'server.extraConfig="{\"hello\": \"world\"}"' \ . | tee /dev/stderr | yq -r '.spec.template.metadata.annotations."consul.hashicorp.com/config-checksum"' | tee /dev/stderr) - [ "${actual}" = 473d54d05b794be1526d42ef04fdc049f4f979a75d3394c897eef149d399207d ] + [ "${actual}" = 87126fac3c7704fba1d4265201ad0345f4d972d2123df6e6fb416b40c5823d80 ] } @test "server/StatefulSet: adds config-checksum annotation when config is updated" { @@ -706,7 +706,7 @@ load _helpers --set 'global.acls.manageSystemACLs=true' \ . | tee /dev/stderr | yq -r '.spec.template.metadata.annotations."consul.hashicorp.com/config-checksum"' | tee /dev/stderr) - [ "${actual}" = 6acd3761c0981d4d6194b3375b0f7a291e3927602ce7857344c26010381d3a61 ] + [ "${actual}" = d98ff135c5dee661058f33e29970675761ecf235676db0d4d24f354908eee425 ] } #-------------------------------------------------------------------- @@ -2773,4 +2773,4 @@ MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ . | tee /dev/stderr | yq -r '.spec.template.spec.containers[1].command[2] | contains("-interval=10h34m5s")' | tee /dev/stderr) [ "${actual}" = "true" ] -} \ No newline at end of file +} diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index edf36a41f0..a0660c5020 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -1131,6 +1131,43 @@ server: # @type: string caCert: null + # Settings for potentially limiting timeouts, rate limiting on clients as well + # as servers, and other settings to limit exposure too many requests, requests + # waiting for too long, and other runtime considerations. + limits: + # This object specifies configurations that limit the rate of RPC and gRPC + # requests on the Consul server. Limiting the rate of gRPC and RPC requests + # also limits HTTP requests to the Consul server. + # https://developer.hashicorp.com/consul/docs/agent/config/config-files#request_limits + requestLimits: + # Setting for disabling or enabling rate limiting. If not disabled, it + # enforces the action that will occur when RequestLimitsReadRate + # or RequestLimitsWriteRate is exceeded. The default value of "disabled" will + # prevent any rate limiting from occuring. A value of "enforce" will block + # the request from processings by returning an error. A value of + # "permissive" will not block the request and will allow the request to + # continue processing. + # @type: string + mode: "disabled" + + # Setting that controls how frequently RPC, gRPC, and HTTP + # queries are allowed to happen. In any large enough time interval, rate + # limiter limits the rate to RequestLimitsReadRate tokens per second. + # + # See https://en.wikipedia.org/wiki/Token_bucket for more about token + # buckets. + # @type: integer + readRate: -1 + + # Setting that controls how frequently RPC, gRPC, and HTTP + # writes are allowed to happen. In any large enough time interval, rate + # limiter limits the rate to RequestLimitsWriteRate tokens per second. + # + # See https://en.wikipedia.org/wiki/Token_bucket for more about token + # buckets. + # @type: integer + writeRate: -1 + # Configuration for Consul servers when the servers are running outside of Kubernetes. # When running external servers, configuring these values is recommended # if setting `global.tls.enableAutoEncrypt` to true From 13c166f2657bfbeb1ca4e80684e2b3f172872169 Mon Sep 17 00:00:00 2001 From: Luke Kysow <1034429+lkysow@users.noreply.github.com> Date: Thu, 25 May 2023 10:05:39 -0700 Subject: [PATCH 164/340] Disable DNS redirection when tproxy is disabled (#2176) * Disable DNS redirection when tproxy is disabled DNS redirection and the various settings that make that possible (like the dataplane binding to a port for DNS) is only useful if tproxy is enabled. Most of the code checked if tproxy was enabled but there was one location where we didn't check. This resulted in a bug with our multiport support where even though tproxy is disabled, we tried to setup the dataplane to proxy DNS. This meant each dataplane tried to bind to 8600 but because there are >1 dataplanes with multiport, there was a port conflict. This PR fixes the location where we didn't check if tproxy was enabled and as a result fixes the multiport issue. --- .changelog/2176.txt | 3 + .../tests/connect/connect_inject_test.go | 7 +- .../webhook/consul_dataplane_sidecar.go | 6 +- .../webhook/consul_dataplane_sidecar_test.go | 124 ++++++++++++++-- .../connect-inject/webhook/container_init.go | 14 +- .../webhook/container_init_test.go | 3 +- .../connect-inject/webhook/mesh_webhook.go | 10 +- .../webhook/mesh_webhook_test.go | 139 ++++++++++++++++++ .../webhook/redirect_traffic.go | 2 +- 9 files changed, 281 insertions(+), 27 deletions(-) create mode 100644 .changelog/2176.txt diff --git a/.changelog/2176.txt b/.changelog/2176.txt new file mode 100644 index 0000000000..0aee796433 --- /dev/null +++ b/.changelog/2176.txt @@ -0,0 +1,3 @@ +```release-note:bug +control-plane: fix issue with multiport pods crashlooping due to dataplane port conflicts by ensuring dns redirection is disabled for non-tproxy pods +``` diff --git a/acceptance/tests/connect/connect_inject_test.go b/acceptance/tests/connect/connect_inject_test.go index dd3865ae46..3f1660319f 100644 --- a/acceptance/tests/connect/connect_inject_test.go +++ b/acceptance/tests/connect/connect_inject_test.go @@ -136,13 +136,10 @@ func TestConnectInject_MultiportServices(t *testing.T) { cfg := suite.Config() ctx := suite.Environment().DefaultContext(t) - // Multi port apps don't work with transparent proxy. - if cfg.EnableTransparentProxy { - t.Skipf("skipping this test because transparent proxy is enabled") - } - helmValues := map[string]string{ "connectInject.enabled": "true", + // Enable DNS so we can test that DNS redirection _isn't_ set in the pod. + "dns.enabled": "true", "global.tls.enabled": strconv.FormatBool(secure), "global.acls.manageSystemACLs": strconv.FormatBool(secure), diff --git a/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go b/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go index 70f3be82da..f5134f208f 100644 --- a/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go +++ b/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go @@ -314,7 +314,11 @@ func (w *MeshWebhook) getContainerSidecarArgs(namespace corev1.Namespace, mpi mu // If Consul DNS is enabled, we want to configure consul-dataplane to be the DNS proxy // for Consul DNS in the pod. - if w.EnableConsulDNS { + dnsEnabled, err := consulDNSEnabled(namespace, pod, w.EnableConsulDNS, w.EnableTransparentProxy) + if err != nil { + return nil, err + } + if dnsEnabled { args = append(args, "-consul-dns-bind-port="+strconv.Itoa(consulDataplaneDNSBindPort)) } diff --git a/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go b/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go index f7cb7bc594..e759b627fe 100644 --- a/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go +++ b/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go @@ -290,24 +290,115 @@ func TestHandlerConsulDataplaneSidecar_Concurrency(t *testing.T) { } } +// Test that we pass the dns proxy flag to dataplane correctly. func TestHandlerConsulDataplaneSidecar_DNSProxy(t *testing.T) { - h := MeshWebhook{ - ConsulConfig: &consul.Config{HTTPPort: 8500, GRPCPort: 8502}, - EnableConsulDNS: true, + + // We only want the flag passed when DNS and tproxy are both enabled. DNS/tproxy can + // both be enabled/disabled with annotations/labels on the pod and namespace and then globally + // through the helm chart. To test this we use an outer loop with the possible DNS settings and then + // and inner loop with possible tproxy settings. + dnsCases := []struct { + GlobalConsulDNS bool + NamespaceDNS *bool + PodDNS *bool + ExpEnabled bool + }{ + { + GlobalConsulDNS: false, + ExpEnabled: false, + }, + { + GlobalConsulDNS: true, + ExpEnabled: true, + }, + { + GlobalConsulDNS: false, + NamespaceDNS: boolPtr(true), + ExpEnabled: true, + }, + { + GlobalConsulDNS: false, + PodDNS: boolPtr(true), + ExpEnabled: true, + }, } - pod := corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{}, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "web", - }, - }, + tproxyCases := []struct { + GlobalTProxy bool + NamespaceTProxy *bool + PodTProxy *bool + ExpEnabled bool + }{ + { + GlobalTProxy: false, + ExpEnabled: false, + }, + { + GlobalTProxy: true, + ExpEnabled: true, + }, + { + GlobalTProxy: false, + NamespaceTProxy: boolPtr(true), + ExpEnabled: true, + }, + { + GlobalTProxy: false, + PodTProxy: boolPtr(true), + ExpEnabled: true, }, } - container, err := h.consulDataplaneSidecar(testNS, pod, multiPortInfo{}) - require.NoError(t, err) - require.Contains(t, container.Args, "-consul-dns-bind-port=8600") + + // Outer loop is permutations of dns being enabled. Inner loop is permutations of tproxy being enabled. + // Both must be enabled for dns to be enabled. + for i, dnsCase := range dnsCases { + for j, tproxyCase := range tproxyCases { + t.Run(fmt.Sprintf("dns=%d,tproxy=%d", i, j), func(t *testing.T) { + + // Test setup. + h := MeshWebhook{ + ConsulConfig: &consul.Config{HTTPPort: 8500, GRPCPort: 8502}, + EnableTransparentProxy: tproxyCase.GlobalTProxy, + EnableConsulDNS: dnsCase.GlobalConsulDNS, + } + pod := corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "web", + }, + }, + }, + } + if dnsCase.PodDNS != nil { + pod.Annotations[constants.KeyConsulDNS] = strconv.FormatBool(*dnsCase.PodDNS) + } + if tproxyCase.PodTProxy != nil { + pod.Annotations[constants.KeyTransparentProxy] = strconv.FormatBool(*tproxyCase.PodTProxy) + } + + ns := testNS + if dnsCase.NamespaceDNS != nil { + ns.Labels[constants.KeyConsulDNS] = strconv.FormatBool(*dnsCase.NamespaceDNS) + } + if tproxyCase.NamespaceTProxy != nil { + ns.Labels[constants.KeyTransparentProxy] = strconv.FormatBool(*tproxyCase.NamespaceTProxy) + } + + // Actual test here. + container, err := h.consulDataplaneSidecar(ns, pod, multiPortInfo{}) + require.NoError(t, err) + // Flag should only be passed if both tproxy and dns are enabled. + if tproxyCase.ExpEnabled && dnsCase.ExpEnabled { + require.Contains(t, container.Args, "-consul-dns-bind-port=8600") + } else { + require.NotContains(t, container.Args, "-consul-dns-bind-port=8600") + } + }) + } + } } func TestHandlerConsulDataplaneSidecar_ProxyHealthCheck(t *testing.T) { @@ -1202,3 +1293,8 @@ func TestHandlerConsulDataplaneSidecar_Metrics(t *testing.T) { }) } } + +// boolPtr returns pointer to b. +func boolPtr(b bool) *bool { + return &b +} diff --git a/control-plane/connect-inject/webhook/container_init.go b/control-plane/connect-inject/webhook/container_init.go index b33c8f4d3e..f180de88a3 100644 --- a/control-plane/connect-inject/webhook/container_init.go +++ b/control-plane/connect-inject/webhook/container_init.go @@ -267,7 +267,17 @@ func (w *MeshWebhook) containerInit(namespace corev1.Namespace, pod corev1.Pod, // consulDNSEnabled returns true if Consul DNS should be enabled for this pod. // It returns an error when the annotation value cannot be parsed by strconv.ParseBool or if we are unable // to read the pod's namespace label when it exists. -func consulDNSEnabled(namespace corev1.Namespace, pod corev1.Pod, globalEnabled bool) (bool, error) { +func consulDNSEnabled(namespace corev1.Namespace, pod corev1.Pod, globalDNSEnabled bool, globalTProxyEnabled bool) (bool, error) { + // DNS is only possible when tproxy is also enabled because it relies + // on traffic being redirected. + tproxy, err := common.TransparentProxyEnabled(namespace, pod, globalTProxyEnabled) + if err != nil { + return false, err + } + if !tproxy { + return false, nil + } + // First check to see if the pod annotation exists to override the namespace or global settings. if raw, ok := pod.Annotations[constants.KeyConsulDNS]; ok { return strconv.ParseBool(raw) @@ -277,7 +287,7 @@ func consulDNSEnabled(namespace corev1.Namespace, pod corev1.Pod, globalEnabled return strconv.ParseBool(raw) } // Else fall back to the global default. - return globalEnabled, nil + return globalDNSEnabled, nil } // splitCommaSeparatedItemsFromAnnotation takes an annotation and a pod diff --git a/control-plane/connect-inject/webhook/container_init_test.go b/control-plane/connect-inject/webhook/container_init_test.go index 0e2de79bb1..fd89d7eba6 100644 --- a/control-plane/connect-inject/webhook/container_init_test.go +++ b/control-plane/connect-inject/webhook/container_init_test.go @@ -937,7 +937,8 @@ func TestHandlerContainerInit_Resources(t *testing.T) { var testNS = corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ - Name: k8sNamespace, + Name: k8sNamespace, + Labels: map[string]string{}, }, } diff --git a/control-plane/connect-inject/webhook/mesh_webhook.go b/control-plane/connect-inject/webhook/mesh_webhook.go index 7105e19e67..96c73d93d4 100644 --- a/control-plane/connect-inject/webhook/mesh_webhook.go +++ b/control-plane/connect-inject/webhook/mesh_webhook.go @@ -396,13 +396,17 @@ func (w *MeshWebhook) Handle(ctx context.Context, req admission.Request) admissi pod.Annotations[constants.KeyTransparentProxyStatus] = constants.Enabled } - // If tproxy with DNS redirection is enabled, we want to configure dns on the pod. - if tproxyEnabled && w.EnableConsulDNS { + // If DNS redirection is enabled, we want to configure dns on the pod. + dnsEnabled, err := consulDNSEnabled(*ns, pod, w.EnableConsulDNS, w.EnableTransparentProxy) + if err != nil { + w.Log.Error(err, "error determining if dns redirection is enabled", "request name", req.Name) + return admission.Errored(http.StatusInternalServerError, fmt.Errorf("error determining if dns redirection is enabled: %s", err)) + } + if dnsEnabled { if err = w.configureDNS(&pod, req.Namespace); err != nil { w.Log.Error(err, "error configuring DNS on the pod", "request name", req.Name) return admission.Errored(http.StatusInternalServerError, fmt.Errorf("error configuring DNS on the pod: %s", err)) } - } // Add annotations for metrics. diff --git a/control-plane/connect-inject/webhook/mesh_webhook_test.go b/control-plane/connect-inject/webhook/mesh_webhook_test.go index 9b93d8d984..20c66bd57a 100644 --- a/control-plane/connect-inject/webhook/mesh_webhook_test.go +++ b/control-plane/connect-inject/webhook/mesh_webhook_test.go @@ -811,6 +811,145 @@ func TestHandlerHandle(t *testing.T) { }, }, }, + { + "dns redirection enabled", + MeshWebhook{ + Log: logrtest.TestLogger{T: t}, + AllowK8sNamespacesSet: mapset.NewSetWith("*"), + DenyK8sNamespacesSet: mapset.NewSet(), + EnableTransparentProxy: true, + TProxyOverwriteProbes: true, + decoder: decoder, + Clientset: defaultTestClientWithNamespace(), + }, + admission.Request{ + AdmissionRequest: admissionv1.AdmissionRequest{ + Namespace: namespaces.DefaultNamespace, + Object: encodeRaw(t, &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{}, + Annotations: map[string]string{constants.KeyConsulDNS: "true"}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "web", + }, + }, + }, + }), + }, + }, + "", + []jsonpatch.Operation{ + { + Operation: "add", + Path: "/spec/volumes", + }, + { + Operation: "add", + Path: "/spec/initContainers", + }, + { + Operation: "add", + Path: "/spec/containers/1", + }, + { + Operation: "add", + Path: "/metadata/labels", + }, + { + Operation: "add", + Path: "/metadata/annotations/" + escapeJSONPointer(constants.KeyInjectStatus), + }, + { + Operation: "add", + Path: "/metadata/annotations/" + escapeJSONPointer(constants.KeyTransparentProxyStatus), + }, + + { + Operation: "add", + Path: "/metadata/annotations/" + escapeJSONPointer(constants.AnnotationOriginalPod), + }, + { + Operation: "add", + Path: "/metadata/annotations/" + escapeJSONPointer(constants.AnnotationConsulK8sVersion), + }, + { + Operation: "add", + Path: "/spec/dnsPolicy", + }, + { + Operation: "add", + Path: "/spec/dnsConfig", + }, + }, + }, + { + "dns redirection only enabled if tproxy enabled", + MeshWebhook{ + Log: logrtest.TestLogger{T: t}, + AllowK8sNamespacesSet: mapset.NewSetWith("*"), + DenyK8sNamespacesSet: mapset.NewSet(), + EnableTransparentProxy: true, + TProxyOverwriteProbes: true, + decoder: decoder, + Clientset: defaultTestClientWithNamespace(), + }, + admission.Request{ + AdmissionRequest: admissionv1.AdmissionRequest{ + Namespace: namespaces.DefaultNamespace, + Object: encodeRaw(t, &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{}, + Annotations: map[string]string{ + constants.KeyConsulDNS: "true", + constants.KeyTransparentProxy: "false", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "web", + }, + }, + }, + }), + }, + }, + "", + []jsonpatch.Operation{ + { + Operation: "add", + Path: "/spec/volumes", + }, + { + Operation: "add", + Path: "/spec/initContainers", + }, + { + Operation: "add", + Path: "/spec/containers/1", + }, + { + Operation: "add", + Path: "/metadata/labels", + }, + { + Operation: "add", + Path: "/metadata/annotations/" + escapeJSONPointer(constants.KeyInjectStatus), + }, + { + Operation: "add", + Path: "/metadata/annotations/" + escapeJSONPointer(constants.AnnotationOriginalPod), + }, + { + Operation: "add", + Path: "/metadata/annotations/" + escapeJSONPointer(constants.AnnotationConsulK8sVersion), + }, + // Note: no DNS policy/config additions. + }, + }, } for _, tt := range cases { diff --git a/control-plane/connect-inject/webhook/redirect_traffic.go b/control-plane/connect-inject/webhook/redirect_traffic.go index 7066929dae..b0cbefeeaa 100644 --- a/control-plane/connect-inject/webhook/redirect_traffic.go +++ b/control-plane/connect-inject/webhook/redirect_traffic.go @@ -98,7 +98,7 @@ func (w *MeshWebhook) iptablesConfigJSON(pod corev1.Pod, ns corev1.Namespace) (s // Add init container user ID to exclude from traffic redirection. cfg.ExcludeUIDs = append(cfg.ExcludeUIDs, strconv.Itoa(initContainersUserAndGroupID)) - dnsEnabled, err := consulDNSEnabled(ns, pod, w.EnableConsulDNS) + dnsEnabled, err := consulDNSEnabled(ns, pod, w.EnableConsulDNS, w.EnableTransparentProxy) if err != nil { return "", err } From eac1df848f551e42e8b7421f881ac94c86eef771 Mon Sep 17 00:00:00 2001 From: Luke Kysow <1034429+lkysow@users.noreply.github.com> Date: Thu, 25 May 2023 13:28:27 -0700 Subject: [PATCH 165/340] Fix tests (#2181) --- .../webhook/consul_dataplane_sidecar_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go b/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go index e759b627fe..e127915218 100644 --- a/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go +++ b/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go @@ -379,7 +379,12 @@ func TestHandlerConsulDataplaneSidecar_DNSProxy(t *testing.T) { pod.Annotations[constants.KeyTransparentProxy] = strconv.FormatBool(*tproxyCase.PodTProxy) } - ns := testNS + ns := corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: k8sNamespace, + Labels: map[string]string{}, + }, + } if dnsCase.NamespaceDNS != nil { ns.Labels[constants.KeyConsulDNS] = strconv.FormatBool(*dnsCase.NamespaceDNS) } From 1aa138ae4d84fc74adaca2c5eefd2d62090a6cb0 Mon Sep 17 00:00:00 2001 From: Andrew Stucki Date: Fri, 26 May 2023 09:42:09 -0400 Subject: [PATCH 166/340] [API Gateway] Add stub acceptance test (#2185) --- acceptance/tests/api-gateway/example_test.go | 64 ++++++++++++++++++++ acceptance/tests/api-gateway/main_test.go | 37 +++++++++++ 2 files changed, 101 insertions(+) create mode 100644 acceptance/tests/api-gateway/example_test.go create mode 100644 acceptance/tests/api-gateway/main_test.go diff --git a/acceptance/tests/api-gateway/example_test.go b/acceptance/tests/api-gateway/example_test.go new file mode 100644 index 0000000000..b324ac31fe --- /dev/null +++ b/acceptance/tests/api-gateway/example_test.go @@ -0,0 +1,64 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// Rename package to your test package. +package example + +import ( + "context" + "testing" + + "github.com/hashicorp/consul-k8s/acceptance/framework/consul" + "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" + "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestExample(t *testing.T) { + // Get test configuration. + cfg := suite.Config() + + // Get the default context. + ctx := suite.Environment().DefaultContext(t) + + // Create Helm values for the Helm install. + helmValues := map[string]string{ + "exampleFeature.enabled": "true", + } + + // Generate a random name for this test. + releaseName := helpers.RandomName() + + // Create a new Consul cluster object. + consulCluster := consul.NewHelmCluster(t, helmValues, ctx, cfg, releaseName) + + // Create the Consul cluster with Helm. + consulCluster.Create(t) + + // Make test assertions. + + // To run kubectl commands, you need to get KubectlOptions from the test context. + // There are a number of kubectl commands available in the helpers/kubectl.go file. + // For example, to call 'kubectl apply' from the test write the following: + k8s.KubectlApply(t, ctx.KubectlOptions(t), "path/to/config") + + // Clean up any Kubernetes resources you have created + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.KubectlDelete(t, ctx.KubectlOptions(t), "path/to/config") + }) + + // Similarly, you can obtain Kubernetes client from your test context. + // You can use it to, for example, read all services in a namespace: + k8sClient := ctx.KubernetesClient(t) + services, err := k8sClient.CoreV1().Services(ctx.KubectlOptions(t).Namespace).List(context.Background(), metav1.ListOptions{}) + require.NoError(t, err) + require.NotNil(t, services.Items) + + // To make Consul API calls, you can get the Consul client from the consulCluster object, + // indicating whether the client needs to be secure or not (i.e. whether TLS and ACLs are enabled on the Consul cluster): + consulClient, _ := consulCluster.SetupConsulClient(t, true) + consulServices, _, err := consulClient.Catalog().Services(nil) + require.NoError(t, err) + require.NotNil(t, consulServices) +} diff --git a/acceptance/tests/api-gateway/main_test.go b/acceptance/tests/api-gateway/main_test.go new file mode 100644 index 0000000000..f92fff8a59 --- /dev/null +++ b/acceptance/tests/api-gateway/main_test.go @@ -0,0 +1,37 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// Rename package to your test package. +package example + +import ( + "testing" + + testsuite "github.com/hashicorp/consul-k8s/acceptance/framework/suite" +) + +var suite testsuite.Suite + +func TestMain(m *testing.M) { + // First, uncomment the line below to create a new suite so that all flags are parsed. + /* + suite = framework.NewSuite(m) + */ + + // If the test suite needs to run only when certain test flags are passed, + // you need to handle that in the TestMain function. + // Uncomment and modify example code below if that is the case. + /* + if suite.Config().EnableExampleFeature { + os.Exit(suite.Run()) + } else { + fmt.Println("Skipping example feature tests because -enable-example-feature is not set") + os.Exit(0) + } + */ + + // If the test suite should run in every case, uncomment the line below. + /* + os.Exit(suite.Run()) + */ +} From 37dd92944b515fd81272c31f5ddd27b88a95c5e1 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Fri, 26 May 2023 10:42:56 -0400 Subject: [PATCH 167/340] Update consul image so that acceptance tests run (#2189) --- charts/consul/Chart.yaml | 4 ++-- charts/consul/values.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/charts/consul/Chart.yaml b/charts/consul/Chart.yaml index d0216aa36e..c55c6be6a2 100644 --- a/charts/consul/Chart.yaml +++ b/charts/consul/Chart.yaml @@ -4,7 +4,7 @@ apiVersion: v2 name: consul version: 1.2.0-dev -appVersion: 1.15.1 +appVersion: 1.16-dev kubeVersion: ">=1.22.0-0" description: Official HashiCorp Consul Chart home: https://www.consul.io @@ -16,7 +16,7 @@ annotations: artifacthub.io/prerelease: true artifacthub.io/images: | - name: consul - image: hashicorp/consul:1.15.1 + image: docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.16-dev - name: consul-k8s-control-plane image: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.2.0-dev - name: consul-dataplane diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index a0660c5020..83b18ae044 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -66,7 +66,7 @@ global: # image: "hashicorp/consul-enterprise:1.10.0-ent" # ``` # @default: hashicorp/consul: - image: "hashicorp/consul:1.15.1" + image: docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.16-dev # Array of objects containing image pull secret names that will be applied to each service account. # This can be used to reference image pull secrets if using a custom consul or consul-k8s-control-plane Docker image. From 8bfcfcf2f8091772e0dd5911aea3ff2984c61909 Mon Sep 17 00:00:00 2001 From: Thomas Eckert Date: Fri, 26 May 2023 11:52:15 -0400 Subject: [PATCH 168/340] API Gateways for Consul on Kubernetes `BETA` (#2152) * Add API Gateway subcommand to Control Plane. Co-authored-by: Thomas Eckert * Add GatewayClassConfig CRD (#2036) * Update dependencies so that CRDs can be added * Generate CRD for GatewayClassConfig * Return empty logger instead of nil due to dependency update * Update sidecar webhook to use ProbeHandler instead of Handler * Update controller sub resources to use sub resource update options * Re-add copyright header that got removed on generation * Use NewTestLogger and ProbeHandler in tests * Add api_gateway_types_test * Remove boilerplate from ctrl-generate as it is no longer required * Add app-copyright-header to Makefile * Clarify GatewayClassConfig description * Remove unneeded fields from GatewayClassConfig * Fix lint issues * Fix TestLogger in enterprise tests * Add Changelog * Fix TestLogger in enterprise test in one more place * Remove the helpers * Remove unused consts * Adds API Gateway Class Config controller * Add Hack for Generating CRDs from external sources (#2060) * Add generate-external-crds to Makefile * Add contributing docs * Add comment about Helm ignoring kustomization.yaml * Update Makefile Co-authored-by: Luke Kysow <1034429+lkysow@users.noreply.github.com> * Update CONTRIBUTING.md Co-authored-by: Nathan Coleman --------- Co-authored-by: Luke Kysow <1034429+lkysow@users.noreply.github.com> Co-authored-by: Nathan Coleman * Remove the api-gateway subcommand we decided not to use (#2062) * APIGW Resource Translation (#2070) * WIP: api-gateway resource conversion * convert meta for apigw from k8s * Added tests and updated config entry translation for APIGW * Fix linting issue, move translation code to correct location * Updates from PR comments * Update config entry translation to use k8s type NamedNamespace, updated tests * switch to standard import rename for consul api * Add GatewayClass Controller (#2055) * Add permissions to connect-inject clusterrole * Add gateway api crd deps * Stub out the gatewayclass controller * Add finalizer functions * Use finalizer functions * Add tests for GatewayClass Controller * Change the controller name * Only register gwv1beta1 * Run tests in parallel * Remove RBAC comments * Remove perms from resources not yet implemented * shouldUpdate -> expectedDidUpdate * Don't requeue if in use * Address PR feedback * Apply suggestions from code review Co-authored-by: Andrew Stucki * Make gatewayClassFinalizer private * Separate out indexers * Move validation of parametersRef to a helper func * Add reason to ensureStatus * Rename GatewayClassReconciler -> GatewayClassController * Add perms to list gateways * Clean up status conditions * Clean up indexes * Set conditions properly and test them * Test incorrect parametersRef * Fix comments on indexer funcs * Fix lint issues * Set conditions without unnecessary updates * Set ObservedGeneration from parent object * Fix infinite loop issue with invalid config * Fix update issue * Return error if the GatewayClass cannot be reached --------- Co-authored-by: Andrew Stucki * Updates GatewayClassConfig Controller to use common finalizer methods * APIGW4CONK8S: HTTP Route/TCPRoute/Secrets Translation (#2088) * Add http route translation * Added copywrite headers * Add namespace translation for service * handle potential nil pointer on section name, check if parent ref if an api gateway, fix comment from PR Review * Added TCPRoute Translation * Fix potential nil pointer deref in tcp service namespace, update tcproute tests * Add inline certs translation, clean up some potential nil pointer derefs * Clean up comments * Linting * Switch out env var usage for field on translator * rename api-gateway/consul package to api-gateway/translation * Adds stub for Gateway Controller * Use the non-deprecated logr test (#2125) * APIGW4CONK8s: Add Consul Cache (#2118) * Added basic cache functionality with most tests, todo: add get method for cache and expand tests * Updated tests for Cache.Run function, removed tests of unexported methods called by Run function * Moved translation function def to translation package, added translate apigw config entry * Add translation for consul config entries to k8s namespaced name meta * Added Get method to cache * Add watch for contoller and setup in inject command * Updated comments, renamed TranslateConsulInlineSecret method to TranslateConsulInlineCertificate * Updates from PR review * Parallelize tests * Bump consul api version * Set api timeout for cache calls * Revert "Bump consul api version" This reverts commit c074b0f749d891f78ddff86b3a7eb62ba1e52a17. * Linting fun * Add Gatekeeper for managing gateway deployment resources (#2117) * Stub out the gatewayclass controller * Change the controller name * Only register gwv1beta1 * Address PR feedback * Adds stub of Gateway Controller * cannot understand why the indexes are not working * some updates, want to do cleanup * rebase and cleanup * Start adding deployer * Flesh out tests * Refactor into a "gatekeeper" * Integrate the gatekeeper into the gateway controller * Simplify the api * Remove the creation of helm config until later * Remove use and rename package to gatekeeper * Add labels to apigateway * Manage ServiceAccount * Manage Deployment * Add more to deployment * Update Helm Values * WIP fleshing out the gateway deployment upsert behavior * Update role and service * Fix merge conflicts * Round out tests * Add test for respecting replicas * Change the Gatekeeper New API and add comments for Upsert and Delete * implement joinResources * accept suggestions from @jm96441n * Use pointer receivers * Separate out mutator * Update deployment correctly * Update Role and ServiceAccount * Fix that silly linting error * Comments on HelmConfig * Add Image to deployment * Merge api-gateway into branch --------- Co-authored-by: Melisa Griffin * Net 3490/reference grants (#2122) * Adds reference grant validation * Adds all necessary methods and tests * lint * some cleanup, fix copypasta test errors * lint * more linting * PR updates, fix capitalization * Add a bunch of TODOs for teamwork * Split out cleanup func and clear up todos * APIGW4CONK8S: Serialize the GatewayClassConfig onto the Gateway for easier retrieval (#2126) * Add serialization of gateway class config * Parallelize tests * Remove prints, fix cache tests * Add outer managed check to ensure we don't fetch config if we don't need to * Stub out where the openshift role info will go (#2145) * APIGW4CONK8S: Function to get all refs for a gateway (#2139) * Added function to get all refs for a gateway * Use k8s objects for references rather than consul objects * Fix comment * [API Gateway] API Gateway Binding Logic (#2142) * initial commit * Add additional TODO * Add some basic lifecycle unit tests * split up implementation * Add more tests and fix some bugs * remove one parallel call in a loop * Fix binding * Add resolvedRefs statuses for routes * Fix issue with empty parent ref that k8s doesn't like * Fix up updates/status ordering * Add basic gateway status setting * Finish up first pass on gateway statuses * Re-organize and begin adding comments * More comments * More comments * More comments * More comments * More comments * Add file that wasn't saved * Add utils unit tests * Add more tests * Final tests * Fix tests * Fix up gateway annotation with binding logic * Update doc comments for linter * Add forgotten file * Fix block in tests due to buffered channel size and better handle context cancelation * Add basic acceptance tests for route binding behavior (#2161) * Configure Gateway Controller with Helm values (#2158) * Stub out the gatewayclass controller * Change the controller name * Only register gwv1beta1 * Address PR feedback * Adds stub of Gateway Controller * cannot understand why the indexes are not working * some updates, want to do cleanup * rebase and cleanup * Start adding deployer * Flesh out tests * Refactor into a "gatekeeper" * Integrate the gatekeeper into the gateway controller * Simplify the api * Remove the creation of helm config until later * Remove use and rename package to gatekeeper * Add labels to apigateway * Manage ServiceAccount * Manage Deployment * Add more to deployment * Update Helm Values * WIP fleshing out the gateway deployment upsert behavior * Update role and service * Fix merge conflicts * Round out tests * Add test for respecting replicas * Change the Gatekeeper New API and add comments for Upsert and Delete * implement joinResources * accept suggestions from @jm96441n * Use pointer receivers * Separate out mutator * Update deployment correctly * Update Role and ServiceAccount * Fix that silly linting error * Comments on HelmConfig * Add Image to deployment * Add Gateway flags to inject-connect * Pass through env vars * Add environment variables to the deployment template * Add conditional injection of environment variables * Add env vars back in * Fix up issues from merge * Test default env vars * Test all of the env vars * Fix up more issues from merge * Pass in values to HelmConfig then to Controller * Just pass config in as a struct * Add gateway-gatewayclass * Add gateway-gatewayclassconfig * Add DeploymentSpec to GatewayClassConfig * Remove deployment configuration settings from HelmConfig * Remove BATs on deployment configuration * Expand gatewayclassconfig * Set deployment replicas in test * Place GatewayClassConfig in the crds/ dir * Update control-plane/api-gateway/gatekeeper/gatekeeper_test.go Co-authored-by: Andrew Stucki --------- Co-authored-by: Melisa Griffin Co-authored-by: Andrew Stucki * Net 4124/handle syncing consul lifecycle events (#2173) * with type switch * latest changes * remove debugging panic * Updated error in test * Fix bug with capacity v length in the cache list and type that is being subscribed to * Fix linting issues/naming from PR review * Added tests for delete function * Plumbing for gatekeeper with snapshot * [API Gateway] Hooking up API Gateways End-to-End (#2175) * updated gatekeeper, added update call, still needs work * still has some print statements, seeing issues with updates * some linting * run ctrl-manifests and generate * get the whole gamut finally working in a minimum configuration * Fix up tests * Add some tests * Move cache package * Fix up tests after other fixes * Fix up test lifecycle * Fix up linter issues * Remove unnecessary test that panics * Add MeshService CRD * fix bats tests * bats bats bats * baaaatttss * Fix up acceptance test cleanup by introducing uninstall hook to cleanup managed GatewayClass and GatewayClassConfig resources * Add test for deletion failures due to finalizers * reorder commands --------- Co-authored-by: Melisa Griffin * Fix crd loading (#2179) * Fix CRD loading for CLI * Adds crds directory to install with consul-k8s cli * fix tests * testing * fix bats tests --------- Co-authored-by: Thomas Eckert Co-authored-by: Andrew Stucki * Add Changelog * Fix up issues after merge back * Fix wildcard usage on enterprise * Don't subscribe to peerings when not enabled * Remove additional changelog entries since we're only going to use 1 --------- Co-authored-by: Melisa Griffin Co-authored-by: Luke Kysow <1034429+lkysow@users.noreply.github.com> Co-authored-by: Nathan Coleman Co-authored-by: John Maguire Co-authored-by: Andrew Stucki Co-authored-by: Melisa Griffin --- .changelog/2152.txt | 3 + CONTRIBUTING.md | 21 + Makefile | 17 +- .../framework/consul/helm_cluster_test.go | 5 + .../framework/environment/environment.go | 36 +- acceptance/framework/k8s/deploy.go | 9 + acceptance/go.mod | 68 +- acceptance/go.sum | 291 +- .../tests/api-gateway/api_gateway_test.go | 248 ++ acceptance/tests/api-gateway/example_test.go | 64 - acceptance/tests/api-gateway/main_test.go | 27 +- .../bases/api-gateway/apigateway.yaml | 16 + .../bases/api-gateway/gatewayclass.yaml | 13 + .../bases/api-gateway/gatewayclassconfig.yaml | 7 + .../fixtures/bases/api-gateway/httproute.yaml | 10 + .../bases/api-gateway/kustomization.yaml | 8 + charts/consul/.helmignore | 1 + ...ewayclassconfigs.consul.hashicorp.com.yaml | 140 + ...ewayclasses.gateway.networking.k8s.io.yaml | 323 ++ ...classes.gateway.networking.k8s.io.yaml.yml | 323 ++ .../gateways.gateway.networking.k8s.io.yaml | 877 ++++++ ...ateways.gateway.networking.k8s.io.yaml.yml | 877 ++++++ .../grpcroutes.gateway.networking.k8s.io.yaml | 761 +++++ ...croutes.gateway.networking.k8s.io.yaml.yml | 761 +++++ .../httproutes.gateway.networking.k8s.io.yaml | 1909 ++++++++++++ ...proutes.gateway.networking.k8s.io.yaml.yml | 1909 ++++++++++++ charts/consul/crds/kustomization.yaml | 10 + ...rencegrants.gateway.networking.k8s.io.yaml | 203 ++ ...egrants.gateway.networking.k8s.io.yaml.yml | 203 ++ .../tcproutes.gateway.networking.k8s.io.yaml | 276 ++ ...proutes.gateway.networking.k8s.io.yaml.yml | 276 ++ .../tlsroutes.gateway.networking.k8s.io.yaml | 286 ++ ...sroutes.gateway.networking.k8s.io.yaml.yml | 286 ++ .../udproutes.gateway.networking.k8s.io.yaml | 276 ++ ...proutes.gateway.networking.k8s.io.yaml.yml | 276 ++ .../templates/connect-inject-clusterrole.yaml | 87 +- .../templates/crd-exportedservices.yaml | 8 +- .../consul/templates/crd-ingressgateways.yaml | 8 +- charts/consul/templates/crd-meshes.yaml | 8 +- charts/consul/templates/crd-meshservices.yaml | 58 + .../templates/crd-peeringacceptors.yaml | 8 +- .../consul/templates/crd-peeringdialers.yaml | 8 +- .../consul/templates/crd-proxydefaults.yaml | 8 +- .../consul/templates/crd-samenessgroups.yaml | 8 +- .../consul/templates/crd-servicedefaults.yaml | 8 +- .../templates/crd-serviceintentions.yaml | 8 +- .../templates/crd-serviceresolvers.yaml | 8 +- .../consul/templates/crd-servicerouters.yaml | 8 +- .../templates/crd-servicesplitters.yaml | 8 +- .../templates/crd-terminatinggateways.yaml | 8 +- .../gateway-cleanup-clusterrole.yaml | 35 + .../gateway-cleanup-clusterrolebinding.yaml | 20 + .../consul/templates/gateway-cleanup-job.yaml | 61 + .../gateway-cleanup-podsecuritypolicy.yaml | 34 + .../gateway-cleanup-serviceaccount.yaml | 13 + .../templates/gateway-gatewayclass.yaml | 18 + .../templates/gateway-gatewayclassconfig.yaml | 74 + .../test/unit/connect-inject-clusterrole.bats | 10 +- .../test/unit/connect-inject-deployment.bats | 1 + .../test/unit/gateway-gatewayclass.bats | 21 + .../test/unit/gateway-gatewayclassconfig.bats | 117 + charts/consul/test/unit/helpers.bats | 2 +- charts/consul/values.yaml | 74 + charts/embed_chart.go | 3 +- cli/helm/chart.go | 24 +- cli/helm/chart_test.go | 1 + cli/helm/test_fixtures/consul/crds/foo.yaml | 4 + .../api-gateway/binding/annotations.go | 41 + .../api-gateway/binding/annotations_test.go | 206 ++ control-plane/api-gateway/binding/binder.go | 433 +++ .../api-gateway/binding/binder_test.go | 2683 +++++++++++++++++ .../api-gateway/binding/references.go | 135 + .../api-gateway/binding/registration.go | 92 + .../api-gateway/binding/registration_test.go | 83 + control-plane/api-gateway/binding/result.go | 471 +++ .../api-gateway/binding/route_binding.go | 489 +++ control-plane/api-gateway/binding/setter.go | 180 ++ .../api-gateway/binding/setter_test.go | 42 + control-plane/api-gateway/binding/snapshot.go | 56 + control-plane/api-gateway/binding/utils.go | 105 + .../api-gateway/binding/utils_test.go | 239 ++ .../api-gateway/binding/validation.go | 332 ++ .../api-gateway/binding/validation_test.go | 672 +++++ control-plane/api-gateway/cache/consul.go | 897 ++++++ .../api-gateway/cache/consul_test.go | 2028 +++++++++++++ .../api-gateway/controllers/finalizer.go | 44 + .../api-gateway/controllers/finalizer_test.go | 84 + .../gateway_class_config_controller.go | 130 + .../gateway_class_config_controller_test.go | 123 + .../controllers/gateway_controller.go | 779 +++++ .../controllers/gateway_controller_test.go | 464 +++ .../controllers/gatewayclass_controller.go | 259 ++ .../gatewayclass_controller_test.go | 275 ++ .../api-gateway/controllers/index.go | 262 ++ .../api-gateway/controllers/index_test.go | 13 + .../controllers/reference_validator.go | 177 ++ .../controllers/reference_validator_test.go | 649 ++++ .../api-gateway/gatekeeper/dataplane.go | 172 ++ .../api-gateway/gatekeeper/deployment.go | 234 ++ .../api-gateway/gatekeeper/gatekeeper.go | 91 + .../api-gateway/gatekeeper/gatekeeper_test.go | 896 ++++++ control-plane/api-gateway/gatekeeper/init.go | 199 ++ control-plane/api-gateway/gatekeeper/role.go | 90 + .../api-gateway/gatekeeper/service.go | 143 + .../api-gateway/gatekeeper/serviceaccount.go | 80 + control-plane/api-gateway/helm_config.go | 35 + control-plane/api-gateway/labels.go | 27 + .../translation/config_entry_translation.go | 516 ++++ .../config_entry_translation_test.go | 1636 ++++++++++ .../translation/k8s_cache_translation.go | 134 + .../translation/k8s_cache_translation_test.go | 460 +++ .../api/common/configentry_webhook_test.go | 2 +- .../api/v1alpha1/api_gateway_types.go | 135 + .../api/v1alpha1/api_gateway_types_test.go | 48 + .../v1alpha1/exportedservices_webhook_test.go | 2 +- .../api/v1alpha1/mesh_webhook_test.go | 2 +- .../v1alpha1/peeringacceptor_webhook_test.go | 2 +- .../v1alpha1/peeringdialer_webhook_test.go | 2 +- .../v1alpha1/proxydefaults_webhook_test.go | 2 +- .../serviceintentions_webhook_test.go | 6 +- .../api/v1alpha1/zz_generated.deepcopy.go | 227 +- .../build-support/controller/README.md | 5 - .../controller/boilerplate.go.txt | 0 .../catalog/to-consul/resource_test.go | 8 +- control-plane/commands.go | 5 + ...consul.hashicorp.com_exportedservices.yaml | 8 +- ...sul.hashicorp.com_gatewayclassconfigs.yaml | 140 + .../consul.hashicorp.com_ingressgateways.yaml | 8 +- .../bases/consul.hashicorp.com_meshes.yaml | 8 +- .../consul.hashicorp.com_meshservices.yaml | 53 + ...consul.hashicorp.com_peeringacceptors.yaml | 8 +- .../consul.hashicorp.com_peeringdialers.yaml | 8 +- .../consul.hashicorp.com_proxydefaults.yaml | 8 +- .../consul.hashicorp.com_samenessgroups.yaml | 8 +- .../consul.hashicorp.com_servicedefaults.yaml | 8 +- ...onsul.hashicorp.com_serviceintentions.yaml | 8 +- ...consul.hashicorp.com_serviceresolvers.yaml | 8 +- .../consul.hashicorp.com_servicerouters.yaml | 8 +- ...consul.hashicorp.com_servicesplitters.yaml | 8 +- ...sul.hashicorp.com_terminatinggateways.yaml | 8 +- .../connect-inject/constants/constants.go | 3 + .../consul_client_health_checks_test.go | 2 +- .../endpoints_controller_ent_test.go | 10 +- .../endpoints/endpoints_controller_test.go | 54 +- .../peering_acceptor_controller_test.go | 12 +- .../peering/peering_dialer_controller_test.go | 12 +- .../webhook/consul_dataplane_sidecar.go | 4 +- .../webhook/consul_dataplane_sidecar_test.go | 10 +- .../webhook/mesh_webhook_ent_test.go | 34 +- .../webhook/mesh_webhook_test.go | 66 +- .../webhook/redirect_traffic_test.go | 22 +- .../controllers/configentry_controller.go | 4 +- .../configentry_controller_ent_test.go | 12 +- .../configentry_controller_test.go | 20 +- .../exportedservices_controller.go | 2 +- .../exportedservices_controller_ent_test.go | 6 +- .../controllers/ingressgateway_controller.go | 2 +- control-plane/controllers/mesh_controller.go | 2 +- .../controllers/proxydefaults_controller.go | 2 +- .../controllers/samenessgroups_controller.go | 3 +- .../controllers/servicedefaults_controller.go | 2 +- .../serviceintentions_controller.go | 2 +- .../controllers/serviceresolver_controller.go | 2 +- .../controllers/servicerouter_controller.go | 2 +- .../controllers/servicesplitter_controller.go | 2 +- .../terminatinggateway_controller.go | 2 +- control-plane/go.mod | 92 +- control-plane/go.sum | 476 +-- control-plane/helper/controller/controller.go | 5 +- control-plane/subcommand/common/common.go | 2 +- .../subcommand/connect-init/command.go | 2 +- .../subcommand/gateway-cleanup/command.go | 193 ++ .../gateway-cleanup/command_test.go | 83 + .../subcommand/inject-connect/command.go | 88 +- hack/copy-crds-to-chart/main.go | 43 +- 175 files changed, 29689 insertions(+), 1065 deletions(-) create mode 100644 .changelog/2152.txt create mode 100644 acceptance/tests/api-gateway/api_gateway_test.go delete mode 100644 acceptance/tests/api-gateway/example_test.go create mode 100644 acceptance/tests/fixtures/bases/api-gateway/apigateway.yaml create mode 100644 acceptance/tests/fixtures/bases/api-gateway/gatewayclass.yaml create mode 100644 acceptance/tests/fixtures/bases/api-gateway/gatewayclassconfig.yaml create mode 100644 acceptance/tests/fixtures/bases/api-gateway/httproute.yaml create mode 100644 acceptance/tests/fixtures/bases/api-gateway/kustomization.yaml create mode 100644 charts/consul/crds/gatewayclassconfigs.consul.hashicorp.com.yaml create mode 100644 charts/consul/crds/gatewayclasses.gateway.networking.k8s.io.yaml create mode 100644 charts/consul/crds/gatewayclasses.gateway.networking.k8s.io.yaml.yml create mode 100644 charts/consul/crds/gateways.gateway.networking.k8s.io.yaml create mode 100644 charts/consul/crds/gateways.gateway.networking.k8s.io.yaml.yml create mode 100644 charts/consul/crds/grpcroutes.gateway.networking.k8s.io.yaml create mode 100644 charts/consul/crds/grpcroutes.gateway.networking.k8s.io.yaml.yml create mode 100644 charts/consul/crds/httproutes.gateway.networking.k8s.io.yaml create mode 100644 charts/consul/crds/httproutes.gateway.networking.k8s.io.yaml.yml create mode 100644 charts/consul/crds/kustomization.yaml create mode 100644 charts/consul/crds/referencegrants.gateway.networking.k8s.io.yaml create mode 100644 charts/consul/crds/referencegrants.gateway.networking.k8s.io.yaml.yml create mode 100644 charts/consul/crds/tcproutes.gateway.networking.k8s.io.yaml create mode 100644 charts/consul/crds/tcproutes.gateway.networking.k8s.io.yaml.yml create mode 100644 charts/consul/crds/tlsroutes.gateway.networking.k8s.io.yaml create mode 100644 charts/consul/crds/tlsroutes.gateway.networking.k8s.io.yaml.yml create mode 100644 charts/consul/crds/udproutes.gateway.networking.k8s.io.yaml create mode 100644 charts/consul/crds/udproutes.gateway.networking.k8s.io.yaml.yml create mode 100644 charts/consul/templates/crd-meshservices.yaml create mode 100644 charts/consul/templates/gateway-cleanup-clusterrole.yaml create mode 100644 charts/consul/templates/gateway-cleanup-clusterrolebinding.yaml create mode 100644 charts/consul/templates/gateway-cleanup-job.yaml create mode 100644 charts/consul/templates/gateway-cleanup-podsecuritypolicy.yaml create mode 100644 charts/consul/templates/gateway-cleanup-serviceaccount.yaml create mode 100644 charts/consul/templates/gateway-gatewayclass.yaml create mode 100644 charts/consul/templates/gateway-gatewayclassconfig.yaml create mode 100755 charts/consul/test/unit/gateway-gatewayclass.bats create mode 100644 charts/consul/test/unit/gateway-gatewayclassconfig.bats create mode 100644 cli/helm/test_fixtures/consul/crds/foo.yaml create mode 100644 control-plane/api-gateway/binding/annotations.go create mode 100644 control-plane/api-gateway/binding/annotations_test.go create mode 100644 control-plane/api-gateway/binding/binder.go create mode 100644 control-plane/api-gateway/binding/binder_test.go create mode 100644 control-plane/api-gateway/binding/references.go create mode 100644 control-plane/api-gateway/binding/registration.go create mode 100644 control-plane/api-gateway/binding/registration_test.go create mode 100644 control-plane/api-gateway/binding/result.go create mode 100644 control-plane/api-gateway/binding/route_binding.go create mode 100644 control-plane/api-gateway/binding/setter.go create mode 100644 control-plane/api-gateway/binding/setter_test.go create mode 100644 control-plane/api-gateway/binding/snapshot.go create mode 100644 control-plane/api-gateway/binding/utils.go create mode 100644 control-plane/api-gateway/binding/utils_test.go create mode 100644 control-plane/api-gateway/binding/validation.go create mode 100644 control-plane/api-gateway/binding/validation_test.go create mode 100644 control-plane/api-gateway/cache/consul.go create mode 100644 control-plane/api-gateway/cache/consul_test.go create mode 100644 control-plane/api-gateway/controllers/finalizer.go create mode 100644 control-plane/api-gateway/controllers/finalizer_test.go create mode 100644 control-plane/api-gateway/controllers/gateway_class_config_controller.go create mode 100644 control-plane/api-gateway/controllers/gateway_class_config_controller_test.go create mode 100644 control-plane/api-gateway/controllers/gateway_controller.go create mode 100644 control-plane/api-gateway/controllers/gateway_controller_test.go create mode 100644 control-plane/api-gateway/controllers/gatewayclass_controller.go create mode 100644 control-plane/api-gateway/controllers/gatewayclass_controller_test.go create mode 100644 control-plane/api-gateway/controllers/index.go create mode 100644 control-plane/api-gateway/controllers/index_test.go create mode 100644 control-plane/api-gateway/controllers/reference_validator.go create mode 100644 control-plane/api-gateway/controllers/reference_validator_test.go create mode 100644 control-plane/api-gateway/gatekeeper/dataplane.go create mode 100644 control-plane/api-gateway/gatekeeper/deployment.go create mode 100644 control-plane/api-gateway/gatekeeper/gatekeeper.go create mode 100644 control-plane/api-gateway/gatekeeper/gatekeeper_test.go create mode 100644 control-plane/api-gateway/gatekeeper/init.go create mode 100644 control-plane/api-gateway/gatekeeper/role.go create mode 100644 control-plane/api-gateway/gatekeeper/service.go create mode 100644 control-plane/api-gateway/gatekeeper/serviceaccount.go create mode 100644 control-plane/api-gateway/helm_config.go create mode 100644 control-plane/api-gateway/labels.go create mode 100644 control-plane/api-gateway/translation/config_entry_translation.go create mode 100644 control-plane/api-gateway/translation/config_entry_translation_test.go create mode 100644 control-plane/api-gateway/translation/k8s_cache_translation.go create mode 100644 control-plane/api-gateway/translation/k8s_cache_translation_test.go create mode 100644 control-plane/api/v1alpha1/api_gateway_types.go create mode 100644 control-plane/api/v1alpha1/api_gateway_types_test.go delete mode 100644 control-plane/build-support/controller/README.md delete mode 100644 control-plane/build-support/controller/boilerplate.go.txt create mode 100644 control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml create mode 100644 control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml create mode 100644 control-plane/subcommand/gateway-cleanup/command.go create mode 100644 control-plane/subcommand/gateway-cleanup/command_test.go diff --git a/.changelog/2152.txt b/.changelog/2152.txt new file mode 100644 index 0000000000..2f0743a9d8 --- /dev/null +++ b/.changelog/2152.txt @@ -0,0 +1,3 @@ +```release-note:feature +api-gateway: Add API Gateway for Consul on Kubernetes leveraging Consul native API Gateway configuration. +``` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3745c80bac..f427421294 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,6 +24,7 @@ 1. [Writing Acceptance tests](#writing-acceptance-tests) 1. [Using the Acceptance Test Framework to Debug](#using-acceptance-test-framework-to-debug) 1. [Helm Reference Docs](#helm-reference-docs) +1. [Managing External CRD Dependencies](#managing-external-crd-dependencies) 1. [Adding a Changelog Entry](#adding-a-changelog-entry) ## Contributing 101 @@ -1214,6 +1215,26 @@ So that the documentation can look like: - `ports` ((#v-ingressgateways-defaults-service-ports)) (`array: [{port: 8080, port: 8443}]`) - Port docs ``` +## Managing External CRD Dependencies + +Some of the features of Consul on Kubernetes make use of CustomResourceDefinitions (CRDs) that we don't directly +manage. One such example is the Gateway API CRDs which we use to configure API Gateways, but are managed by SIG +Networking. + +To pull external CRDs into our Helm chart and make sure they get installed, we generate their configuration using +[Kustomize](https://kustomize.io/) which can pull in Kubernetes config from external sources. We split these +generated CRDs into individual files and store them in the +[Helm `/crds` directory](https://helm.sh/docs/chart_best_practices/custom_resource_definitions/). + +If you need to update the external CRDs we depend on, or add to them, you can do this by editing the +[crds/kustomization.yaml](/charts/consul/crds/kustomization.yaml) file. Once modified, running + +```bash +make generate-external-crds +``` + +will update the CRDs in the `/crds` directory. + ## Adding a Changelog Entry Any change that a Consul-K8s user might need to know about should have a changelog entry. diff --git a/Makefile b/Makefile index ea94b303e9..6fa53d77c3 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,10 @@ gen-helm-docs: ## Generate Helm reference docs from values.yaml and update Consu copy-crds-to-chart: ## Copy generated CRD YAML into charts/consul. Usage: make copy-crds-to-chart @cd hack/copy-crds-to-chart; go run ./... +generate-external-crds: ## Generate CRDs for externally defined CRDs and copy them to charts/consul. Usage: make generate-external-crds + @cd ./charts/consul/crds/; \ + kustomize build | yq --split-exp '.metadata.name + ".yaml"' --no-doc + bats-tests: ## Run Helm chart bats tests. bats --jobs 4 charts/consul/test/unit @@ -65,7 +69,7 @@ cni-plugin-lint: cd control-plane/cni; golangci-lint run -c ../../.golangci.yml ctrl-generate: get-controller-gen ## Run CRD code generation. - cd control-plane; $(CONTROLLER_GEN) object:headerFile="build-support/controller/boilerplate.go.txt" paths="./..." + cd control-plane; $(CONTROLLER_GEN) object paths="./..." # Helper target for doing local cni acceptance testing kind-cni: @@ -124,6 +128,8 @@ lint: cni-plugin-lint ## Run linter in the control-plane, cli, and acceptance di ctrl-manifests: get-controller-gen ## Generate CRD manifests. cd control-plane; $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases make copy-crds-to-chart + make generate-external-crds + make add-copyright-header get-controller-gen: ## Download controller-gen program needed for operator SDK. ifeq (, $(shell which controller-gen)) @@ -140,6 +146,13 @@ else CONTROLLER_GEN=$(shell which controller-gen) endif +add-copyright-header: ## Add copyright header to all files in the project +ifeq (, $(shell which copywrite)) + @echo "Installing copywrite" + @go install github.com/hashicorp/copywrite@latest +endif + @copywrite headers --spdx "MPL-2.0" + # ===========> CI Targets ci.aws-acceptance-test-cleanup: ## Deletes AWS resources left behind after failed acceptance tests. @@ -178,7 +191,7 @@ endif # ===========> Makefile config .DEFAULT_GOAL := help -.PHONY: gen-helm-docs copy-crds-to-chart bats-tests help ci.aws-acceptance-test-cleanup version cli-dev prepare-dev prepare-release +.PHONY: gen-helm-docs copy-crds-to-chart generate-external-crds bats-tests help ci.aws-acceptance-test-cleanup version cli-dev prepare-dev prepare-release SHELL = bash GOOS?=$(shell go env GOOS) GOARCH?=$(shell go env GOARCH) diff --git a/acceptance/framework/consul/helm_cluster_test.go b/acceptance/framework/consul/helm_cluster_test.go index 435d9f4d7f..9c44006d43 100644 --- a/acceptance/framework/consul/helm_cluster_test.go +++ b/acceptance/framework/consul/helm_cluster_test.go @@ -11,6 +11,8 @@ import ( "github.com/stretchr/testify/require" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/fake" + "sigs.k8s.io/controller-runtime/pkg/client" + runtimefake "sigs.k8s.io/controller-runtime/pkg/client/fake" ) // Test that if TestConfig has values that need to be provided @@ -81,3 +83,6 @@ func (c *ctx) KubectlOptions(_ *testing.T) *k8s.KubectlOptions { func (c *ctx) KubernetesClient(_ *testing.T) kubernetes.Interface { return fake.NewSimpleClientset() } +func (c *ctx) ControllerRuntimeClient(_ *testing.T) client.Client { + return runtimefake.NewClientBuilder().Build() +} diff --git a/acceptance/framework/environment/environment.go b/acceptance/framework/environment/environment.go index 2c86b43707..58e4e83a5b 100644 --- a/acceptance/framework/environment/environment.go +++ b/acceptance/framework/environment/environment.go @@ -9,9 +9,15 @@ import ( "github.com/gruntwork-io/terratest/modules/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/config" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) const ( @@ -31,6 +37,7 @@ type TestEnvironment interface { type TestContext interface { KubectlOptions(t *testing.T) *k8s.KubectlOptions KubernetesClient(t *testing.T) kubernetes.Interface + ControllerRuntimeClient(t *testing.T) client.Client } type KubernetesEnvironment struct { @@ -85,7 +92,9 @@ type kubernetesContext struct { kubeContextName string namespace string - client kubernetes.Interface + client kubernetes.Interface + runtimeClient client.Client + options *k8s.KubectlOptions } @@ -164,6 +173,31 @@ func (k kubernetesContext) KubernetesClient(t *testing.T) kubernetes.Interface { return k.client } +func (k kubernetesContext) ControllerRuntimeClient(t *testing.T) client.Client { + if k.runtimeClient != nil { + return k.runtimeClient + } + + options := k.KubectlOptions(t) + configPath, err := options.GetConfigPath(t) + require.NoError(t, err) + config, err := k8s.LoadApiClientConfigE(configPath, options.ContextName) + require.NoError(t, err) + + s := runtime.NewScheme() + require.NoError(t, clientgoscheme.AddToScheme(s)) + require.NoError(t, gwv1alpha2.Install(s)) + require.NoError(t, gwv1beta1.Install(s)) + require.NoError(t, v1alpha1.AddToScheme(s)) + + client, err := client.New(config, client.Options{Scheme: s}) + require.NoError(t, err) + + k.runtimeClient = client + + return k.runtimeClient +} + func NewContext(namespace, pathToKubeConfig, kubeContextName string) *kubernetesContext { return &kubernetesContext{ namespace: namespace, diff --git a/acceptance/framework/k8s/deploy.go b/acceptance/framework/k8s/deploy.go index 771aab3fe1..6834284c33 100644 --- a/acceptance/framework/k8s/deploy.go +++ b/acceptance/framework/k8s/deploy.go @@ -153,6 +153,15 @@ func CheckStaticServerConnectionFailing(t *testing.T, options *k8s.KubectlOption }, "", curlArgs...) } +// CheckStaticServerHTTPConnectionFailing is just like CheckStaticServerConnectionFailing +// except with HTTP-based intentions. +func CheckStaticServerHTTPConnectionFailing(t *testing.T, options *k8s.KubectlOptions, sourceApp string, curlArgs ...string) { + t.Helper() + CheckStaticServerConnection(t, options, sourceApp, false, []string{ + "curl: (22) The requested URL returned error: 403", + }, "", curlArgs...) +} + // labelMapToString takes a label map[string]string // and returns the string-ified version of, e.g app=foo,env=dev. func labelMapToString(labelMap map[string]string) string { diff --git a/acceptance/go.mod b/acceptance/go.mod index 0693467102..fb2cb62725 100644 --- a/acceptance/go.mod +++ b/acceptance/go.mod @@ -10,35 +10,43 @@ require ( github.com/hashicorp/go-uuid v1.0.3 github.com/hashicorp/go-version v1.6.0 github.com/hashicorp/vault/api v1.2.0 - github.com/stretchr/testify v1.7.2 + github.com/stretchr/testify v1.8.2 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.22.2 - k8s.io/apimachinery v0.22.2 - k8s.io/client-go v0.22.2 + k8s.io/api v0.26.3 + k8s.io/apimachinery v0.26.3 + k8s.io/client-go v0.26.3 + sigs.k8s.io/controller-runtime v0.14.6 + sigs.k8s.io/gateway-api v0.7.0 ) require ( - cloud.google.com/go v0.81.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/aws/aws-sdk-go v1.30.27 // indirect + github.com/beorn7/perks v1.0.1 // indirect github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect github.com/cenkalti/backoff/v3 v3.0.0 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect + github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/fatih/color v1.13.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect - github.com/go-errors/errors v1.0.2-0.20180813162953-d98b870cc4e0 // indirect - github.com/go-logr/logr v0.4.0 // indirect + github.com/go-errors/errors v1.4.2 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.1 // indirect + github.com/go-openapi/swag v0.22.3 // indirect github.com/go-sql-driver/mysql v1.5.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.1 // indirect - github.com/google/go-cmp v0.5.7 // indirect + github.com/google/gnostic v0.5.7-v3refs // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.1.0 // indirect - github.com/google/uuid v1.1.2 // indirect - github.com/googleapis/gnostic v0.5.5 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/gruntwork-io/gruntwork-cli v0.7.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect @@ -58,9 +66,13 @@ require ( github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/jmespath/go-jmespath v0.3.0 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect + github.com/miekg/dns v1.1.41 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.0 // indirect @@ -69,26 +81,29 @@ require ( github.com/moby/spdystream v0.2.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oklog/run v1.0.0 // indirect - github.com/onsi/ginkgo v1.16.4 // indirect - github.com/onsi/gomega v1.15.0 // indirect github.com/pierrec/lz4 v2.5.2+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pquerna/otp v1.2.0 // indirect - github.com/russross/blackfriday/v2 v2.0.1 // indirect + github.com/prometheus/client_golang v1.14.0 // indirect + github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/common v0.37.0 // indirect + github.com/prometheus/procfs v0.8.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect - github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/urfave/cli v1.22.2 // indirect go.uber.org/atomic v1.7.0 // indirect golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/term v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect - golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect + golang.org/x/net v0.8.0 // indirect + golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/term v0.6.0 // indirect + golang.org/x/text v0.8.0 // indirect + golang.org/x/time v0.3.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect google.golang.org/grpc v1.48.0 // indirect @@ -96,9 +111,10 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/square/go-jose.v2 v2.5.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/klog/v2 v2.9.0 // indirect - k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect - k8s.io/utils v0.0.0-20220812165043-ad590609e2e5 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect - sigs.k8s.io/yaml v1.2.0 // indirect + k8s.io/klog/v2 v2.100.1 // indirect + k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect + k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/acceptance/go.sum b/acceptance/go.sum index a2c392ed4e..811c11c123 100644 --- a/acceptance/go.sum +++ b/acceptance/go.sum @@ -15,12 +15,6 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -49,14 +43,12 @@ github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8 github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= github.com/Azure/go-autorest/autorest v0.11.0/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= github.com/Azure/go-autorest/autorest v0.11.5/go.mod h1:foo3aIXRQ90zFve3r0QiDsrjGDUwWhKl0ZOQy1CT14k= -github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.2/go.mod h1:/3SMAM86bP6wC9Ev35peQDUeqFZBMH07vvUOmg4z/fE= -github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= github.com/Azure/go-autorest/autorest/azure/auth v0.5.1/go.mod h1:ea90/jvmnAwDrSooLH4sRIehEPtG/EPUXavDh31MnA4= github.com/Azure/go-autorest/autorest/azure/cli v0.4.0/go.mod h1:JljT387FplPzBA31vUcvsetLKF3pec5bdAxjVU4kI2s= github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= @@ -73,7 +65,6 @@ github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQ github.com/Azure/go-autorest/autorest/validation v0.3.0/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -92,6 +83,7 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= @@ -103,7 +95,7 @@ github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.16.26/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= @@ -112,6 +104,7 @@ github.com/aws/aws-sdk-go v1.30.27 h1:9gPjZWVDSoQrBO2AvqrWObS6KAZByfEJxQoCYo4ZfK github.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= @@ -121,6 +114,8 @@ github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3 github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -128,7 +123,6 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= @@ -189,69 +183,82 @@ github.com/elazarl/goproxy v0.0.0-20190911111923-ecfe977594f1/go.mod h1:Ro8st/El github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= +github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= 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.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= +github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= github.com/frankban/quicktest v1.13.0 h1:yNZif1OkDfNoDfb9zZa9aXIpejNR4F23Wely0c+Qdqk= github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-errors/errors v1.0.2-0.20180813162953-d98b870cc4e0 h1:skJKxRtNmevLqnayafdLe2AsenqRupVmzZSqrvb5caU= github.com/go-errors/errors v1.0.2-0.20180813162953-d98b870cc4e0/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-ldap/ldap/v3 v3.1.3/go.mod h1:3rbOH3jRS2u6jg2rJnKAMLE/xQyCKIveG2Sa/Cohzb8= github.com/go-ldap/ldap/v3 v3.1.10/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= +github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= @@ -267,7 +274,7 @@ github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -275,7 +282,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -292,15 +298,16 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= +github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -308,13 +315,11 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/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.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/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.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/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.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.0.0-20200110202235-f4fb41bf00a3/go.mod h1:2wIuQute9+hhWqvL3vEI7YB0EKluF4WcPzI1eAliazk= github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -322,7 +327,6 @@ github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -330,23 +334,17 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= -github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= @@ -442,18 +440,21 @@ github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -466,12 +467,14 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -487,6 +490,8 @@ github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -505,6 +510,8 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM= +github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= @@ -541,14 +548,11 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= @@ -556,18 +560,13 @@ github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo/v2 v2.6.0 h1:9t9b9vRUbFq3C4qKFCGkVuq/fIHji802N1nrtkh1mNc= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU= -github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= +github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= @@ -601,18 +600,35 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= 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= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= +github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= +github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -620,8 +636,9 @@ github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYe github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= @@ -630,12 +647,12 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= @@ -654,17 +671,22 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= @@ -689,15 +711,15 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -713,8 +735,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -741,7 +761,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= @@ -751,8 +770,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -786,38 +803,28 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= 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.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= 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= @@ -829,6 +836,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -854,7 +862,6 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -863,9 +870,9 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -878,57 +885,52 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -983,20 +985,14 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 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= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= +gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= @@ -1017,11 +1013,6 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1060,17 +1051,7 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -1089,13 +1070,8 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w= google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= @@ -1119,8 +1095,8 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= @@ -1134,7 +1110,6 @@ gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76 gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= @@ -1149,7 +1124,6 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= @@ -1163,20 +1137,22 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI= k8s.io/api v0.19.3/go.mod h1:VF+5FT1B74Pw3KxMdKyinLo+zynBaMBiAfGMuldcNDs= -k8s.io/api v0.22.2 h1:M8ZzAD0V6725Fjg53fKeTJxGsJvRbk4TEm/fexHMtfw= -k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8= +k8s.io/api v0.26.3 h1:emf74GIQMTik01Aum9dPP0gAypL8JTLl/lHa4V9RFSU= +k8s.io/api v0.26.3/go.mod h1:PXsqwPMXBSBcL1lJ9CYDKy7kIReUydukS5JiRlxC3qE= +k8s.io/apiextensions-apiserver v0.26.3 h1:5PGMm3oEzdB1W/FTMgGIDmm100vn7IaUP5er36dB+YE= k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= k8s.io/apimachinery v0.19.3/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= -k8s.io/apimachinery v0.22.2 h1:ejz6y/zNma8clPVfNDLnPbleBo6MpoFy/HBiBqCouVk= -k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= +k8s.io/apimachinery v0.26.3 h1:dQx6PNETJ7nODU3XPtrwkfuubs6w7sX0M8n61zHIV/k= +k8s.io/apimachinery v0.26.3/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I= k8s.io/apiserver v0.17.0/go.mod h1:ABM+9x/prjINN6iiffRVNCBR2Wk7uY4z+EtEGZD48cg= k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k= k8s.io/client-go v0.19.3/go.mod h1:+eEMktZM+MG0KO+PTkci8xnbCZHvj9TqR6Q1XDUIJOM= -k8s.io/client-go v0.22.2 h1:DaSQgs02aCC1QcwUdkKZWOeaVsQjYvWv8ZazcZ6JcHc= -k8s.io/client-go v0.22.2/go.mod h1:sAlhrkVDf50ZHx6z4K0S40wISNTarf1r800F+RlCF6U= +k8s.io/client-go v0.26.3 h1:k1UY+KXfkxV2ScEL3gilKcF7761xkYsSD6BC9szIu8s= +k8s.io/client-go v0.26.3/go.mod h1:ZPNu9lm8/dbRIPAgteN30RSXea6vrCpFvq+MateTUuQ= k8s.io/cloud-provider v0.17.0/go.mod h1:Ze4c3w2C0bRsjkBUoHpFi+qWe3ob1wI2/7cUn+YQIDE= k8s.io/code-generator v0.0.0-20191121015212-c4c8f8345c7e/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= k8s.io/component-base v0.17.0/go.mod h1:rKuRAokNMY2nn2A6LP/MiwpoaMRHpfRnrPaUJJj1Yoc= +k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4= k8s.io/csi-translation-lib v0.17.0/go.mod h1:HEF7MEz7pOLJCnxabi45IPkhSsE/KmxPQksuCrHKWls= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= @@ -1186,18 +1162,17 @@ k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM= -k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= +k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= +k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= -k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM= -k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= +k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= +k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= k8s.io/legacy-cloud-providers v0.17.0/go.mod h1:DdzaepJ3RtRy+e5YhNtrCYwlgyK87j/5+Yfp0L9Syp8= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20220812165043-ad590609e2e5 h1:XmRqFcQlCy/lKRZ39j+RVpokYNroHPqV3mcBRfnhT5o= -k8s.io/utils v0.0.0-20220812165043-ad590609e2e5/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= +k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= @@ -1206,12 +1181,18 @@ modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/controller-runtime v0.14.6 h1:oxstGVvXGNnMvY7TAESYk+lzr6S3V5VFxQ6d92KcwQA= +sigs.k8s.io/controller-runtime v0.14.6/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= +sigs.k8s.io/gateway-api v0.7.0 h1:/mG8yyJNBifqvuVLW5gwlI4CQs0NR/5q4BKUlf1bVdY= +sigs.k8s.io/gateway-api v0.7.0/go.mod h1:Xv0+ZMxX0lu1nSSDIIPEfbVztgNZ+3cfiYrJsa2Ooso= +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/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18= sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/acceptance/tests/api-gateway/api_gateway_test.go b/acceptance/tests/api-gateway/api_gateway_test.go new file mode 100644 index 0000000000..251d966803 --- /dev/null +++ b/acceptance/tests/api-gateway/api_gateway_test.go @@ -0,0 +1,248 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package apigateway + +import ( + "context" + "fmt" + "strconv" + "testing" + "time" + + "github.com/hashicorp/consul-k8s/acceptance/framework/consul" + "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" + "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" + "github.com/hashicorp/consul-k8s/acceptance/framework/logger" + "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/sdk/testutil/retry" + + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +const ( + StaticClientName = "static-client" + gatewayClassControllerName = "hashicorp.com/consul-api-gateway-controller" + gatewayClassFinalizer = "gateway-exists-finalizer.consul.hashicorp.com" + gatewayFinalizer = "gateway-finalizer.consul.hashicorp.com" +) + +// Test that api gateway basic functionality works in a default installation and a secure installation. +func TestAPIGateway(t *testing.T) { + cases := []struct { + secure bool + }{ + { + secure: false, + }, + { + secure: true, + }, + } + for _, c := range cases { + name := fmt.Sprintf("secure: %t", c.secure) + t.Run(name, func(t *testing.T) { + ctx := suite.Environment().DefaultContext(t) + cfg := suite.Config() + helmValues := map[string]string{ + "connectInject.enabled": "true", + "global.acls.manageSystemACLs": strconv.FormatBool(c.secure), + "global.tls.enabled": strconv.FormatBool(c.secure), + "global.logLevel": "trace", + } + + releaseName := helpers.RandomName() + consulCluster := consul.NewHelmCluster(t, helmValues, ctx, cfg, releaseName) + + consulCluster.Create(t) + + // Override the default proxy config settings for this test + consulClient, _ := consulCluster.SetupConsulClient(t, c.secure) + _, _, err := consulClient.ConfigEntries().Set(&api.ProxyConfigEntry{ + Kind: api.ProxyDefaults, + Name: api.ProxyConfigGlobal, + Config: map[string]interface{}{ + "protocol": "http", + }, + }, nil) + require.NoError(t, err) + + logger.Log(t, "creating api-gateway resources") + out, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "apply", "-k", "../fixtures/bases/api-gateway") + require.NoError(t, err, out) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + // Ignore errors here because if the test ran as expected + // the custom resources will have been deleted. + k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-k", "../fixtures/bases/api-gateway") + }) + + logger.Log(t, "creating target server") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + + logger.Log(t, "patching route to target server") + k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "httproute", "http-route", "-p", `{"spec":{"rules":[{"backendRefs":[{"name":"static-server","port":80}]}]}}`, "--type=merge") + + // We use the static-client pod so that we can make calls to the api gateway + // via kubectl exec without needing a route into the cluster from the test machine. + logger.Log(t, "creating static-client pod") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-client") + + // Grab a kubernetes client so that we can verify binding + // behavior prior to issuing requests through the gateway. + k8sClient := ctx.ControllerRuntimeClient(t) + + // On startup, the controller can take upwards of 1m to perform + // leader election so we may need to wait a long time for + // the reconcile loop to run (hence the 1m timeout here). + var gatewayAddress string + counter := &retry.Counter{Count: 60, Wait: 2 * time.Second} + retry.RunWith(counter, t, func(r *retry.R) { + var gateway gwv1beta1.Gateway + err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway", Namespace: "default"}, &gateway) + require.NoError(r, err) + + // check our finalizers + require.Len(r, gateway.Finalizers, 1) + require.EqualValues(r, gatewayFinalizer, gateway.Finalizers[0]) + + // check our statuses + checkStatusCondition(r, gateway.Status.Conditions, trueCondition("Accepted", "Accepted")) + require.Len(r, gateway.Status.Listeners, 2) + require.EqualValues(r, 1, gateway.Status.Listeners[0].AttachedRoutes) + checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, trueCondition("Accepted", "Accepted")) + checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, falseCondition("Conflicted", "NoConflicts")) + checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) + require.EqualValues(r, 0, gateway.Status.Listeners[1].AttachedRoutes) + checkStatusCondition(r, gateway.Status.Listeners[1].Conditions, trueCondition("Accepted", "Accepted")) + checkStatusCondition(r, gateway.Status.Listeners[1].Conditions, falseCondition("Conflicted", "NoConflicts")) + checkStatusCondition(r, gateway.Status.Listeners[1].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) + + // check that we have an address to use + require.Len(r, gateway.Status.Addresses, 1) + // now we know we have an address, set it so we can use it + gatewayAddress = gateway.Status.Addresses[0].Value + }) + + // now that we've satisfied those assertions, we know reconciliation is done + // so we can run assertions on the routes and the other objects + + // gateway class checks + var gatewayClass gwv1beta1.GatewayClass + err = k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway-class"}, &gatewayClass) + require.NoError(t, err) + + // check our finalizers + require.Len(t, gatewayClass.Finalizers, 1) + require.EqualValues(t, gatewayClassFinalizer, gatewayClass.Finalizers[0]) + + // http route checks + var httproute gwv1beta1.HTTPRoute + err = k8sClient.Get(context.Background(), types.NamespacedName{Name: "http-route", Namespace: "default"}, &httproute) + require.NoError(t, err) + + // check our finalizers + require.Len(t, httproute.Finalizers, 1) + require.EqualValues(t, gatewayFinalizer, httproute.Finalizers[0]) + + // check parent status + require.Len(t, httproute.Status.Parents, 1) + require.EqualValues(t, gatewayClassControllerName, httproute.Status.Parents[0].ControllerName) + require.EqualValues(t, "gateway", httproute.Status.Parents[0].ParentRef.Name) + checkStatusCondition(t, httproute.Status.Parents[0].Conditions, trueCondition("Accepted", "Accepted")) + checkStatusCondition(t, httproute.Status.Parents[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) + + // check that the Consul entries were created + entry, _, err := consulClient.ConfigEntries().Get(api.APIGateway, "gateway", nil) + require.NoError(t, err) + gateway := entry.(*api.APIGatewayConfigEntry) + + entry, _, err = consulClient.ConfigEntries().Get(api.HTTPRoute, "http-route", nil) + require.NoError(t, err) + route := entry.(*api.HTTPRouteConfigEntry) + + // now check the gateway status conditions + checkConsulStatusCondition(t, gateway.Status.Conditions, trueConsulCondition("Accepted", "Accepted")) + + // and the route status conditions + checkConsulStatusCondition(t, route.Status.Conditions, trueConsulCondition("Bound", "Bound")) + + // finally we check that we can actually route to the service via the gateway + k8sOptions := ctx.KubectlOptions(t) + targetAddress := fmt.Sprintf("http://%s:8080/", gatewayAddress) + + if c.secure { + // check that intentions keep our connection from happening + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, targetAddress) + + // Now we create the allow intention. + _, _, err = consulClient.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{ + Kind: api.ServiceIntentions, + Name: "static-server", + Sources: []*api.SourceIntention{ + { + Name: "gateway", + Action: api.IntentionActionAllow, + }, + }, + }, nil) + require.NoError(t, err) + } + + // Test that we can make a call to the api gateway + // via the static-client pod. It should route to the static-server pod. + logger.Log(t, "trying calls to api gateway") + k8s.CheckStaticServerConnectionSuccessful(t, k8sOptions, StaticClientName, targetAddress) + }) + } +} + +func checkStatusCondition(t require.TestingT, conditions []metav1.Condition, toCheck metav1.Condition) { + for _, c := range conditions { + if c.Type == toCheck.Type { + require.EqualValues(t, toCheck.Reason, c.Reason) + require.EqualValues(t, toCheck.Status, c.Status) + return + } + } + + t.Errorf("expected condition not found: %s", toCheck.Type) +} + +func trueCondition(conditionType, reason string) metav1.Condition { + return metav1.Condition{ + Type: conditionType, + Reason: reason, + Status: metav1.ConditionTrue, + } +} + +func falseCondition(conditionType, reason string) metav1.Condition { + return metav1.Condition{ + Type: conditionType, + Reason: reason, + Status: metav1.ConditionFalse, + } +} + +func checkConsulStatusCondition(t require.TestingT, conditions []api.Condition, toCheck api.Condition) { + for _, c := range conditions { + if c.Type == toCheck.Type { + require.EqualValues(t, toCheck.Reason, c.Reason) + require.EqualValues(t, toCheck.Status, c.Status) + return + } + } + + t.Errorf("expected condition not found: %s", toCheck.Type) +} + +func trueConsulCondition(conditionType, reason string) api.Condition { + return api.Condition{ + Type: conditionType, + Reason: reason, + Status: "True", + } +} diff --git a/acceptance/tests/api-gateway/example_test.go b/acceptance/tests/api-gateway/example_test.go deleted file mode 100644 index b324ac31fe..0000000000 --- a/acceptance/tests/api-gateway/example_test.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -// Rename package to your test package. -package example - -import ( - "context" - "testing" - - "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" - "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" - "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func TestExample(t *testing.T) { - // Get test configuration. - cfg := suite.Config() - - // Get the default context. - ctx := suite.Environment().DefaultContext(t) - - // Create Helm values for the Helm install. - helmValues := map[string]string{ - "exampleFeature.enabled": "true", - } - - // Generate a random name for this test. - releaseName := helpers.RandomName() - - // Create a new Consul cluster object. - consulCluster := consul.NewHelmCluster(t, helmValues, ctx, cfg, releaseName) - - // Create the Consul cluster with Helm. - consulCluster.Create(t) - - // Make test assertions. - - // To run kubectl commands, you need to get KubectlOptions from the test context. - // There are a number of kubectl commands available in the helpers/kubectl.go file. - // For example, to call 'kubectl apply' from the test write the following: - k8s.KubectlApply(t, ctx.KubectlOptions(t), "path/to/config") - - // Clean up any Kubernetes resources you have created - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { - k8s.KubectlDelete(t, ctx.KubectlOptions(t), "path/to/config") - }) - - // Similarly, you can obtain Kubernetes client from your test context. - // You can use it to, for example, read all services in a namespace: - k8sClient := ctx.KubernetesClient(t) - services, err := k8sClient.CoreV1().Services(ctx.KubectlOptions(t).Namespace).List(context.Background(), metav1.ListOptions{}) - require.NoError(t, err) - require.NotNil(t, services.Items) - - // To make Consul API calls, you can get the Consul client from the consulCluster object, - // indicating whether the client needs to be secure or not (i.e. whether TLS and ACLs are enabled on the Consul cluster): - consulClient, _ := consulCluster.SetupConsulClient(t, true) - consulServices, _, err := consulClient.Catalog().Services(nil) - require.NoError(t, err) - require.NotNil(t, consulServices) -} diff --git a/acceptance/tests/api-gateway/main_test.go b/acceptance/tests/api-gateway/main_test.go index f92fff8a59..f408845b3e 100644 --- a/acceptance/tests/api-gateway/main_test.go +++ b/acceptance/tests/api-gateway/main_test.go @@ -1,10 +1,10 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -// Rename package to your test package. -package example +package apigateway import ( + "os" "testing" testsuite "github.com/hashicorp/consul-k8s/acceptance/framework/suite" @@ -13,25 +13,6 @@ import ( var suite testsuite.Suite func TestMain(m *testing.M) { - // First, uncomment the line below to create a new suite so that all flags are parsed. - /* - suite = framework.NewSuite(m) - */ - - // If the test suite needs to run only when certain test flags are passed, - // you need to handle that in the TestMain function. - // Uncomment and modify example code below if that is the case. - /* - if suite.Config().EnableExampleFeature { - os.Exit(suite.Run()) - } else { - fmt.Println("Skipping example feature tests because -enable-example-feature is not set") - os.Exit(0) - } - */ - - // If the test suite should run in every case, uncomment the line below. - /* - os.Exit(suite.Run()) - */ + suite = testsuite.NewSuite(m) + os.Exit(suite.Run()) } diff --git a/acceptance/tests/fixtures/bases/api-gateway/apigateway.yaml b/acceptance/tests/fixtures/bases/api-gateway/apigateway.yaml new file mode 100644 index 0000000000..de7ac7b5de --- /dev/null +++ b/acceptance/tests/fixtures/bases/api-gateway/apigateway.yaml @@ -0,0 +1,16 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: gateway +spec: + gatewayClassName: gateway-class + listeners: + - protocol: HTTP + port: 8080 + name: http + - protocol: TCP + port: 8081 + name: tcp \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/api-gateway/gatewayclass.yaml b/acceptance/tests/fixtures/bases/api-gateway/gatewayclass.yaml new file mode 100644 index 0000000000..872faeb78c --- /dev/null +++ b/acceptance/tests/fixtures/bases/api-gateway/gatewayclass.yaml @@ -0,0 +1,13 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: GatewayClass +metadata: + name: gateway-class +spec: + controllerName: "hashicorp.com/consul-api-gateway-controller" + parametersRef: + group: consul.hashicorp.com + kind: GatewayClassConfig + name: gateway-class-config diff --git a/acceptance/tests/fixtures/bases/api-gateway/gatewayclassconfig.yaml b/acceptance/tests/fixtures/bases/api-gateway/gatewayclassconfig.yaml new file mode 100644 index 0000000000..b8dfae7aa5 --- /dev/null +++ b/acceptance/tests/fixtures/bases/api-gateway/gatewayclassconfig.yaml @@ -0,0 +1,7 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: GatewayClassConfig +metadata: + name: gateway-class-config \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/api-gateway/httproute.yaml b/acceptance/tests/fixtures/bases/api-gateway/httproute.yaml new file mode 100644 index 0000000000..d59c4e067e --- /dev/null +++ b/acceptance/tests/fixtures/bases/api-gateway/httproute.yaml @@ -0,0 +1,10 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: http-route +spec: + parentRefs: + - name: gateway \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/api-gateway/kustomization.yaml b/acceptance/tests/fixtures/bases/api-gateway/kustomization.yaml new file mode 100644 index 0000000000..2049f1af0b --- /dev/null +++ b/acceptance/tests/fixtures/bases/api-gateway/kustomization.yaml @@ -0,0 +1,8 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - gatewayclassconfig.yaml + - gatewayclass.yaml + - apigateway.yaml + - httproute.yaml \ No newline at end of file diff --git a/charts/consul/.helmignore b/charts/consul/.helmignore index d1180d2fb7..3fa2f24edf 100644 --- a/charts/consul/.helmignore +++ b/charts/consul/.helmignore @@ -2,3 +2,4 @@ .terraform/ bin/ test/ +crds/kustomization.yaml diff --git a/charts/consul/crds/gatewayclassconfigs.consul.hashicorp.com.yaml b/charts/consul/crds/gatewayclassconfigs.consul.hashicorp.com.yaml new file mode 100644 index 0000000000..a8393cd8fd --- /dev/null +++ b/charts/consul/crds/gatewayclassconfigs.consul.hashicorp.com.yaml @@ -0,0 +1,140 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: gatewayclassconfigs.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: GatewayClassConfig + listKind: GatewayClassConfigList + plural: gatewayclassconfigs + singular: gatewayclassconfig + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: GatewayClassConfig defines the values that may be set on a GatewayClass + for Consul API Gateway. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of GatewayClassConfig. + properties: + copyAnnotations: + description: Annotation Information to copy to services or deployments + properties: + service: + description: List of annotations to copy to the gateway service. + items: + type: string + type: array + type: object + deployment: + description: Deployment defines the deployment configuration for the + gateway. + properties: + defaultInstances: + default: 1 + description: Number of gateway instances that should be deployed + by default + format: int32 + maximum: 8 + minimum: 1 + type: integer + maxInstances: + default: 8 + description: Max allowed number of gateway instances + format: int32 + maximum: 8 + minimum: 1 + type: integer + minInstances: + default: 1 + description: Minimum allowed number of gateway instances + format: int32 + maximum: 8 + minimum: 1 + type: integer + type: object + nodeSelector: + additionalProperties: + type: string + description: 'NodeSelector is a selector which must be true for the + pod to fit on a node. Selector which must match a node''s labels + for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' + type: object + podSecurityPolicy: + description: The name of an existing Kubernetes PodSecurityPolicy + to bind to the managed ServiceAccount if ACLs are managed. + type: string + serviceType: + description: Service Type string describes ingress methods for a service + enum: + - ClusterIP + - NodePort + - LoadBalancer + type: string + tolerations: + description: 'Tolerations allow the scheduler to schedule nodes with + matching taints. More Info: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/' + items: + description: The pod this Toleration is attached to tolerates any + taint that matches the triple using the matching + operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty + means match all taint effects. When specified, allowed values + are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match all + values and all keys. + type: string + operator: + description: Operator represents a key's relationship to the + value. Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod + can tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time + the toleration (which must be of effect NoExecute, otherwise + this field is ignored) tolerates the taint. By default, it + is not set, which means tolerate the taint forever (do not + evict). Zero and negative values will be treated as 0 (evict + immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array + type: object + type: object + served: true + storage: true diff --git a/charts/consul/crds/gatewayclasses.gateway.networking.k8s.io.yaml b/charts/consul/crds/gatewayclasses.gateway.networking.k8s.io.yaml new file mode 100644 index 0000000000..044c7af939 --- /dev/null +++ b/charts/consul/crds/gatewayclasses.gateway.networking.k8s.io.yaml @@ -0,0 +1,323 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + name: gatewayclasses.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: GatewayClass + listKind: GatewayClassList + plural: gatewayclasses + shortNames: + - gc + singular: gatewayclass + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .spec.controllerName + name: Controller + type: string + - jsonPath: .status.conditions[?(@.type=="Accepted")].status + name: Accepted + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .spec.description + name: Description + priority: 1 + type: string + deprecated: true + deprecationWarning: The v1alpha2 version of GatewayClass has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1. + name: v1alpha2 + schema: + openAPIV3Schema: + description: "GatewayClass describes a class of Gateways available to the user for creating Gateway resources. \n It is recommended that this resource be used as a template for Gateways. This means that a Gateway is based on the state of the GatewayClass at the time it was created and changes to the GatewayClass or associated parameters are not propagated down to existing Gateways. This recommendation is intended to limit the blast radius of changes to GatewayClass or associated parameters. If implementations choose to propagate GatewayClass changes to existing Gateways, that MUST be clearly documented by the implementation. \n Whenever one or more Gateways are using a GatewayClass, implementations MUST add the `gateway-exists-finalizer.gateway.networking.k8s.io` finalizer on the associated GatewayClass. This ensures that a GatewayClass associated with a Gateway is not deleted while in use. \n GatewayClass is a Cluster level resource." + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of GatewayClass. + properties: + controllerName: + description: "ControllerName is the name of the controller that is managing Gateways of this class. The value of this field MUST be a domain prefixed path. \n Example: \"example.net/gateway-controller\". \n This field is not mutable and cannot be empty. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + description: + description: Description helps describe a GatewayClass with more details. + maxLength: 64 + type: string + parametersRef: + description: "ParametersRef is a reference to a resource that contains the configuration parameters corresponding to the GatewayClass. This is optional if the controller does not require any additional configuration. \n ParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap, or an implementation-specific custom resource. The resource can be cluster-scoped or namespace-scoped. \n If the referent cannot be found, the GatewayClass's \"InvalidParameters\" status condition will be true. \n Support: Implementation-specific" + properties: + group: + description: Group is the group of the referent. + 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. + 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 + namespace: + description: Namespace is the namespace of the referent. This field is required when referring to a Namespace-scoped resource and MUST be unset when referring to a Cluster-scoped resource. + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - name + type: object + required: + - controllerName + type: object + status: + default: + conditions: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Waiting + status: Unknown + type: Accepted + description: Status defines the current state of GatewayClass. + properties: + conditions: + default: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Accepted + description: "Conditions is the current status from the controller for this GatewayClass. \n Controllers should prefer to publish conditions using values of GatewayClassConditionType for the type of each Condition." + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.controllerName + name: Controller + type: string + - jsonPath: .status.conditions[?(@.type=="Accepted")].status + name: Accepted + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .spec.description + name: Description + priority: 1 + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: "GatewayClass describes a class of Gateways available to the user for creating Gateway resources. \n It is recommended that this resource be used as a template for Gateways. This means that a Gateway is based on the state of the GatewayClass at the time it was created and changes to the GatewayClass or associated parameters are not propagated down to existing Gateways. This recommendation is intended to limit the blast radius of changes to GatewayClass or associated parameters. If implementations choose to propagate GatewayClass changes to existing Gateways, that MUST be clearly documented by the implementation. \n Whenever one or more Gateways are using a GatewayClass, implementations MUST add the `gateway-exists-finalizer.gateway.networking.k8s.io` finalizer on the associated GatewayClass. This ensures that a GatewayClass associated with a Gateway is not deleted while in use. \n GatewayClass is a Cluster level resource." + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of GatewayClass. + properties: + controllerName: + description: "ControllerName is the name of the controller that is managing Gateways of this class. The value of this field MUST be a domain prefixed path. \n Example: \"example.net/gateway-controller\". \n This field is not mutable and cannot be empty. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + description: + description: Description helps describe a GatewayClass with more details. + maxLength: 64 + type: string + parametersRef: + description: "ParametersRef is a reference to a resource that contains the configuration parameters corresponding to the GatewayClass. This is optional if the controller does not require any additional configuration. \n ParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap, or an implementation-specific custom resource. The resource can be cluster-scoped or namespace-scoped. \n If the referent cannot be found, the GatewayClass's \"InvalidParameters\" status condition will be true. \n Support: Implementation-specific" + properties: + group: + description: Group is the group of the referent. + 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. + 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 + namespace: + description: Namespace is the namespace of the referent. This field is required when referring to a Namespace-scoped resource and MUST be unset when referring to a Cluster-scoped resource. + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - name + type: object + required: + - controllerName + type: object + status: + default: + conditions: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Waiting + status: Unknown + type: Accepted + description: Status defines the current state of GatewayClass. + properties: + conditions: + default: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Accepted + description: "Conditions is the current status from the controller for this GatewayClass. \n Controllers should prefer to publish conditions using values of GatewayClassConditionType for the type of each Condition." + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/consul/crds/gatewayclasses.gateway.networking.k8s.io.yaml.yml b/charts/consul/crds/gatewayclasses.gateway.networking.k8s.io.yaml.yml new file mode 100644 index 0000000000..044c7af939 --- /dev/null +++ b/charts/consul/crds/gatewayclasses.gateway.networking.k8s.io.yaml.yml @@ -0,0 +1,323 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + name: gatewayclasses.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: GatewayClass + listKind: GatewayClassList + plural: gatewayclasses + shortNames: + - gc + singular: gatewayclass + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .spec.controllerName + name: Controller + type: string + - jsonPath: .status.conditions[?(@.type=="Accepted")].status + name: Accepted + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .spec.description + name: Description + priority: 1 + type: string + deprecated: true + deprecationWarning: The v1alpha2 version of GatewayClass has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1. + name: v1alpha2 + schema: + openAPIV3Schema: + description: "GatewayClass describes a class of Gateways available to the user for creating Gateway resources. \n It is recommended that this resource be used as a template for Gateways. This means that a Gateway is based on the state of the GatewayClass at the time it was created and changes to the GatewayClass or associated parameters are not propagated down to existing Gateways. This recommendation is intended to limit the blast radius of changes to GatewayClass or associated parameters. If implementations choose to propagate GatewayClass changes to existing Gateways, that MUST be clearly documented by the implementation. \n Whenever one or more Gateways are using a GatewayClass, implementations MUST add the `gateway-exists-finalizer.gateway.networking.k8s.io` finalizer on the associated GatewayClass. This ensures that a GatewayClass associated with a Gateway is not deleted while in use. \n GatewayClass is a Cluster level resource." + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of GatewayClass. + properties: + controllerName: + description: "ControllerName is the name of the controller that is managing Gateways of this class. The value of this field MUST be a domain prefixed path. \n Example: \"example.net/gateway-controller\". \n This field is not mutable and cannot be empty. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + description: + description: Description helps describe a GatewayClass with more details. + maxLength: 64 + type: string + parametersRef: + description: "ParametersRef is a reference to a resource that contains the configuration parameters corresponding to the GatewayClass. This is optional if the controller does not require any additional configuration. \n ParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap, or an implementation-specific custom resource. The resource can be cluster-scoped or namespace-scoped. \n If the referent cannot be found, the GatewayClass's \"InvalidParameters\" status condition will be true. \n Support: Implementation-specific" + properties: + group: + description: Group is the group of the referent. + 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. + 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 + namespace: + description: Namespace is the namespace of the referent. This field is required when referring to a Namespace-scoped resource and MUST be unset when referring to a Cluster-scoped resource. + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - name + type: object + required: + - controllerName + type: object + status: + default: + conditions: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Waiting + status: Unknown + type: Accepted + description: Status defines the current state of GatewayClass. + properties: + conditions: + default: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Accepted + description: "Conditions is the current status from the controller for this GatewayClass. \n Controllers should prefer to publish conditions using values of GatewayClassConditionType for the type of each Condition." + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.controllerName + name: Controller + type: string + - jsonPath: .status.conditions[?(@.type=="Accepted")].status + name: Accepted + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .spec.description + name: Description + priority: 1 + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: "GatewayClass describes a class of Gateways available to the user for creating Gateway resources. \n It is recommended that this resource be used as a template for Gateways. This means that a Gateway is based on the state of the GatewayClass at the time it was created and changes to the GatewayClass or associated parameters are not propagated down to existing Gateways. This recommendation is intended to limit the blast radius of changes to GatewayClass or associated parameters. If implementations choose to propagate GatewayClass changes to existing Gateways, that MUST be clearly documented by the implementation. \n Whenever one or more Gateways are using a GatewayClass, implementations MUST add the `gateway-exists-finalizer.gateway.networking.k8s.io` finalizer on the associated GatewayClass. This ensures that a GatewayClass associated with a Gateway is not deleted while in use. \n GatewayClass is a Cluster level resource." + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of GatewayClass. + properties: + controllerName: + description: "ControllerName is the name of the controller that is managing Gateways of this class. The value of this field MUST be a domain prefixed path. \n Example: \"example.net/gateway-controller\". \n This field is not mutable and cannot be empty. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + description: + description: Description helps describe a GatewayClass with more details. + maxLength: 64 + type: string + parametersRef: + description: "ParametersRef is a reference to a resource that contains the configuration parameters corresponding to the GatewayClass. This is optional if the controller does not require any additional configuration. \n ParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap, or an implementation-specific custom resource. The resource can be cluster-scoped or namespace-scoped. \n If the referent cannot be found, the GatewayClass's \"InvalidParameters\" status condition will be true. \n Support: Implementation-specific" + properties: + group: + description: Group is the group of the referent. + 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. + 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 + namespace: + description: Namespace is the namespace of the referent. This field is required when referring to a Namespace-scoped resource and MUST be unset when referring to a Cluster-scoped resource. + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - name + type: object + required: + - controllerName + type: object + status: + default: + conditions: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Waiting + status: Unknown + type: Accepted + description: Status defines the current state of GatewayClass. + properties: + conditions: + default: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Accepted + description: "Conditions is the current status from the controller for this GatewayClass. \n Controllers should prefer to publish conditions using values of GatewayClassConditionType for the type of each Condition." + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/consul/crds/gateways.gateway.networking.k8s.io.yaml b/charts/consul/crds/gateways.gateway.networking.k8s.io.yaml new file mode 100644 index 0000000000..b7a7c8a7d1 --- /dev/null +++ b/charts/consul/crds/gateways.gateway.networking.k8s.io.yaml @@ -0,0 +1,877 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + name: gateways.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: Gateway + listKind: GatewayList + plural: gateways + shortNames: + - gtw + singular: gateway + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.gatewayClassName + name: Class + type: string + - jsonPath: .status.addresses[*].value + name: Address + type: string + - jsonPath: .status.conditions[?(@.type=="Programmed")].status + name: Programmed + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: The v1alpha2 version of Gateway has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1. + name: v1alpha2 + schema: + openAPIV3Schema: + description: Gateway represents an instance of a service-traffic handling infrastructure by binding Listeners to a set of IP addresses. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of Gateway. + properties: + addresses: + description: "Addresses requested for this Gateway. This is optional and behavior can depend on the implementation. If a value is set in the spec and the requested address is invalid or unavailable, the implementation MUST indicate this in the associated entry in GatewayStatus.Addresses. \n The Addresses field represents a request for the address(es) on the \"outside of the Gateway\", that traffic bound for this Gateway will use. This could be the IP address or hostname of an external load balancer or other networking infrastructure, or some other address that traffic will be sent to. \n The .listener.hostname field is used to route traffic that has already arrived at the Gateway to the correct in-cluster destination. \n If no Addresses are specified, the implementation MAY schedule the Gateway in an implementation-specific manner, assigning an appropriate set of Addresses. \n The implementation MUST bind all Listeners to every GatewayAddress that it assigns to the Gateway and add a corresponding entry in GatewayStatus.Addresses. \n Support: Extended" + items: + description: GatewayAddress describes an address that can be bound to a Gateway. + properties: + type: + default: IPAddress + description: Type of the address. + maxLength: 253 + minLength: 1 + pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + value: + description: "Value of the address. The validity of the values will depend on the type and support by the controller. \n Examples: `1.2.3.4`, `128::1`, `my-ip-address`." + maxLength: 253 + minLength: 1 + type: string + required: + - value + type: object + maxItems: 16 + type: array + gatewayClassName: + description: GatewayClassName used for this Gateway. This is the name of a GatewayClass resource. + maxLength: 253 + minLength: 1 + type: string + listeners: + description: "Listeners associated with this Gateway. Listeners define logical endpoints that are bound on this Gateway's addresses. At least one Listener MUST be specified. \n Each listener in a Gateway must have a unique combination of Hostname, Port, and Protocol. \n An implementation MAY group Listeners by Port and then collapse each group of Listeners into a single Listener if the implementation determines that the Listeners in the group are \"compatible\". An implementation MAY also group together and collapse compatible Listeners belonging to different Gateways. \n For example, an implementation might consider Listeners to be compatible with each other if all of the following conditions are met: \n 1. Either each Listener within the group specifies the \"HTTP\" Protocol or each Listener within the group specifies either the \"HTTPS\" or \"TLS\" Protocol. \n 2. Each Listener within the group specifies a Hostname that is unique within the group. \n 3. As a special case, one Listener within a group may omit Hostname, in which case this Listener matches when no other Listener matches. \n If the implementation does collapse compatible Listeners, the hostname provided in the incoming client request MUST be matched to a Listener to find the correct set of Routes. The incoming hostname MUST be matched using the Hostname field for each Listener in order of most to least specific. That is, exact matches must be processed before wildcard matches. \n If this field specifies multiple Listeners that have the same Port value but are not compatible, the implementation must raise a \"Conflicted\" condition in the Listener status. \n Support: Core" + items: + description: Listener embodies the concept of a logical endpoint where a Gateway accepts network connections. + properties: + allowedRoutes: + default: + namespaces: + from: Same + description: "AllowedRoutes defines the types of routes that MAY be attached to a Listener and the trusted namespaces where those Route resources MAY be present. \n Although a client request may match multiple route rules, only one rule may ultimately receive the request. Matching precedence MUST be determined in order of the following criteria: \n * The most specific match as defined by the Route type. * The oldest Route based on creation timestamp. For example, a Route with a creation timestamp of \"2020-09-08 01:02:03\" is given precedence over a Route with a creation timestamp of \"2020-09-08 01:02:04\". * If everything else is equivalent, the Route appearing first in alphabetical order (namespace/name) should be given precedence. For example, foo/bar is given precedence over foo/baz. \n All valid rules within a Route attached to this Listener should be implemented. Invalid Route rules can be ignored (sometimes that will mean the full Route). If a Route rule transitions from valid to invalid, support for that Route rule should be dropped to ensure consistency. For example, even if a filter specified by a Route rule is invalid, the rest of the rules within that Route should still be supported. \n Support: Core" + properties: + kinds: + description: "Kinds specifies the groups and kinds of Routes that are allowed to bind to this Gateway Listener. When unspecified or empty, the kinds of Routes selected are determined using the Listener protocol. \n A RouteGroupKind MUST correspond to kinds of Routes that are compatible with the application protocol specified in the Listener's Protocol field. If an implementation does not support or recognize this resource type, it MUST set the \"ResolvedRefs\" condition to False for this Listener with the \"InvalidRouteKinds\" reason. \n Support: Core" + items: + description: RouteGroupKind indicates the group and kind of a Route resource. + properties: + group: + default: gateway.networking.k8s.io + description: Group is the group of the Route. + 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 the kind of the Route. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + required: + - kind + type: object + maxItems: 8 + type: array + namespaces: + default: + from: Same + description: "Namespaces indicates namespaces from which Routes may be attached to this Listener. This is restricted to the namespace of this Gateway by default. \n Support: Core" + properties: + from: + default: Same + description: "From indicates where Routes will be selected for this Gateway. Possible values are: * All: Routes in all namespaces may be used by this Gateway. * Selector: Routes in namespaces selected by the selector may be used by this Gateway. * Same: Only Routes in the same namespace may be used by this Gateway. \n Support: Core" + enum: + - All + - Selector + - Same + type: string + selector: + description: "Selector must be specified when From is set to \"Selector\". In that case, only Routes in Namespaces matching this Selector will be selected by this Gateway. This field is ignored for other values of \"From\". \n Support: Core" + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + type: object + type: object + hostname: + description: "Hostname specifies the virtual hostname to match for protocol types that define this concept. When unspecified, all hostnames are matched. This field is ignored for protocols that don't require hostname based matching. \n Implementations MUST apply Hostname matching appropriately for each of the following protocols: \n * TLS: The Listener Hostname MUST match the SNI. * HTTP: The Listener Hostname MUST match the Host header of the request. * HTTPS: The Listener Hostname SHOULD match at both the TLS and HTTP protocol layers as described above. If an implementation does not ensure that both the SNI and Host header match the Listener hostname, it MUST clearly document that. \n For HTTPRoute and TLSRoute resources, there is an interaction with the `spec.hostnames` array. When both listener and route specify hostnames, there MUST be an intersection between the values for a Route to be accepted. For more information, refer to the Route specific Hostnames documentation. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + name: + description: "Name is the name of the Listener. This name MUST be unique within a Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + port: + description: "Port is the network port. Multiple listeners may use the same port, subject to the Listener compatibility rules. \n Support: Core" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + protocol: + description: "Protocol specifies the network protocol this listener expects to receive. \n Support: Core" + maxLength: 255 + minLength: 1 + pattern: ^[a-zA-Z0-9]([-a-zSA-Z0-9]*[a-zA-Z0-9])?$|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9]+$ + type: string + tls: + description: "TLS is the TLS configuration for the Listener. This field is required if the Protocol field is \"HTTPS\" or \"TLS\". It is invalid to set this field if the Protocol field is \"HTTP\", \"TCP\", or \"UDP\". \n The association of SNIs to Certificate defined in GatewayTLSConfig is defined based on the Hostname field for this listener. \n The GatewayClass MUST use the longest matching SNI out of all available certificates for any TLS handshake. \n Support: Core" + properties: + certificateRefs: + description: "CertificateRefs contains a series of references to Kubernetes objects that contains TLS certificates and private keys. These certificates are used to establish a TLS handshake for requests that match the hostname of the associated listener. \n A single CertificateRef to a Kubernetes Secret has \"Core\" support. Implementations MAY choose to support attaching multiple certificates to a Listener, but this behavior is implementation-specific. \n References to a resource in different namespace are invalid UNLESS there is a ReferenceGrant in the target namespace that allows the certificate to be attached. If a ReferenceGrant does not allow this reference, the \"ResolvedRefs\" condition MUST be set to False for this listener with the \"RefNotPermitted\" reason. \n This field is required to have at least one element when the mode is set to \"Terminate\" (default) and is optional otherwise. \n CertificateRefs can reference to standard Kubernetes resources, i.e. Secret, or implementation-specific custom resources. \n Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls \n Support: Implementation-specific (More than one reference or other resource types)" + items: + description: "SecretObjectReference identifies an API object including its namespace, defaulting to Secret. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. \n References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object." + properties: + group: + default: "" + 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: + default: Secret + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - name + type: object + maxItems: 64 + type: array + mode: + default: Terminate + description: "Mode defines the TLS behavior for the TLS session initiated by the client. There are two possible modes: \n - Terminate: The TLS session between the downstream client and the Gateway is terminated at the Gateway. This mode requires certificateRefs to be set and contain at least one element. - Passthrough: The TLS session is NOT terminated by the Gateway. This implies that the Gateway can't decipher the TLS stream except for the ClientHello message of the TLS protocol. CertificateRefs field is ignored in this mode. \n Support: Core" + enum: + - Terminate + - Passthrough + type: string + options: + additionalProperties: + description: AnnotationValue is the value of an annotation in Gateway API. This is used for validation of maps such as TLS options. This roughly matches Kubernetes annotation validation, although the length validation in that case is based on the entire size of the annotations struct. + maxLength: 4096 + minLength: 0 + type: string + description: "Options are a list of key/value pairs to enable extended TLS configuration for each implementation. For example, configuring the minimum TLS version or supported cipher suites. \n A set of common keys MAY be defined by the API in the future. To avoid any ambiguity, implementation-specific definitions MUST use domain-prefixed names, such as `example.com/my-custom-option`. Un-prefixed names are reserved for key names defined by Gateway API. \n Support: Implementation-specific" + maxProperties: 16 + type: object + type: object + required: + - name + - port + - protocol + type: object + maxItems: 64 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + required: + - gatewayClassName + - listeners + type: object + status: + default: + conditions: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: NotReconciled + status: Unknown + type: Accepted + description: Status defines the current state of Gateway. + properties: + addresses: + description: Addresses lists the IP addresses that have actually been bound to the Gateway. These addresses may differ from the addresses in the Spec, e.g. if the Gateway automatically assigns an address from a reserved pool. + items: + description: GatewayAddress describes an address that can be bound to a Gateway. + properties: + type: + default: IPAddress + description: Type of the address. + maxLength: 253 + minLength: 1 + pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + value: + description: "Value of the address. The validity of the values will depend on the type and support by the controller. \n Examples: `1.2.3.4`, `128::1`, `my-ip-address`." + maxLength: 253 + minLength: 1 + type: string + required: + - value + type: object + maxItems: 16 + type: array + conditions: + default: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Accepted + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Programmed + description: "Conditions describe the current conditions of the Gateway. \n Implementations should prefer to express Gateway conditions using the `GatewayConditionType` and `GatewayConditionReason` constants so that operators and tools can converge on a common vocabulary to describe Gateway state. \n Known condition types are: \n * \"Accepted\" * \"Ready\"" + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + listeners: + description: Listeners provide status for each unique listener port defined in the Spec. + items: + description: ListenerStatus is the status associated with a Listener. + properties: + attachedRoutes: + description: AttachedRoutes represents the total number of Routes that have been successfully attached to this Listener. + format: int32 + type: integer + conditions: + description: Conditions describe the current condition of this listener. + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + name: + description: Name is the name of the Listener that this status corresponds to. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + supportedKinds: + description: "SupportedKinds is the list indicating the Kinds supported by this listener. This MUST represent the kinds an implementation supports for that Listener configuration. \n If kinds are specified in Spec that are not supported, they MUST NOT appear in this list and an implementation MUST set the \"ResolvedRefs\" condition to \"False\" with the \"InvalidRouteKinds\" reason. If both valid and invalid Route kinds are specified, the implementation MUST reference the valid Route kinds that have been specified." + items: + description: RouteGroupKind indicates the group and kind of a Route resource. + properties: + group: + default: gateway.networking.k8s.io + description: Group is the group of the Route. + 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 the kind of the Route. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + required: + - kind + type: object + maxItems: 8 + type: array + required: + - attachedRoutes + - conditions + - name + - supportedKinds + type: object + maxItems: 64 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.gatewayClassName + name: Class + type: string + - jsonPath: .status.addresses[*].value + name: Address + type: string + - jsonPath: .status.conditions[?(@.type=="Programmed")].status + name: Programmed + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: Gateway represents an instance of a service-traffic handling infrastructure by binding Listeners to a set of IP addresses. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of Gateway. + properties: + addresses: + description: "Addresses requested for this Gateway. This is optional and behavior can depend on the implementation. If a value is set in the spec and the requested address is invalid or unavailable, the implementation MUST indicate this in the associated entry in GatewayStatus.Addresses. \n The Addresses field represents a request for the address(es) on the \"outside of the Gateway\", that traffic bound for this Gateway will use. This could be the IP address or hostname of an external load balancer or other networking infrastructure, or some other address that traffic will be sent to. \n The .listener.hostname field is used to route traffic that has already arrived at the Gateway to the correct in-cluster destination. \n If no Addresses are specified, the implementation MAY schedule the Gateway in an implementation-specific manner, assigning an appropriate set of Addresses. \n The implementation MUST bind all Listeners to every GatewayAddress that it assigns to the Gateway and add a corresponding entry in GatewayStatus.Addresses. \n Support: Extended" + items: + description: GatewayAddress describes an address that can be bound to a Gateway. + properties: + type: + default: IPAddress + description: Type of the address. + maxLength: 253 + minLength: 1 + pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + value: + description: "Value of the address. The validity of the values will depend on the type and support by the controller. \n Examples: `1.2.3.4`, `128::1`, `my-ip-address`." + maxLength: 253 + minLength: 1 + type: string + required: + - value + type: object + maxItems: 16 + type: array + gatewayClassName: + description: GatewayClassName used for this Gateway. This is the name of a GatewayClass resource. + maxLength: 253 + minLength: 1 + type: string + listeners: + description: "Listeners associated with this Gateway. Listeners define logical endpoints that are bound on this Gateway's addresses. At least one Listener MUST be specified. \n Each listener in a Gateway must have a unique combination of Hostname, Port, and Protocol. \n An implementation MAY group Listeners by Port and then collapse each group of Listeners into a single Listener if the implementation determines that the Listeners in the group are \"compatible\". An implementation MAY also group together and collapse compatible Listeners belonging to different Gateways. \n For example, an implementation might consider Listeners to be compatible with each other if all of the following conditions are met: \n 1. Either each Listener within the group specifies the \"HTTP\" Protocol or each Listener within the group specifies either the \"HTTPS\" or \"TLS\" Protocol. \n 2. Each Listener within the group specifies a Hostname that is unique within the group. \n 3. As a special case, one Listener within a group may omit Hostname, in which case this Listener matches when no other Listener matches. \n If the implementation does collapse compatible Listeners, the hostname provided in the incoming client request MUST be matched to a Listener to find the correct set of Routes. The incoming hostname MUST be matched using the Hostname field for each Listener in order of most to least specific. That is, exact matches must be processed before wildcard matches. \n If this field specifies multiple Listeners that have the same Port value but are not compatible, the implementation must raise a \"Conflicted\" condition in the Listener status. \n Support: Core" + items: + description: Listener embodies the concept of a logical endpoint where a Gateway accepts network connections. + properties: + allowedRoutes: + default: + namespaces: + from: Same + description: "AllowedRoutes defines the types of routes that MAY be attached to a Listener and the trusted namespaces where those Route resources MAY be present. \n Although a client request may match multiple route rules, only one rule may ultimately receive the request. Matching precedence MUST be determined in order of the following criteria: \n * The most specific match as defined by the Route type. * The oldest Route based on creation timestamp. For example, a Route with a creation timestamp of \"2020-09-08 01:02:03\" is given precedence over a Route with a creation timestamp of \"2020-09-08 01:02:04\". * If everything else is equivalent, the Route appearing first in alphabetical order (namespace/name) should be given precedence. For example, foo/bar is given precedence over foo/baz. \n All valid rules within a Route attached to this Listener should be implemented. Invalid Route rules can be ignored (sometimes that will mean the full Route). If a Route rule transitions from valid to invalid, support for that Route rule should be dropped to ensure consistency. For example, even if a filter specified by a Route rule is invalid, the rest of the rules within that Route should still be supported. \n Support: Core" + properties: + kinds: + description: "Kinds specifies the groups and kinds of Routes that are allowed to bind to this Gateway Listener. When unspecified or empty, the kinds of Routes selected are determined using the Listener protocol. \n A RouteGroupKind MUST correspond to kinds of Routes that are compatible with the application protocol specified in the Listener's Protocol field. If an implementation does not support or recognize this resource type, it MUST set the \"ResolvedRefs\" condition to False for this Listener with the \"InvalidRouteKinds\" reason. \n Support: Core" + items: + description: RouteGroupKind indicates the group and kind of a Route resource. + properties: + group: + default: gateway.networking.k8s.io + description: Group is the group of the Route. + 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 the kind of the Route. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + required: + - kind + type: object + maxItems: 8 + type: array + namespaces: + default: + from: Same + description: "Namespaces indicates namespaces from which Routes may be attached to this Listener. This is restricted to the namespace of this Gateway by default. \n Support: Core" + properties: + from: + default: Same + description: "From indicates where Routes will be selected for this Gateway. Possible values are: * All: Routes in all namespaces may be used by this Gateway. * Selector: Routes in namespaces selected by the selector may be used by this Gateway. * Same: Only Routes in the same namespace may be used by this Gateway. \n Support: Core" + enum: + - All + - Selector + - Same + type: string + selector: + description: "Selector must be specified when From is set to \"Selector\". In that case, only Routes in Namespaces matching this Selector will be selected by this Gateway. This field is ignored for other values of \"From\". \n Support: Core" + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + type: object + type: object + hostname: + description: "Hostname specifies the virtual hostname to match for protocol types that define this concept. When unspecified, all hostnames are matched. This field is ignored for protocols that don't require hostname based matching. \n Implementations MUST apply Hostname matching appropriately for each of the following protocols: \n * TLS: The Listener Hostname MUST match the SNI. * HTTP: The Listener Hostname MUST match the Host header of the request. * HTTPS: The Listener Hostname SHOULD match at both the TLS and HTTP protocol layers as described above. If an implementation does not ensure that both the SNI and Host header match the Listener hostname, it MUST clearly document that. \n For HTTPRoute and TLSRoute resources, there is an interaction with the `spec.hostnames` array. When both listener and route specify hostnames, there MUST be an intersection between the values for a Route to be accepted. For more information, refer to the Route specific Hostnames documentation. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + name: + description: "Name is the name of the Listener. This name MUST be unique within a Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + port: + description: "Port is the network port. Multiple listeners may use the same port, subject to the Listener compatibility rules. \n Support: Core" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + protocol: + description: "Protocol specifies the network protocol this listener expects to receive. \n Support: Core" + maxLength: 255 + minLength: 1 + pattern: ^[a-zA-Z0-9]([-a-zSA-Z0-9]*[a-zA-Z0-9])?$|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9]+$ + type: string + tls: + description: "TLS is the TLS configuration for the Listener. This field is required if the Protocol field is \"HTTPS\" or \"TLS\". It is invalid to set this field if the Protocol field is \"HTTP\", \"TCP\", or \"UDP\". \n The association of SNIs to Certificate defined in GatewayTLSConfig is defined based on the Hostname field for this listener. \n The GatewayClass MUST use the longest matching SNI out of all available certificates for any TLS handshake. \n Support: Core" + properties: + certificateRefs: + description: "CertificateRefs contains a series of references to Kubernetes objects that contains TLS certificates and private keys. These certificates are used to establish a TLS handshake for requests that match the hostname of the associated listener. \n A single CertificateRef to a Kubernetes Secret has \"Core\" support. Implementations MAY choose to support attaching multiple certificates to a Listener, but this behavior is implementation-specific. \n References to a resource in different namespace are invalid UNLESS there is a ReferenceGrant in the target namespace that allows the certificate to be attached. If a ReferenceGrant does not allow this reference, the \"ResolvedRefs\" condition MUST be set to False for this listener with the \"RefNotPermitted\" reason. \n This field is required to have at least one element when the mode is set to \"Terminate\" (default) and is optional otherwise. \n CertificateRefs can reference to standard Kubernetes resources, i.e. Secret, or implementation-specific custom resources. \n Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls \n Support: Implementation-specific (More than one reference or other resource types)" + items: + description: "SecretObjectReference identifies an API object including its namespace, defaulting to Secret. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. \n References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object." + properties: + group: + default: "" + 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: + default: Secret + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - name + type: object + maxItems: 64 + type: array + mode: + default: Terminate + description: "Mode defines the TLS behavior for the TLS session initiated by the client. There are two possible modes: \n - Terminate: The TLS session between the downstream client and the Gateway is terminated at the Gateway. This mode requires certificateRefs to be set and contain at least one element. - Passthrough: The TLS session is NOT terminated by the Gateway. This implies that the Gateway can't decipher the TLS stream except for the ClientHello message of the TLS protocol. CertificateRefs field is ignored in this mode. \n Support: Core" + enum: + - Terminate + - Passthrough + type: string + options: + additionalProperties: + description: AnnotationValue is the value of an annotation in Gateway API. This is used for validation of maps such as TLS options. This roughly matches Kubernetes annotation validation, although the length validation in that case is based on the entire size of the annotations struct. + maxLength: 4096 + minLength: 0 + type: string + description: "Options are a list of key/value pairs to enable extended TLS configuration for each implementation. For example, configuring the minimum TLS version or supported cipher suites. \n A set of common keys MAY be defined by the API in the future. To avoid any ambiguity, implementation-specific definitions MUST use domain-prefixed names, such as `example.com/my-custom-option`. Un-prefixed names are reserved for key names defined by Gateway API. \n Support: Implementation-specific" + maxProperties: 16 + type: object + type: object + required: + - name + - port + - protocol + type: object + maxItems: 64 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + required: + - gatewayClassName + - listeners + type: object + status: + default: + conditions: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: NotReconciled + status: Unknown + type: Accepted + description: Status defines the current state of Gateway. + properties: + addresses: + description: Addresses lists the IP addresses that have actually been bound to the Gateway. These addresses may differ from the addresses in the Spec, e.g. if the Gateway automatically assigns an address from a reserved pool. + items: + description: GatewayAddress describes an address that can be bound to a Gateway. + properties: + type: + default: IPAddress + description: Type of the address. + maxLength: 253 + minLength: 1 + pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + value: + description: "Value of the address. The validity of the values will depend on the type and support by the controller. \n Examples: `1.2.3.4`, `128::1`, `my-ip-address`." + maxLength: 253 + minLength: 1 + type: string + required: + - value + type: object + maxItems: 16 + type: array + conditions: + default: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Accepted + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Programmed + description: "Conditions describe the current conditions of the Gateway. \n Implementations should prefer to express Gateway conditions using the `GatewayConditionType` and `GatewayConditionReason` constants so that operators and tools can converge on a common vocabulary to describe Gateway state. \n Known condition types are: \n * \"Accepted\" * \"Ready\"" + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + listeners: + description: Listeners provide status for each unique listener port defined in the Spec. + items: + description: ListenerStatus is the status associated with a Listener. + properties: + attachedRoutes: + description: AttachedRoutes represents the total number of Routes that have been successfully attached to this Listener. + format: int32 + type: integer + conditions: + description: Conditions describe the current condition of this listener. + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + name: + description: Name is the name of the Listener that this status corresponds to. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + supportedKinds: + description: "SupportedKinds is the list indicating the Kinds supported by this listener. This MUST represent the kinds an implementation supports for that Listener configuration. \n If kinds are specified in Spec that are not supported, they MUST NOT appear in this list and an implementation MUST set the \"ResolvedRefs\" condition to \"False\" with the \"InvalidRouteKinds\" reason. If both valid and invalid Route kinds are specified, the implementation MUST reference the valid Route kinds that have been specified." + items: + description: RouteGroupKind indicates the group and kind of a Route resource. + properties: + group: + default: gateway.networking.k8s.io + description: Group is the group of the Route. + 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 the kind of the Route. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + required: + - kind + type: object + maxItems: 8 + type: array + required: + - attachedRoutes + - conditions + - name + - supportedKinds + type: object + maxItems: 64 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/consul/crds/gateways.gateway.networking.k8s.io.yaml.yml b/charts/consul/crds/gateways.gateway.networking.k8s.io.yaml.yml new file mode 100644 index 0000000000..b7a7c8a7d1 --- /dev/null +++ b/charts/consul/crds/gateways.gateway.networking.k8s.io.yaml.yml @@ -0,0 +1,877 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + name: gateways.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: Gateway + listKind: GatewayList + plural: gateways + shortNames: + - gtw + singular: gateway + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.gatewayClassName + name: Class + type: string + - jsonPath: .status.addresses[*].value + name: Address + type: string + - jsonPath: .status.conditions[?(@.type=="Programmed")].status + name: Programmed + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: The v1alpha2 version of Gateway has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1. + name: v1alpha2 + schema: + openAPIV3Schema: + description: Gateway represents an instance of a service-traffic handling infrastructure by binding Listeners to a set of IP addresses. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of Gateway. + properties: + addresses: + description: "Addresses requested for this Gateway. This is optional and behavior can depend on the implementation. If a value is set in the spec and the requested address is invalid or unavailable, the implementation MUST indicate this in the associated entry in GatewayStatus.Addresses. \n The Addresses field represents a request for the address(es) on the \"outside of the Gateway\", that traffic bound for this Gateway will use. This could be the IP address or hostname of an external load balancer or other networking infrastructure, or some other address that traffic will be sent to. \n The .listener.hostname field is used to route traffic that has already arrived at the Gateway to the correct in-cluster destination. \n If no Addresses are specified, the implementation MAY schedule the Gateway in an implementation-specific manner, assigning an appropriate set of Addresses. \n The implementation MUST bind all Listeners to every GatewayAddress that it assigns to the Gateway and add a corresponding entry in GatewayStatus.Addresses. \n Support: Extended" + items: + description: GatewayAddress describes an address that can be bound to a Gateway. + properties: + type: + default: IPAddress + description: Type of the address. + maxLength: 253 + minLength: 1 + pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + value: + description: "Value of the address. The validity of the values will depend on the type and support by the controller. \n Examples: `1.2.3.4`, `128::1`, `my-ip-address`." + maxLength: 253 + minLength: 1 + type: string + required: + - value + type: object + maxItems: 16 + type: array + gatewayClassName: + description: GatewayClassName used for this Gateway. This is the name of a GatewayClass resource. + maxLength: 253 + minLength: 1 + type: string + listeners: + description: "Listeners associated with this Gateway. Listeners define logical endpoints that are bound on this Gateway's addresses. At least one Listener MUST be specified. \n Each listener in a Gateway must have a unique combination of Hostname, Port, and Protocol. \n An implementation MAY group Listeners by Port and then collapse each group of Listeners into a single Listener if the implementation determines that the Listeners in the group are \"compatible\". An implementation MAY also group together and collapse compatible Listeners belonging to different Gateways. \n For example, an implementation might consider Listeners to be compatible with each other if all of the following conditions are met: \n 1. Either each Listener within the group specifies the \"HTTP\" Protocol or each Listener within the group specifies either the \"HTTPS\" or \"TLS\" Protocol. \n 2. Each Listener within the group specifies a Hostname that is unique within the group. \n 3. As a special case, one Listener within a group may omit Hostname, in which case this Listener matches when no other Listener matches. \n If the implementation does collapse compatible Listeners, the hostname provided in the incoming client request MUST be matched to a Listener to find the correct set of Routes. The incoming hostname MUST be matched using the Hostname field for each Listener in order of most to least specific. That is, exact matches must be processed before wildcard matches. \n If this field specifies multiple Listeners that have the same Port value but are not compatible, the implementation must raise a \"Conflicted\" condition in the Listener status. \n Support: Core" + items: + description: Listener embodies the concept of a logical endpoint where a Gateway accepts network connections. + properties: + allowedRoutes: + default: + namespaces: + from: Same + description: "AllowedRoutes defines the types of routes that MAY be attached to a Listener and the trusted namespaces where those Route resources MAY be present. \n Although a client request may match multiple route rules, only one rule may ultimately receive the request. Matching precedence MUST be determined in order of the following criteria: \n * The most specific match as defined by the Route type. * The oldest Route based on creation timestamp. For example, a Route with a creation timestamp of \"2020-09-08 01:02:03\" is given precedence over a Route with a creation timestamp of \"2020-09-08 01:02:04\". * If everything else is equivalent, the Route appearing first in alphabetical order (namespace/name) should be given precedence. For example, foo/bar is given precedence over foo/baz. \n All valid rules within a Route attached to this Listener should be implemented. Invalid Route rules can be ignored (sometimes that will mean the full Route). If a Route rule transitions from valid to invalid, support for that Route rule should be dropped to ensure consistency. For example, even if a filter specified by a Route rule is invalid, the rest of the rules within that Route should still be supported. \n Support: Core" + properties: + kinds: + description: "Kinds specifies the groups and kinds of Routes that are allowed to bind to this Gateway Listener. When unspecified or empty, the kinds of Routes selected are determined using the Listener protocol. \n A RouteGroupKind MUST correspond to kinds of Routes that are compatible with the application protocol specified in the Listener's Protocol field. If an implementation does not support or recognize this resource type, it MUST set the \"ResolvedRefs\" condition to False for this Listener with the \"InvalidRouteKinds\" reason. \n Support: Core" + items: + description: RouteGroupKind indicates the group and kind of a Route resource. + properties: + group: + default: gateway.networking.k8s.io + description: Group is the group of the Route. + 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 the kind of the Route. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + required: + - kind + type: object + maxItems: 8 + type: array + namespaces: + default: + from: Same + description: "Namespaces indicates namespaces from which Routes may be attached to this Listener. This is restricted to the namespace of this Gateway by default. \n Support: Core" + properties: + from: + default: Same + description: "From indicates where Routes will be selected for this Gateway. Possible values are: * All: Routes in all namespaces may be used by this Gateway. * Selector: Routes in namespaces selected by the selector may be used by this Gateway. * Same: Only Routes in the same namespace may be used by this Gateway. \n Support: Core" + enum: + - All + - Selector + - Same + type: string + selector: + description: "Selector must be specified when From is set to \"Selector\". In that case, only Routes in Namespaces matching this Selector will be selected by this Gateway. This field is ignored for other values of \"From\". \n Support: Core" + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + type: object + type: object + hostname: + description: "Hostname specifies the virtual hostname to match for protocol types that define this concept. When unspecified, all hostnames are matched. This field is ignored for protocols that don't require hostname based matching. \n Implementations MUST apply Hostname matching appropriately for each of the following protocols: \n * TLS: The Listener Hostname MUST match the SNI. * HTTP: The Listener Hostname MUST match the Host header of the request. * HTTPS: The Listener Hostname SHOULD match at both the TLS and HTTP protocol layers as described above. If an implementation does not ensure that both the SNI and Host header match the Listener hostname, it MUST clearly document that. \n For HTTPRoute and TLSRoute resources, there is an interaction with the `spec.hostnames` array. When both listener and route specify hostnames, there MUST be an intersection between the values for a Route to be accepted. For more information, refer to the Route specific Hostnames documentation. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + name: + description: "Name is the name of the Listener. This name MUST be unique within a Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + port: + description: "Port is the network port. Multiple listeners may use the same port, subject to the Listener compatibility rules. \n Support: Core" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + protocol: + description: "Protocol specifies the network protocol this listener expects to receive. \n Support: Core" + maxLength: 255 + minLength: 1 + pattern: ^[a-zA-Z0-9]([-a-zSA-Z0-9]*[a-zA-Z0-9])?$|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9]+$ + type: string + tls: + description: "TLS is the TLS configuration for the Listener. This field is required if the Protocol field is \"HTTPS\" or \"TLS\". It is invalid to set this field if the Protocol field is \"HTTP\", \"TCP\", or \"UDP\". \n The association of SNIs to Certificate defined in GatewayTLSConfig is defined based on the Hostname field for this listener. \n The GatewayClass MUST use the longest matching SNI out of all available certificates for any TLS handshake. \n Support: Core" + properties: + certificateRefs: + description: "CertificateRefs contains a series of references to Kubernetes objects that contains TLS certificates and private keys. These certificates are used to establish a TLS handshake for requests that match the hostname of the associated listener. \n A single CertificateRef to a Kubernetes Secret has \"Core\" support. Implementations MAY choose to support attaching multiple certificates to a Listener, but this behavior is implementation-specific. \n References to a resource in different namespace are invalid UNLESS there is a ReferenceGrant in the target namespace that allows the certificate to be attached. If a ReferenceGrant does not allow this reference, the \"ResolvedRefs\" condition MUST be set to False for this listener with the \"RefNotPermitted\" reason. \n This field is required to have at least one element when the mode is set to \"Terminate\" (default) and is optional otherwise. \n CertificateRefs can reference to standard Kubernetes resources, i.e. Secret, or implementation-specific custom resources. \n Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls \n Support: Implementation-specific (More than one reference or other resource types)" + items: + description: "SecretObjectReference identifies an API object including its namespace, defaulting to Secret. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. \n References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object." + properties: + group: + default: "" + 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: + default: Secret + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - name + type: object + maxItems: 64 + type: array + mode: + default: Terminate + description: "Mode defines the TLS behavior for the TLS session initiated by the client. There are two possible modes: \n - Terminate: The TLS session between the downstream client and the Gateway is terminated at the Gateway. This mode requires certificateRefs to be set and contain at least one element. - Passthrough: The TLS session is NOT terminated by the Gateway. This implies that the Gateway can't decipher the TLS stream except for the ClientHello message of the TLS protocol. CertificateRefs field is ignored in this mode. \n Support: Core" + enum: + - Terminate + - Passthrough + type: string + options: + additionalProperties: + description: AnnotationValue is the value of an annotation in Gateway API. This is used for validation of maps such as TLS options. This roughly matches Kubernetes annotation validation, although the length validation in that case is based on the entire size of the annotations struct. + maxLength: 4096 + minLength: 0 + type: string + description: "Options are a list of key/value pairs to enable extended TLS configuration for each implementation. For example, configuring the minimum TLS version or supported cipher suites. \n A set of common keys MAY be defined by the API in the future. To avoid any ambiguity, implementation-specific definitions MUST use domain-prefixed names, such as `example.com/my-custom-option`. Un-prefixed names are reserved for key names defined by Gateway API. \n Support: Implementation-specific" + maxProperties: 16 + type: object + type: object + required: + - name + - port + - protocol + type: object + maxItems: 64 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + required: + - gatewayClassName + - listeners + type: object + status: + default: + conditions: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: NotReconciled + status: Unknown + type: Accepted + description: Status defines the current state of Gateway. + properties: + addresses: + description: Addresses lists the IP addresses that have actually been bound to the Gateway. These addresses may differ from the addresses in the Spec, e.g. if the Gateway automatically assigns an address from a reserved pool. + items: + description: GatewayAddress describes an address that can be bound to a Gateway. + properties: + type: + default: IPAddress + description: Type of the address. + maxLength: 253 + minLength: 1 + pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + value: + description: "Value of the address. The validity of the values will depend on the type and support by the controller. \n Examples: `1.2.3.4`, `128::1`, `my-ip-address`." + maxLength: 253 + minLength: 1 + type: string + required: + - value + type: object + maxItems: 16 + type: array + conditions: + default: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Accepted + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Programmed + description: "Conditions describe the current conditions of the Gateway. \n Implementations should prefer to express Gateway conditions using the `GatewayConditionType` and `GatewayConditionReason` constants so that operators and tools can converge on a common vocabulary to describe Gateway state. \n Known condition types are: \n * \"Accepted\" * \"Ready\"" + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + listeners: + description: Listeners provide status for each unique listener port defined in the Spec. + items: + description: ListenerStatus is the status associated with a Listener. + properties: + attachedRoutes: + description: AttachedRoutes represents the total number of Routes that have been successfully attached to this Listener. + format: int32 + type: integer + conditions: + description: Conditions describe the current condition of this listener. + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + name: + description: Name is the name of the Listener that this status corresponds to. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + supportedKinds: + description: "SupportedKinds is the list indicating the Kinds supported by this listener. This MUST represent the kinds an implementation supports for that Listener configuration. \n If kinds are specified in Spec that are not supported, they MUST NOT appear in this list and an implementation MUST set the \"ResolvedRefs\" condition to \"False\" with the \"InvalidRouteKinds\" reason. If both valid and invalid Route kinds are specified, the implementation MUST reference the valid Route kinds that have been specified." + items: + description: RouteGroupKind indicates the group and kind of a Route resource. + properties: + group: + default: gateway.networking.k8s.io + description: Group is the group of the Route. + 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 the kind of the Route. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + required: + - kind + type: object + maxItems: 8 + type: array + required: + - attachedRoutes + - conditions + - name + - supportedKinds + type: object + maxItems: 64 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.gatewayClassName + name: Class + type: string + - jsonPath: .status.addresses[*].value + name: Address + type: string + - jsonPath: .status.conditions[?(@.type=="Programmed")].status + name: Programmed + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: Gateway represents an instance of a service-traffic handling infrastructure by binding Listeners to a set of IP addresses. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of Gateway. + properties: + addresses: + description: "Addresses requested for this Gateway. This is optional and behavior can depend on the implementation. If a value is set in the spec and the requested address is invalid or unavailable, the implementation MUST indicate this in the associated entry in GatewayStatus.Addresses. \n The Addresses field represents a request for the address(es) on the \"outside of the Gateway\", that traffic bound for this Gateway will use. This could be the IP address or hostname of an external load balancer or other networking infrastructure, or some other address that traffic will be sent to. \n The .listener.hostname field is used to route traffic that has already arrived at the Gateway to the correct in-cluster destination. \n If no Addresses are specified, the implementation MAY schedule the Gateway in an implementation-specific manner, assigning an appropriate set of Addresses. \n The implementation MUST bind all Listeners to every GatewayAddress that it assigns to the Gateway and add a corresponding entry in GatewayStatus.Addresses. \n Support: Extended" + items: + description: GatewayAddress describes an address that can be bound to a Gateway. + properties: + type: + default: IPAddress + description: Type of the address. + maxLength: 253 + minLength: 1 + pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + value: + description: "Value of the address. The validity of the values will depend on the type and support by the controller. \n Examples: `1.2.3.4`, `128::1`, `my-ip-address`." + maxLength: 253 + minLength: 1 + type: string + required: + - value + type: object + maxItems: 16 + type: array + gatewayClassName: + description: GatewayClassName used for this Gateway. This is the name of a GatewayClass resource. + maxLength: 253 + minLength: 1 + type: string + listeners: + description: "Listeners associated with this Gateway. Listeners define logical endpoints that are bound on this Gateway's addresses. At least one Listener MUST be specified. \n Each listener in a Gateway must have a unique combination of Hostname, Port, and Protocol. \n An implementation MAY group Listeners by Port and then collapse each group of Listeners into a single Listener if the implementation determines that the Listeners in the group are \"compatible\". An implementation MAY also group together and collapse compatible Listeners belonging to different Gateways. \n For example, an implementation might consider Listeners to be compatible with each other if all of the following conditions are met: \n 1. Either each Listener within the group specifies the \"HTTP\" Protocol or each Listener within the group specifies either the \"HTTPS\" or \"TLS\" Protocol. \n 2. Each Listener within the group specifies a Hostname that is unique within the group. \n 3. As a special case, one Listener within a group may omit Hostname, in which case this Listener matches when no other Listener matches. \n If the implementation does collapse compatible Listeners, the hostname provided in the incoming client request MUST be matched to a Listener to find the correct set of Routes. The incoming hostname MUST be matched using the Hostname field for each Listener in order of most to least specific. That is, exact matches must be processed before wildcard matches. \n If this field specifies multiple Listeners that have the same Port value but are not compatible, the implementation must raise a \"Conflicted\" condition in the Listener status. \n Support: Core" + items: + description: Listener embodies the concept of a logical endpoint where a Gateway accepts network connections. + properties: + allowedRoutes: + default: + namespaces: + from: Same + description: "AllowedRoutes defines the types of routes that MAY be attached to a Listener and the trusted namespaces where those Route resources MAY be present. \n Although a client request may match multiple route rules, only one rule may ultimately receive the request. Matching precedence MUST be determined in order of the following criteria: \n * The most specific match as defined by the Route type. * The oldest Route based on creation timestamp. For example, a Route with a creation timestamp of \"2020-09-08 01:02:03\" is given precedence over a Route with a creation timestamp of \"2020-09-08 01:02:04\". * If everything else is equivalent, the Route appearing first in alphabetical order (namespace/name) should be given precedence. For example, foo/bar is given precedence over foo/baz. \n All valid rules within a Route attached to this Listener should be implemented. Invalid Route rules can be ignored (sometimes that will mean the full Route). If a Route rule transitions from valid to invalid, support for that Route rule should be dropped to ensure consistency. For example, even if a filter specified by a Route rule is invalid, the rest of the rules within that Route should still be supported. \n Support: Core" + properties: + kinds: + description: "Kinds specifies the groups and kinds of Routes that are allowed to bind to this Gateway Listener. When unspecified or empty, the kinds of Routes selected are determined using the Listener protocol. \n A RouteGroupKind MUST correspond to kinds of Routes that are compatible with the application protocol specified in the Listener's Protocol field. If an implementation does not support or recognize this resource type, it MUST set the \"ResolvedRefs\" condition to False for this Listener with the \"InvalidRouteKinds\" reason. \n Support: Core" + items: + description: RouteGroupKind indicates the group and kind of a Route resource. + properties: + group: + default: gateway.networking.k8s.io + description: Group is the group of the Route. + 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 the kind of the Route. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + required: + - kind + type: object + maxItems: 8 + type: array + namespaces: + default: + from: Same + description: "Namespaces indicates namespaces from which Routes may be attached to this Listener. This is restricted to the namespace of this Gateway by default. \n Support: Core" + properties: + from: + default: Same + description: "From indicates where Routes will be selected for this Gateway. Possible values are: * All: Routes in all namespaces may be used by this Gateway. * Selector: Routes in namespaces selected by the selector may be used by this Gateway. * Same: Only Routes in the same namespace may be used by this Gateway. \n Support: Core" + enum: + - All + - Selector + - Same + type: string + selector: + description: "Selector must be specified when From is set to \"Selector\". In that case, only Routes in Namespaces matching this Selector will be selected by this Gateway. This field is ignored for other values of \"From\". \n Support: Core" + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + type: object + type: object + hostname: + description: "Hostname specifies the virtual hostname to match for protocol types that define this concept. When unspecified, all hostnames are matched. This field is ignored for protocols that don't require hostname based matching. \n Implementations MUST apply Hostname matching appropriately for each of the following protocols: \n * TLS: The Listener Hostname MUST match the SNI. * HTTP: The Listener Hostname MUST match the Host header of the request. * HTTPS: The Listener Hostname SHOULD match at both the TLS and HTTP protocol layers as described above. If an implementation does not ensure that both the SNI and Host header match the Listener hostname, it MUST clearly document that. \n For HTTPRoute and TLSRoute resources, there is an interaction with the `spec.hostnames` array. When both listener and route specify hostnames, there MUST be an intersection between the values for a Route to be accepted. For more information, refer to the Route specific Hostnames documentation. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + name: + description: "Name is the name of the Listener. This name MUST be unique within a Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + port: + description: "Port is the network port. Multiple listeners may use the same port, subject to the Listener compatibility rules. \n Support: Core" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + protocol: + description: "Protocol specifies the network protocol this listener expects to receive. \n Support: Core" + maxLength: 255 + minLength: 1 + pattern: ^[a-zA-Z0-9]([-a-zSA-Z0-9]*[a-zA-Z0-9])?$|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9]+$ + type: string + tls: + description: "TLS is the TLS configuration for the Listener. This field is required if the Protocol field is \"HTTPS\" or \"TLS\". It is invalid to set this field if the Protocol field is \"HTTP\", \"TCP\", or \"UDP\". \n The association of SNIs to Certificate defined in GatewayTLSConfig is defined based on the Hostname field for this listener. \n The GatewayClass MUST use the longest matching SNI out of all available certificates for any TLS handshake. \n Support: Core" + properties: + certificateRefs: + description: "CertificateRefs contains a series of references to Kubernetes objects that contains TLS certificates and private keys. These certificates are used to establish a TLS handshake for requests that match the hostname of the associated listener. \n A single CertificateRef to a Kubernetes Secret has \"Core\" support. Implementations MAY choose to support attaching multiple certificates to a Listener, but this behavior is implementation-specific. \n References to a resource in different namespace are invalid UNLESS there is a ReferenceGrant in the target namespace that allows the certificate to be attached. If a ReferenceGrant does not allow this reference, the \"ResolvedRefs\" condition MUST be set to False for this listener with the \"RefNotPermitted\" reason. \n This field is required to have at least one element when the mode is set to \"Terminate\" (default) and is optional otherwise. \n CertificateRefs can reference to standard Kubernetes resources, i.e. Secret, or implementation-specific custom resources. \n Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls \n Support: Implementation-specific (More than one reference or other resource types)" + items: + description: "SecretObjectReference identifies an API object including its namespace, defaulting to Secret. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. \n References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object." + properties: + group: + default: "" + 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: + default: Secret + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - name + type: object + maxItems: 64 + type: array + mode: + default: Terminate + description: "Mode defines the TLS behavior for the TLS session initiated by the client. There are two possible modes: \n - Terminate: The TLS session between the downstream client and the Gateway is terminated at the Gateway. This mode requires certificateRefs to be set and contain at least one element. - Passthrough: The TLS session is NOT terminated by the Gateway. This implies that the Gateway can't decipher the TLS stream except for the ClientHello message of the TLS protocol. CertificateRefs field is ignored in this mode. \n Support: Core" + enum: + - Terminate + - Passthrough + type: string + options: + additionalProperties: + description: AnnotationValue is the value of an annotation in Gateway API. This is used for validation of maps such as TLS options. This roughly matches Kubernetes annotation validation, although the length validation in that case is based on the entire size of the annotations struct. + maxLength: 4096 + minLength: 0 + type: string + description: "Options are a list of key/value pairs to enable extended TLS configuration for each implementation. For example, configuring the minimum TLS version or supported cipher suites. \n A set of common keys MAY be defined by the API in the future. To avoid any ambiguity, implementation-specific definitions MUST use domain-prefixed names, such as `example.com/my-custom-option`. Un-prefixed names are reserved for key names defined by Gateway API. \n Support: Implementation-specific" + maxProperties: 16 + type: object + type: object + required: + - name + - port + - protocol + type: object + maxItems: 64 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + required: + - gatewayClassName + - listeners + type: object + status: + default: + conditions: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: NotReconciled + status: Unknown + type: Accepted + description: Status defines the current state of Gateway. + properties: + addresses: + description: Addresses lists the IP addresses that have actually been bound to the Gateway. These addresses may differ from the addresses in the Spec, e.g. if the Gateway automatically assigns an address from a reserved pool. + items: + description: GatewayAddress describes an address that can be bound to a Gateway. + properties: + type: + default: IPAddress + description: Type of the address. + maxLength: 253 + minLength: 1 + pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + value: + description: "Value of the address. The validity of the values will depend on the type and support by the controller. \n Examples: `1.2.3.4`, `128::1`, `my-ip-address`." + maxLength: 253 + minLength: 1 + type: string + required: + - value + type: object + maxItems: 16 + type: array + conditions: + default: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Accepted + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Programmed + description: "Conditions describe the current conditions of the Gateway. \n Implementations should prefer to express Gateway conditions using the `GatewayConditionType` and `GatewayConditionReason` constants so that operators and tools can converge on a common vocabulary to describe Gateway state. \n Known condition types are: \n * \"Accepted\" * \"Ready\"" + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + listeners: + description: Listeners provide status for each unique listener port defined in the Spec. + items: + description: ListenerStatus is the status associated with a Listener. + properties: + attachedRoutes: + description: AttachedRoutes represents the total number of Routes that have been successfully attached to this Listener. + format: int32 + type: integer + conditions: + description: Conditions describe the current condition of this listener. + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + name: + description: Name is the name of the Listener that this status corresponds to. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + supportedKinds: + description: "SupportedKinds is the list indicating the Kinds supported by this listener. This MUST represent the kinds an implementation supports for that Listener configuration. \n If kinds are specified in Spec that are not supported, they MUST NOT appear in this list and an implementation MUST set the \"ResolvedRefs\" condition to \"False\" with the \"InvalidRouteKinds\" reason. If both valid and invalid Route kinds are specified, the implementation MUST reference the valid Route kinds that have been specified." + items: + description: RouteGroupKind indicates the group and kind of a Route resource. + properties: + group: + default: gateway.networking.k8s.io + description: Group is the group of the Route. + 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 the kind of the Route. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + required: + - kind + type: object + maxItems: 8 + type: array + required: + - attachedRoutes + - conditions + - name + - supportedKinds + type: object + maxItems: 64 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/consul/crds/grpcroutes.gateway.networking.k8s.io.yaml b/charts/consul/crds/grpcroutes.gateway.networking.k8s.io.yaml new file mode 100644 index 0000000000..8f3ab6d385 --- /dev/null +++ b/charts/consul/crds/grpcroutes.gateway.networking.k8s.io.yaml @@ -0,0 +1,761 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + name: grpcroutes.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: GRPCRoute + listKind: GRPCRouteList + plural: grpcroutes + singular: grpcroute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.hostnames + name: Hostnames + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: "GRPCRoute provides a way to route gRPC requests. This includes the capability to match requests by hostname, gRPC service, gRPC method, or HTTP/2 header. Filters can be used to specify additional processing steps. Backends specify where matching requests will be routed. \n GRPCRoute falls under extended support within the Gateway API. Within the following specification, the word \"MUST\" indicates that an implementation supporting GRPCRoute must conform to the indicated requirement, but an implementation not supporting this route type need not follow the requirement unless explicitly indicated. \n Implementations supporting `GRPCRoute` with the `HTTPS` `ProtocolType` MUST accept HTTP/2 connections without an initial upgrade from HTTP/1.1, i.e. via ALPN. If the implementation does not support this, then it MUST set the \"Accepted\" condition to \"False\" for the affected listener with a reason of \"UnsupportedProtocol\". Implementations MAY also accept HTTP/2 connections with an upgrade from HTTP/1. \n Implementations supporting `GRPCRoute` with the `HTTP` `ProtocolType` MUST support HTTP/2 over cleartext TCP (h2c, https://www.rfc-editor.org/rfc/rfc7540#section-3.1) without an initial upgrade from HTTP/1.1, i.e. with prior knowledge (https://www.rfc-editor.org/rfc/rfc7540#section-3.4). If the implementation does not support this, then it MUST set the \"Accepted\" condition to \"False\" for the affected listener with a reason of \"UnsupportedProtocol\". Implementations MAY also accept HTTP/2 connections with an upgrade from HTTP/1, i.e. without prior knowledge. \n Support: Extended" + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of GRPCRoute. + properties: + hostnames: + description: "Hostnames defines a set of hostnames to match against the GRPC Host header to select a GRPCRoute to process the request. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label MUST appear by itself as the first label. \n If a hostname is specified by both the Listener and GRPCRoute, there MUST be at least one intersecting hostname for the GRPCRoute to be attached to the Listener. For example: \n * A Listener with `test.example.com` as the hostname matches GRPCRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches GRPCRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `test.example.com` and `*.example.com` would both match. On the other hand, `example.com` and `test.example.net` would not match. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n If both the Listener and GRPCRoute have specified hostnames, any GRPCRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the GRPCRoute specified `test.example.com` and `test.example.net`, `test.example.net` MUST NOT be considered for a match. \n If both the Listener and GRPCRoute have specified hostnames, and none match with the criteria above, then the GRPCRoute MUST NOT be accepted by the implementation. The implementation MUST raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. \n If a Route (A) of type HTTPRoute or GRPCRoute is attached to a Listener and that listener already has another Route (B) of the other type attached and the intersection of the hostnames of A and B is non-empty, then the implementation MUST accept exactly one of these two routes, determined by the following criteria, in order: \n * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n The rejected Route MUST raise an 'Accepted' condition with a status of 'False' in the corresponding RouteParentStatus. \n Support: Core" + items: + description: "Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n Hostname can be \"precise\" which is a domain name without the terminating dot of a network host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). \n Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed." + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + type: array + parentRefs: + description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." + items: + description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + rules: + default: + - matches: + - method: + type: Exact + description: Rules are a list of GRPC matchers, filters and actions. + items: + description: GRPCRouteRule defines the semantics for matching an gRPC request based on conditions (matches), processing it (filters), and forwarding the request to an API object (backendRefs). + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching requests should be sent. \n Failure behavior here depends on how many BackendRefs are specified and how many are invalid. \n If *all* entries in BackendRefs are invalid, and there are also no filters specified in this route rule, *all* traffic which matches this rule MUST receive an `UNAVAILABLE` status. \n See the GRPCBackendRef definition for the rules about what makes a single GRPCBackendRef invalid. \n When a GRPCBackendRef is invalid, `UNAVAILABLE` statuses MUST be returned for requests that would have otherwise been routed to an invalid backend. If multiple backends are specified, and some are invalid, the proportion of requests that would otherwise have been routed to an invalid backend MUST receive an `UNAVAILABLE` status. \n For example, if two backends are specified with equal weights, and one is invalid, 50 percent of traffic MUST receive an `UNAVAILABLE` status. Implementations may choose how that 50 percent is determined. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Core" + items: + description: GRPCBackendRef defines how a GRPCRoute forwards a gRPC request. + properties: + filters: + description: "Filters defined at this level MUST be executed if and only if the request is being forwarded to the backend defined here. \n Support: Implementation-specific (For broader support of filters, use the Filters field in GRPCRouteRule.)" + items: + description: GRPCRouteFilter defines processing steps that must be completed during the request or response lifecycle. GRPCRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" + 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 + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" + properties: + add: + description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" + properties: + group: + default: "" + 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: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " + properties: + add: + description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations supporting GRPCRoute MUST support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` MUST be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n " + enum: + - ResponseHeaderModifier + - RequestHeaderModifier + - RequestMirror + - ExtensionRef + type: string + required: + - type + type: object + maxItems: 16 + type: array + group: + default: "" + 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: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support for this field varies based on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + type: array + filters: + description: "Filters define the filters that are applied to requests that match this rule. \n The effects of ordering of multiple behaviors are currently unspecified. This can change in the future based on feedback during the alpha stage. \n Conformance-levels at this level are defined based on the type of filter: \n - ALL core filters MUST be supported by all implementations that support GRPCRoute. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. \n Specifying a core filter multiple times has unspecified or implementation-specific conformance. Support: Core" + items: + description: GRPCRouteFilter defines processing steps that must be completed during the request or response lifecycle. GRPCRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" + 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 + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" + properties: + add: + description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" + properties: + group: + default: "" + 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: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " + properties: + add: + description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations supporting GRPCRoute MUST support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` MUST be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n " + enum: + - ResponseHeaderModifier + - RequestHeaderModifier + - RequestMirror + - ExtensionRef + type: string + required: + - type + type: object + maxItems: 16 + type: array + matches: + default: + - method: + type: Exact + description: "Matches define conditions used for matching the rule against incoming gRPC requests. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied. \n For example, take the following matches configuration: \n ``` matches: - method: service: foo.bar headers: values: version: 2 - method: service: foo.bar.v2 ``` \n For a request to match against this rule, it MUST satisfy EITHER of the two conditions: \n - service of foo.bar AND contains the header `version: 2` - service of foo.bar.v2 \n See the documentation for GRPCRouteMatch on how to specify multiple match conditions to be ANDed together. \n If no matches are specified, the implementation MUST match every gRPC request. \n Proxy or Load Balancer routing configuration generated from GRPCRoutes MUST prioritize rules based on the following criteria, continuing on ties. Merging MUST not be done between GRPCRoutes and HTTPRoutes. Precedence MUST be given to the rule with the largest number of: \n * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. * Characters in a matching service. * Characters in a matching method. * Header matches. \n If ties still exist across multiple Routes, matching precedence MUST be determined in order of the following criteria, continuing on ties: \n * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n If ties still exist within the Route that has been given precedence, matching precedence MUST be granted to the first matching rule meeting the above criteria." + items: + description: "GRPCRouteMatch defines the predicate used to match requests to a given action. Multiple match types are ANDed together, i.e. the match will evaluate to true only if all conditions are satisfied. \n For example, the match below will match a gRPC request only if its service is `foo` AND it contains the `version: v1` header: \n ``` matches: - method: type: Exact service: \"foo\" headers: - name: \"version\" value \"v1\" \n ```" + properties: + headers: + description: Headers specifies gRPC request header matchers. Multiple match values are ANDed together, meaning, a request MUST match all the specified headers to select the route. + items: + description: GRPCHeaderMatch describes how to select a gRPC route by matching gRPC request headers. + properties: + name: + description: "Name is the name of the gRPC Header to be matched. \n If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: Type specifies how to match against the value of the header. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of the gRPC Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + method: + default: + type: Exact + description: Method specifies a gRPC request service/method matcher. If this field is not specified, all services and methods will match. + properties: + method: + description: "Value of the method to match against. If left empty or omitted, will match all services. \n At least one of Service and Method MUST be a non-empty string. \n A GRPC Method must be a valid Protobuf Method (https://protobuf.com/docs/language-spec#methods)." + maxLength: 1024 + pattern: ^[A-Za-z_][A-Za-z_0-9]*$ + type: string + service: + description: "Value of the service to match against. If left empty or omitted, will match any service. \n At least one of Service and Method MUST be a non-empty string. \n A GRPC Service must be a valid Protobuf Type Name (https://protobuf.com/docs/language-spec#type-references)." + maxLength: 1024 + pattern: ^(?i)\.?[a-z_][a-z_0-9]*(\.[a-z_][a-z_0-9]*)*$ + type: string + type: + default: Exact + description: "Type specifies how to match against the service and/or method. Support: Core (Exact with service and method specified) \n Support: Implementation-specific (Exact with method specified but no service specified) \n Support: Implementation-specific (RegularExpression)" + enum: + - Exact + - RegularExpression + type: string + type: object + type: object + maxItems: 8 + type: array + type: object + maxItems: 16 + type: array + type: object + status: + description: Status defines the current state of GRPCRoute. + properties: + parents: + description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." + items: + description: RouteParentStatus describes the status of a route with respect to an associated Parent. + properties: + conditions: + description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/gateway-controller\". \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/consul/crds/grpcroutes.gateway.networking.k8s.io.yaml.yml b/charts/consul/crds/grpcroutes.gateway.networking.k8s.io.yaml.yml new file mode 100644 index 0000000000..8f3ab6d385 --- /dev/null +++ b/charts/consul/crds/grpcroutes.gateway.networking.k8s.io.yaml.yml @@ -0,0 +1,761 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + name: grpcroutes.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: GRPCRoute + listKind: GRPCRouteList + plural: grpcroutes + singular: grpcroute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.hostnames + name: Hostnames + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: "GRPCRoute provides a way to route gRPC requests. This includes the capability to match requests by hostname, gRPC service, gRPC method, or HTTP/2 header. Filters can be used to specify additional processing steps. Backends specify where matching requests will be routed. \n GRPCRoute falls under extended support within the Gateway API. Within the following specification, the word \"MUST\" indicates that an implementation supporting GRPCRoute must conform to the indicated requirement, but an implementation not supporting this route type need not follow the requirement unless explicitly indicated. \n Implementations supporting `GRPCRoute` with the `HTTPS` `ProtocolType` MUST accept HTTP/2 connections without an initial upgrade from HTTP/1.1, i.e. via ALPN. If the implementation does not support this, then it MUST set the \"Accepted\" condition to \"False\" for the affected listener with a reason of \"UnsupportedProtocol\". Implementations MAY also accept HTTP/2 connections with an upgrade from HTTP/1. \n Implementations supporting `GRPCRoute` with the `HTTP` `ProtocolType` MUST support HTTP/2 over cleartext TCP (h2c, https://www.rfc-editor.org/rfc/rfc7540#section-3.1) without an initial upgrade from HTTP/1.1, i.e. with prior knowledge (https://www.rfc-editor.org/rfc/rfc7540#section-3.4). If the implementation does not support this, then it MUST set the \"Accepted\" condition to \"False\" for the affected listener with a reason of \"UnsupportedProtocol\". Implementations MAY also accept HTTP/2 connections with an upgrade from HTTP/1, i.e. without prior knowledge. \n Support: Extended" + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of GRPCRoute. + properties: + hostnames: + description: "Hostnames defines a set of hostnames to match against the GRPC Host header to select a GRPCRoute to process the request. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label MUST appear by itself as the first label. \n If a hostname is specified by both the Listener and GRPCRoute, there MUST be at least one intersecting hostname for the GRPCRoute to be attached to the Listener. For example: \n * A Listener with `test.example.com` as the hostname matches GRPCRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches GRPCRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `test.example.com` and `*.example.com` would both match. On the other hand, `example.com` and `test.example.net` would not match. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n If both the Listener and GRPCRoute have specified hostnames, any GRPCRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the GRPCRoute specified `test.example.com` and `test.example.net`, `test.example.net` MUST NOT be considered for a match. \n If both the Listener and GRPCRoute have specified hostnames, and none match with the criteria above, then the GRPCRoute MUST NOT be accepted by the implementation. The implementation MUST raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. \n If a Route (A) of type HTTPRoute or GRPCRoute is attached to a Listener and that listener already has another Route (B) of the other type attached and the intersection of the hostnames of A and B is non-empty, then the implementation MUST accept exactly one of these two routes, determined by the following criteria, in order: \n * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n The rejected Route MUST raise an 'Accepted' condition with a status of 'False' in the corresponding RouteParentStatus. \n Support: Core" + items: + description: "Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n Hostname can be \"precise\" which is a domain name without the terminating dot of a network host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). \n Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed." + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + type: array + parentRefs: + description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." + items: + description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + rules: + default: + - matches: + - method: + type: Exact + description: Rules are a list of GRPC matchers, filters and actions. + items: + description: GRPCRouteRule defines the semantics for matching an gRPC request based on conditions (matches), processing it (filters), and forwarding the request to an API object (backendRefs). + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching requests should be sent. \n Failure behavior here depends on how many BackendRefs are specified and how many are invalid. \n If *all* entries in BackendRefs are invalid, and there are also no filters specified in this route rule, *all* traffic which matches this rule MUST receive an `UNAVAILABLE` status. \n See the GRPCBackendRef definition for the rules about what makes a single GRPCBackendRef invalid. \n When a GRPCBackendRef is invalid, `UNAVAILABLE` statuses MUST be returned for requests that would have otherwise been routed to an invalid backend. If multiple backends are specified, and some are invalid, the proportion of requests that would otherwise have been routed to an invalid backend MUST receive an `UNAVAILABLE` status. \n For example, if two backends are specified with equal weights, and one is invalid, 50 percent of traffic MUST receive an `UNAVAILABLE` status. Implementations may choose how that 50 percent is determined. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Core" + items: + description: GRPCBackendRef defines how a GRPCRoute forwards a gRPC request. + properties: + filters: + description: "Filters defined at this level MUST be executed if and only if the request is being forwarded to the backend defined here. \n Support: Implementation-specific (For broader support of filters, use the Filters field in GRPCRouteRule.)" + items: + description: GRPCRouteFilter defines processing steps that must be completed during the request or response lifecycle. GRPCRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" + 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 + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" + properties: + add: + description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" + properties: + group: + default: "" + 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: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " + properties: + add: + description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations supporting GRPCRoute MUST support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` MUST be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n " + enum: + - ResponseHeaderModifier + - RequestHeaderModifier + - RequestMirror + - ExtensionRef + type: string + required: + - type + type: object + maxItems: 16 + type: array + group: + default: "" + 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: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support for this field varies based on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + type: array + filters: + description: "Filters define the filters that are applied to requests that match this rule. \n The effects of ordering of multiple behaviors are currently unspecified. This can change in the future based on feedback during the alpha stage. \n Conformance-levels at this level are defined based on the type of filter: \n - ALL core filters MUST be supported by all implementations that support GRPCRoute. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. \n Specifying a core filter multiple times has unspecified or implementation-specific conformance. Support: Core" + items: + description: GRPCRouteFilter defines processing steps that must be completed during the request or response lifecycle. GRPCRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" + 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 + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" + properties: + add: + description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" + properties: + group: + default: "" + 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: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " + properties: + add: + description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations supporting GRPCRoute MUST support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` MUST be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n " + enum: + - ResponseHeaderModifier + - RequestHeaderModifier + - RequestMirror + - ExtensionRef + type: string + required: + - type + type: object + maxItems: 16 + type: array + matches: + default: + - method: + type: Exact + description: "Matches define conditions used for matching the rule against incoming gRPC requests. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied. \n For example, take the following matches configuration: \n ``` matches: - method: service: foo.bar headers: values: version: 2 - method: service: foo.bar.v2 ``` \n For a request to match against this rule, it MUST satisfy EITHER of the two conditions: \n - service of foo.bar AND contains the header `version: 2` - service of foo.bar.v2 \n See the documentation for GRPCRouteMatch on how to specify multiple match conditions to be ANDed together. \n If no matches are specified, the implementation MUST match every gRPC request. \n Proxy or Load Balancer routing configuration generated from GRPCRoutes MUST prioritize rules based on the following criteria, continuing on ties. Merging MUST not be done between GRPCRoutes and HTTPRoutes. Precedence MUST be given to the rule with the largest number of: \n * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. * Characters in a matching service. * Characters in a matching method. * Header matches. \n If ties still exist across multiple Routes, matching precedence MUST be determined in order of the following criteria, continuing on ties: \n * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n If ties still exist within the Route that has been given precedence, matching precedence MUST be granted to the first matching rule meeting the above criteria." + items: + description: "GRPCRouteMatch defines the predicate used to match requests to a given action. Multiple match types are ANDed together, i.e. the match will evaluate to true only if all conditions are satisfied. \n For example, the match below will match a gRPC request only if its service is `foo` AND it contains the `version: v1` header: \n ``` matches: - method: type: Exact service: \"foo\" headers: - name: \"version\" value \"v1\" \n ```" + properties: + headers: + description: Headers specifies gRPC request header matchers. Multiple match values are ANDed together, meaning, a request MUST match all the specified headers to select the route. + items: + description: GRPCHeaderMatch describes how to select a gRPC route by matching gRPC request headers. + properties: + name: + description: "Name is the name of the gRPC Header to be matched. \n If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: Type specifies how to match against the value of the header. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of the gRPC Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + method: + default: + type: Exact + description: Method specifies a gRPC request service/method matcher. If this field is not specified, all services and methods will match. + properties: + method: + description: "Value of the method to match against. If left empty or omitted, will match all services. \n At least one of Service and Method MUST be a non-empty string. \n A GRPC Method must be a valid Protobuf Method (https://protobuf.com/docs/language-spec#methods)." + maxLength: 1024 + pattern: ^[A-Za-z_][A-Za-z_0-9]*$ + type: string + service: + description: "Value of the service to match against. If left empty or omitted, will match any service. \n At least one of Service and Method MUST be a non-empty string. \n A GRPC Service must be a valid Protobuf Type Name (https://protobuf.com/docs/language-spec#type-references)." + maxLength: 1024 + pattern: ^(?i)\.?[a-z_][a-z_0-9]*(\.[a-z_][a-z_0-9]*)*$ + type: string + type: + default: Exact + description: "Type specifies how to match against the service and/or method. Support: Core (Exact with service and method specified) \n Support: Implementation-specific (Exact with method specified but no service specified) \n Support: Implementation-specific (RegularExpression)" + enum: + - Exact + - RegularExpression + type: string + type: object + type: object + maxItems: 8 + type: array + type: object + maxItems: 16 + type: array + type: object + status: + description: Status defines the current state of GRPCRoute. + properties: + parents: + description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." + items: + description: RouteParentStatus describes the status of a route with respect to an associated Parent. + properties: + conditions: + description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/gateway-controller\". \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/consul/crds/httproutes.gateway.networking.k8s.io.yaml b/charts/consul/crds/httproutes.gateway.networking.k8s.io.yaml new file mode 100644 index 0000000000..b455d788de --- /dev/null +++ b/charts/consul/crds/httproutes.gateway.networking.k8s.io.yaml @@ -0,0 +1,1909 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + name: httproutes.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: HTTPRoute + listKind: HTTPRouteList + plural: httproutes + singular: httproute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.hostnames + name: Hostnames + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: The v1alpha2 version of HTTPRoute has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1. + name: v1alpha2 + schema: + openAPIV3Schema: + description: HTTPRoute provides a way to route HTTP requests. This includes the capability to match requests by hostname, path, header, or query param. Filters can be used to specify additional processing steps. Backends specify where matching requests should be routed. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of HTTPRoute. + properties: + hostnames: + description: "Hostnames defines a set of hostname that should match against the HTTP Host header to select a HTTPRoute to process the request. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n If a hostname is specified by both the Listener and HTTPRoute, there must be at least one intersecting hostname for the HTTPRoute to be attached to the Listener. For example: \n * A Listener with `test.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `*.example.com`, `test.example.com`, and `foo.test.example.com` would all match. On the other hand, `example.com` and `test.example.net` would not match. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n If both the Listener and HTTPRoute have specified hostnames, any HTTPRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the HTTPRoute specified `test.example.com` and `test.example.net`, `test.example.net` must not be considered for a match. \n If both the Listener and HTTPRoute have specified hostnames, and none match with the criteria above, then the HTTPRoute is not accepted. The implementation must raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. \n In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. overlapping wildcard matching and exact matching hostnames), precedence must be given to rules from the HTTPRoute with the largest number of: \n * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. \n If ties exist across multiple Routes, the matching precedence rules for HTTPRouteMatches takes over. \n Support: Core" + items: + description: "Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n Hostname can be \"precise\" which is a domain name without the terminating dot of a network host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). \n Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed." + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + type: array + parentRefs: + description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." + items: + description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + rules: + default: + - matches: + - path: + type: PathPrefix + value: / + description: Rules are a list of HTTP matchers, filters and actions. + items: + description: HTTPRouteRule defines semantics for matching an HTTP request based on conditions (matches), processing it (filters), and forwarding the request to an API object (backendRefs). + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching requests should be sent. \n Failure behavior here depends on how many BackendRefs are specified and how many are invalid. \n If *all* entries in BackendRefs are invalid, and there are also no filters specified in this route rule, *all* traffic which matches this rule MUST receive a 500 status code. \n See the HTTPBackendRef definition for the rules about what makes a single HTTPBackendRef invalid. \n When a HTTPBackendRef is invalid, 500 status codes MUST be returned for requests that would have otherwise been routed to an invalid backend. If multiple backends are specified, and some are invalid, the proportion of requests that would otherwise have been routed to an invalid backend MUST receive a 500 status code. \n For example, if two backends are specified with equal weights, and one is invalid, 50 percent of traffic must receive a 500. Implementations may choose how that 50 percent is determined. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Core" + items: + description: HTTPBackendRef defines how a HTTPRoute should forward an HTTP request. + properties: + filters: + description: "Filters defined at this level should be executed if and only if the request is being forwarded to the backend defined here. \n Support: Implementation-specific (For broader support of filters, use the Filters field in HTTPRouteRule.)" + items: + description: HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" + 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 + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" + properties: + add: + description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" + properties: + group: + default: "" + 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: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n Support: Core" + properties: + hostname: + description: "Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname of the request is used. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n Support: Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: "Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " + properties: + add: + description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " + properties: + hostname: + description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines a path rewrite. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + group: + default: "" + 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: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support for this field varies based on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + type: array + filters: + description: "Filters define the filters that are applied to requests that match this rule. \n The effects of ordering of multiple behaviors are currently unspecified. This can change in the future based on feedback during the alpha stage. \n Conformance-levels at this level are defined based on the type of filter: \n - ALL core filters MUST be supported by all implementations. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. \n Specifying a core filter multiple times has unspecified or implementation-specific conformance. \n All filters are expected to be compatible with each other except for the URLRewrite and RequestRedirect filters, which may not be combined. If an implementation can not support other combinations of filters, they must clearly document that limitation. In all cases where incompatible or unsupported filters are specified, implementations MUST add a warning condition to status. \n Support: Core" + items: + description: HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" + 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 + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" + properties: + add: + description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" + properties: + group: + default: "" + 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: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n Support: Core" + properties: + hostname: + description: "Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname of the request is used. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n Support: Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: "Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " + properties: + add: + description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " + properties: + hostname: + description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines a path rewrite. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + default: + - path: + type: PathPrefix + value: / + description: "Matches define conditions used for matching the rule against incoming HTTP requests. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied. \n For example, take the following matches configuration: \n ``` matches: - path: value: \"/foo\" headers: - name: \"version\" value: \"v2\" - path: value: \"/v2/foo\" ``` \n For a request to match against this rule, a request must satisfy EITHER of the two conditions: \n - path prefixed with `/foo` AND contains the header `version: v2` - path prefix of `/v2/foo` \n See the documentation for HTTPRouteMatch on how to specify multiple match conditions that should be ANDed together. \n If no matches are specified, the default is a prefix path match on \"/\", which has the effect of matching every HTTP request. \n Proxy or Load Balancer routing configuration generated from HTTPRoutes MUST prioritize matches based on the following criteria, continuing on ties. Across all rules specified on applicable Routes, precedence must be given to the match with the largest number of: \n * Characters in a matching path. * Header matches. * Query param matches. \n If ties still exist across multiple Routes, matching precedence MUST be determined in order of the following criteria, continuing on ties: \n * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n If ties still exist within an HTTPRoute, matching precedence MUST be granted to the FIRST matching rule (in list order) with a match meeting the above criteria. \n When no rules matching a request have been successfully attached to the parent a request is coming from, a HTTP 404 status code MUST be returned." + items: + description: "HTTPRouteMatch defines the predicate used to match requests to a given action. Multiple match types are ANDed together, i.e. the match will evaluate to true only if all conditions are satisfied. \n For example, the match below will match a HTTP request only if its path starts with `/foo` AND it contains the `version: v1` header: \n ``` match: \n \tpath: \t value: \"/foo\" \theaders: \t- name: \"version\" \t value \"v1\" \n ```" + properties: + headers: + description: Headers specifies HTTP request header matchers. Multiple match values are ANDed together, meaning, a request must match all the specified headers to select the route. + items: + description: HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request headers. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent. \n When a header is repeated in an HTTP request, it is implementation-specific behavior as to how this is represented. Generally, proxies should follow the guidance from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding processing a repeated header, with special handling for \"Set-Cookie\"." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: "Type specifies how to match against the value of the header. \n Support: Core (Exact) \n Support: Implementation-specific (RegularExpression) \n Since RegularExpression HeaderMatchType has implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect." + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + method: + description: "Method specifies HTTP method matcher. When specified, this route will be matched only if the request has the specified method. \n Support: Extended" + enum: + - GET + - HEAD + - POST + - PUT + - DELETE + - CONNECT + - OPTIONS + - TRACE + - PATCH + type: string + path: + default: + type: PathPrefix + value: / + description: Path specifies a HTTP request path matcher. If this field is not specified, a default prefix match on the "/" path is provided. + properties: + type: + default: PathPrefix + description: "Type specifies how to match against the path Value. \n Support: Core (Exact, PathPrefix) \n Support: Implementation-specific (RegularExpression)" + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: "QueryParams specifies HTTP query parameter matchers. Multiple match values are ANDed together, meaning, a request must match all the specified query parameters to select the route. \n Support: Extended" + items: + description: HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP query parameters. + properties: + name: + description: "Name is the name of the HTTP query param to be matched. This must be an exact string match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). \n If multiple entries specify equivalent query param names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent query param name MUST be ignored. \n If a query param is repeated in an HTTP request, the behavior is purposely left undefined, since different data planes have different capabilities. However, it is *recommended* that implementations should match against the first value of the param if the data plane supports it, as this behavior is expected in other load balancing contexts outside of the Gateway API. \n Users SHOULD NOT route traffic based on repeated query params to guard themselves against potential differences in the implementations." + maxLength: 256 + minLength: 1 + type: string + type: + default: Exact + description: "Type specifies how to match against the value of the query parameter. \n Support: Extended (Exact) \n Support: Implementation-specific (RegularExpression) \n Since RegularExpression QueryParamMatchType has Implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect." + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + type: object + maxItems: 16 + type: array + type: object + status: + description: Status defines the current state of HTTPRoute. + properties: + parents: + description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." + items: + description: RouteParentStatus describes the status of a route with respect to an associated Parent. + properties: + conditions: + description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/gateway-controller\". \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.hostnames + name: Hostnames + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: HTTPRoute provides a way to route HTTP requests. This includes the capability to match requests by hostname, path, header, or query param. Filters can be used to specify additional processing steps. Backends specify where matching requests should be routed. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of HTTPRoute. + properties: + hostnames: + description: "Hostnames defines a set of hostname that should match against the HTTP Host header to select a HTTPRoute to process the request. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n If a hostname is specified by both the Listener and HTTPRoute, there must be at least one intersecting hostname for the HTTPRoute to be attached to the Listener. For example: \n * A Listener with `test.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `*.example.com`, `test.example.com`, and `foo.test.example.com` would all match. On the other hand, `example.com` and `test.example.net` would not match. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n If both the Listener and HTTPRoute have specified hostnames, any HTTPRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the HTTPRoute specified `test.example.com` and `test.example.net`, `test.example.net` must not be considered for a match. \n If both the Listener and HTTPRoute have specified hostnames, and none match with the criteria above, then the HTTPRoute is not accepted. The implementation must raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. \n In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. overlapping wildcard matching and exact matching hostnames), precedence must be given to rules from the HTTPRoute with the largest number of: \n * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. \n If ties exist across multiple Routes, the matching precedence rules for HTTPRouteMatches takes over. \n Support: Core" + items: + description: "Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n Hostname can be \"precise\" which is a domain name without the terminating dot of a network host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). \n Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed." + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + type: array + parentRefs: + description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." + items: + description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + rules: + default: + - matches: + - path: + type: PathPrefix + value: / + description: Rules are a list of HTTP matchers, filters and actions. + items: + description: HTTPRouteRule defines semantics for matching an HTTP request based on conditions (matches), processing it (filters), and forwarding the request to an API object (backendRefs). + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching requests should be sent. \n Failure behavior here depends on how many BackendRefs are specified and how many are invalid. \n If *all* entries in BackendRefs are invalid, and there are also no filters specified in this route rule, *all* traffic which matches this rule MUST receive a 500 status code. \n See the HTTPBackendRef definition for the rules about what makes a single HTTPBackendRef invalid. \n When a HTTPBackendRef is invalid, 500 status codes MUST be returned for requests that would have otherwise been routed to an invalid backend. If multiple backends are specified, and some are invalid, the proportion of requests that would otherwise have been routed to an invalid backend MUST receive a 500 status code. \n For example, if two backends are specified with equal weights, and one is invalid, 50 percent of traffic must receive a 500. Implementations may choose how that 50 percent is determined. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Core" + items: + description: HTTPBackendRef defines how a HTTPRoute should forward an HTTP request. + properties: + filters: + description: "Filters defined at this level should be executed if and only if the request is being forwarded to the backend defined here. \n Support: Implementation-specific (For broader support of filters, use the Filters field in HTTPRouteRule.)" + items: + description: HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" + 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 + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" + properties: + add: + description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" + properties: + group: + default: "" + 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: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n Support: Core" + properties: + hostname: + description: "Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname of the request is used. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n Support: Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: "Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " + properties: + add: + description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " + properties: + hostname: + description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines a path rewrite. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + group: + default: "" + 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: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support for this field varies based on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + type: array + filters: + description: "Filters define the filters that are applied to requests that match this rule. \n The effects of ordering of multiple behaviors are currently unspecified. This can change in the future based on feedback during the alpha stage. \n Conformance-levels at this level are defined based on the type of filter: \n - ALL core filters MUST be supported by all implementations. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. \n Specifying a core filter multiple times has unspecified or implementation-specific conformance. \n All filters are expected to be compatible with each other except for the URLRewrite and RequestRedirect filters, which may not be combined. If an implementation can not support other combinations of filters, they must clearly document that limitation. In all cases where incompatible or unsupported filters are specified, implementations MUST add a warning condition to status. \n Support: Core" + items: + description: HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" + 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 + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" + properties: + add: + description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" + properties: + group: + default: "" + 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: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n Support: Core" + properties: + hostname: + description: "Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname of the request is used. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n Support: Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: "Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " + properties: + add: + description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " + properties: + hostname: + description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines a path rewrite. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + default: + - path: + type: PathPrefix + value: / + description: "Matches define conditions used for matching the rule against incoming HTTP requests. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied. \n For example, take the following matches configuration: \n ``` matches: - path: value: \"/foo\" headers: - name: \"version\" value: \"v2\" - path: value: \"/v2/foo\" ``` \n For a request to match against this rule, a request must satisfy EITHER of the two conditions: \n - path prefixed with `/foo` AND contains the header `version: v2` - path prefix of `/v2/foo` \n See the documentation for HTTPRouteMatch on how to specify multiple match conditions that should be ANDed together. \n If no matches are specified, the default is a prefix path match on \"/\", which has the effect of matching every HTTP request. \n Proxy or Load Balancer routing configuration generated from HTTPRoutes MUST prioritize matches based on the following criteria, continuing on ties. Across all rules specified on applicable Routes, precedence must be given to the match with the largest number of: \n * Characters in a matching path. * Header matches. * Query param matches. \n If ties still exist across multiple Routes, matching precedence MUST be determined in order of the following criteria, continuing on ties: \n * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n If ties still exist within an HTTPRoute, matching precedence MUST be granted to the FIRST matching rule (in list order) with a match meeting the above criteria. \n When no rules matching a request have been successfully attached to the parent a request is coming from, a HTTP 404 status code MUST be returned." + items: + description: "HTTPRouteMatch defines the predicate used to match requests to a given action. Multiple match types are ANDed together, i.e. the match will evaluate to true only if all conditions are satisfied. \n For example, the match below will match a HTTP request only if its path starts with `/foo` AND it contains the `version: v1` header: \n ``` match: \n \tpath: \t value: \"/foo\" \theaders: \t- name: \"version\" \t value \"v1\" \n ```" + properties: + headers: + description: Headers specifies HTTP request header matchers. Multiple match values are ANDed together, meaning, a request must match all the specified headers to select the route. + items: + description: HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request headers. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent. \n When a header is repeated in an HTTP request, it is implementation-specific behavior as to how this is represented. Generally, proxies should follow the guidance from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding processing a repeated header, with special handling for \"Set-Cookie\"." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: "Type specifies how to match against the value of the header. \n Support: Core (Exact) \n Support: Implementation-specific (RegularExpression) \n Since RegularExpression HeaderMatchType has implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect." + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + method: + description: "Method specifies HTTP method matcher. When specified, this route will be matched only if the request has the specified method. \n Support: Extended" + enum: + - GET + - HEAD + - POST + - PUT + - DELETE + - CONNECT + - OPTIONS + - TRACE + - PATCH + type: string + path: + default: + type: PathPrefix + value: / + description: Path specifies a HTTP request path matcher. If this field is not specified, a default prefix match on the "/" path is provided. + properties: + type: + default: PathPrefix + description: "Type specifies how to match against the path Value. \n Support: Core (Exact, PathPrefix) \n Support: Implementation-specific (RegularExpression)" + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: "QueryParams specifies HTTP query parameter matchers. Multiple match values are ANDed together, meaning, a request must match all the specified query parameters to select the route. \n Support: Extended" + items: + description: HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP query parameters. + properties: + name: + description: "Name is the name of the HTTP query param to be matched. This must be an exact string match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). \n If multiple entries specify equivalent query param names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent query param name MUST be ignored. \n If a query param is repeated in an HTTP request, the behavior is purposely left undefined, since different data planes have different capabilities. However, it is *recommended* that implementations should match against the first value of the param if the data plane supports it, as this behavior is expected in other load balancing contexts outside of the Gateway API. \n Users SHOULD NOT route traffic based on repeated query params to guard themselves against potential differences in the implementations." + maxLength: 256 + minLength: 1 + type: string + type: + default: Exact + description: "Type specifies how to match against the value of the query parameter. \n Support: Extended (Exact) \n Support: Implementation-specific (RegularExpression) \n Since RegularExpression QueryParamMatchType has Implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect." + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + type: object + maxItems: 16 + type: array + type: object + status: + description: Status defines the current state of HTTPRoute. + properties: + parents: + description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." + items: + description: RouteParentStatus describes the status of a route with respect to an associated Parent. + properties: + conditions: + description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/gateway-controller\". \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/consul/crds/httproutes.gateway.networking.k8s.io.yaml.yml b/charts/consul/crds/httproutes.gateway.networking.k8s.io.yaml.yml new file mode 100644 index 0000000000..b455d788de --- /dev/null +++ b/charts/consul/crds/httproutes.gateway.networking.k8s.io.yaml.yml @@ -0,0 +1,1909 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + name: httproutes.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: HTTPRoute + listKind: HTTPRouteList + plural: httproutes + singular: httproute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.hostnames + name: Hostnames + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: The v1alpha2 version of HTTPRoute has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1. + name: v1alpha2 + schema: + openAPIV3Schema: + description: HTTPRoute provides a way to route HTTP requests. This includes the capability to match requests by hostname, path, header, or query param. Filters can be used to specify additional processing steps. Backends specify where matching requests should be routed. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of HTTPRoute. + properties: + hostnames: + description: "Hostnames defines a set of hostname that should match against the HTTP Host header to select a HTTPRoute to process the request. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n If a hostname is specified by both the Listener and HTTPRoute, there must be at least one intersecting hostname for the HTTPRoute to be attached to the Listener. For example: \n * A Listener with `test.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `*.example.com`, `test.example.com`, and `foo.test.example.com` would all match. On the other hand, `example.com` and `test.example.net` would not match. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n If both the Listener and HTTPRoute have specified hostnames, any HTTPRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the HTTPRoute specified `test.example.com` and `test.example.net`, `test.example.net` must not be considered for a match. \n If both the Listener and HTTPRoute have specified hostnames, and none match with the criteria above, then the HTTPRoute is not accepted. The implementation must raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. \n In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. overlapping wildcard matching and exact matching hostnames), precedence must be given to rules from the HTTPRoute with the largest number of: \n * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. \n If ties exist across multiple Routes, the matching precedence rules for HTTPRouteMatches takes over. \n Support: Core" + items: + description: "Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n Hostname can be \"precise\" which is a domain name without the terminating dot of a network host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). \n Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed." + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + type: array + parentRefs: + description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." + items: + description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + rules: + default: + - matches: + - path: + type: PathPrefix + value: / + description: Rules are a list of HTTP matchers, filters and actions. + items: + description: HTTPRouteRule defines semantics for matching an HTTP request based on conditions (matches), processing it (filters), and forwarding the request to an API object (backendRefs). + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching requests should be sent. \n Failure behavior here depends on how many BackendRefs are specified and how many are invalid. \n If *all* entries in BackendRefs are invalid, and there are also no filters specified in this route rule, *all* traffic which matches this rule MUST receive a 500 status code. \n See the HTTPBackendRef definition for the rules about what makes a single HTTPBackendRef invalid. \n When a HTTPBackendRef is invalid, 500 status codes MUST be returned for requests that would have otherwise been routed to an invalid backend. If multiple backends are specified, and some are invalid, the proportion of requests that would otherwise have been routed to an invalid backend MUST receive a 500 status code. \n For example, if two backends are specified with equal weights, and one is invalid, 50 percent of traffic must receive a 500. Implementations may choose how that 50 percent is determined. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Core" + items: + description: HTTPBackendRef defines how a HTTPRoute should forward an HTTP request. + properties: + filters: + description: "Filters defined at this level should be executed if and only if the request is being forwarded to the backend defined here. \n Support: Implementation-specific (For broader support of filters, use the Filters field in HTTPRouteRule.)" + items: + description: HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" + 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 + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" + properties: + add: + description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" + properties: + group: + default: "" + 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: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n Support: Core" + properties: + hostname: + description: "Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname of the request is used. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n Support: Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: "Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " + properties: + add: + description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " + properties: + hostname: + description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines a path rewrite. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + group: + default: "" + 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: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support for this field varies based on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + type: array + filters: + description: "Filters define the filters that are applied to requests that match this rule. \n The effects of ordering of multiple behaviors are currently unspecified. This can change in the future based on feedback during the alpha stage. \n Conformance-levels at this level are defined based on the type of filter: \n - ALL core filters MUST be supported by all implementations. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. \n Specifying a core filter multiple times has unspecified or implementation-specific conformance. \n All filters are expected to be compatible with each other except for the URLRewrite and RequestRedirect filters, which may not be combined. If an implementation can not support other combinations of filters, they must clearly document that limitation. In all cases where incompatible or unsupported filters are specified, implementations MUST add a warning condition to status. \n Support: Core" + items: + description: HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" + 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 + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" + properties: + add: + description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" + properties: + group: + default: "" + 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: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n Support: Core" + properties: + hostname: + description: "Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname of the request is used. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n Support: Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: "Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " + properties: + add: + description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " + properties: + hostname: + description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines a path rewrite. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + default: + - path: + type: PathPrefix + value: / + description: "Matches define conditions used for matching the rule against incoming HTTP requests. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied. \n For example, take the following matches configuration: \n ``` matches: - path: value: \"/foo\" headers: - name: \"version\" value: \"v2\" - path: value: \"/v2/foo\" ``` \n For a request to match against this rule, a request must satisfy EITHER of the two conditions: \n - path prefixed with `/foo` AND contains the header `version: v2` - path prefix of `/v2/foo` \n See the documentation for HTTPRouteMatch on how to specify multiple match conditions that should be ANDed together. \n If no matches are specified, the default is a prefix path match on \"/\", which has the effect of matching every HTTP request. \n Proxy or Load Balancer routing configuration generated from HTTPRoutes MUST prioritize matches based on the following criteria, continuing on ties. Across all rules specified on applicable Routes, precedence must be given to the match with the largest number of: \n * Characters in a matching path. * Header matches. * Query param matches. \n If ties still exist across multiple Routes, matching precedence MUST be determined in order of the following criteria, continuing on ties: \n * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n If ties still exist within an HTTPRoute, matching precedence MUST be granted to the FIRST matching rule (in list order) with a match meeting the above criteria. \n When no rules matching a request have been successfully attached to the parent a request is coming from, a HTTP 404 status code MUST be returned." + items: + description: "HTTPRouteMatch defines the predicate used to match requests to a given action. Multiple match types are ANDed together, i.e. the match will evaluate to true only if all conditions are satisfied. \n For example, the match below will match a HTTP request only if its path starts with `/foo` AND it contains the `version: v1` header: \n ``` match: \n \tpath: \t value: \"/foo\" \theaders: \t- name: \"version\" \t value \"v1\" \n ```" + properties: + headers: + description: Headers specifies HTTP request header matchers. Multiple match values are ANDed together, meaning, a request must match all the specified headers to select the route. + items: + description: HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request headers. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent. \n When a header is repeated in an HTTP request, it is implementation-specific behavior as to how this is represented. Generally, proxies should follow the guidance from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding processing a repeated header, with special handling for \"Set-Cookie\"." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: "Type specifies how to match against the value of the header. \n Support: Core (Exact) \n Support: Implementation-specific (RegularExpression) \n Since RegularExpression HeaderMatchType has implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect." + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + method: + description: "Method specifies HTTP method matcher. When specified, this route will be matched only if the request has the specified method. \n Support: Extended" + enum: + - GET + - HEAD + - POST + - PUT + - DELETE + - CONNECT + - OPTIONS + - TRACE + - PATCH + type: string + path: + default: + type: PathPrefix + value: / + description: Path specifies a HTTP request path matcher. If this field is not specified, a default prefix match on the "/" path is provided. + properties: + type: + default: PathPrefix + description: "Type specifies how to match against the path Value. \n Support: Core (Exact, PathPrefix) \n Support: Implementation-specific (RegularExpression)" + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: "QueryParams specifies HTTP query parameter matchers. Multiple match values are ANDed together, meaning, a request must match all the specified query parameters to select the route. \n Support: Extended" + items: + description: HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP query parameters. + properties: + name: + description: "Name is the name of the HTTP query param to be matched. This must be an exact string match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). \n If multiple entries specify equivalent query param names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent query param name MUST be ignored. \n If a query param is repeated in an HTTP request, the behavior is purposely left undefined, since different data planes have different capabilities. However, it is *recommended* that implementations should match against the first value of the param if the data plane supports it, as this behavior is expected in other load balancing contexts outside of the Gateway API. \n Users SHOULD NOT route traffic based on repeated query params to guard themselves against potential differences in the implementations." + maxLength: 256 + minLength: 1 + type: string + type: + default: Exact + description: "Type specifies how to match against the value of the query parameter. \n Support: Extended (Exact) \n Support: Implementation-specific (RegularExpression) \n Since RegularExpression QueryParamMatchType has Implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect." + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + type: object + maxItems: 16 + type: array + type: object + status: + description: Status defines the current state of HTTPRoute. + properties: + parents: + description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." + items: + description: RouteParentStatus describes the status of a route with respect to an associated Parent. + properties: + conditions: + description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/gateway-controller\". \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.hostnames + name: Hostnames + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: HTTPRoute provides a way to route HTTP requests. This includes the capability to match requests by hostname, path, header, or query param. Filters can be used to specify additional processing steps. Backends specify where matching requests should be routed. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of HTTPRoute. + properties: + hostnames: + description: "Hostnames defines a set of hostname that should match against the HTTP Host header to select a HTTPRoute to process the request. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n If a hostname is specified by both the Listener and HTTPRoute, there must be at least one intersecting hostname for the HTTPRoute to be attached to the Listener. For example: \n * A Listener with `test.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `*.example.com`, `test.example.com`, and `foo.test.example.com` would all match. On the other hand, `example.com` and `test.example.net` would not match. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n If both the Listener and HTTPRoute have specified hostnames, any HTTPRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the HTTPRoute specified `test.example.com` and `test.example.net`, `test.example.net` must not be considered for a match. \n If both the Listener and HTTPRoute have specified hostnames, and none match with the criteria above, then the HTTPRoute is not accepted. The implementation must raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. \n In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. overlapping wildcard matching and exact matching hostnames), precedence must be given to rules from the HTTPRoute with the largest number of: \n * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. \n If ties exist across multiple Routes, the matching precedence rules for HTTPRouteMatches takes over. \n Support: Core" + items: + description: "Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n Hostname can be \"precise\" which is a domain name without the terminating dot of a network host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). \n Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed." + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + type: array + parentRefs: + description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." + items: + description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + rules: + default: + - matches: + - path: + type: PathPrefix + value: / + description: Rules are a list of HTTP matchers, filters and actions. + items: + description: HTTPRouteRule defines semantics for matching an HTTP request based on conditions (matches), processing it (filters), and forwarding the request to an API object (backendRefs). + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching requests should be sent. \n Failure behavior here depends on how many BackendRefs are specified and how many are invalid. \n If *all* entries in BackendRefs are invalid, and there are also no filters specified in this route rule, *all* traffic which matches this rule MUST receive a 500 status code. \n See the HTTPBackendRef definition for the rules about what makes a single HTTPBackendRef invalid. \n When a HTTPBackendRef is invalid, 500 status codes MUST be returned for requests that would have otherwise been routed to an invalid backend. If multiple backends are specified, and some are invalid, the proportion of requests that would otherwise have been routed to an invalid backend MUST receive a 500 status code. \n For example, if two backends are specified with equal weights, and one is invalid, 50 percent of traffic must receive a 500. Implementations may choose how that 50 percent is determined. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Core" + items: + description: HTTPBackendRef defines how a HTTPRoute should forward an HTTP request. + properties: + filters: + description: "Filters defined at this level should be executed if and only if the request is being forwarded to the backend defined here. \n Support: Implementation-specific (For broader support of filters, use the Filters field in HTTPRouteRule.)" + items: + description: HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" + 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 + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" + properties: + add: + description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" + properties: + group: + default: "" + 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: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n Support: Core" + properties: + hostname: + description: "Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname of the request is used. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n Support: Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: "Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " + properties: + add: + description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " + properties: + hostname: + description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines a path rewrite. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + group: + default: "" + 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: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support for this field varies based on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + type: array + filters: + description: "Filters define the filters that are applied to requests that match this rule. \n The effects of ordering of multiple behaviors are currently unspecified. This can change in the future based on feedback during the alpha stage. \n Conformance-levels at this level are defined based on the type of filter: \n - ALL core filters MUST be supported by all implementations. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. \n Specifying a core filter multiple times has unspecified or implementation-specific conformance. \n All filters are expected to be compatible with each other except for the URLRewrite and RequestRedirect filters, which may not be combined. If an implementation can not support other combinations of filters, they must clearly document that limitation. In all cases where incompatible or unsupported filters are specified, implementations MUST add a warning condition to status. \n Support: Core" + items: + description: HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" + 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 + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" + properties: + add: + description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" + properties: + group: + default: "" + 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: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n Support: Core" + properties: + hostname: + description: "Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname of the request is used. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n Support: Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: "Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " + properties: + add: + description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " + properties: + hostname: + description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines a path rewrite. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + default: + - path: + type: PathPrefix + value: / + description: "Matches define conditions used for matching the rule against incoming HTTP requests. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied. \n For example, take the following matches configuration: \n ``` matches: - path: value: \"/foo\" headers: - name: \"version\" value: \"v2\" - path: value: \"/v2/foo\" ``` \n For a request to match against this rule, a request must satisfy EITHER of the two conditions: \n - path prefixed with `/foo` AND contains the header `version: v2` - path prefix of `/v2/foo` \n See the documentation for HTTPRouteMatch on how to specify multiple match conditions that should be ANDed together. \n If no matches are specified, the default is a prefix path match on \"/\", which has the effect of matching every HTTP request. \n Proxy or Load Balancer routing configuration generated from HTTPRoutes MUST prioritize matches based on the following criteria, continuing on ties. Across all rules specified on applicable Routes, precedence must be given to the match with the largest number of: \n * Characters in a matching path. * Header matches. * Query param matches. \n If ties still exist across multiple Routes, matching precedence MUST be determined in order of the following criteria, continuing on ties: \n * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n If ties still exist within an HTTPRoute, matching precedence MUST be granted to the FIRST matching rule (in list order) with a match meeting the above criteria. \n When no rules matching a request have been successfully attached to the parent a request is coming from, a HTTP 404 status code MUST be returned." + items: + description: "HTTPRouteMatch defines the predicate used to match requests to a given action. Multiple match types are ANDed together, i.e. the match will evaluate to true only if all conditions are satisfied. \n For example, the match below will match a HTTP request only if its path starts with `/foo` AND it contains the `version: v1` header: \n ``` match: \n \tpath: \t value: \"/foo\" \theaders: \t- name: \"version\" \t value \"v1\" \n ```" + properties: + headers: + description: Headers specifies HTTP request header matchers. Multiple match values are ANDed together, meaning, a request must match all the specified headers to select the route. + items: + description: HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request headers. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent. \n When a header is repeated in an HTTP request, it is implementation-specific behavior as to how this is represented. Generally, proxies should follow the guidance from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding processing a repeated header, with special handling for \"Set-Cookie\"." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: "Type specifies how to match against the value of the header. \n Support: Core (Exact) \n Support: Implementation-specific (RegularExpression) \n Since RegularExpression HeaderMatchType has implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect." + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + method: + description: "Method specifies HTTP method matcher. When specified, this route will be matched only if the request has the specified method. \n Support: Extended" + enum: + - GET + - HEAD + - POST + - PUT + - DELETE + - CONNECT + - OPTIONS + - TRACE + - PATCH + type: string + path: + default: + type: PathPrefix + value: / + description: Path specifies a HTTP request path matcher. If this field is not specified, a default prefix match on the "/" path is provided. + properties: + type: + default: PathPrefix + description: "Type specifies how to match against the path Value. \n Support: Core (Exact, PathPrefix) \n Support: Implementation-specific (RegularExpression)" + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: "QueryParams specifies HTTP query parameter matchers. Multiple match values are ANDed together, meaning, a request must match all the specified query parameters to select the route. \n Support: Extended" + items: + description: HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP query parameters. + properties: + name: + description: "Name is the name of the HTTP query param to be matched. This must be an exact string match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). \n If multiple entries specify equivalent query param names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent query param name MUST be ignored. \n If a query param is repeated in an HTTP request, the behavior is purposely left undefined, since different data planes have different capabilities. However, it is *recommended* that implementations should match against the first value of the param if the data plane supports it, as this behavior is expected in other load balancing contexts outside of the Gateway API. \n Users SHOULD NOT route traffic based on repeated query params to guard themselves against potential differences in the implementations." + maxLength: 256 + minLength: 1 + type: string + type: + default: Exact + description: "Type specifies how to match against the value of the query parameter. \n Support: Extended (Exact) \n Support: Implementation-specific (RegularExpression) \n Since RegularExpression QueryParamMatchType has Implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect." + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + type: object + maxItems: 16 + type: array + type: object + status: + description: Status defines the current state of HTTPRoute. + properties: + parents: + description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." + items: + description: RouteParentStatus describes the status of a route with respect to an associated Parent. + properties: + conditions: + description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/gateway-controller\". \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/consul/crds/kustomization.yaml b/charts/consul/crds/kustomization.yaml new file mode 100644 index 0000000000..a1a0e349ff --- /dev/null +++ b/charts/consul/crds/kustomization.yaml @@ -0,0 +1,10 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +# This file is Helm ignored. It is only used for the `make generate-external-crds` command. + +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - github.com/kubernetes-sigs/gateway-api/config/crd/experimental?ref=v0.6.2 diff --git a/charts/consul/crds/referencegrants.gateway.networking.k8s.io.yaml b/charts/consul/crds/referencegrants.gateway.networking.k8s.io.yaml new file mode 100644 index 0000000000..cd39b9c12a --- /dev/null +++ b/charts/consul/crds/referencegrants.gateway.networking.k8s.io.yaml @@ -0,0 +1,203 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + name: referencegrants.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: ReferenceGrant + listKind: ReferenceGrantList + plural: referencegrants + shortNames: + - refgrant + singular: referencegrant + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: "ReferenceGrant identifies kinds of resources in other namespaces that are trusted to reference the specified kinds of resources in the same namespace as the policy. \n Each ReferenceGrant can be used to represent a unique trust relationship. Additional Reference Grants can be used to add to the set of trusted sources of inbound references for the namespace they are defined within. \n All cross-namespace references in Gateway API (with the exception of cross-namespace Gateway-route attachment) require a ReferenceGrant. \n ReferenceGrant is a form of runtime verification allowing users to assert which cross-namespace object references are permitted. Implementations that support ReferenceGrant MUST NOT permit cross-namespace references which have no grant, and MUST respond to the removal of a grant by revoking the access that the grant allowed. \n Support: Core" + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of ReferenceGrant. + properties: + from: + description: "From describes the trusted namespaces and kinds that can reference the resources described in \"To\". Each entry in this list MUST be considered to be an additional place that references can be valid from, or to put this another way, entries MUST be combined using OR. \n Support: Core" + items: + description: ReferenceGrantFrom describes trusted namespaces and kinds. + properties: + group: + description: "Group is the group of the referent. When empty, the Kubernetes core API group is inferred. \n Support: Core" + 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 the kind of the referent. Although implementations may support additional resources, the following types are part of the \"Core\" support level for this field. \n When used to permit a SecretObjectReference: \n * Gateway \n When used to permit a BackendObjectReference: \n * GRPCRoute * HTTPRoute * TCPRoute * TLSRoute * UDPRoute" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + namespace: + description: "Namespace is the namespace of the referent. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - namespace + type: object + maxItems: 16 + minItems: 1 + type: array + to: + description: "To describes the resources that may be referenced by the resources described in \"From\". Each entry in this list MUST be considered to be an additional place that references can be valid to, or to put this another way, entries MUST be combined using OR. \n Support: Core" + items: + description: ReferenceGrantTo describes what Kinds are allowed as targets of the references. + properties: + group: + description: "Group is the group of the referent. When empty, the Kubernetes core API group is inferred. \n Support: Core" + 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 the kind of the referent. Although implementations may support additional resources, the following types are part of the \"Core\" support level for this field: \n * Secret when used to permit a SecretObjectReference * Service when used to permit a BackendObjectReference" + 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. When unspecified, this policy refers to all resources of the specified Group and Kind in the local namespace. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + type: object + maxItems: 16 + minItems: 1 + type: array + required: + - from + - to + type: object + type: object + served: true + storage: true + subresources: {} + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: "ReferenceGrant identifies kinds of resources in other namespaces that are trusted to reference the specified kinds of resources in the same namespace as the policy. \n Each ReferenceGrant can be used to represent a unique trust relationship. Additional Reference Grants can be used to add to the set of trusted sources of inbound references for the namespace they are defined within. \n All cross-namespace references in Gateway API (with the exception of cross-namespace Gateway-route attachment) require a ReferenceGrant. \n ReferenceGrant is a form of runtime verification allowing users to assert which cross-namespace object references are permitted. Implementations that support ReferenceGrant MUST NOT permit cross-namespace references which have no grant, and MUST respond to the removal of a grant by revoking the access that the grant allowed. \n Support: Core" + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of ReferenceGrant. + properties: + from: + description: "From describes the trusted namespaces and kinds that can reference the resources described in \"To\". Each entry in this list MUST be considered to be an additional place that references can be valid from, or to put this another way, entries MUST be combined using OR. \n Support: Core" + items: + description: ReferenceGrantFrom describes trusted namespaces and kinds. + properties: + group: + description: "Group is the group of the referent. When empty, the Kubernetes core API group is inferred. \n Support: Core" + 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 the kind of the referent. Although implementations may support additional resources, the following types are part of the \"Core\" support level for this field. \n When used to permit a SecretObjectReference: \n * Gateway \n When used to permit a BackendObjectReference: \n * GRPCRoute * HTTPRoute * TCPRoute * TLSRoute * UDPRoute" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + namespace: + description: "Namespace is the namespace of the referent. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - namespace + type: object + maxItems: 16 + minItems: 1 + type: array + to: + description: "To describes the resources that may be referenced by the resources described in \"From\". Each entry in this list MUST be considered to be an additional place that references can be valid to, or to put this another way, entries MUST be combined using OR. \n Support: Core" + items: + description: ReferenceGrantTo describes what Kinds are allowed as targets of the references. + properties: + group: + description: "Group is the group of the referent. When empty, the Kubernetes core API group is inferred. \n Support: Core" + 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 the kind of the referent. Although implementations may support additional resources, the following types are part of the \"Core\" support level for this field: \n * Secret when used to permit a SecretObjectReference * Service when used to permit a BackendObjectReference" + 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. When unspecified, this policy refers to all resources of the specified Group and Kind in the local namespace. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + type: object + maxItems: 16 + minItems: 1 + type: array + required: + - from + - to + type: object + type: object + served: true + storage: false + subresources: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/consul/crds/referencegrants.gateway.networking.k8s.io.yaml.yml b/charts/consul/crds/referencegrants.gateway.networking.k8s.io.yaml.yml new file mode 100644 index 0000000000..cd39b9c12a --- /dev/null +++ b/charts/consul/crds/referencegrants.gateway.networking.k8s.io.yaml.yml @@ -0,0 +1,203 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + name: referencegrants.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: ReferenceGrant + listKind: ReferenceGrantList + plural: referencegrants + shortNames: + - refgrant + singular: referencegrant + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: "ReferenceGrant identifies kinds of resources in other namespaces that are trusted to reference the specified kinds of resources in the same namespace as the policy. \n Each ReferenceGrant can be used to represent a unique trust relationship. Additional Reference Grants can be used to add to the set of trusted sources of inbound references for the namespace they are defined within. \n All cross-namespace references in Gateway API (with the exception of cross-namespace Gateway-route attachment) require a ReferenceGrant. \n ReferenceGrant is a form of runtime verification allowing users to assert which cross-namespace object references are permitted. Implementations that support ReferenceGrant MUST NOT permit cross-namespace references which have no grant, and MUST respond to the removal of a grant by revoking the access that the grant allowed. \n Support: Core" + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of ReferenceGrant. + properties: + from: + description: "From describes the trusted namespaces and kinds that can reference the resources described in \"To\". Each entry in this list MUST be considered to be an additional place that references can be valid from, or to put this another way, entries MUST be combined using OR. \n Support: Core" + items: + description: ReferenceGrantFrom describes trusted namespaces and kinds. + properties: + group: + description: "Group is the group of the referent. When empty, the Kubernetes core API group is inferred. \n Support: Core" + 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 the kind of the referent. Although implementations may support additional resources, the following types are part of the \"Core\" support level for this field. \n When used to permit a SecretObjectReference: \n * Gateway \n When used to permit a BackendObjectReference: \n * GRPCRoute * HTTPRoute * TCPRoute * TLSRoute * UDPRoute" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + namespace: + description: "Namespace is the namespace of the referent. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - namespace + type: object + maxItems: 16 + minItems: 1 + type: array + to: + description: "To describes the resources that may be referenced by the resources described in \"From\". Each entry in this list MUST be considered to be an additional place that references can be valid to, or to put this another way, entries MUST be combined using OR. \n Support: Core" + items: + description: ReferenceGrantTo describes what Kinds are allowed as targets of the references. + properties: + group: + description: "Group is the group of the referent. When empty, the Kubernetes core API group is inferred. \n Support: Core" + 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 the kind of the referent. Although implementations may support additional resources, the following types are part of the \"Core\" support level for this field: \n * Secret when used to permit a SecretObjectReference * Service when used to permit a BackendObjectReference" + 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. When unspecified, this policy refers to all resources of the specified Group and Kind in the local namespace. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + type: object + maxItems: 16 + minItems: 1 + type: array + required: + - from + - to + type: object + type: object + served: true + storage: true + subresources: {} + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: "ReferenceGrant identifies kinds of resources in other namespaces that are trusted to reference the specified kinds of resources in the same namespace as the policy. \n Each ReferenceGrant can be used to represent a unique trust relationship. Additional Reference Grants can be used to add to the set of trusted sources of inbound references for the namespace they are defined within. \n All cross-namespace references in Gateway API (with the exception of cross-namespace Gateway-route attachment) require a ReferenceGrant. \n ReferenceGrant is a form of runtime verification allowing users to assert which cross-namespace object references are permitted. Implementations that support ReferenceGrant MUST NOT permit cross-namespace references which have no grant, and MUST respond to the removal of a grant by revoking the access that the grant allowed. \n Support: Core" + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of ReferenceGrant. + properties: + from: + description: "From describes the trusted namespaces and kinds that can reference the resources described in \"To\". Each entry in this list MUST be considered to be an additional place that references can be valid from, or to put this another way, entries MUST be combined using OR. \n Support: Core" + items: + description: ReferenceGrantFrom describes trusted namespaces and kinds. + properties: + group: + description: "Group is the group of the referent. When empty, the Kubernetes core API group is inferred. \n Support: Core" + 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 the kind of the referent. Although implementations may support additional resources, the following types are part of the \"Core\" support level for this field. \n When used to permit a SecretObjectReference: \n * Gateway \n When used to permit a BackendObjectReference: \n * GRPCRoute * HTTPRoute * TCPRoute * TLSRoute * UDPRoute" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + namespace: + description: "Namespace is the namespace of the referent. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - namespace + type: object + maxItems: 16 + minItems: 1 + type: array + to: + description: "To describes the resources that may be referenced by the resources described in \"From\". Each entry in this list MUST be considered to be an additional place that references can be valid to, or to put this another way, entries MUST be combined using OR. \n Support: Core" + items: + description: ReferenceGrantTo describes what Kinds are allowed as targets of the references. + properties: + group: + description: "Group is the group of the referent. When empty, the Kubernetes core API group is inferred. \n Support: Core" + 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 the kind of the referent. Although implementations may support additional resources, the following types are part of the \"Core\" support level for this field: \n * Secret when used to permit a SecretObjectReference * Service when used to permit a BackendObjectReference" + 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. When unspecified, this policy refers to all resources of the specified Group and Kind in the local namespace. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + type: object + maxItems: 16 + minItems: 1 + type: array + required: + - from + - to + type: object + type: object + served: true + storage: false + subresources: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/consul/crds/tcproutes.gateway.networking.k8s.io.yaml b/charts/consul/crds/tcproutes.gateway.networking.k8s.io.yaml new file mode 100644 index 0000000000..906b442d31 --- /dev/null +++ b/charts/consul/crds/tcproutes.gateway.networking.k8s.io.yaml @@ -0,0 +1,276 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + name: tcproutes.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: TCPRoute + listKind: TCPRouteList + plural: tcproutes + singular: tcproute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: TCPRoute provides a way to route TCP requests. When combined with a Gateway listener, it can be used to forward connections on the port specified by the listener to a set of backends specified by the TCPRoute. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of TCPRoute. + properties: + parentRefs: + description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." + items: + description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + rules: + description: Rules are a list of TCP matchers and actions. + items: + description: TCPRouteRule is the configuration for a given rule. + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a non-existent resource or a Service with no endpoints), the underlying implementation MUST actively reject connection attempts to this backend. Connection rejections must respect weight; if an invalid backend is requested to have 80% of connections, then 80% of connections must be rejected instead. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Extended" + items: + description: "BackendRef defines how a Route should forward a request to a Kubernetes resource. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details." + properties: + group: + default: "" + 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: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support for this field varies based on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + minItems: 1 + type: array + type: object + maxItems: 16 + minItems: 1 + type: array + required: + - rules + type: object + status: + description: Status defines the current state of TCPRoute. + properties: + parents: + description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." + items: + description: RouteParentStatus describes the status of a route with respect to an associated Parent. + properties: + conditions: + description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/gateway-controller\". \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/consul/crds/tcproutes.gateway.networking.k8s.io.yaml.yml b/charts/consul/crds/tcproutes.gateway.networking.k8s.io.yaml.yml new file mode 100644 index 0000000000..906b442d31 --- /dev/null +++ b/charts/consul/crds/tcproutes.gateway.networking.k8s.io.yaml.yml @@ -0,0 +1,276 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + name: tcproutes.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: TCPRoute + listKind: TCPRouteList + plural: tcproutes + singular: tcproute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: TCPRoute provides a way to route TCP requests. When combined with a Gateway listener, it can be used to forward connections on the port specified by the listener to a set of backends specified by the TCPRoute. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of TCPRoute. + properties: + parentRefs: + description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." + items: + description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + rules: + description: Rules are a list of TCP matchers and actions. + items: + description: TCPRouteRule is the configuration for a given rule. + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a non-existent resource or a Service with no endpoints), the underlying implementation MUST actively reject connection attempts to this backend. Connection rejections must respect weight; if an invalid backend is requested to have 80% of connections, then 80% of connections must be rejected instead. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Extended" + items: + description: "BackendRef defines how a Route should forward a request to a Kubernetes resource. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details." + properties: + group: + default: "" + 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: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support for this field varies based on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + minItems: 1 + type: array + type: object + maxItems: 16 + minItems: 1 + type: array + required: + - rules + type: object + status: + description: Status defines the current state of TCPRoute. + properties: + parents: + description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." + items: + description: RouteParentStatus describes the status of a route with respect to an associated Parent. + properties: + conditions: + description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/gateway-controller\". \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/consul/crds/tlsroutes.gateway.networking.k8s.io.yaml b/charts/consul/crds/tlsroutes.gateway.networking.k8s.io.yaml new file mode 100644 index 0000000000..2e22b04ef0 --- /dev/null +++ b/charts/consul/crds/tlsroutes.gateway.networking.k8s.io.yaml @@ -0,0 +1,286 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + name: tlsroutes.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: TLSRoute + listKind: TLSRouteList + plural: tlsroutes + singular: tlsroute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: "The TLSRoute resource is similar to TCPRoute, but can be configured to match against TLS-specific metadata. This allows more flexibility in matching streams for a given TLS listener. \n If you need to forward traffic to a single target for a TLS listener, you could choose to use a TCPRoute with a TLS listener." + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of TLSRoute. + properties: + hostnames: + description: "Hostnames defines a set of SNI names that should match against the SNI attribute of TLS ClientHello message in TLS handshake. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed in SNI names per RFC 6066. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n If a hostname is specified by both the Listener and TLSRoute, there must be at least one intersecting hostname for the TLSRoute to be attached to the Listener. For example: \n * A Listener with `test.example.com` as the hostname matches TLSRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches TLSRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `test.example.com` and `*.example.com` would both match. On the other hand, `example.com` and `test.example.net` would not match. \n If both the Listener and TLSRoute have specified hostnames, any TLSRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the TLSRoute specified `test.example.com` and `test.example.net`, `test.example.net` must not be considered for a match. \n If both the Listener and TLSRoute have specified hostnames, and none match with the criteria above, then the TLSRoute is not accepted. The implementation must raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. \n Support: Core" + items: + description: "Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n Hostname can be \"precise\" which is a domain name without the terminating dot of a network host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). \n Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed." + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + type: array + parentRefs: + description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." + items: + description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + rules: + description: Rules are a list of TLS matchers and actions. + items: + description: TLSRouteRule is the configuration for a given rule. + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a non-existent resource or a Service with no endpoints), the rule performs no forwarding; if no filters are specified that would result in a response being sent, the underlying implementation must actively reject request attempts to this backend, by rejecting the connection or returning a 500 status code. Request rejections must respect weight; if an invalid backend is requested to have 80% of requests, then 80% of requests must be rejected instead. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Extended" + items: + description: "BackendRef defines how a Route should forward a request to a Kubernetes resource. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details." + properties: + group: + default: "" + 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: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support for this field varies based on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + minItems: 1 + type: array + type: object + maxItems: 16 + minItems: 1 + type: array + required: + - rules + type: object + status: + description: Status defines the current state of TLSRoute. + properties: + parents: + description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." + items: + description: RouteParentStatus describes the status of a route with respect to an associated Parent. + properties: + conditions: + description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/gateway-controller\". \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/consul/crds/tlsroutes.gateway.networking.k8s.io.yaml.yml b/charts/consul/crds/tlsroutes.gateway.networking.k8s.io.yaml.yml new file mode 100644 index 0000000000..2e22b04ef0 --- /dev/null +++ b/charts/consul/crds/tlsroutes.gateway.networking.k8s.io.yaml.yml @@ -0,0 +1,286 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + name: tlsroutes.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: TLSRoute + listKind: TLSRouteList + plural: tlsroutes + singular: tlsroute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: "The TLSRoute resource is similar to TCPRoute, but can be configured to match against TLS-specific metadata. This allows more flexibility in matching streams for a given TLS listener. \n If you need to forward traffic to a single target for a TLS listener, you could choose to use a TCPRoute with a TLS listener." + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of TLSRoute. + properties: + hostnames: + description: "Hostnames defines a set of SNI names that should match against the SNI attribute of TLS ClientHello message in TLS handshake. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed in SNI names per RFC 6066. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n If a hostname is specified by both the Listener and TLSRoute, there must be at least one intersecting hostname for the TLSRoute to be attached to the Listener. For example: \n * A Listener with `test.example.com` as the hostname matches TLSRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches TLSRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `test.example.com` and `*.example.com` would both match. On the other hand, `example.com` and `test.example.net` would not match. \n If both the Listener and TLSRoute have specified hostnames, any TLSRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the TLSRoute specified `test.example.com` and `test.example.net`, `test.example.net` must not be considered for a match. \n If both the Listener and TLSRoute have specified hostnames, and none match with the criteria above, then the TLSRoute is not accepted. The implementation must raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. \n Support: Core" + items: + description: "Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n Hostname can be \"precise\" which is a domain name without the terminating dot of a network host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). \n Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed." + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + type: array + parentRefs: + description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." + items: + description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + rules: + description: Rules are a list of TLS matchers and actions. + items: + description: TLSRouteRule is the configuration for a given rule. + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a non-existent resource or a Service with no endpoints), the rule performs no forwarding; if no filters are specified that would result in a response being sent, the underlying implementation must actively reject request attempts to this backend, by rejecting the connection or returning a 500 status code. Request rejections must respect weight; if an invalid backend is requested to have 80% of requests, then 80% of requests must be rejected instead. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Extended" + items: + description: "BackendRef defines how a Route should forward a request to a Kubernetes resource. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details." + properties: + group: + default: "" + 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: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support for this field varies based on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + minItems: 1 + type: array + type: object + maxItems: 16 + minItems: 1 + type: array + required: + - rules + type: object + status: + description: Status defines the current state of TLSRoute. + properties: + parents: + description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." + items: + description: RouteParentStatus describes the status of a route with respect to an associated Parent. + properties: + conditions: + description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/gateway-controller\". \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/consul/crds/udproutes.gateway.networking.k8s.io.yaml b/charts/consul/crds/udproutes.gateway.networking.k8s.io.yaml new file mode 100644 index 0000000000..19b03dcd0b --- /dev/null +++ b/charts/consul/crds/udproutes.gateway.networking.k8s.io.yaml @@ -0,0 +1,276 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + name: udproutes.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: UDPRoute + listKind: UDPRouteList + plural: udproutes + singular: udproute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: UDPRoute provides a way to route UDP traffic. When combined with a Gateway listener, it can be used to forward traffic on the port specified by the listener to a set of backends specified by the UDPRoute. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of UDPRoute. + properties: + parentRefs: + description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." + items: + description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + rules: + description: Rules are a list of UDP matchers and actions. + items: + description: UDPRouteRule is the configuration for a given rule. + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a non-existent resource or a Service with no endpoints), the underlying implementation MUST actively reject connection attempts to this backend. Packet drops must respect weight; if an invalid backend is requested to have 80% of the packets, then 80% of packets must be dropped instead. \n Support: Core for Kubernetes Service Support: Implementation-specific for any other resource \n Support for weight: Extended" + items: + description: "BackendRef defines how a Route should forward a request to a Kubernetes resource. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details." + properties: + group: + default: "" + 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: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support for this field varies based on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + minItems: 1 + type: array + type: object + maxItems: 16 + minItems: 1 + type: array + required: + - rules + type: object + status: + description: Status defines the current state of UDPRoute. + properties: + parents: + description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." + items: + description: RouteParentStatus describes the status of a route with respect to an associated Parent. + properties: + conditions: + description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/gateway-controller\". \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/consul/crds/udproutes.gateway.networking.k8s.io.yaml.yml b/charts/consul/crds/udproutes.gateway.networking.k8s.io.yaml.yml new file mode 100644 index 0000000000..19b03dcd0b --- /dev/null +++ b/charts/consul/crds/udproutes.gateway.networking.k8s.io.yaml.yml @@ -0,0 +1,276 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + name: udproutes.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: UDPRoute + listKind: UDPRouteList + plural: udproutes + singular: udproute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: UDPRoute provides a way to route UDP traffic. When combined with a Gateway listener, it can be used to forward traffic on the port specified by the listener to a set of backends specified by the UDPRoute. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of UDPRoute. + properties: + parentRefs: + description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." + items: + description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + rules: + description: Rules are a list of UDP matchers and actions. + items: + description: UDPRouteRule is the configuration for a given rule. + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a non-existent resource or a Service with no endpoints), the underlying implementation MUST actively reject connection attempts to this backend. Packet drops must respect weight; if an invalid backend is requested to have 80% of the packets, then 80% of packets must be dropped instead. \n Support: Core for Kubernetes Service Support: Implementation-specific for any other resource \n Support for weight: Extended" + items: + description: "BackendRef defines how a Route should forward a request to a Kubernetes resource. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details." + properties: + group: + default: "" + 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: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + 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 + namespace: + description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support for this field varies based on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + minItems: 1 + type: array + type: object + maxItems: 16 + minItems: 1 + type: array + required: + - rules + type: object + status: + description: Status defines the current state of UDPRoute. + properties: + parents: + description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." + items: + description: RouteParentStatus describes the status of a route with respect to an associated Parent. + properties: + conditions: + description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/gateway-controller\". \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + 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. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/consul/templates/connect-inject-clusterrole.yaml b/charts/consul/templates/connect-inject-clusterrole.yaml index e383e5ce28..e152511469 100644 --- a/charts/consul/templates/connect-inject-clusterrole.yaml +++ b/charts/consul/templates/connect-inject-clusterrole.yaml @@ -24,6 +24,8 @@ rules: - serviceintentions - ingressgateways - terminatinggateways + - gatewayclassconfigs + - meshservices - samenessgroups {{- if .Values.global.peering.enabled }} - peeringacceptors @@ -59,18 +61,23 @@ rules: - get - patch - update -{{- if .Values.global.acls.manageSystemACLs }} - apiGroups: [ "" ] - resources: [ "serviceaccounts", "secrets" ] + resources: [ "secrets", "serviceaccounts", "endpoints", "services", "namespaces", "nodes" ] verbs: + - create - get -{{- end }} -- apiGroups: [ "" ] - resources: [ "endpoints", "services", "namespaces", "nodes" ] + - list + - watch + - delete +- apiGroups: [ "rbac.authorization.k8s.io" ] + resources: [ "roles" ] verbs: - - "get" - - "list" - - "watch" + - get + - list + - watch + - delete + - create + - update - apiGroups: [ "" ] resources: - pods @@ -110,12 +117,68 @@ rules: - "update" - "delete" {{- end }} -{{- if .Values.global.enablePodSecurityPolicies }} - apiGroups: [ "policy" ] resources: [ "podsecuritypolicies" ] - resourceNames: - - {{ template "consul.fullname" . }}-connect-injector verbs: - use -{{- end }} +- apiGroups: + - gateway.networking.k8s.io + resources: + - gatewayclasses + - gateways + - httproutes + - tcproutes + - referencegrants + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - gatewayclasses/finalizers + - gateways/finalizers + - httproutes/finalizers + - tcproutes/finalizers + verbs: + - update +- apiGroups: + - gateway.networking.k8s.io + resources: + - gatewayclasses/status + - gateways/status + - httproutes/status + - tcproutes/status + verbs: + - get + - patch + - update +- apiGroups: + - apps + resources: + - deployments + verbs: + - create + - get + - list + - update + - watch + - delete +- apiGroups: + - core + resources: + - services + verbs: + - watch + - list +- apiGroups: [ "" ] + resources: [ "secrets" ] + verbs: + - "get" + - "list" + - "watch" {{- end }} diff --git a/charts/consul/templates/crd-exportedservices.yaml b/charts/consul/templates/crd-exportedservices.yaml index 591500cb12..7ffddf7537 100644 --- a/charts/consul/templates/crd-exportedservices.yaml +++ b/charts/consul/templates/crd-exportedservices.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: exportedservices.consul.hashicorp.com labels: @@ -138,10 +138,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-ingressgateways.yaml b/charts/consul/templates/crd-ingressgateways.yaml index a01fafd8dd..ef33890461 100644 --- a/charts/consul/templates/crd-ingressgateways.yaml +++ b/charts/consul/templates/crd-ingressgateways.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: ingressgateways.consul.hashicorp.com labels: @@ -368,10 +368,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-meshes.yaml b/charts/consul/templates/crd-meshes.yaml index 0710d41280..cdc11b6ed9 100644 --- a/charts/consul/templates/crd-meshes.yaml +++ b/charts/consul/templates/crd-meshes.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: meshes.consul.hashicorp.com labels: @@ -206,10 +206,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-meshservices.yaml b/charts/consul/templates/crd-meshservices.yaml new file mode 100644 index 0000000000..859c8683ee --- /dev/null +++ b/charts/consul/templates/crd-meshservices.yaml @@ -0,0 +1,58 @@ +{{- if .Values.connectInject.enabled }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: meshservices.consul.hashicorp.com + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd +spec: + group: consul.hashicorp.com + names: + kind: MeshService + listKind: MeshServiceList + plural: meshservices + singular: meshservice + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: MeshService holds a reference to an externally managed Consul + Service Mesh service. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of MeshService. + properties: + name: + description: Name holds the service name for a Consul service. + type: string + peer: + description: Peer optionally specifies the name of the peer exporting + the Consul service. If not specified, the Consul service is assumed + to be in the local datacenter. + type: string + type: object + type: object + served: true + storage: true +{{- end }} diff --git a/charts/consul/templates/crd-peeringacceptors.yaml b/charts/consul/templates/crd-peeringacceptors.yaml index e06e830f04..3822f3bdfe 100644 --- a/charts/consul/templates/crd-peeringacceptors.yaml +++ b/charts/consul/templates/crd-peeringacceptors.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: peeringacceptors.consul.hashicorp.com labels: @@ -145,10 +145,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-peeringdialers.yaml b/charts/consul/templates/crd-peeringdialers.yaml index e24401e761..405361c486 100644 --- a/charts/consul/templates/crd-peeringdialers.yaml +++ b/charts/consul/templates/crd-peeringdialers.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: peeringdialers.consul.hashicorp.com labels: @@ -145,10 +145,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-proxydefaults.yaml b/charts/consul/templates/crd-proxydefaults.yaml index 362672c1c1..30dd25f674 100644 --- a/charts/consul/templates/crd-proxydefaults.yaml +++ b/charts/consul/templates/crd-proxydefaults.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: proxydefaults.consul.hashicorp.com labels: @@ -254,10 +254,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-samenessgroups.yaml b/charts/consul/templates/crd-samenessgroups.yaml index 60beb5662c..c1d1c85a8e 100644 --- a/charts/consul/templates/crd-samenessgroups.yaml +++ b/charts/consul/templates/crd-samenessgroups.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: samenessgroups.consul.hashicorp.com labels: @@ -128,10 +128,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-servicedefaults.yaml b/charts/consul/templates/crd-servicedefaults.yaml index 36900d1bda..2562c53320 100644 --- a/charts/consul/templates/crd-servicedefaults.yaml +++ b/charts/consul/templates/crd-servicedefaults.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: servicedefaults.consul.hashicorp.com labels: @@ -494,10 +494,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-serviceintentions.yaml b/charts/consul/templates/crd-serviceintentions.yaml index 67998f776c..f16d0a0d8a 100644 --- a/charts/consul/templates/crd-serviceintentions.yaml +++ b/charts/consul/templates/crd-serviceintentions.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: serviceintentions.consul.hashicorp.com labels: @@ -235,10 +235,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-serviceresolvers.yaml b/charts/consul/templates/crd-serviceresolvers.yaml index 2a6f7923b8..ed95c15846 100644 --- a/charts/consul/templates/crd-serviceresolvers.yaml +++ b/charts/consul/templates/crd-serviceresolvers.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: serviceresolvers.consul.hashicorp.com labels: @@ -333,10 +333,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-servicerouters.yaml b/charts/consul/templates/crd-servicerouters.yaml index 5052facc06..e7a3239e75 100644 --- a/charts/consul/templates/crd-servicerouters.yaml +++ b/charts/consul/templates/crd-servicerouters.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: servicerouters.consul.hashicorp.com labels: @@ -307,10 +307,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-servicesplitters.yaml b/charts/consul/templates/crd-servicesplitters.yaml index a2af050c3d..18fb10341e 100644 --- a/charts/consul/templates/crd-servicesplitters.yaml +++ b/charts/consul/templates/crd-servicesplitters.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: servicesplitters.consul.hashicorp.com labels: @@ -185,10 +185,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-terminatinggateways.yaml b/charts/consul/templates/crd-terminatinggateways.yaml index 583c218be8..955496aeee 100644 --- a/charts/consul/templates/crd-terminatinggateways.yaml +++ b/charts/consul/templates/crd-terminatinggateways.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: terminatinggateways.consul.hashicorp.com labels: @@ -136,10 +136,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/gateway-cleanup-clusterrole.yaml b/charts/consul/templates/gateway-cleanup-clusterrole.yaml new file mode 100644 index 0000000000..c533a882f5 --- /dev/null +++ b/charts/consul/templates/gateway-cleanup-clusterrole.yaml @@ -0,0 +1,35 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "consul.fullname" . }}-gateway-cleanup + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gateway-cleanup +rules: + - apiGroups: + - consul.hashicorp.com + resources: + - gatewayclassconfigs + verbs: + - get + - delete + - apiGroups: + - gateway.networking.k8s.io + resources: + - gatewayclasses + verbs: + - get + - delete +{{- if .Values.global.enablePodSecurityPolicies }} + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" . }}-gateway-cleanup + verbs: + - use +{{- end }} +{{- end }} diff --git a/charts/consul/templates/gateway-cleanup-clusterrolebinding.yaml b/charts/consul/templates/gateway-cleanup-clusterrolebinding.yaml new file mode 100644 index 0000000000..9235f32101 --- /dev/null +++ b/charts/consul/templates/gateway-cleanup-clusterrolebinding.yaml @@ -0,0 +1,20 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "consul.fullname" . }}-gateway-cleanup + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gateway-cleanup +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "consul.fullname" . }}-gateway-cleanup +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" . }}-gateway-cleanup + namespace: {{ .Release.Namespace }} +{{- end }} \ No newline at end of file diff --git a/charts/consul/templates/gateway-cleanup-job.yaml b/charts/consul/templates/gateway-cleanup-job.yaml new file mode 100644 index 0000000000..ff6f295357 --- /dev/null +++ b/charts/consul/templates/gateway-cleanup-job.yaml @@ -0,0 +1,61 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "consul.fullname" . }}-gateway-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gateway-cleanup + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} + annotations: + "helm.sh/hook": pre-delete + "helm.sh/hook-weight": "0" + "helm.sh/hook-delete-policy": hook-succeeded,hook-failed +spec: + template: + metadata: + name: {{ template "consul.fullname" . }}-gateway-cleanup + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: gateway-cleanup + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} + annotations: + "consul.hashicorp.com/connect-inject": "false" + spec: + restartPolicy: Never + serviceAccountName: {{ template "consul.fullname" . }}-gateway-cleanup + containers: + - name: gateway-cleanup + image: {{ .Values.global.imageK8S }} + command: + - consul-k8s-control-plane + args: + - gateway-cleanup + - -gateway-class-name=consul-api-gateway + - -gateway-class-config-name=consul-api-gateway + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" + {{- if .Values.global.acls.tolerations }} + tolerations: + {{ tpl .Values.global.acls.tolerations . | indent 8 | trim }} + {{- end }} + {{- if .Values.global.acls.nodeSelector }} + nodeSelector: + {{ tpl .Values.global.acls.nodeSelector . | indent 8 | trim }} + {{- end }} +{{- end }} diff --git a/charts/consul/templates/gateway-cleanup-podsecuritypolicy.yaml b/charts/consul/templates/gateway-cleanup-podsecuritypolicy.yaml new file mode 100644 index 0000000000..c4d4e8acca --- /dev/null +++ b/charts/consul/templates/gateway-cleanup-podsecuritypolicy.yaml @@ -0,0 +1,34 @@ +{{- if .Values.global.enablePodSecurityPolicies }} +{{- if .Values.connectInject.enabled }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-gateway-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gateway-cleanup +spec: + privileged: false + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} +{{- end }} diff --git a/charts/consul/templates/gateway-cleanup-serviceaccount.yaml b/charts/consul/templates/gateway-cleanup-serviceaccount.yaml new file mode 100644 index 0000000000..f50eb72d97 --- /dev/null +++ b/charts/consul/templates/gateway-cleanup-serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-gateway-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gateway-cleanup +{{- end }} diff --git a/charts/consul/templates/gateway-gatewayclass.yaml b/charts/consul/templates/gateway-gatewayclass.yaml new file mode 100644 index 0000000000..612627fbda --- /dev/null +++ b/charts/consul/templates/gateway-gatewayclass.yaml @@ -0,0 +1,18 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: GatewayClass +metadata: + name: consul-api-gateway + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: api-gateway-controller +spec: + controllerName: consul.hashicorp.com/consul-api-gateway-controller + parametersRef: + group: consul.hashicorp.com + kind: GatewayClassConfig + name: consul-api-gateway +{{- end }} diff --git a/charts/consul/templates/gateway-gatewayclassconfig.yaml b/charts/consul/templates/gateway-gatewayclassconfig.yaml new file mode 100644 index 0000000000..b812ebd814 --- /dev/null +++ b/charts/consul/templates/gateway-gatewayclassconfig.yaml @@ -0,0 +1,74 @@ +{{- if .Values.connectInject.enabled }} +--- +apiVersion: consul.hashicorp.com/v1alpha1 +kind: GatewayClassConfig +metadata: + name: consul-api-gateway + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: api-gateway +spec: + {{- if .Values.apiGateway.enabled }} # Overide values from the old stanza. To be removed in 1.17 (t-eckert 2023-05-19) + + {{- if .Values.apiGateway.managedGatewayClass.deployment }} + deployment: + {{- if .Values.apiGateway.managedGatewayClass.deployment.defaultInstances }} + defaultInstances: {{ .Values.apiGateway.managedGatewayClass.deployment.defaultInstances }} + {{- end}} + {{- if .Values.apiGateway.managedGatewayClass.deployment.maxInstances }} + maxInstances: {{ .Values.apiGateway.managedGatewayClass.deployment.maxInstances }} + {{- end}} + {{- if .Values.apiGateway.managedGatewayClass.deployment.minInstances }} + minInstances: {{ .Values.apiGateway.managedGatewayClass.deployment.minInstances }} + {{- end}} + {{- end}} + {{- if .Values.apiGateway.managedGatewayClass.nodeSelector }} + nodeSelector: + {{ tpl .Values.apiGateway.managedGatewayClass.nodeSelector . | indent 4 | trim }} + {{- end }} + {{- if .Values.apiGateway.managedGatewayClass.tolerations }} + tolerations: + {{ tpl .Values.apiGateway.managedGatewayClass.tolerations . | indent 4 | trim }} + {{- end }} + {{- if .Values.apiGateway.managedGatewayClass.copyAnnotations.service }} + copyAnnotations: + service: + {{ tpl .Values.apiGateway.managedGatewayClass.copyAnnotations.service.annotations . | nindent 6 | trim }} + {{- end }} + serviceType: {{ .Values.apiGateway.managedGatewayClass.serviceType }} + + {{- else }} + + {{- if .Values.connectInject.apiGateway.managedGatewayClass.deployment }} + deployment: + {{- if .Values.connectInject.apiGateway.managedGatewayClass.deployment.defaultInstances }} + defaultInstances: {{ .Values.connectInject.apiGateway.managedGatewayClass.deployment.defaultInstances }} + {{- end}} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.deployment.maxInstances }} + maxInstances: {{ .Values.connectInject.apiGateway.managedGatewayClass.deployment.maxInstances }} + {{- end}} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.deployment.minInstances }} + minInstances: {{ .Values.connectInject.apiGateway.managedGatewayClass.deployment.minInstances }} + {{- end}} + {{- end}} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.nodeSelector }} + nodeSelector: + {{ tpl .Values.connectInject.apiGateway.managedGatewayClass.nodeSelector . | indent 4 | trim }} + {{- end }} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.tolerations }} + tolerations: + {{ tpl .Values.connectInject.apiGateway.managedGatewayClass.tolerations . | indent 4 | trim }} + {{- end }} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.copyAnnotations.service }} + copyAnnotations: + service: + {{ tpl .Values.connectInject.apiGateway.managedGatewayClass.copyAnnotations.service.annotations . | nindent 6 | trim }} + {{- end }} + serviceType: {{ .Values.connectInject.apiGateway.managedGatewayClass.serviceType }} + + {{- end }} + +{{- end }} diff --git a/charts/consul/test/unit/connect-inject-clusterrole.bats b/charts/consul/test/unit/connect-inject-clusterrole.bats index 4acdf211d2..ace8c18d4a 100644 --- a/charts/consul/test/unit/connect-inject-clusterrole.bats +++ b/charts/consul/test/unit/connect-inject-clusterrole.bats @@ -77,7 +77,7 @@ load _helpers --set 'client.enabled=true' \ --set 'connectInject.enabled=true' \ . | tee /dev/stderr | - yq -r '.rules[3]' | tee /dev/stderr) + yq -r '.rules[4]' | tee /dev/stderr) local actual=$(echo $object | yq -r '.resources[| index("pods")' | tee /dev/stderr) [ "${actual}" != null ] @@ -106,7 +106,7 @@ load _helpers --set 'client.enabled=true' \ --set 'connectInject.enabled=true' \ . | tee /dev/stderr | - yq -r '.rules[4]' | tee /dev/stderr) + yq -r '.rules[5]' | tee /dev/stderr) local actual=$(echo $object | yq -r '.resources[| index("leases")' | tee /dev/stderr) [ "${actual}" != null ] @@ -154,7 +154,7 @@ load _helpers #-------------------------------------------------------------------- # global.enablePodSecurityPolicies -@test "connectInject/ClusterRole: no podsecuritypolicies access with global.enablePodSecurityPolicies=false" { +@test "connectInject/ClusterRole: allows podsecuritypolicies access with global.enablePodSecurityPolicies=false" { cd `chart_dir` local actual=$(helm template \ -s templates/connect-inject-clusterrole.yaml \ @@ -162,7 +162,7 @@ load _helpers --set 'global.enablePodSecurityPolicies=false' \ . | tee /dev/stderr | yq -r '.rules | map(select(.resources[0] == "podsecuritypolicies")) | length' | tee /dev/stderr) - [ "${actual}" = "0" ] + [ "${actual}" = "1" ] } @test "connectInject/ClusterRole: allows podsecuritypolicies access with global.enablePodSecurityPolicies=true" { @@ -197,7 +197,7 @@ load _helpers --set 'global.secretsBackend.vault.consulServerRole=bar' \ --set 'global.secretsBackend.vault.consulCARole=test2' \ . | tee /dev/stderr | - yq -r '.rules[5]' | tee /dev/stderr) + yq -r '.rules[6]' | tee /dev/stderr) local actual=$(echo $object | yq -r '.resources[0]' | tee /dev/stderr) [ "${actual}" = "mutatingwebhookconfigurations" ] diff --git a/charts/consul/test/unit/connect-inject-deployment.bats b/charts/consul/test/unit/connect-inject-deployment.bats index 90166daca2..c60ea14f1f 100755 --- a/charts/consul/test/unit/connect-inject-deployment.bats +++ b/charts/consul/test/unit/connect-inject-deployment.bats @@ -2436,3 +2436,4 @@ reservedNameTest() { jq -r '. | select( .name == "CONSUL_TLS_SERVER_NAME").value' | tee /dev/stderr) [ "${actual}" = "server.dc1.consul" ] } + diff --git a/charts/consul/test/unit/gateway-gatewayclass.bats b/charts/consul/test/unit/gateway-gatewayclass.bats new file mode 100755 index 0000000000..ac8a53aed9 --- /dev/null +++ b/charts/consul/test/unit/gateway-gatewayclass.bats @@ -0,0 +1,21 @@ +#!/usr/bin/env bats + +load _helpers + +@test "apiGateway/GatewayClass: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/gateway-gatewayclass.yaml \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "apiGateway/GatewayClass: disabled with connectInject.enabled false" { + cd `chart_dir` + assert_empty helm template \ + -s templates/gateway-gatewayclass.yaml \ + --set 'connectInject.enabled=false' \ + . +} + diff --git a/charts/consul/test/unit/gateway-gatewayclassconfig.bats b/charts/consul/test/unit/gateway-gatewayclassconfig.bats new file mode 100644 index 0000000000..30aa972cbe --- /dev/null +++ b/charts/consul/test/unit/gateway-gatewayclassconfig.bats @@ -0,0 +1,117 @@ +#!/usr/bin/env bats + +load _helpers + +@test "apiGateway/GatewayClassConfig: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/gateway-gatewayclassconfig.yaml \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "apiGateway/GatewayClassConfig: disabled with connectInject.enabled" { + cd `chart_dir` + assert_empty helm template \ + -s templates/gateway-gatewayclassconfig.yaml \ + --set 'connectInject.enabled=false' \ + . +} + +#-------------------------------------------------------------------- +# fallback configuration +# to be removed in 1.17 (t-eckert 2023-05-23) + +@test "apiGateway/GatewayClassConfig: fallback configuration is used when apiGateway.enabled is true" { + cd `chart_dir` + local spec=$(helm template \ + -s templates/gateway-gatewayclassconfig.yaml \ + --set 'apiGateway.enabled=true' \ + --set 'apiGateway.image=testing' \ + --set 'apiGateway.managedGatewayClass.nodeSelector=foo: bar' \ + --set 'apiGateway.managedGatewayClass.tolerations=- key: bar' \ + --set 'apiGateway.managedGatewayClass.copyAnnotations.service.annotations=- bingo' \ + --set 'apiGateway.managedGatewayClass.serviceType=LoadBalancer' \ + . | tee /dev/stderr | + yq '.spec' | tee /dev/stderr) + + local actual=$(echo "$spec" | + jq -r '.nodeSelector.foo') + [ "${actual}" = "bar" ] + + local actual=$(echo "$spec" | + jq -r '.tolerations[0].key') + [ "${actual}" = "bar" ] + + local actual=$(echo "$spec" | + jq -r '.copyAnnotations.service[0]') + [ "${actual}" = "bingo" ] + + local actual=$(echo "$spec" | + jq -r '.serviceType') + [ "${actual}" = "LoadBalancer" ] +} + +#-------------------------------------------------------------------- +# configuration + +@test "apiGateway/GatewayClassConfig: default configuration" { + cd `chart_dir` + local spec=$(helm template \ + -s templates/gateway-gatewayclassconfig.yaml \ + . | tee /dev/stderr | + yq '.spec' | tee /dev/stderr) + + local actual=$(echo "$spec" | + jq -r '.deployment.defaultInstances') + [ "${actual}" = 1 ] + + local actual=$(echo "$spec" | + jq -r '.deployment.maxInstances') + [ "${actual}" = 1 ] + + local actual=$(echo "$spec" | + jq -r '.deployment.minInstances') + [ "${actual}" = 1 ] +} + +@test "apigateway/gatewayclassconfig: custom configuration" { + cd `chart_dir` + local spec=$(helm template \ + -s templates/gateway-gatewayclassconfig.yaml \ + --set 'connectInject.apiGateway.managedGatewayClass.nodeSelector=foo: bar' \ + --set 'connectInject.apiGateway.managedGatewayClass.tolerations=- key: bar' \ + --set 'connectInject.apiGateway.managedGatewayClass.copyAnnotations.service.annotations=- bingo' \ + --set 'connectInject.apiGateway.managedGatewayClass.serviceType=LoadBalancer' \ + . | tee /dev/stderr | + yq '.spec' | tee /dev/stderr) + + local actual=$(echo "$spec" | + jq -r '.deployment.defaultInstances') + [ "${actual}" = "1" ] + + local actual=$(echo "$spec" | + jq -r '.deployment.maxInstances') + [ "${actual}" = "1" ] + + local actual=$(echo "$spec" | + jq -r '.deployment.minInstances') + [ "${actual}" = "1" ] + + local actual=$(echo "$spec" | + jq -r '.nodeSelector.foo') + [ "${actual}" = "bar" ] + + local actual=$(echo "$spec" | + jq -r '.tolerations[0].key') + [ "${actual}" = "bar" ] + + local actual=$(echo "$spec" | + jq -r '.copyAnnotations.service[0]') + [ "${actual}" = "bingo" ] + + local actual=$(echo "$spec" | + jq -r '.serviceType') + [ "${actual}" = "LoadBalancer" ] +} diff --git a/charts/consul/test/unit/helpers.bats b/charts/consul/test/unit/helpers.bats index 4245b519c4..efcbc116b5 100644 --- a/charts/consul/test/unit/helpers.bats +++ b/charts/consul/test/unit/helpers.bats @@ -115,7 +115,7 @@ load _helpers @test "helper/namespace: used everywhere" { cd `chart_dir` # Grep for files that don't have 'namespace: ' in them - local actual=$(grep -L 'namespace: ' templates/*.yaml | grep -v 'crd' | grep -v 'clusterrole' | grep -v 'api-gateway-gateway' | tee /dev/stderr ) + local actual=$(grep -L 'namespace: ' templates/*.yaml | grep -v 'crd' | grep -v 'clusterrole' | grep -v 'gateway-gateway' | tee /dev/stderr ) [ "${actual}" = '' ] } diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 83b18ae044..e71282c520 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -1993,6 +1993,79 @@ connectInject: # @type: integer minAvailable: null + # Configuration settings for the Consul API Gateway integration. + apiGateway: + # Configuration settings for the GatewayClass installed by Consul on Kubernetes. + managedGatewayClass: + # This value defines [`nodeSelector`](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) + # labels for gateway pod assignment, formatted as a multi-line string. + # + # Example: + # + # ```yaml + # nodeSelector: | + # beta.kubernetes.io/arch: amd64 + # ``` + # + # @type: string + nodeSelector: null + + # Toleration settings for gateway pods created with the managed gateway class. + # This should be a multi-line string matching the + # [Tolerations](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) array in a Pod spec. + # + # @type: string + tolerations: null + + # This value defines the type of Service created for gateways (e.g. LoadBalancer, ClusterIP) + serviceType: LoadBalancer + + # Configuration settings for annotations to be copied from the Gateway to other child resources. + copyAnnotations: + # This value defines a list of annotations to be copied from the Gateway to the Service created, formatted as a multi-line string. + # + # Example: + # + # ```yaml + # service: + # annotations: | + # - external-dns.alpha.kubernetes.io/hostname + # ``` + # + # @type: string + service: null + + # This value defines the number of pods to deploy for each Gateway as well as a min and max number of pods for all Gateways + deployment: + defaultInstances: 1 + maxInstances: 1 + minInstances: 1 + + # Configuration for the ServiceAccount created for the api-gateway component + serviceAccount: + # This value defines additional annotations for the client service account. This should be formatted as a multi-line + # string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # The resource settings for Pods handling traffic for Gateway API. + # @recurse: false + # @type: map + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "100Mi" + cpu: "100m" + # Configures consul-cni plugin for Consul Service mesh services cni: # If true, then all traffic redirection setup uses the consul-cni plugin. @@ -2922,6 +2995,7 @@ terminatingGateways: gateways: - name: terminating-gateway +# [DEPRECATED] Use connectInject.apiGateway instead. This stanza will be removed with the release of Consul 1.17 # Configuration settings for the Consul API Gateway integration apiGateway: # When true the helm chart will install the Consul API Gateway controller diff --git a/charts/embed_chart.go b/charts/embed_chart.go index ed416f04a5..bb3898a421 100644 --- a/charts/embed_chart.go +++ b/charts/embed_chart.go @@ -15,7 +15,8 @@ import "embed" // // The embed directive does not include files with underscores unless explicitly listed, which is why _helpers.tpl is // explicitly embedded. -//go:embed consul/Chart.yaml consul/values.yaml consul/templates consul/templates/_helpers.tpl +// +//go:embed consul/Chart.yaml consul/values.yaml consul/crds consul/templates consul/templates/_helpers.tpl var ConsulHelmChart embed.FS //go:embed demo/Chart.yaml demo/values.yaml demo/templates diff --git a/cli/helm/chart.go b/cli/helm/chart.go index 6fc033e1f4..3bb9484487 100644 --- a/cli/helm/chart.go +++ b/cli/helm/chart.go @@ -18,6 +18,7 @@ const ( chartFileName = "Chart.yaml" valuesFileName = "values.yaml" templatesDirName = "templates" + crdDirName = "crds" ) // LoadChart will attempt to load a Helm chart from the embedded file system. @@ -65,7 +66,7 @@ func readChartFiles(chart embed.FS, chartDirName string) ([]*loader.BufferedFile // filepath.* functions, then Go on Windows will try to use `\` delimiters to access // the embedded filesystem, which will then fail. - // Load Chart.yaml and values.yaml first. + // Load Chart.yaml and values.yaml. for _, f := range []string{chartFileName, valuesFileName} { file, err := readFile(chart, path.Join(chartDirName, f), chartDirName) if err != nil { @@ -74,7 +75,7 @@ func readChartFiles(chart embed.FS, chartDirName string) ([]*loader.BufferedFile chartFiles = append(chartFiles, file) } - // Now load everything under templates/. + // Load everything under templates/. dirs, err := chart.ReadDir(path.Join(chartDirName, templatesDirName)) if err != nil { return nil, err @@ -93,6 +94,25 @@ func readChartFiles(chart embed.FS, chartDirName string) ([]*loader.BufferedFile chartFiles = append(chartFiles, file) } + // Load everything under crds/. + dirs, err = chart.ReadDir(path.Join(chartDirName, crdDirName)) + if err != nil { + return nil, err + } + + for _, f := range dirs { + if f.IsDir() || f.Name() == "kustomization.yaml" { + // We only need to include files in the crds directory. + continue + } + + file, err := readFile(chart, path.Join(chartDirName, crdDirName, f.Name()), chartDirName) + if err != nil { + return nil, err + } + chartFiles = append(chartFiles, file) + } + return chartFiles, nil } diff --git a/cli/helm/chart_test.go b/cli/helm/chart_test.go index f0e33e092b..7fe6cf50a2 100644 --- a/cli/helm/chart_test.go +++ b/cli/helm/chart_test.go @@ -42,6 +42,7 @@ func TestReadChartFiles(t *testing.T) { "values.yaml": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\n# This is a mock Helm values.yaml file used for testing.\nkey: value", "templates/_helpers.tpl": "helpers", "templates/foo.yaml": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\nfoo: bar\n", + "crds/foo.yaml": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\nfoo: bar\n", } files, err := readChartFiles(testChartFiles, directory) diff --git a/cli/helm/test_fixtures/consul/crds/foo.yaml b/cli/helm/test_fixtures/consul/crds/foo.yaml new file mode 100644 index 0000000000..b17972509f --- /dev/null +++ b/cli/helm/test_fixtures/consul/crds/foo.yaml @@ -0,0 +1,4 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +foo: bar diff --git a/control-plane/api-gateway/binding/annotations.go b/control-plane/api-gateway/binding/annotations.go new file mode 100644 index 0000000000..2cb8f41792 --- /dev/null +++ b/control-plane/api-gateway/binding/annotations.go @@ -0,0 +1,41 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package binding + +import ( + "encoding/json" + + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" +) + +const ( + group = "api-gateway.consul.hashicorp.com" + annotationConfigKey = "api-gateway.consul.hashicorp.com/config" +) + +func serializeGatewayClassConfig(gw *gwv1beta1.Gateway, gwcc *v1alpha1.GatewayClassConfig) (*v1alpha1.GatewayClassConfig, bool) { + if gwcc == nil { + return nil, false + } + + if gw.Annotations == nil { + gw.Annotations = make(map[string]string) + } + + if annotatedConfig, ok := gw.Annotations[annotationConfigKey]; ok { + var config v1alpha1.GatewayClassConfig + if err := json.Unmarshal([]byte(annotatedConfig), &config.Spec); err == nil { + // if we can unmarshal the gateway, return it + return &config, false + } + } + + // otherwise if we failed to unmarshal or there was no annotation, marshal it onto + // the gateway + marshaled, _ := json.Marshal(gwcc.Spec) + gw.Annotations[annotationConfigKey] = string(marshaled) + return gwcc, true +} diff --git a/control-plane/api-gateway/binding/annotations_test.go b/control-plane/api-gateway/binding/annotations_test.go new file mode 100644 index 0000000000..1886ba80f5 --- /dev/null +++ b/control-plane/api-gateway/binding/annotations_test.go @@ -0,0 +1,206 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package binding + +import ( + "encoding/json" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" +) + +func TestSerializeGatewayClassConfig_HappyPath(t *testing.T) { + t.Parallel() + + type args struct { + gw *gwv1beta1.Gateway + gwcc *v1alpha1.GatewayClassConfig + } + tests := []struct { + name string + args args + expectedDidUpdate bool + }{ + { + name: "when gateway has not been annotated yet and annotations are nil", + args: args{ + gw: &gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-gw", + }, + Spec: gwv1beta1.GatewaySpec{}, + Status: gwv1beta1.GatewayStatus{}, + }, + gwcc: &v1alpha1.GatewayClassConfig{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "the config", + }, + Spec: v1alpha1.GatewayClassConfigSpec{ + ServiceType: pointerTo(corev1.ServiceType("serviceType")), + NodeSelector: map[string]string{ + "selector": "of node", + }, + Tolerations: []v1.Toleration{ + { + Key: "key", + Operator: "op", + Value: "120", + Effect: "to the moon", + TolerationSeconds: new(int64), + }, + }, + CopyAnnotations: v1alpha1.CopyAnnotationsSpec{ + Service: []string{"service"}, + }, + }, + }, + }, + expectedDidUpdate: true, + }, + { + name: "when gateway has not been annotated yet but annotations are empty", + args: args{ + gw: &gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-gw", + Annotations: make(map[string]string), + }, + Spec: gwv1beta1.GatewaySpec{}, + Status: gwv1beta1.GatewayStatus{}, + }, + gwcc: &v1alpha1.GatewayClassConfig{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "the config", + }, + Spec: v1alpha1.GatewayClassConfigSpec{ + ServiceType: pointerTo(corev1.ServiceType("serviceType")), + NodeSelector: map[string]string{ + "selector": "of node", + }, + Tolerations: []v1.Toleration{ + { + Key: "key", + Operator: "op", + Value: "120", + Effect: "to the moon", + TolerationSeconds: new(int64), + }, + }, + CopyAnnotations: v1alpha1.CopyAnnotationsSpec{ + Service: []string{"service"}, + }, + }, + }, + }, + expectedDidUpdate: true, + }, + { + name: "when gateway has been annotated", + args: args{ + gw: &gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-gw", + Annotations: map[string]string{ + annotationConfigKey: `{"serviceType":"serviceType","nodeSelector":{"selector":"of node"},"tolerations":[{"key":"key","operator":"op","value":"120","effect":"to the moon","tolerationSeconds":0}],"copyAnnotations":{"service":["service"]}}`, + }, + }, + Spec: gwv1beta1.GatewaySpec{}, + Status: gwv1beta1.GatewayStatus{}, + }, + gwcc: &v1alpha1.GatewayClassConfig{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "the config", + }, + Spec: v1alpha1.GatewayClassConfigSpec{ + ServiceType: pointerTo(corev1.ServiceType("serviceType")), + NodeSelector: map[string]string{ + "selector": "of node", + }, + Tolerations: []v1.Toleration{ + { + Key: "key", + Operator: "op", + Value: "120", + Effect: "to the moon", + TolerationSeconds: new(int64), + }, + }, + CopyAnnotations: v1alpha1.CopyAnnotationsSpec{ + Service: []string{"service"}, + }, + }, + }, + }, + expectedDidUpdate: false, + }, + { + name: "when gateway has been annotated but the serialization was invalid", + args: args{ + gw: &gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-gw", + Annotations: map[string]string{ + // we remove the opening brace to make unmarshalling fail + annotationConfigKey: `"serviceType":"serviceType","nodeSelector":{"selector":"of node"},"tolerations":[{"key":"key","operator":"op","value":"120","effect":"to the moon","tolerationSeconds":0}],"copyAnnotations":{"service":["service"]}}`, + }, + }, + Spec: gwv1beta1.GatewaySpec{}, + Status: gwv1beta1.GatewayStatus{}, + }, + gwcc: &v1alpha1.GatewayClassConfig{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "the config", + }, + Spec: v1alpha1.GatewayClassConfigSpec{ + ServiceType: pointerTo(corev1.ServiceType("serviceType")), + NodeSelector: map[string]string{ + "selector": "of node", + }, + Tolerations: []v1.Toleration{ + { + Key: "key", + Operator: "op", + Value: "120", + Effect: "to the moon", + TolerationSeconds: new(int64), + }, + }, + CopyAnnotations: v1alpha1.CopyAnnotationsSpec{ + Service: []string{"service"}, + }, + }, + }, + }, + expectedDidUpdate: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, actualDidUpdate := serializeGatewayClassConfig(tt.args.gw, tt.args.gwcc) + + if actualDidUpdate != tt.expectedDidUpdate { + t.Errorf("SerializeGatewayClassConfig() = %v, want %v", actualDidUpdate, tt.expectedDidUpdate) + } + + var config v1alpha1.GatewayClassConfig + err := json.Unmarshal([]byte(tt.args.gw.Annotations[annotationConfigKey]), &config.Spec) + require.NoError(t, err) + + if diff := cmp.Diff(config.Spec, tt.args.gwcc.Spec); diff != "" { + t.Errorf("Expected gwconfig spec to match serialized version (-want,+got):\n%s", diff) + } + }) + } +} diff --git a/control-plane/api-gateway/binding/binder.go b/control-plane/api-gateway/binding/binder.go new file mode 100644 index 0000000000..00e928db0a --- /dev/null +++ b/control-plane/api-gateway/binding/binder.go @@ -0,0 +1,433 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package binding + +import ( + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/translation" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul/api" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +const ( + // gatewayFinalizer is the finalizer we add to any gateway object. + gatewayFinalizer = "gateway-finalizer.consul.hashicorp.com" + + // namespaceNameLabel represents that label added automatically to namespaces in newer Kubernetes clusters. + namespaceNameLabel = "kubernetes.io/metadata.name" +) + +var ( + // constants extracted for ease of use. + kindGateway = "Gateway" + kindSecret = "Secret" + betaGroup = gwv1beta1.GroupVersion.Group + + // the list of kinds we can support by listener protocol. + supportedKindsForProtocol = map[gwv1beta1.ProtocolType][]gwv1beta1.RouteGroupKind{ + gwv1beta1.HTTPProtocolType: {{ + Group: (*gwv1beta1.Group)(&gwv1beta1.GroupVersion.Group), + Kind: "HTTPRoute", + }}, + gwv1beta1.HTTPSProtocolType: {{ + Group: (*gwv1beta1.Group)(&gwv1beta1.GroupVersion.Group), + Kind: "HTTPRoute", + }}, + gwv1beta1.TCPProtocolType: {{ + Group: (*gwv1alpha2.Group)(&gwv1alpha2.GroupVersion.Group), + Kind: "TCPRoute", + }}, + } +) + +// BinderConfig configures a binder instance with all of the information +// that it needs to know to generate a snapshot of bound state. +type BinderConfig struct { + // Translator instance initialized with proper name/namespace translation + // configuration from helm. + Translator translation.K8sToConsulTranslator + // ControllerName is the name of the controller used in determining which + // gateways we control, also leveraged for setting route statuses. + ControllerName string + + // GatewayClassConfig is the configuration corresponding to the given + // GatewayClass -- if it is nil we should treat the gateway as deleted + // since the gateway is now pointing to an invalid gateway class + GatewayClassConfig *v1alpha1.GatewayClassConfig + // GatewayClass is the GatewayClass corresponding to the Gateway we want to + // bind routes to. It is passed as a pointer because it could be nil. If no + // GatewayClass corresponds to a Gateway, we ought to clean up any sort of + // state that we may have set on the Gateway, its corresponding Routes or in + // Consul, because we should no longer be managing the Gateway (its association + // to our controller is through a parameter on the GatewayClass). + GatewayClass *gwv1beta1.GatewayClass + // Gateway is the Gateway being reconciled that we want to bind routes to. + Gateway gwv1beta1.Gateway + // HTTPRoutes is a list of HTTPRoute objects that ought to be bound to the Gateway. + HTTPRoutes []gwv1beta1.HTTPRoute + // TCPRoutes is a list of TCPRoute objects that ought to be bound to the Gateway. + TCPRoutes []gwv1alpha2.TCPRoute + // Secrets is a list of Secret objects that a Gateway references. + Secrets []corev1.Secret + // Pods are any pods that are part of the Gateway deployment + Pods []corev1.Pod + // MeshServices are all the MeshService objects that can be used for service lookup + MeshServices []v1alpha1.MeshService + // Service is the service associated with a Gateway deployment + Service *corev1.Service + + // TODO: Do we need to pass in Routes that have references to a Gateway in their statuses + // for cleanup purposes or is the below enough for record keeping? + + // ConsulGateway is the config entry we've created in Consul. + ConsulGateway *api.APIGatewayConfigEntry + // ConsulHTTPRoutes are a list of HTTPRouteConfigEntry objects that currently reference the + // Gateway we've created in Consul. + ConsulHTTPRoutes []api.HTTPRouteConfigEntry + // ConsulTCPRoutes are a list of TCPRouteConfigEntry objects that currently reference the + // Gateway we've created in Consul. + ConsulTCPRoutes []api.TCPRouteConfigEntry + // ConsulInlineCertificates is a list of certificates that have been created in Consul. + ConsulInlineCertificates []api.InlineCertificateConfigEntry + // ConnectInjectedServices is a list of all services that have been injected by our connect-injector + // and that we can, therefore reference on the mesh. + ConnectInjectedServices []api.CatalogService + // GatewayServices are the consul services for a given gateway + GatewayServices []api.CatalogService + + // Namespaces is a map of all namespaces in Kubernetes indexed by their names for looking up labels + // for AllowedRoutes matching purposes. + Namespaces map[string]corev1.Namespace + // ControlledGateways is a map of all Gateway objects that we currently should be interested in. This + // is used to determine whether we should garbage collect Certificate or Route objects when they become + // disassociated with a particular Gateway. + ControlledGateways map[types.NamespacedName]gwv1beta1.Gateway +} + +// Binder is used for generating a Snapshot of all operations that should occur both +// in Kubernetes and Consul as a result of binding routes to a Gateway. +type Binder struct { + statusSetter *setter + config BinderConfig +} + +// NewBinder creates a Binder object with the given configuration. +func NewBinder(config BinderConfig) *Binder { + return &Binder{config: config, statusSetter: newSetter(config.ControllerName)} +} + +// gatewayRef returns a Consul-based reference for the given Kubernetes gateway to +// be used for marking a deletion that is needed in Consul. +func (b *Binder) gatewayRef() api.ResourceReference { + return b.config.Translator.ReferenceForGateway(&b.config.Gateway) +} + +// isGatewayDeleted returns whether we should treat the given gateway as a deleted object. +// This is true if the gateway has a deleted timestamp, if its GatewayClass does not match +// our controller name, or if the GatewayClass it references doesn't exist. +func (b *Binder) isGatewayDeleted() bool { + gatewayClassMismatch := b.config.GatewayClass == nil || b.config.ControllerName != string(b.config.GatewayClass.Spec.ControllerName) + isGatewayDeleted := isDeleted(&b.config.Gateway) || gatewayClassMismatch || b.config.GatewayClassConfig == nil + return isGatewayDeleted +} + +// Snapshot generates a snapshot of operations that need to occur in Kubernetes and Consul +// in order for a Gateway to be reconciled. +func (b *Binder) Snapshot() Snapshot { + // at this point we assume all tcp routes and http routes + // actually reference this gateway + tracker := b.references() + meshServiceMap := meshServiceMap(b.config.MeshServices) + serviceMap := serviceMap(b.config.ConnectInjectedServices) + seenRoutes := map[api.ResourceReference]struct{}{} + snapshot := Snapshot{} + gwcc := b.config.GatewayClassConfig + + isGatewayDeleted := b.isGatewayDeleted() + if !isGatewayDeleted { + var updated bool + gwcc, updated = serializeGatewayClassConfig(&b.config.Gateway, gwcc) + + // we don't have a deletion but if we add a finalizer for the gateway, then just add it and return + // otherwise try and resolve as much as possible + if ensureFinalizer(&b.config.Gateway) || updated { + // if we've added the finalizer or serialized the class config, then update + snapshot.Kubernetes.Updates = append(snapshot.Kubernetes.Updates, &b.config.Gateway) + return snapshot + } + } + + httpRouteBinder := b.newHTTPRouteBinder(tracker, serviceMap, meshServiceMap) + tcpRouteBinder := b.newTCPRouteBinder(tracker, serviceMap, meshServiceMap) + + // used for tracking how many routes have successfully bound to which listeners + // on a gateway for reporting the number of bound routes in a gateway listener's + // status + boundCounts := make(map[gwv1beta1.SectionName]int) + + // attempt to bind all routes + + for _, r := range b.config.HTTPRoutes { + snapshot = httpRouteBinder.bind(pointerTo(r), boundCounts, seenRoutes, snapshot) + } + + for _, r := range b.config.TCPRoutes { + snapshot = tcpRouteBinder.bind(pointerTo(r), boundCounts, seenRoutes, snapshot) + } + + // now cleanup any routes that we haven't already processed + + for _, r := range b.config.ConsulHTTPRoutes { + snapshot = b.cleanHTTPRoute(pointerTo(r), seenRoutes, snapshot) + } + + for _, r := range b.config.ConsulTCPRoutes { + snapshot = b.cleanTCPRoute(pointerTo(r), seenRoutes, snapshot) + } + + // process certificates + + seenCerts := make(map[types.NamespacedName]api.ResourceReference) + for _, secret := range b.config.Secrets { + if isGatewayDeleted { + // we bypass the secret creation since we want to be able to GC if necessary + continue + } + + certificate := b.config.Translator.SecretToInlineCertificate(secret) + certificateRef := translation.EntryToReference(&certificate) + + // mark the certificate as processed + seenCerts[objectToMeta(&secret)] = certificateRef + // add the certificate to the set of upsert operations needed in Consul + snapshot.Consul.Updates = append(snapshot.Consul.Updates, &certificate) + } + + // clean up any inline certs that are now stale and can be GC'd + for _, cert := range b.config.ConsulInlineCertificates { + certRef := translation.EntryToNamespacedName(&cert) + if _, ok := seenCerts[certRef]; !ok { + // check to see if nothing is now referencing the certificate + if tracker.canGCSecret(certRef) { + ref := translation.EntryToReference(&cert) + // we can GC this now since it's not referenced by any Gateway + snapshot.Consul.Deletions = append(snapshot.Consul.Deletions, ref) + } + } + } + + // we only want to upsert the gateway into Consul or update its status + // if the gateway hasn't been marked for deletion + if !isGatewayDeleted { + snapshot.GatewayClassConfig = gwcc + snapshot.UpsertGatewayDeployment = true + + entry := b.config.Translator.GatewayToAPIGateway(b.config.Gateway, seenCerts) + snapshot.Consul.Updates = append(snapshot.Consul.Updates, &entry) + + registrationPods := []corev1.Pod{} + // filter out any pod that is being deleted + for _, pod := range b.config.Pods { + if !isDeleted(&pod) { + registrationPods = append(registrationPods, pod) + } + } + + registrations := registrationsForPods(entry.Namespace, b.config.Gateway, registrationPods) + snapshot.Consul.Registrations = registrations + + // deregister any not explicitly registered service + for _, service := range b.config.GatewayServices { + found := false + for _, registration := range registrations { + if service.ServiceID == registration.Service.ID { + found = true + } + } + if !found { + // we didn't register the service instance, so drop it + snapshot.Consul.Deregistrations = append(snapshot.Consul.Deregistrations, api.CatalogDeregistration{ + Node: service.Node, + ServiceID: service.ServiceID, + Namespace: service.Namespace, + }) + } + } + + // calculate the status for the gateway + var status gwv1beta1.GatewayStatus + gatewayValidation := validateGateway(b.config.Gateway, registrationPods, b.config.ConsulGateway) + listenerValidation := validateListeners(b.config.Gateway.Namespace, b.config.Gateway.Spec.Listeners, b.config.Secrets) + for i, listener := range b.config.Gateway.Spec.Listeners { + status.Listeners = append(status.Listeners, gwv1beta1.ListenerStatus{ + Name: listener.Name, + SupportedKinds: supportedKindsForProtocol[listener.Protocol], + AttachedRoutes: int32(boundCounts[listener.Name]), + Conditions: listenerValidation.Conditions(b.config.Gateway.Generation, i), + }) + } + status.Conditions = gatewayValidation.Conditions(b.config.Gateway.Generation, listenerValidation.Invalid()) + status.Addresses = addressesForGateway(b.config.Service, registrationPods) + + // only mark the gateway as needing a status update if there's a diff with its old + // status, this keeps the controller from infinitely reconciling + if !cmp.Equal(status, b.config.Gateway.Status, cmp.FilterPath(func(p cmp.Path) bool { + path := p.String() + return path == "Listeners.Conditions.LastTransitionTime" || path == "Conditions.LastTransitionTime" + }, cmp.Ignore())) { + b.config.Gateway.Status = status + snapshot.Kubernetes.StatusUpdates = append(snapshot.Kubernetes.StatusUpdates, &b.config.Gateway) + } + } else { + // if the gateway has been deleted, unset whatever we've set on it + ref := b.gatewayRef() + snapshot.Consul.Deletions = append(snapshot.Consul.Deletions, ref) + for _, service := range b.config.GatewayServices { + // deregister all gateways + snapshot.Consul.Deregistrations = append(snapshot.Consul.Deregistrations, api.CatalogDeregistration{ + Node: service.Node, + ServiceID: service.ServiceID, + Namespace: service.Namespace, + }) + } + if removeFinalizer(&b.config.Gateway) { + snapshot.Kubernetes.Updates = append(snapshot.Kubernetes.Updates, &b.config.Gateway) + } + } + + return snapshot +} + +// serviceMap constructs a map of services indexed by their Kubernetes namespace and name +// from the annotations that are set on the service. +func serviceMap(services []api.CatalogService) map[types.NamespacedName]api.CatalogService { + smap := make(map[types.NamespacedName]api.CatalogService) + for _, service := range services { + smap[serviceToNamespacedName(&service)] = service + } + return smap +} + +// meshServiceMap constructs a map of services indexed by their Kubernetes namespace and name. +func meshServiceMap(services []v1alpha1.MeshService) map[types.NamespacedName]v1alpha1.MeshService { + smap := make(map[types.NamespacedName]v1alpha1.MeshService) + for _, service := range services { + smap[client.ObjectKeyFromObject(&service)] = service + } + return smap +} + +// serviceToNamespacedName returns the Kubernetes namespace and name of a Consul catalog service +// based on the Metadata annotations written on the service. +func serviceToNamespacedName(s *api.CatalogService) types.NamespacedName { + var ( + metaKeyKubeNS = "k8s-namespace" + metaKeyKubeServiceName = "k8s-service-name" + ) + return types.NamespacedName{ + Namespace: s.ServiceMeta[metaKeyKubeNS], + Name: s.ServiceMeta[metaKeyKubeServiceName], + } +} + +func addressesForGateway(service *corev1.Service, pods []corev1.Pod) []gwv1beta1.GatewayAddress { + if service == nil { + return addressesFromPods(pods) + } + + switch service.Spec.Type { + case corev1.ServiceTypeLoadBalancer: + return addressesFromLoadBalancer(service) + case corev1.ServiceTypeClusterIP: + return addressesFromClusterIP(service) + case corev1.ServiceTypeNodePort: + /* For serviceType: NodePort, there isn't a consistent way to guarantee access to the + * service from outside the k8s cluster. For now, we're putting the IP address of the + * nodes that the gateway pods are running on. + * The practitioner will have to understand that they may need to port forward into the + * cluster (in the case of Kind) or open firewall rules (in the case of GKE) in order to + * access the gateway from outside the cluster. + */ + return addressesFromPodHosts(pods) + } + + return []gwv1beta1.GatewayAddress{} +} + +func addressesFromLoadBalancer(service *corev1.Service) []gwv1beta1.GatewayAddress { + addresses := []gwv1beta1.GatewayAddress{} + + for _, ingress := range service.Status.LoadBalancer.Ingress { + if ingress.IP != "" { + addresses = append(addresses, gwv1beta1.GatewayAddress{ + Type: pointerTo(gwv1beta1.IPAddressType), + Value: ingress.IP, + }) + } + if ingress.Hostname != "" { + addresses = append(addresses, gwv1beta1.GatewayAddress{ + Type: pointerTo(gwv1beta1.HostnameAddressType), + Value: ingress.Hostname, + }) + } + } + + return addresses +} + +func addressesFromClusterIP(service *corev1.Service) []gwv1beta1.GatewayAddress { + addresses := []gwv1beta1.GatewayAddress{} + + if service.Spec.ClusterIP != "" { + addresses = append(addresses, gwv1beta1.GatewayAddress{ + Type: pointerTo(gwv1beta1.IPAddressType), + Value: service.Spec.ClusterIP, + }) + } + + return addresses +} + +func addressesFromPods(pods []corev1.Pod) []gwv1beta1.GatewayAddress { + addresses := []gwv1beta1.GatewayAddress{} + seenIPs := make(map[string]struct{}) + + for _, pod := range pods { + if pod.Status.PodIP != "" { + if _, found := seenIPs[pod.Status.PodIP]; !found { + addresses = append(addresses, gwv1beta1.GatewayAddress{ + Type: pointerTo(gwv1beta1.IPAddressType), + Value: pod.Status.PodIP, + }) + seenIPs[pod.Status.PodIP] = struct{}{} + } + } + } + + return addresses +} + +func addressesFromPodHosts(pods []corev1.Pod) []gwv1beta1.GatewayAddress { + addresses := []gwv1beta1.GatewayAddress{} + seenIPs := make(map[string]struct{}) + + for _, pod := range pods { + if pod.Status.HostIP != "" { + if _, found := seenIPs[pod.Status.HostIP]; !found { + addresses = append(addresses, gwv1beta1.GatewayAddress{ + Type: pointerTo(gwv1beta1.IPAddressType), + Value: pod.Status.HostIP, + }) + seenIPs[pod.Status.HostIP] = struct{}{} + } + } + } + + return addresses +} diff --git a/control-plane/api-gateway/binding/binder_test.go b/control-plane/api-gateway/binding/binder_test.go new file mode 100644 index 0000000000..6078944004 --- /dev/null +++ b/control-plane/api-gateway/binding/binder_test.go @@ -0,0 +1,2683 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package binding + +import ( + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul/api" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +func TestBinder_Lifecycle(t *testing.T) { + t.Parallel() + + className := "gateway-class" + gatewayClassName := gwv1beta1.ObjectName(className) + controllerName := "test-controller" + deletionTimestamp := pointerTo(metav1.Now()) + gatewayClass := &gwv1beta1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: className, + }, + Spec: gwv1beta1.GatewayClassSpec{ + ControllerName: gwv1beta1.GatewayController(controllerName), + }, + } + + for name, tt := range map[string]struct { + config BinderConfig + expected Snapshot + }{ + "no gateway class and empty routes": { + config: BinderConfig{ + Gateway: gwv1beta1.Gateway{}, + }, + expected: Snapshot{ + Consul: ConsulSnapshot{ + Deletions: []api.ResourceReference{{ + Kind: api.APIGateway, + }}, + }, + }, + }, + "no gateway class and empty routes remove finalizer": { + config: BinderConfig{ + Gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Finalizers: []string{gatewayFinalizer}, + }, + }, + }, + expected: Snapshot{ + Kubernetes: KubernetesSnapshot{ + Updates: []client.Object{ + &gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Finalizers: []string{}, + }, + }, + }, + }, + Consul: ConsulSnapshot{ + Deletions: []api.ResourceReference{{ + Kind: api.APIGateway, + }}, + }, + }, + }, + "deleting gateway empty routes": { + config: BinderConfig{ + ControllerName: controllerName, + GatewayClass: gatewayClass, + Gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + DeletionTimestamp: deletionTimestamp, + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gatewayClassName, + }, + }, + }, + expected: Snapshot{ + Kubernetes: KubernetesSnapshot{ + Updates: []client.Object{ + &gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + DeletionTimestamp: deletionTimestamp, + Finalizers: []string{}, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gatewayClassName, + }, + }, + }, + }, + Consul: ConsulSnapshot{ + Deletions: []api.ResourceReference{{ + Kind: api.APIGateway, + }}, + }, + }, + }, + "basic gateway no finalizer": { + config: BinderConfig{ + ControllerName: controllerName, + GatewayClass: gatewayClass, + Gateway: gwv1beta1.Gateway{ + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gatewayClassName, + }, + }, + }, + expected: Snapshot{ + Kubernetes: KubernetesSnapshot{ + Updates: []client.Object{ + &gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gatewayClassName, + }, + }, + }, + }, + Consul: ConsulSnapshot{}, + }, + }, + "basic gateway": { + config: BinderConfig{ + ControllerName: controllerName, + GatewayClass: gatewayClass, + Gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gatewayClassName, + Listeners: []gwv1beta1.Listener{{ + TLS: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{{ + Name: "secret-one", + }}, + }, + }}, + }, + }, + Secrets: []corev1.Secret{{ + ObjectMeta: metav1.ObjectMeta{ + Name: "secret-one", + }, + }}, + }, + expected: Snapshot{ + Kubernetes: KubernetesSnapshot{ + StatusUpdates: []client.Object{ + &gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gatewayClassName, + Listeners: []gwv1beta1.Listener{{ + TLS: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{{ + Name: "secret-one", + }}, + }, + }}, + }, + Status: gwv1beta1.GatewayStatus{ + Addresses: []gwv1beta1.GatewayAddress{}, + Conditions: []metav1.Condition{{ + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "ListenersNotValid", + Message: "one or more listeners are invalid", + }, { + Type: "Programmed", + Status: metav1.ConditionFalse, + Reason: "Pending", + Message: "gateway pods are still being scheduled", + }}, + Listeners: []gwv1beta1.ListenerStatus{{ + Conditions: []metav1.Condition{{ + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "UnsupportedProtocol", + Message: "listener protocol is unsupported", + }, { + Type: "Conflicted", + Status: metav1.ConditionFalse, + Reason: "NoConflicts", + Message: "listener has no conflicts", + }, { + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved certificate references", + }}, + }}, + }, + }, + }, + }, + Consul: ConsulSnapshot{ + Updates: []api.ConfigEntry{ + &api.InlineCertificateConfigEntry{ + Kind: api.InlineCertificate, + Name: "secret-one", + Meta: map[string]string{ + "k8s-name": "secret-one", + "k8s-namespace": "", + "k8s-service-name": "secret-one", + "managed-by": "consul-k8s-gateway-controller", + }, + }, + &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Meta: map[string]string{ + "k8s-name": "", + "k8s-namespace": "", + "k8s-service-name": "", + "managed-by": "consul-k8s-gateway-controller", + }, + Listeners: []api.APIGatewayListener{{ + TLS: api.APIGatewayTLSConfiguration{ + Certificates: []api.ResourceReference{{ + Kind: api.InlineCertificate, + Name: "secret-one", + }}, + }, + }}, + }, + }, + Registrations: []api.CatalogRegistration{}, + }, + }, + }, + "gateway http route no finalizer": { + config: BinderConfig{ + ControllerName: controllerName, + GatewayClass: gatewayClass, + Gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gatewayClassName, + }, + }, + HTTPRoutes: []gwv1beta1.HTTPRoute{{ + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, + }, + }, + }}, + }, + expected: Snapshot{ + Kubernetes: KubernetesSnapshot{ + Updates: []client.Object{ + &gwv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, + }, + }, + }, + }, + StatusUpdates: []client.Object{ + &gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gatewayClassName, + }, + Status: gwv1beta1.GatewayStatus{ + Addresses: []gwv1beta1.GatewayAddress{}, + Conditions: []metav1.Condition{{ + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "gateway accepted", + }, { + Type: "Programmed", + Status: metav1.ConditionFalse, + Reason: "Pending", + Message: "gateway pods are still being scheduled", + }}, + }, + }, + }, + }, + Consul: ConsulSnapshot{ + Updates: []api.ConfigEntry{ + &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "gateway", + Meta: map[string]string{ + "k8s-name": "gateway", + "k8s-namespace": "", + "k8s-service-name": "gateway", + "managed-by": "consul-k8s-gateway-controller", + }, + Listeners: []api.APIGatewayListener{}, + }, + }, + Registrations: []api.CatalogRegistration{}, + }, + }, + }, + "gateway http route deleting": { + config: BinderConfig{ + ControllerName: controllerName, + GatewayClass: gatewayClass, + Gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gatewayClassName, + }, + }, + HTTPRoutes: []gwv1beta1.HTTPRoute{{ + ObjectMeta: metav1.ObjectMeta{ + DeletionTimestamp: deletionTimestamp, + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, + }, + }, + }}, + }, + expected: Snapshot{ + Kubernetes: KubernetesSnapshot{ + Updates: []client.Object{ + &gwv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + DeletionTimestamp: deletionTimestamp, + Finalizers: []string{}, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, + }, + }, + }, + }, + StatusUpdates: []client.Object{ + &gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gatewayClassName, + }, + Status: gwv1beta1.GatewayStatus{ + Addresses: []gwv1beta1.GatewayAddress{}, + Conditions: []metav1.Condition{{ + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "gateway accepted", + }, { + Type: "Programmed", + Status: metav1.ConditionFalse, + Reason: "Pending", + Message: "gateway pods are still being scheduled", + }}, + }, + }, + }, + }, + Consul: ConsulSnapshot{ + Updates: []api.ConfigEntry{ + &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "gateway", + Meta: map[string]string{ + "k8s-name": "gateway", + "k8s-namespace": "", + "k8s-service-name": "gateway", + "managed-by": "consul-k8s-gateway-controller", + }, + Listeners: []api.APIGatewayListener{}, + }, + }, + Deletions: []api.ResourceReference{{ + Kind: api.HTTPRoute, + }}, + Registrations: []api.CatalogRegistration{}, + }, + }, + }, + "gateway tcp route no finalizer": { + config: BinderConfig{ + ControllerName: controllerName, + GatewayClass: gatewayClass, + Gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gatewayClassName, + }, + }, + TCPRoutes: []gwv1alpha2.TCPRoute{{ + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, + }, + }, + }}, + }, + expected: Snapshot{ + Kubernetes: KubernetesSnapshot{ + Updates: []client.Object{ + &gwv1alpha2.TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, + }, + }, + }, + }, + StatusUpdates: []client.Object{ + &gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gatewayClassName, + }, + Status: gwv1beta1.GatewayStatus{ + Addresses: []gwv1beta1.GatewayAddress{}, + Conditions: []metav1.Condition{{ + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "gateway accepted", + }, { + Type: "Programmed", + Status: metav1.ConditionFalse, + Reason: "Pending", + Message: "gateway pods are still being scheduled", + }}, + }, + }, + }, + }, + Consul: ConsulSnapshot{ + Updates: []api.ConfigEntry{ + &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "gateway", + Meta: map[string]string{ + "k8s-name": "gateway", + "k8s-namespace": "", + "k8s-service-name": "gateway", + "managed-by": "consul-k8s-gateway-controller", + }, + Listeners: []api.APIGatewayListener{}, + }, + }, + Registrations: []api.CatalogRegistration{}, + }, + }, + }, + "gateway tcp route deleting": { + config: BinderConfig{ + ControllerName: controllerName, + GatewayClass: gatewayClass, + Gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gatewayClassName, + }, + }, + TCPRoutes: []gwv1alpha2.TCPRoute{{ + ObjectMeta: metav1.ObjectMeta{ + DeletionTimestamp: deletionTimestamp, + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, + }, + }, + }}, + }, + expected: Snapshot{ + Kubernetes: KubernetesSnapshot{ + Updates: []client.Object{ + &gwv1alpha2.TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + DeletionTimestamp: deletionTimestamp, + Finalizers: []string{}, + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, + }, + }, + }, + }, + StatusUpdates: []client.Object{ + &gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gatewayClassName, + }, + Status: gwv1beta1.GatewayStatus{ + Addresses: []gwv1beta1.GatewayAddress{}, + Conditions: []metav1.Condition{{ + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "gateway accepted", + }, { + Type: "Programmed", + Status: metav1.ConditionFalse, + Reason: "Pending", + Message: "gateway pods are still being scheduled", + }}, + }, + }, + }, + }, + Consul: ConsulSnapshot{ + Updates: []api.ConfigEntry{ + &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "gateway", + Meta: map[string]string{ + "k8s-name": "gateway", + "k8s-namespace": "", + "k8s-service-name": "gateway", + "managed-by": "consul-k8s-gateway-controller", + }, + Listeners: []api.APIGatewayListener{}, + }, + }, + Deletions: []api.ResourceReference{{ + Kind: api.TCPRoute, + }}, + Registrations: []api.CatalogRegistration{}, + }, + }, + }, + "gateway deletion routes and secrets": { + config: BinderConfig{ + ControllerName: controllerName, + GatewayClass: gatewayClass, + Gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway", + DeletionTimestamp: deletionTimestamp, + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gatewayClassName, + Listeners: []gwv1beta1.Listener{{ + TLS: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{{ + Name: "secret-one", + }, { + Name: "secret-two", + }}, + }, + }}, + }, + }, + ControlledGateways: map[types.NamespacedName]gwv1beta1.Gateway{ + {Name: "gateway"}: { + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway", + DeletionTimestamp: deletionTimestamp, + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gatewayClassName, + Listeners: []gwv1beta1.Listener{{ + TLS: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{{ + Name: "secret-one", + }, { + Name: "secret-two", + }}, + }, + }}, + }, + }, + {Name: "gateway-two"}: { + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway-two", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gatewayClassName, + Listeners: []gwv1beta1.Listener{{ + TLS: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{{ + Name: "secret-one", + }, { + Name: "secret-three", + }}, + }, + }}, + }, + }, + }, + Secrets: []corev1.Secret{{ + ObjectMeta: metav1.ObjectMeta{ + Name: "secret-one", + }, + }, { + ObjectMeta: metav1.ObjectMeta{ + Name: "secret-two", + }, + }}, + HTTPRoutes: []gwv1beta1.HTTPRoute{{ + ObjectMeta: metav1.ObjectMeta{ + Name: "http-route-one", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, + }, + }, + }, { + ObjectMeta: metav1.ObjectMeta{ + Name: "http-route-two", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }, { + Name: "gateway-two", + }}, + }, + }, + Status: gwv1beta1.HTTPRouteStatus{ + RouteStatus: gwv1beta1.RouteStatus{ + Parents: []gwv1beta1.RouteParentStatus{{ + ControllerName: gwv1beta1.GatewayController(controllerName), + ParentRef: gwv1beta1.ParentReference{Name: "gateway"}, + Conditions: []metav1.Condition{{ + Type: "Accepted", + Status: metav1.ConditionTrue, + }}, + }, { + ControllerName: gwv1beta1.GatewayController(controllerName), + ParentRef: gwv1beta1.ParentReference{Name: "gateway-two"}, + Conditions: []metav1.Condition{{ + Type: "Accepted", + Status: metav1.ConditionTrue, + }}, + }}, + }, + }, + }}, + TCPRoutes: []gwv1alpha2.TCPRoute{{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tcp-route-one", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, + }, + }, + }, { + ObjectMeta: metav1.ObjectMeta{ + Name: "tcp-route-two", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }, { + Name: "gateway-two", + }}, + }, + }, + Status: gwv1alpha2.TCPRouteStatus{ + RouteStatus: gwv1beta1.RouteStatus{ + Parents: []gwv1beta1.RouteParentStatus{{ + ControllerName: gwv1beta1.GatewayController(controllerName), + ParentRef: gwv1beta1.ParentReference{Name: "gateway"}, + Conditions: []metav1.Condition{{ + Type: "Accepted", + Status: metav1.ConditionTrue, + }}, + }, { + ControllerName: gwv1beta1.GatewayController(controllerName), + ParentRef: gwv1beta1.ParentReference{Name: "gateway-two"}, + Conditions: []metav1.Condition{{ + Type: "Accepted", + Status: metav1.ConditionTrue, + }}, + }}, + }, + }, + }}, + ConsulHTTPRoutes: []api.HTTPRouteConfigEntry{{ + Kind: api.HTTPRoute, + Name: "http-route-two", + Meta: map[string]string{ + "k8s-name": "http-route-two", + "k8s-namespace": "", + "k8s-service-name": "http-route-two", + "managed-by": "consul-k8s-gateway-controller", + }, + Parents: []api.ResourceReference{{ + Kind: api.APIGateway, + Name: "gateway", + }, { + Kind: api.APIGateway, + Name: "gateway-two", + }}, + }}, + ConsulTCPRoutes: []api.TCPRouteConfigEntry{{ + Kind: api.TCPRoute, + Name: "tcp-route-two", + Meta: map[string]string{ + "k8s-name": "tcp-route-two", + "k8s-namespace": "", + "k8s-service-name": "tcp-route-two", + "managed-by": "consul-k8s-gateway-controller", + }, + Parents: []api.ResourceReference{{ + Kind: api.APIGateway, + Name: "gateway", + }, { + Kind: api.APIGateway, + Name: "gateway-two", + }}, + }}, + ConsulInlineCertificates: []api.InlineCertificateConfigEntry{{ + Kind: api.InlineCertificate, + Name: "secret-one", + Meta: map[string]string{ + "k8s-name": "secret-one", + "k8s-namespace": "", + "k8s-service-name": "secret-one", + "managed-by": "consul-k8s-gateway-controller", + }, + }, { + Kind: api.InlineCertificate, + Name: "secret-two", + Meta: map[string]string{ + "k8s-name": "secret-two", + "k8s-namespace": "", + "k8s-service-name": "secret-two", + "managed-by": "consul-k8s-gateway-controller", + }, + }}, + }, + expected: Snapshot{ + Kubernetes: KubernetesSnapshot{ + StatusUpdates: []client.Object{ + &gwv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "http-route-two", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }, { + Name: "gateway-two", + }}, + }, + }, + Status: gwv1beta1.HTTPRouteStatus{ + RouteStatus: gwv1beta1.RouteStatus{ + // removed gateway status + Parents: []gwv1beta1.RouteParentStatus{{ + ControllerName: gwv1beta1.GatewayController(controllerName), + ParentRef: gwv1beta1.ParentReference{Name: "gateway-two"}, + Conditions: []metav1.Condition{{ + Type: "Accepted", + Status: metav1.ConditionTrue, + }}, + }}, + }, + }, + }, + &gwv1alpha2.TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tcp-route-two", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }, { + Name: "gateway-two", + }}, + }, + }, + // removed gateway status + Status: gwv1alpha2.TCPRouteStatus{ + RouteStatus: gwv1beta1.RouteStatus{ + Parents: []gwv1beta1.RouteParentStatus{{ + ControllerName: gwv1beta1.GatewayController(controllerName), + ParentRef: gwv1beta1.ParentReference{Name: "gateway-two"}, + Conditions: []metav1.Condition{{ + Type: "Accepted", + Status: metav1.ConditionTrue, + }}, + }}, + }, + }, + }, + }, + Updates: []client.Object{ + &gwv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "http-route-one", + Finalizers: []string{}, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, + }, + }, + }, + &gwv1alpha2.TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tcp-route-one", + Finalizers: []string{}, + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, + }, + }, + }, + &gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway", + DeletionTimestamp: deletionTimestamp, + Finalizers: []string{}, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gatewayClassName, + Listeners: []gwv1beta1.Listener{{ + TLS: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{{ + Name: "secret-one", + }, { + Name: "secret-two", + }}, + }, + }}, + }, + }, + }, + }, + Consul: ConsulSnapshot{ + Updates: []api.ConfigEntry{ + &api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, + Name: "http-route-two", + Meta: map[string]string{ + "k8s-name": "http-route-two", + "k8s-namespace": "", + "k8s-service-name": "http-route-two", + "managed-by": "consul-k8s-gateway-controller", + }, + // dropped ref to gateway + Parents: []api.ResourceReference{{ + Kind: api.APIGateway, + Name: "gateway-two", + }}, + }, + &api.TCPRouteConfigEntry{ + Kind: api.TCPRoute, + Name: "tcp-route-two", + Meta: map[string]string{ + "k8s-name": "tcp-route-two", + "k8s-namespace": "", + "k8s-service-name": "tcp-route-two", + "managed-by": "consul-k8s-gateway-controller", + }, + // dropped ref to gateway + Parents: []api.ResourceReference{{ + Kind: api.APIGateway, + Name: "gateway-two", + }}, + }, + }, + Deletions: []api.ResourceReference{{ + Kind: api.HTTPRoute, + Name: "http-route-one", + }, { + Kind: api.TCPRoute, + Name: "tcp-route-one", + }, { + Kind: api.InlineCertificate, + Name: "secret-two", + }, { + Kind: api.APIGateway, + Name: "gateway", + }}, + }, + }, + }, + } { + t.Run(name, func(t *testing.T) { + tt.config.ControllerName = controllerName + tt.config.GatewayClassConfig = &v1alpha1.GatewayClassConfig{} + serializeGatewayClassConfig(&tt.config.Gateway, tt.config.GatewayClassConfig) + + binder := NewBinder(tt.config) + actual := binder.Snapshot() + + diff := cmp.Diff(tt.expected, actual, cmp.FilterPath(func(p cmp.Path) bool { + return p.String() == "GatewayClassConfig" || strings.HasSuffix(p.String(), "LastTransitionTime") || strings.HasSuffix(p.String(), "Annotations") || strings.HasSuffix(p.String(), "UpsertGatewayDeployment") + }, cmp.Ignore())) + if diff != "" { + t.Error("undexpected diff", diff) + } + }) + } +} + +func TestBinder_Registrations(t *testing.T) { + t.Parallel() + + className := "gateway-class" + gatewayClassName := gwv1beta1.ObjectName(className) + controllerName := "test-controller" + deletionTimestamp := pointerTo(metav1.Now()) + gatewayClass := &gwv1beta1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: className, + }, + Spec: gwv1beta1.GatewayClassSpec{ + ControllerName: gwv1beta1.GatewayController(controllerName), + }, + } + gatewayName := "gateway" + + for name, tt := range map[string]struct { + config BinderConfig + expectedRegistrations []string + expectedDeregistrations []api.CatalogDeregistration + }{ + "deleting gateway with consul services": { + config: BinderConfig{ + ControllerName: controllerName, + GatewayClass: gatewayClass, + Gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: gatewayName, + Finalizers: []string{gatewayFinalizer}, + DeletionTimestamp: deletionTimestamp, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gatewayClassName, + }, + }, + GatewayServices: []api.CatalogService{ + {Node: "test", ServiceID: "pod1", Namespace: "namespace1"}, + {Node: "test", ServiceID: "pod2", Namespace: "namespace1"}, + {Node: "test", ServiceID: "pod3", Namespace: "namespace1"}, + }, + }, + expectedDeregistrations: []api.CatalogDeregistration{ + {Node: "test", ServiceID: "pod1", Namespace: "namespace1"}, + {Node: "test", ServiceID: "pod2", Namespace: "namespace1"}, + {Node: "test", ServiceID: "pod3", Namespace: "namespace1"}, + }, + }, + "gateway with consul services and mixed pods": { + config: BinderConfig{ + ControllerName: controllerName, + GatewayClass: gatewayClass, + Gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: gatewayName, + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gatewayClassName, + }, + }, + Pods: []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{Name: "pod1", Namespace: "namespace1"}, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + Conditions: []corev1.PodCondition{{Type: corev1.PodReady, Status: corev1.ConditionTrue}}, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "pod3", Namespace: "namespace1"}, + Status: corev1.PodStatus{ + Phase: corev1.PodFailed, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "pod4", Namespace: "namespace1"}, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + Conditions: []corev1.PodCondition{{Type: corev1.PodReady, Status: corev1.ConditionTrue}}, + }, + }, + }, + GatewayServices: []api.CatalogService{ + {Node: "test", ServiceID: "pod1", Namespace: "namespace1"}, + {Node: "test", ServiceID: "pod2", Namespace: "namespace1"}, + {Node: "test", ServiceID: "pod3", Namespace: "namespace1"}, + }, + }, + expectedRegistrations: []string{"pod1", "pod3", "pod4"}, + expectedDeregistrations: []api.CatalogDeregistration{ + {Node: "test", ServiceID: "pod2", Namespace: "namespace1"}, + }, + }, + } { + t.Run(name, func(t *testing.T) { + tt.config.ControllerName = controllerName + tt.config.GatewayClassConfig = &v1alpha1.GatewayClassConfig{} + serializeGatewayClassConfig(&tt.config.Gateway, tt.config.GatewayClassConfig) + + binder := NewBinder(tt.config) + actual := binder.Snapshot() + + require.Len(t, actual.Consul.Registrations, len(tt.expectedRegistrations)) + for i := range actual.Consul.Registrations { + registration := actual.Consul.Registrations[i] + expected := tt.expectedRegistrations[i] + + require.EqualValues(t, expected, registration.Service.ID) + require.EqualValues(t, gatewayName, registration.Service.Service) + } + + require.EqualValues(t, tt.expectedDeregistrations, actual.Consul.Deregistrations) + }) + } +} + +func TestBinder_BindingRulesKitchenSink(t *testing.T) { + t.Parallel() + + className := "gateway-class" + gatewayClassName := gwv1beta1.ObjectName(className) + controllerName := "test-controller" + gatewayClass := &gwv1beta1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: className, + }, + Spec: gwv1beta1.GatewayClassSpec{ + ControllerName: gwv1beta1.GatewayController(controllerName), + }, + } + + gateway := gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gatewayClassName, + Listeners: []gwv1beta1.Listener{{ + Name: "http-listener-default-same", + Protocol: gwv1beta1.HTTPProtocolType, + }, { + Name: "http-listener-hostname", + Protocol: gwv1beta1.HTTPProtocolType, + Hostname: pointerTo[gwv1beta1.Hostname]("host.name"), + }, { + Name: "http-listener-mismatched-kind-allowed", + Protocol: gwv1beta1.HTTPProtocolType, + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Kinds: []gwv1beta1.RouteGroupKind{{ + Kind: "Foo", + }}, + }, + }, { + Name: "http-listener-explicit-all-allowed", + Protocol: gwv1beta1.HTTPProtocolType, + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: pointerTo(gwv1beta1.NamespacesFromAll), + }, + }, + }, { + Name: "http-listener-explicit-allowed-same", + Protocol: gwv1beta1.HTTPProtocolType, + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: pointerTo(gwv1beta1.NamespacesFromSame), + }, + }, + }, { + Name: "http-listener-allowed-selector", + Protocol: gwv1beta1.HTTPProtocolType, + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: pointerTo(gwv1beta1.NamespacesFromSelector), + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "test": "foo", + }, + }, + }, + }, + }, { + Name: "http-listener-tls", + Protocol: gwv1beta1.HTTPSProtocolType, + TLS: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{{ + Name: "secret-one", + }}, + }, + }, { + Name: "tcp-listener-default-same", + Protocol: gwv1beta1.TCPProtocolType, + }, { + Name: "tcp-listener-mismatched-kind-allowed", + Protocol: gwv1beta1.TCPProtocolType, + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Kinds: []gwv1beta1.RouteGroupKind{{ + Kind: "Foo", + }}, + }, + }, { + Name: "tcp-listener-explicit-all-allowed", + Protocol: gwv1beta1.TCPProtocolType, + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: pointerTo(gwv1beta1.NamespacesFromAll), + }, + }, + }, { + Name: "tcp-listener-explicit-allowed-same", + Protocol: gwv1beta1.TCPProtocolType, + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: pointerTo(gwv1beta1.NamespacesFromSame), + }, + }, + }, { + Name: "tcp-listener-allowed-selector", + Protocol: gwv1beta1.TCPProtocolType, + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: pointerTo(gwv1beta1.NamespacesFromSelector), + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "test": "foo", + }, + }, + }, + }, + }, { + Name: "tcp-listener-tls", + Protocol: gwv1beta1.TCPProtocolType, + TLS: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{{ + Name: "secret-one", + }}, + }, + }}, + }, + } + + namespaces := map[string]corev1.Namespace{ + "": { + ObjectMeta: metav1.ObjectMeta{ + Name: "", + }, + }, + "test": { + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Labels: map[string]string{ + "test": "foo", + }, + }, + }, + } + + defaultNamespacePointer := pointerTo[gwv1beta1.Namespace]("") + + httpTypeMeta := metav1.TypeMeta{} + httpTypeMeta.SetGroupVersionKind(gwv1beta1.SchemeGroupVersion.WithKind("HTTPRoute")) + tcpTypeMeta := metav1.TypeMeta{} + tcpTypeMeta.SetGroupVersionKind(gwv1beta1.SchemeGroupVersion.WithKind("TCPRoute")) + + for name, tt := range map[string]struct { + httpRoute *gwv1beta1.HTTPRoute + expectedHTTPRouteUpdate *gwv1beta1.HTTPRoute + expectedHTTPRouteUpdateStatus *gwv1beta1.HTTPRoute + expectedHTTPConsulRouteUpdate *api.HTTPRouteConfigEntry + expectedHTTPConsulRouteDelete *api.ResourceReference + + tcpRoute *gwv1alpha2.TCPRoute + expectedTCPRouteUpdate *gwv1alpha2.TCPRoute + expectedTCPRouteUpdateStatus *gwv1alpha2.TCPRoute + expectedTCPConsulRouteUpdate *api.TCPRouteConfigEntry + expectedTCPConsulRouteDelete *api.ResourceReference + }{ + "untargeted http route same namespace": { + httpRoute: &gwv1beta1.HTTPRoute{ + TypeMeta: httpTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, + }, + }, + }, + expectedHTTPRouteUpdateStatus: &gwv1beta1.HTTPRoute{ + TypeMeta: httpTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, + }, + }, + Status: gwv1beta1.HTTPRouteStatus{ + RouteStatus: gwv1beta1.RouteStatus{ + Parents: []gwv1beta1.RouteParentStatus{{ + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }}, + }, + }, + }, + }, + "untargeted http route same namespace missing backend": { + httpRoute: &gwv1beta1.HTTPRoute{ + TypeMeta: httpTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, + }, + Rules: []gwv1beta1.HTTPRouteRule{{ + BackendRefs: []gwv1beta1.HTTPBackendRef{{ + BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: gwv1beta1.ObjectName("backend"), + }, + }, + }}, + }}, + }, + }, + expectedHTTPRouteUpdateStatus: &gwv1beta1.HTTPRoute{ + TypeMeta: httpTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, + }, + Rules: []gwv1beta1.HTTPRouteRule{{ + BackendRefs: []gwv1beta1.HTTPBackendRef{{ + BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: gwv1beta1.ObjectName("backend"), + }, + }, + }}, + }}, + }, + Status: gwv1beta1.HTTPRouteStatus{ + RouteStatus: gwv1beta1.RouteStatus{ + Parents: []gwv1beta1.RouteParentStatus{{ + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionFalse, + Reason: "BackendNotFound", + Message: "/backend: backend not found", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }}, + }, + }, + }, + }, + "untargeted http route same namespace invalid backend type": { + httpRoute: &gwv1beta1.HTTPRoute{ + TypeMeta: httpTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, + }, + Rules: []gwv1beta1.HTTPRouteRule{{ + BackendRefs: []gwv1beta1.HTTPBackendRef{{ + BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: gwv1beta1.ObjectName("backend"), + Group: pointerTo[gwv1beta1.Group]("invalid.foo.com"), + }, + }, + }}, + }}, + }, + }, + expectedHTTPRouteUpdateStatus: &gwv1beta1.HTTPRoute{ + TypeMeta: httpTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, + }, + Rules: []gwv1beta1.HTTPRouteRule{{ + BackendRefs: []gwv1beta1.HTTPBackendRef{{ + BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: gwv1beta1.ObjectName("backend"), + Group: pointerTo[gwv1beta1.Group]("invalid.foo.com"), + }, + }, + }}, + }}, + }, + Status: gwv1beta1.HTTPRouteStatus{ + RouteStatus: gwv1beta1.RouteStatus{ + Parents: []gwv1beta1.RouteParentStatus{{ + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionFalse, + Reason: "InvalidKind", + Message: "/backend [Service.invalid.foo.com]: invalid backend kind", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }}, + }, + }, + }, + }, + "untargeted http route different namespace": { + httpRoute: &gwv1beta1.HTTPRoute{ + TypeMeta: httpTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + Namespace: "test", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + Namespace: defaultNamespacePointer, + }}, + }, + }, + }, + expectedHTTPRouteUpdateStatus: &gwv1beta1.HTTPRoute{ + TypeMeta: httpTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + Namespace: "test", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + Namespace: defaultNamespacePointer, + }}, + }, + }, + Status: gwv1beta1.HTTPRouteStatus{ + RouteStatus: gwv1beta1.RouteStatus{ + Parents: []gwv1beta1.RouteParentStatus{{ + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + Namespace: defaultNamespacePointer, + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }}, + }, + }, + }, + }, + "targeted http route same namespace": { + httpRoute: &gwv1beta1.HTTPRoute{ + TypeMeta: httpTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-default-same"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-hostname"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-tls"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), + }}, + }, + }, + }, + expectedHTTPRouteUpdateStatus: &gwv1beta1.HTTPRoute{ + TypeMeta: httpTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-default-same"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-hostname"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-tls"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), + }}, + }, + }, + Status: gwv1beta1.HTTPRouteStatus{ + RouteStatus: gwv1beta1.RouteStatus{ + Parents: []gwv1beta1.RouteParentStatus{{ + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-default-same"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-hostname"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-mismatched-kind-allowed: listener does not support route protocol", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-allowed-selector: listener does not allow binding routes from the given namespace", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-tls"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "tcp-listener-explicit-all-allowed: listener does not support route protocol", + }}, + }}, + }, + }, + }, + }, + "targeted http route different namespace": { + httpRoute: &gwv1beta1.HTTPRoute{ + TypeMeta: httpTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + Namespace: "test", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-default-same"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-hostname"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-tls"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), + }}, + }, + }, + }, + expectedHTTPRouteUpdateStatus: &gwv1beta1.HTTPRoute{ + TypeMeta: httpTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + Namespace: "test", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-default-same"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-hostname"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-tls"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), + }}, + }, + }, + Status: gwv1beta1.HTTPRouteStatus{ + RouteStatus: gwv1beta1.RouteStatus{ + Parents: []gwv1beta1.RouteParentStatus{{ + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-default-same"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-default-same: listener does not allow binding routes from the given namespace", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-hostname"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-hostname: listener does not allow binding routes from the given namespace", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-mismatched-kind-allowed: listener does not support route protocol", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-explicit-allowed-same: listener does not allow binding routes from the given namespace", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-tls"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-tls: listener does not allow binding routes from the given namespace", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "tcp-listener-explicit-all-allowed: listener does not support route protocol", + }}, + }}, + }, + }, + }, + }, + "untargeted tcp route same namespace": { + tcpRoute: &gwv1alpha2.TCPRoute{ + TypeMeta: tcpTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, + }, + }, + }, + expectedTCPRouteUpdateStatus: &gwv1alpha2.TCPRoute{ + TypeMeta: tcpTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, + }, + }, + Status: gwv1alpha2.TCPRouteStatus{ + RouteStatus: gwv1beta1.RouteStatus{ + Parents: []gwv1beta1.RouteParentStatus{{ + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }}, + }, + }, + }, + }, + "untargeted tcp route same namespace missing backend": { + tcpRoute: &gwv1alpha2.TCPRoute{ + TypeMeta: tcpTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, + }, + Rules: []gwv1alpha2.TCPRouteRule{{ + BackendRefs: []gwv1beta1.BackendRef{{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: gwv1beta1.ObjectName("backend"), + }, + }}, + }}, + }, + }, + expectedTCPRouteUpdateStatus: &gwv1alpha2.TCPRoute{ + TypeMeta: tcpTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, + }, + Rules: []gwv1alpha2.TCPRouteRule{{ + BackendRefs: []gwv1beta1.BackendRef{{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: gwv1beta1.ObjectName("backend"), + }, + }}, + }}, + }, + Status: gwv1alpha2.TCPRouteStatus{ + RouteStatus: gwv1beta1.RouteStatus{ + Parents: []gwv1beta1.RouteParentStatus{{ + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionFalse, + Reason: "BackendNotFound", + Message: "/backend: backend not found", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }}, + }, + }, + }, + }, + "untargeted tcp route same namespace invalid backend type": { + tcpRoute: &gwv1alpha2.TCPRoute{ + TypeMeta: tcpTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, + }, + Rules: []gwv1alpha2.TCPRouteRule{{ + BackendRefs: []gwv1beta1.BackendRef{{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: gwv1beta1.ObjectName("backend"), + Group: pointerTo[gwv1beta1.Group]("invalid.foo.com"), + }, + }}, + }}, + }, + }, + expectedTCPRouteUpdateStatus: &gwv1alpha2.TCPRoute{ + TypeMeta: tcpTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, + }, + Rules: []gwv1alpha2.TCPRouteRule{{ + BackendRefs: []gwv1beta1.BackendRef{{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: gwv1beta1.ObjectName("backend"), + Group: pointerTo[gwv1beta1.Group]("invalid.foo.com"), + }, + }}, + }}, + }, + Status: gwv1alpha2.TCPRouteStatus{ + RouteStatus: gwv1beta1.RouteStatus{ + Parents: []gwv1beta1.RouteParentStatus{{ + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionFalse, + Reason: "InvalidKind", + Message: "/backend [Service.invalid.foo.com]: invalid backend kind", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }}, + }, + }, + }, + }, + "untargeted tcp route different namespace": { + tcpRoute: &gwv1alpha2.TCPRoute{ + TypeMeta: tcpTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + Namespace: "test", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + Namespace: defaultNamespacePointer, + }}, + }, + }, + }, + expectedTCPRouteUpdateStatus: &gwv1alpha2.TCPRoute{ + TypeMeta: tcpTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + Namespace: "test", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + Namespace: defaultNamespacePointer, + }}, + }, + }, + Status: gwv1alpha2.TCPRouteStatus{ + RouteStatus: gwv1beta1.RouteStatus{ + Parents: []gwv1beta1.RouteParentStatus{{ + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + Namespace: defaultNamespacePointer, + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }}, + }, + }, + }, + }, + "targeted tcp route same namespace": { + tcpRoute: &gwv1alpha2.TCPRoute{ + TypeMeta: tcpTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-default-same"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-mismatched-kind-allowed"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-allowed-same"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-allowed-selector"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-tls"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), + }}, + }, + }, + }, + expectedTCPRouteUpdateStatus: &gwv1alpha2.TCPRoute{ + TypeMeta: tcpTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-default-same"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-mismatched-kind-allowed"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-allowed-same"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-allowed-selector"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-tls"), + }, { + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), + }}, + }, + }, + Status: gwv1alpha2.TCPRouteStatus{ + RouteStatus: gwv1beta1.RouteStatus{ + Parents: []gwv1beta1.RouteParentStatus{{ + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-default-same"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-mismatched-kind-allowed"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "tcp-listener-mismatched-kind-allowed: listener does not support route protocol", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-allowed-same"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-allowed-selector"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "tcp-listener-allowed-selector: listener does not allow binding routes from the given namespace", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-tls"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-explicit-all-allowed: listener does not support route protocol", + }}, + }}, + }, + }, + }, + }, + "targeted tcp route different namespace": { + tcpRoute: &gwv1alpha2.TCPRoute{ + TypeMeta: tcpTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + Namespace: "test", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-default-same"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-mismatched-kind-allowed"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-allowed-same"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-allowed-selector"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-tls"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), + }}, + }, + }, + }, + expectedTCPRouteUpdateStatus: &gwv1alpha2.TCPRoute{ + TypeMeta: tcpTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + Namespace: "test", + Finalizers: []string{gatewayFinalizer}, + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-default-same"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-mismatched-kind-allowed"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-allowed-same"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-allowed-selector"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-tls"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), + }}, + }, + }, + Status: gwv1alpha2.TCPRouteStatus{ + RouteStatus: gwv1beta1.RouteStatus{ + Parents: []gwv1beta1.RouteParentStatus{{ + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-default-same"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "tcp-listener-default-same: listener does not allow binding routes from the given namespace", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-mismatched-kind-allowed"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "tcp-listener-mismatched-kind-allowed: listener does not support route protocol", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-allowed-same"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "tcp-listener-explicit-allowed-same: listener does not allow binding routes from the given namespace", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-allowed-selector"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-tls"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "tcp-listener-tls: listener does not allow binding routes from the given namespace", + }}, + }, { + ControllerName: gatewayClass.Spec.ControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-explicit-all-allowed: listener does not support route protocol", + }}, + }}, + }, + }, + }, + }, + } { + t.Run(name, func(t *testing.T) { + config := BinderConfig{ + ControllerName: controllerName, + GatewayClassConfig: &v1alpha1.GatewayClassConfig{}, + GatewayClass: gatewayClass, + Gateway: gateway, + Namespaces: namespaces, + ControlledGateways: map[types.NamespacedName]gwv1beta1.Gateway{ + {Name: "gateway"}: gateway, + }, + Secrets: []corev1.Secret{{ + ObjectMeta: metav1.ObjectMeta{ + Name: "secret-one", + }, + }}, + } + serializeGatewayClassConfig(&config.Gateway, config.GatewayClassConfig) + + if tt.httpRoute != nil { + config.HTTPRoutes = append(config.HTTPRoutes, *tt.httpRoute) + } + if tt.tcpRoute != nil { + config.TCPRoutes = append(config.TCPRoutes, *tt.tcpRoute) + } + + binder := NewBinder(config) + actual := binder.Snapshot() + + compareUpdates(t, tt.expectedHTTPRouteUpdate, actual.Kubernetes.Updates) + compareUpdates(t, tt.expectedTCPRouteUpdate, actual.Kubernetes.Updates) + compareUpdates(t, tt.expectedHTTPRouteUpdateStatus, actual.Kubernetes.StatusUpdates) + compareUpdates(t, tt.expectedTCPRouteUpdateStatus, actual.Kubernetes.StatusUpdates) + }) + } +} + +func compareUpdates[T client.Object](t *testing.T, expected T, updates []client.Object) { + t.Helper() + + if isNil(expected) { + for _, update := range updates { + if u, ok := update.(T); ok { + t.Error("found unexpected update", u) + } + } + } else { + found := false + for _, update := range updates { + if u, ok := update.(T); ok { + diff := cmp.Diff(expected, u, cmp.FilterPath(func(p cmp.Path) bool { + return p.String() == "Status.RouteStatus.Parents.Conditions.LastTransitionTime" + }, cmp.Ignore())) + if diff != "" { + t.Error("diff between actual and expected", diff) + } + found = true + } + } + if !found { + t.Error("expected route update not found in", updates) + } + } +} diff --git a/control-plane/api-gateway/binding/references.go b/control-plane/api-gateway/binding/references.go new file mode 100644 index 0000000000..ec598c6364 --- /dev/null +++ b/control-plane/api-gateway/binding/references.go @@ -0,0 +1,135 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package binding + +import ( + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +// referenceTracker acts as a reference counting object for: +// 1. the number of controlled gateways that are referenced by an HTTPRoute +// 2. the number of controlled gateways that are referenced by a TCPRoute +// 3. the number of gateways that reference a certificate Secret +// +// These are used for determining when dissasociating from a gateway +// should cause us to cleanup a route or certificate both in Consul and +// whatever state we have set on the object in Kubernetes. +type referenceTracker struct { + httpRouteReferencesGateways map[types.NamespacedName]int + tcpRouteReferencesGateways map[types.NamespacedName]int + certificatesReferencedByGateways map[types.NamespacedName]int +} + +// isLastReference checks if the given gateway is the last controlled gateway +// that a route references. If it is and the gateway has been deleted, we +// should clean up all state created for the route. +func (r referenceTracker) isLastReference(object client.Object) bool { + key := types.NamespacedName{ + Namespace: object.GetNamespace(), + Name: object.GetName(), + } + + switch object.(type) { + case *gwv1alpha2.TCPRoute: + return r.tcpRouteReferencesGateways[key] == 1 + case *gwv1beta1.HTTPRoute: + return r.httpRouteReferencesGateways[key] == 1 + default: + return false + } +} + +// canGCSecret checks if we can garbage collect a secret that has +// not been upserted. +func (r referenceTracker) canGCSecret(key types.NamespacedName) bool { + // should this be 1 or 0? + return r.certificatesReferencedByGateways[key] == 1 +} + +// references initializes a referenceTracker based on the HTTPRoutes, TCPRoutes, +// and ControlledGateways associated with this Binder. +func (b *Binder) references() referenceTracker { + tracker := referenceTracker{ + httpRouteReferencesGateways: make(map[types.NamespacedName]int), + tcpRouteReferencesGateways: make(map[types.NamespacedName]int), + certificatesReferencedByGateways: make(map[types.NamespacedName]int), + } + + for _, route := range b.config.HTTPRoutes { + references := map[types.NamespacedName]struct{}{} + for _, ref := range route.Spec.ParentRefs { + for _, gateway := range b.config.ControlledGateways { + parentName := string(ref.Name) + parentNamespace := valueOr(ref.Namespace, route.Namespace) + if nilOrEqual(ref.Group, betaGroup) && + nilOrEqual(ref.Kind, kindGateway) && + gateway.Namespace == parentNamespace && + gateway.Name == parentName { + // the route references a gateway we control, store the ref to this gateway + references[types.NamespacedName{ + Namespace: parentNamespace, + Name: parentName, + }] = struct{}{} + } + } + } + tracker.httpRouteReferencesGateways[types.NamespacedName{ + Namespace: route.Namespace, + Name: route.Name, + }] = len(references) + } + + for _, route := range b.config.TCPRoutes { + references := map[types.NamespacedName]struct{}{} + for _, ref := range route.Spec.ParentRefs { + for _, gateway := range b.config.ControlledGateways { + parentName := string(ref.Name) + parentNamespace := valueOr(ref.Namespace, route.Namespace) + if nilOrEqual(ref.Group, betaGroup) && + nilOrEqual(ref.Kind, kindGateway) && + gateway.Namespace == parentNamespace && + gateway.Name == parentName { + // the route references a gateway we control, store the ref to this gateway + references[types.NamespacedName{ + Namespace: parentNamespace, + Name: parentName, + }] = struct{}{} + } + } + } + tracker.tcpRouteReferencesGateways[types.NamespacedName{ + Namespace: route.Namespace, + Name: route.Name, + }] = len(references) + } + + for _, gateway := range b.config.ControlledGateways { + references := map[types.NamespacedName]struct{}{} + for _, listener := range gateway.Spec.Listeners { + if listener.TLS == nil { + continue + } + for _, ref := range listener.TLS.CertificateRefs { + if nilOrEqual(ref.Group, "") && + nilOrEqual(ref.Kind, kindSecret) { + // the gateway references a secret, store it + references[types.NamespacedName{ + Namespace: valueOr(ref.Namespace, gateway.Namespace), + Name: string(ref.Name), + }] = struct{}{} + } + } + } + + for ref := range references { + count := tracker.certificatesReferencedByGateways[ref] + tracker.certificatesReferencedByGateways[ref] = count + 1 + } + } + + return tracker +} diff --git a/control-plane/api-gateway/binding/registration.go b/control-plane/api-gateway/binding/registration.go new file mode 100644 index 0000000000..ae26ab51f6 --- /dev/null +++ b/control-plane/api-gateway/binding/registration.go @@ -0,0 +1,92 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package binding + +import ( + "fmt" + + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/common" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" + "github.com/hashicorp/consul/api" + corev1 "k8s.io/api/core/v1" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +const ( + metaKeySyntheticNode = "synthetic-node" + kubernetesSuccessReasonMsg = "Kubernetes health checks passing" + + // consulKubernetesCheckType is the type of health check in Consul for Kubernetes readiness status. + consulKubernetesCheckType = "kubernetes-readiness" + + // consulKubernetesCheckName is the name of health check in Consul for Kubernetes readiness status. + consulKubernetesCheckName = "Kubernetes Readiness Check" +) + +func registrationsForPods(namespace string, gateway gwv1beta1.Gateway, pods []corev1.Pod) []api.CatalogRegistration { + registrations := []api.CatalogRegistration{} + for _, pod := range pods { + registrations = append(registrations, registrationForPod(namespace, gateway, pod)) + } + return registrations +} + +func registrationForPod(namespace string, gateway gwv1beta1.Gateway, pod corev1.Pod) api.CatalogRegistration { + healthStatus := api.HealthCritical + if isPodReady(pod) { + healthStatus = api.HealthPassing + } + + return api.CatalogRegistration{ + Node: common.ConsulNodeNameFromK8sNode(pod.Spec.NodeName), + Address: pod.Status.HostIP, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, + Service: &api.AgentService{ + Kind: api.ServiceKindAPIGateway, + ID: pod.Name, + Service: gateway.Name, + Address: pod.Status.PodIP, + Namespace: namespace, + Meta: map[string]string{ + constants.MetaKeyPodName: pod.Name, + constants.MetaKeyKubeNS: pod.Namespace, + constants.MetaKeyKubeServiceName: gateway.Name, + "external-source": "consul-api-gateway", + }, + }, + Check: &api.AgentCheck{ + CheckID: fmt.Sprintf("%s/%s", pod.Namespace, pod.Name), + Name: consulKubernetesCheckName, + Type: consulKubernetesCheckType, + Status: healthStatus, + ServiceID: pod.Name, + Output: getHealthCheckStatusReason(healthStatus, pod.Name, pod.Namespace), + Namespace: namespace, + }, + SkipNodeUpdate: true, + } +} + +func getHealthCheckStatusReason(healthCheckStatus, podName, podNamespace string) string { + if healthCheckStatus == api.HealthPassing { + return kubernetesSuccessReasonMsg + } + + return fmt.Sprintf("Pod \"%s/%s\" is not ready", podNamespace, podName) +} + +func isPodReady(pod corev1.Pod) bool { + if corev1.PodRunning != pod.Status.Phase { + return false + } + + for _, condition := range pod.Status.Conditions { + if condition.Type == corev1.PodReady && condition.Status == corev1.ConditionTrue { + return true + } + } + return false +} diff --git a/control-plane/api-gateway/binding/registration_test.go b/control-plane/api-gateway/binding/registration_test.go new file mode 100644 index 0000000000..356915f9f7 --- /dev/null +++ b/control-plane/api-gateway/binding/registration_test.go @@ -0,0 +1,83 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package binding + +import ( + "testing" + + "github.com/hashicorp/consul/api" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +func TestRegistrationsForPods_Health(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + consulNamespace string + gateway gwv1beta1.Gateway + pods []corev1.Pod + expected []string + }{ + "empty": { + consulNamespace: "", + gateway: gwv1beta1.Gateway{}, + pods: []corev1.Pod{}, + expected: []string{}, + }, + "mix": { + consulNamespace: "", + gateway: gwv1beta1.Gateway{}, + pods: []corev1.Pod{ + // Pods without a running status + {Status: corev1.PodStatus{Phase: corev1.PodFailed}}, + {Status: corev1.PodStatus{Phase: corev1.PodPending}}, + {Status: corev1.PodStatus{Phase: corev1.PodSucceeded}}, + {Status: corev1.PodStatus{Phase: corev1.PodUnknown}}, + // Running statuses that don't show readiness + {Status: corev1.PodStatus{Phase: corev1.PodRunning, Conditions: []corev1.PodCondition{ + {Type: corev1.PodScheduled, Status: corev1.ConditionTrue}, + }}}, + {Status: corev1.PodStatus{Phase: corev1.PodRunning, Conditions: []corev1.PodCondition{ + {Type: corev1.PodInitialized, Status: corev1.ConditionTrue}, + }}}, + {Status: corev1.PodStatus{Phase: corev1.PodRunning, Conditions: []corev1.PodCondition{ + {Type: corev1.DisruptionTarget, Status: corev1.ConditionTrue}, + }}}, + {Status: corev1.PodStatus{Phase: corev1.PodRunning, Conditions: []corev1.PodCondition{ + {Type: corev1.ContainersReady, Status: corev1.ConditionTrue}, + }}}, + // And finally, the successful check + {Status: corev1.PodStatus{Phase: corev1.PodRunning, Conditions: []corev1.PodCondition{ + {Type: corev1.PodReady, Status: corev1.ConditionTrue}, + }}}, + }, + expected: []string{ + api.HealthCritical, + api.HealthCritical, + api.HealthCritical, + api.HealthCritical, + api.HealthCritical, + api.HealthCritical, + api.HealthCritical, + api.HealthCritical, + api.HealthPassing, + }, + }, + } { + t.Run(name, func(t *testing.T) { + registrations := registrationsForPods(tt.consulNamespace, tt.gateway, tt.pods) + require.Len(t, registrations, len(tt.expected)) + + for i := range registrations { + registration := registrations[i] + expected := tt.expected[i] + + require.EqualValues(t, "Kubernetes Readiness Check", registration.Check.Name) + require.EqualValues(t, expected, registration.Check.Status) + } + }) + } +} diff --git a/control-plane/api-gateway/binding/result.go b/control-plane/api-gateway/binding/result.go new file mode 100644 index 0000000000..0fd291b0ef --- /dev/null +++ b/control-plane/api-gateway/binding/result.go @@ -0,0 +1,471 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package binding + +import ( + "errors" + "fmt" + "sort" + "strings" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +var ( + // Each of the below are specified in the Gateway spec under RouteConditionReason + // the general usage is that each error is specified as errRoute* where * corresponds + // to the RouteConditionReason given in the spec. If a reason is overloaded and can + // be used with two different types of things (i.e. something is not found or it's not supported) + // then we distinguish those two usages with errRoute*_Usage. + errRouteNotAllowedByListeners_Namespace = errors.New("listener does not allow binding routes from the given namespace") + errRouteNotAllowedByListeners_Protocol = errors.New("listener does not support route protocol") + errRouteNoMatchingListenerHostname = errors.New("listener cannot bind route with a non-aligned hostname") + errRouteInvalidKind = errors.New("invalid backend kind") + errRouteBackendNotFound = errors.New("backend not found") + errRouteRefNotPermitted = errors.New("reference not permitted due to lack of ReferenceGrant") +) + +// routeValidationResult holds the result of validating a route globally, in other +// words, for a particular backend reference without consideration to its particular +// gateway. Unfortunately, due to the fact that the spec requires a route status be +// associated with a parent reference, what it means is that anything that is global +// in nature, like this status will need to be duplicated for every parent reference +// on a given route status. +type routeValidationResult struct { + namespace string + backend gwv1beta1.BackendRef + err error +} + +// Type is used for error printing a backend reference type that we don't support on +// a validation error. +func (v routeValidationResult) Type() string { + return (&metav1.GroupKind{ + Group: valueOr(v.backend.Group, ""), + Kind: valueOr(v.backend.Kind, "Service"), + }).String() +} + +// String is the namespace/name of the reference that has an error. +func (v routeValidationResult) String() string { + return (types.NamespacedName{Namespace: v.namespace, Name: string(v.backend.Name)}).String() +} + +// routeValidationResults contains a list of validation results for the backend references +// on a route. +type routeValidationResults []routeValidationResult + +// Condition returns the ResolvedRefs condition that gets duplicated across every relevant +// parent on a route's status. +func (e routeValidationResults) Condition() metav1.Condition { + // we only use the first error due to the way the spec is structured + // where you can only have a single condition + for _, v := range e { + err := v.err + if err != nil { + switch err { + case errRouteInvalidKind: + return metav1.Condition{ + Type: "ResolvedRefs", + Status: metav1.ConditionFalse, + Reason: "InvalidKind", + Message: fmt.Sprintf("%s [%s]: %s", v.String(), v.Type(), err.Error()), + } + case errRouteBackendNotFound: + return metav1.Condition{ + Type: "ResolvedRefs", + Status: metav1.ConditionFalse, + Reason: "BackendNotFound", + Message: fmt.Sprintf("%s: %s", v.String(), err.Error()), + } + case errRouteRefNotPermitted: + return metav1.Condition{ + Type: "ResolvedRefs", + Status: metav1.ConditionFalse, + Reason: "RefNotPermitted", + Message: fmt.Sprintf("%s: %s", v.String(), err.Error()), + } + default: + // this should never happen + return metav1.Condition{ + Type: "ResolvedRefs", + Status: metav1.ConditionFalse, + Reason: "UnhandledValidationError", + Message: err.Error(), + } + } + } + } + return metav1.Condition{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + } +} + +// bindResult holds the result of attempting to bind a route to a particular gateway listener +// an error value here means that the route did not bind successfully, no error means that +// the route should be considered bound. +type bindResult struct { + section gwv1beta1.SectionName + err error +} + +// bindResults holds the results of attempting to bind a route to a gateway, having a separate +// bindResult for each listener on the gateway. +type bindResults []bindResult + +// Error constructs a human readable error for bindResults, containing any errors that a route +// had in binding to a gateway, note that this is only used if a route failed to bind to every +// listener it attempted to bind to. +func (b bindResults) Error() string { + messages := []string{} + for _, result := range b { + if result.err != nil { + messages = append(messages, fmt.Sprintf("%s: %s", result.section, result.err.Error())) + } + } + + sort.Strings(messages) + return strings.Join(messages, "; ") +} + +// DidBind returns whether a route successfully bound to any listener on a gateway. +func (b bindResults) DidBind() bool { + for _, result := range b { + if result.err == nil { + return true + } + } + return false +} + +// Condition constructs an Accepted condition for a route that will be scoped +// to the particular parent reference it's using to attempt binding. +func (b bindResults) Condition() metav1.Condition { + // if we bound to any listeners, say we're accepted + if b.DidBind() { + return metav1.Condition{ + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + } + } + + // default to the most generic reason in the spec "NotAllowedByListeners" + reason := "NotAllowedByListeners" + + // if we only have a single binding error, we can get more specific + if len(b) == 1 { + for _, result := range b { + // if we have a hostname mismatch error, then use the more specific reason + if result.err == errRouteNoMatchingListenerHostname { + reason = "NoMatchingListenerHostname" + } + } + } + + return metav1.Condition{ + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: reason, + Message: b.Error(), + } +} + +// parentBindResult associates a binding result with the given parent reference. +type parentBindResult struct { + parent gwv1beta1.ParentReference + results bindResults +} + +// parentBindResults contains the list of all results that occurred when this route +// attempted to bind to a gateway using its parent references. +type parentBindResults []parentBindResult + +var ( + // Each of the below are specified in the Gateway spec under ListenerConditionReason + // the general usage is that each error is specified as errListener* where * corresponds + // to the ListenerConditionReason given in the spec. If a reason is overloaded and can + // be used with two different types of things (i.e. something is not found or it's not supported) + // then we distinguish those two usages with errListener*_Usage. + errListenerUnsupportedProtocol = errors.New("listener protocol is unsupported") + errListenerPortUnavailable = errors.New("listener port is unavailable") + errListenerHostnameConflict = errors.New("listener hostname conflicts with another listener") + errListenerProtocolConflict = errors.New("listener protocol conflicts with another listener") + errListenerInvalidCertificateRef_NotFound = errors.New("certificate not found") + errListenerInvalidCertificateRef_NotSupported = errors.New("certificate type is not supported") + + // Below is where any custom generic listener validation errors should go. + // We map anything under here to a custom ListenerConditionReason of Invalid on + // an Accepted status type. + errListenerNoTLSPassthrough = errors.New("TLS passthrough is not supported") +) + +// listenerValidationResult contains the result of internally validating a single listener +// as well as the result of validating it in relation to all its peers (via conflictedErr). +// an error set on any of its members corresponds to an error condition on the corresponding +// status type. +type listenerValidationResult struct { + // status type: Accepted + acceptedErr error + // status type: Conflicted + conflictedErr error + // status type: ResolvedRefs + refErr error + // TODO: programmed +} + +// acceptedCondition constructs the condition for the Accepted status type. +func (l listenerValidationResult) acceptedCondition(generation int64) metav1.Condition { + now := metav1.Now() + switch l.acceptedErr { + case errListenerPortUnavailable: + return metav1.Condition{ + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "PortUnavailable", + ObservedGeneration: generation, + Message: l.acceptedErr.Error(), + LastTransitionTime: now, + } + case errListenerUnsupportedProtocol: + return metav1.Condition{ + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "UnsupportedProtocol", + ObservedGeneration: generation, + Message: l.acceptedErr.Error(), + LastTransitionTime: now, + } + case nil: + return metav1.Condition{ + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + ObservedGeneration: generation, + Message: "listener accepted", + LastTransitionTime: now, + } + default: + // falback to invalid + return metav1.Condition{ + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "Invalid", + ObservedGeneration: generation, + Message: l.acceptedErr.Error(), + LastTransitionTime: now, + } + } +} + +// conflictedCondition constructs the condition for the Conflicted status type. +func (l listenerValidationResult) conflictedCondition(generation int64) metav1.Condition { + now := metav1.Now() + + switch l.conflictedErr { + case errListenerProtocolConflict: + return metav1.Condition{ + Type: "Conflicted", + Status: metav1.ConditionTrue, + Reason: "ProtocolConflict", + ObservedGeneration: generation, + Message: l.conflictedErr.Error(), + LastTransitionTime: now, + } + case errListenerHostnameConflict: + return metav1.Condition{ + Type: "Conflicted", + Status: metav1.ConditionTrue, + Reason: "HostnameConflict", + ObservedGeneration: generation, + Message: l.conflictedErr.Error(), + LastTransitionTime: now, + } + default: + return metav1.Condition{ + Type: "Conflicted", + Status: metav1.ConditionFalse, + Reason: "NoConflicts", + ObservedGeneration: generation, + Message: "listener has no conflicts", + LastTransitionTime: now, + } + } +} + +// acceptedCondition constructs the condition for the ResolvedRefs status type. +func (l listenerValidationResult) resolvedRefsCondition(generation int64) metav1.Condition { + now := metav1.Now() + + switch l.refErr { + case errListenerInvalidCertificateRef_NotFound: + return metav1.Condition{ + Type: "ResolvedRefs", + Status: metav1.ConditionFalse, + Reason: "InvalidCertificateRef", + ObservedGeneration: generation, + Message: l.refErr.Error(), + LastTransitionTime: now, + } + case errListenerInvalidCertificateRef_NotSupported: + return metav1.Condition{ + Type: "ResolvedRefs", + Status: metav1.ConditionFalse, + Reason: "InvalidCertificateRef", + ObservedGeneration: generation, + Message: l.refErr.Error(), + LastTransitionTime: now, + } + default: + return metav1.Condition{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + ObservedGeneration: generation, + Message: "resolved certificate references", + LastTransitionTime: now, + } + } +} + +// Conditions constructs the entire set of conditions for a given gateway listener. +func (l listenerValidationResult) Conditions(generation int64) []metav1.Condition { + return []metav1.Condition{ + l.acceptedCondition(generation), + l.conflictedCondition(generation), + l.resolvedRefsCondition(generation), + } +} + +// listenerValidationResults holds all of the results for a gateway's listeners +// the index of each result needs to correspond exactly to the index of the listener +// on the gateway spec for which it is describing. +type listenerValidationResults []listenerValidationResult + +// Invalid returns whether or not there is any listener that is not "Accepted" +// this is used in constructing a gateway's status where the Accepted status +// at the top-level can have a GatewayConditionReason of ListenersNotValid. +func (l listenerValidationResults) Invalid() bool { + for _, r := range l { + if r.acceptedErr != nil { + return true + } + } + return false +} + +// Conditions returns the listener conditions at a given index. +func (l listenerValidationResults) Conditions(generation int64, index int) []metav1.Condition { + result := l[index] + return result.Conditions(generation) +} + +var ( + // Each of the below are specified in the Gateway spec under GatewayConditionReason + // the general usage is that each error is specified as errGateway* where * corresponds + // to the GatewayConditionReason given in the spec. + errGatewayUnsupportedAddress = errors.New("gateway does not support specifying addresses") + errGatewayListenersNotValid = errors.New("one or more listeners are invalid") + errGatewayPending_Pods = errors.New("gateway pods are still being scheduled") + errGatewayPending_Consul = errors.New("gateway configuration is not yet synced to Consul") +) + +// gatewayValidationResult contains the result of internally validating a gateway. +// An error set on any of its members corresponds to an error condition on the corresponding +// status type. +type gatewayValidationResult struct { + acceptedErr error + programmedErr error +} + +// programmedCondition returns a condition for the Programmed status type. +func (l gatewayValidationResult) programmedCondition(generation int64) metav1.Condition { + now := metav1.Now() + + switch l.programmedErr { + case errGatewayPending_Pods, errGatewayPending_Consul: + return metav1.Condition{ + Type: "Programmed", + Status: metav1.ConditionFalse, + Reason: "Pending", + ObservedGeneration: generation, + Message: l.programmedErr.Error(), + LastTransitionTime: now, + } + default: + return metav1.Condition{ + Type: "Programmed", + Status: metav1.ConditionTrue, + Reason: "Programmed", + ObservedGeneration: generation, + Message: "gateway programmed", + LastTransitionTime: now, + } + } +} + +// acceptedCondition returns a condition for the Accepted status type. It takes a boolean argument +// for whether or not any of the gateway's listeners are invalid, if they are, it overrides whatever +// Reason is set as an error on the result and instead uses the ListenersNotValid reason. +func (l gatewayValidationResult) acceptedCondition(generation int64, listenersInvalid bool) metav1.Condition { + now := metav1.Now() + + if l.acceptedErr == nil { + if listenersInvalid { + return metav1.Condition{ + Type: "Accepted", + // should one invalid listener cause the entire gateway to become invalid? + Status: metav1.ConditionFalse, + Reason: "ListenersNotValid", + ObservedGeneration: generation, + Message: errGatewayListenersNotValid.Error(), + LastTransitionTime: now, + } + } + + return metav1.Condition{ + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + ObservedGeneration: generation, + Message: "gateway accepted", + LastTransitionTime: now, + } + } + + if l.acceptedErr == errGatewayUnsupportedAddress { + return metav1.Condition{ + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "UnsupportedAddress", + ObservedGeneration: generation, + Message: l.acceptedErr.Error(), + LastTransitionTime: now, + } + } + + // fallback to Invalid reason + return metav1.Condition{ + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "Invalid", + ObservedGeneration: generation, + Message: l.acceptedErr.Error(), + LastTransitionTime: now, + } +} + +// Conditions constructs the gateway conditions given whether its listeners are valid. +func (l gatewayValidationResult) Conditions(generation int64, listenersInvalid bool) []metav1.Condition { + return []metav1.Condition{ + l.acceptedCondition(generation, listenersInvalid), + l.programmedCondition(generation), + } +} diff --git a/control-plane/api-gateway/binding/route_binding.go b/control-plane/api-gateway/binding/route_binding.go new file mode 100644 index 0000000000..5175c8f647 --- /dev/null +++ b/control-plane/api-gateway/binding/route_binding.go @@ -0,0 +1,489 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package binding + +import ( + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/translation" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul/api" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +// consulHTTPRouteFor returns the Consul HTTPRouteConfigEntry for the given reference. +func (b *Binder) consulHTTPRouteFor(ref api.ResourceReference) *api.HTTPRouteConfigEntry { + for _, route := range b.config.ConsulHTTPRoutes { + if route.Namespace == ref.Namespace && route.Partition == ref.Partition && route.Name == ref.Name { + return &route + } + } + return nil +} + +// consulTCPRouteFor returns the Consul TCPRouteConfigEntry for the given reference. +func (b *Binder) consulTCPRouteFor(ref api.ResourceReference) *api.TCPRouteConfigEntry { + for _, route := range b.config.ConsulTCPRoutes { + if route.Namespace == ref.Namespace && route.Partition == ref.Partition && route.Name == ref.Name { + return &route + } + } + return nil +} + +// routeBinder encapsulates the binding logic for binding a route to the given Gateway. +// The logic for route binding is almost identical between different route types, but +// due to the strong typing in the Spec and Go's inability to deal with fields via generics +// we have to pull in a bunch of accessors (which ideally should be in the upstream spec) +// for each route type. +// +// From the generic signature -- T: the type of Kubernetes route, U: the type of Consul config entry +// +// TODO: consider moving the function closures to something like an interface that we can +// implement the accessors on for each route type. +type routeBinder[T client.Object, U api.ConfigEntry] struct { + // isGatewayDeleted is used to determine whether we should just ignore + // attempting to bind the route (since we no longer know whether we + // should manage the route we only want to remove any state we've + // set on it). + isGatewayDeleted bool + // gateway is the gateway that we want to use for binding + gateway *gwv1beta1.Gateway + // gatewayRef is a Consul reference used to prune no-longer bound + // parents from a Consul resource we've created. + gatewayRef api.ResourceReference + // tracker is the referenceTracker used to determine when we want to cleanup + // routes based on a deleted gateway. + tracker referenceTracker + // namespaces is the set of namespaces in Consul that use for determining + // whether a route in a given namespace can bind to a gateway with AllowedRoutes set + namespaces map[string]corev1.Namespace + // services is a catalog of all connect-injected services to check a route against + // for resolving its backend refs + services map[types.NamespacedName]api.CatalogService + // meshServices is a catalog of all mesh service objects to check a route against + // for resolving its backend refs + meshServices map[types.NamespacedName]v1alpha1.MeshService + + // translationReferenceFunc is a function used to translate a Kubernetes object into + // a Consul object reference + translationReferenceFunc func(route T) api.ResourceReference + // lookupFunc is a function used for finding an existing Consul object based on + // its object reference + lookupFunc func(api.ResourceReference) U + // getParentsFunc is a function used for getting the parent references of a Consul route object + getParentsFunc func(U) []api.ResourceReference + // setParentsFunc is a function used for setting the parent references of a route object + setParentsFunc func(U, []api.ResourceReference) + // removeStatusRefsFunc is a function used for removing the statuses for the given parent + // references from a route + removeStatusRefsFunc func(T, []gwv1beta1.ParentReference) bool + // getHostnamesFunc is a function used for getting the hostnames associated with a route + getHostnamesFunc func(T) []gwv1beta1.Hostname + // getParentRefsFunc is used for getting the parent references of a Kubernetes route object + getParentRefsFunc func(T) []gwv1beta1.ParentReference + // translationFunc is used for translating a Kubernetes route into the corresponding Consul config entry + translationFunc func(T, map[types.NamespacedName]api.ResourceReference, map[types.NamespacedName]api.CatalogService, map[types.NamespacedName]v1alpha1.MeshService) U + // setRouteConditionFunc is used for adding or overwriting a condition on a route at the given + // parent + setRouteConditionFunc func(T, *gwv1beta1.ParentReference, metav1.Condition) bool + // getBackendRefsFunc returns a list of all backend references that we need to validate against the + // list of known connect-injected services + getBackendRefsFunc func(T) []gwv1beta1.BackendRef + // removeControllerStatusFunc is used to remove all of the statuses set by our controller when GC'ing + // a route + removeControllerStatusFunc func(T) bool +} + +// newRouteBinder creates a new route binder for the given Kubernetes and Consul route types +// generally this is lightly wrapped by other constructors that pass in the various closures +// needed for accessing fields on the objects. +func newRouteBinder[T client.Object, U api.ConfigEntry]( + isGatewayDeleted bool, + gateway *gwv1beta1.Gateway, + gatewayRef api.ResourceReference, + namespaces map[string]corev1.Namespace, + services map[types.NamespacedName]api.CatalogService, + meshServices map[types.NamespacedName]v1alpha1.MeshService, + tracker referenceTracker, + translationReferenceFunc func(route T) api.ResourceReference, + lookupFunc func(api.ResourceReference) U, + getParentsFunc func(U) []api.ResourceReference, + setParentsFunc func(U, []api.ResourceReference), + removeStatusRefsFunc func(T, []gwv1beta1.ParentReference) bool, + getHostnamesFunc func(T) []gwv1beta1.Hostname, + getParentRefsFunc func(T) []gwv1beta1.ParentReference, + translationFunc func(T, map[types.NamespacedName]api.ResourceReference, map[types.NamespacedName]api.CatalogService, map[types.NamespacedName]v1alpha1.MeshService) U, + setRouteConditionFunc func(T, *gwv1beta1.ParentReference, metav1.Condition) bool, + getBackendRefsFunc func(T) []gwv1beta1.BackendRef, + removeControllerStatusFunc func(T) bool, +) *routeBinder[T, U] { + return &routeBinder[T, U]{ + isGatewayDeleted: isGatewayDeleted, + gateway: gateway, + gatewayRef: gatewayRef, + namespaces: namespaces, + services: services, + meshServices: meshServices, + tracker: tracker, + translationReferenceFunc: translationReferenceFunc, + lookupFunc: lookupFunc, + getParentsFunc: getParentsFunc, + setParentsFunc: setParentsFunc, + removeStatusRefsFunc: removeStatusRefsFunc, + getHostnamesFunc: getHostnamesFunc, + getParentRefsFunc: getParentRefsFunc, + translationFunc: translationFunc, + setRouteConditionFunc: setRouteConditionFunc, + getBackendRefsFunc: getBackendRefsFunc, + removeControllerStatusFunc: removeControllerStatusFunc, + } +} + +// bind contains the main logic for binding a route to a given gateway. +func (r *routeBinder[T, U]) bind(route T, boundCount map[gwv1beta1.SectionName]int, seenRoutes map[api.ResourceReference]struct{}, snapshot Snapshot) (updatedSnapshot Snapshot) { + routeRef := r.translationReferenceFunc(route) + existing := r.lookupFunc(routeRef) + gatewayRefs := filterParentRefs(objectToMeta(r.gateway), route.GetNamespace(), r.getParentRefsFunc(route)) + + // mark this route as having been processed + seenRoutes[routeRef] = struct{}{} + + // flags to mark that some operation needs to occur + consulNeedsDelete := false + kubernetesNeedsUpdate := false + kubernetesNeedsStatusUpdate := false + // Since the update can either be for an existing resource (in the case + // of a deleted gateway) or for a resource generated by translating a + // bound gateway we just set the resource that we want to push out an + // update for. If this is not nil, we push it into the snapshot. + var consulUpdate U + + // we do this in a closure at the end to make sure we don't accidentally + // add something multiple times into the list of update/delete operations + // instead we just set a flag indicating that an update is needed and then + // append to the snapshot right before returning + defer func() { + if !isNil(consulUpdate) { + snapshot.Consul.Updates = append(snapshot.Consul.Updates, consulUpdate) + } + if consulNeedsDelete { + snapshot.Consul.Deletions = append(snapshot.Consul.Deletions, routeRef) + } + if kubernetesNeedsUpdate { + snapshot.Kubernetes.Updates = append(snapshot.Kubernetes.Updates, route) + } + if kubernetesNeedsStatusUpdate { + snapshot.Kubernetes.StatusUpdates = append(snapshot.Kubernetes.StatusUpdates, route) + } + + updatedSnapshot = snapshot + }() + + if isDeleted(route) { + // mark the route as needing to get cleaned up if we detect that it's being deleted + consulNeedsDelete = true + if removeFinalizer(route) { + kubernetesNeedsUpdate = true + } + return + } + + if r.isGatewayDeleted { + // first check if this is our only ref for the route + if r.tracker.isLastReference(route) { + // if it is, then mark everything for deletion + consulNeedsDelete = true + if r.removeControllerStatusFunc(route) { + kubernetesNeedsStatusUpdate = true + } + if removeFinalizer(route) { + kubernetesNeedsUpdate = true + } + return + } + + // otherwise remove the condition since we no longer know if we should + // control the route and drop any references for the Consul route + if !isNil(existing) { + // this drops all the parent refs + r.setParentsFunc(existing, parentsForRoute(r.gatewayRef, r.getParentsFunc(existing), nil)) + // and then we mark the route as needing updated + consulUpdate = existing + // drop the status conditions + if r.removeStatusRefsFunc(route, gatewayRefs) { + kubernetesNeedsStatusUpdate = true + } + } + return + } + + if ensureFinalizer(route) { + kubernetesNeedsUpdate = true + return + } + + // TODO: scrub route refs from statuses that no longer exist + + validation := validateRefs(route.GetNamespace(), r.getBackendRefsFunc(route), r.services, r.meshServices) + // the spec is dumb and makes you set a parent for any status, even when the + // status is not with respect to a parent, as is the case of resolved refs + // so we need to set the status on all parents + for _, ref := range gatewayRefs { + if r.setRouteConditionFunc(route, &ref, validation.Condition()) { + kubernetesNeedsStatusUpdate = true + } + } + + results := make(parentBindResults, 0) + namespace := r.namespaces[route.GetNamespace()] + gk := route.GetObjectKind().GroupVersionKind().GroupKind() + for _, ref := range gatewayRefs { + result := make(bindResults, 0) + for _, listener := range listenersFor(r.gateway, ref.SectionName) { + if !routeKindIsAllowedForListener(supportedKindsForProtocol[listener.Protocol], gk) { + result = append(result, bindResult{ + section: listener.Name, + err: errRouteNotAllowedByListeners_Protocol, + }) + continue + } + + if !routeKindIsAllowedForListenerExplicit(listener.AllowedRoutes, gk) { + result = append(result, bindResult{ + section: listener.Name, + err: errRouteNotAllowedByListeners_Protocol, + }) + continue + } + + if !routeAllowedForListenerNamespaces(r.gateway.Namespace, listener.AllowedRoutes, namespace) { + result = append(result, bindResult{ + section: listener.Name, + err: errRouteNotAllowedByListeners_Namespace, + }) + continue + } + + if !routeAllowedForListenerHostname(listener.Hostname, r.getHostnamesFunc(route)) { + result = append(result, bindResult{ + section: listener.Name, + err: errRouteNoMatchingListenerHostname, + }) + continue + } + + result = append(result, bindResult{ + section: listener.Name, + }) + + boundCount[listener.Name] = boundCount[listener.Name] + 1 + } + + results = append(results, parentBindResult{ + parent: ref, + results: result, + }) + } + + updated := false + for _, result := range results { + if r.setRouteConditionFunc(route, &result.parent, result.results.Condition()) { + updated = true + } + } + + if updated { + kubernetesNeedsStatusUpdate = true + } + + entry := r.translationFunc(route, nil, r.services, r.meshServices) + // make all parent refs explicit based on what actually bound + if isNil(existing) { + r.setParentsFunc(entry, parentsForRoute(r.gatewayRef, nil, results)) + } else { + r.setParentsFunc(entry, parentsForRoute(r.gatewayRef, r.getParentsFunc(existing), results)) + } + consulUpdate = entry + + return +} + +// newTCPRouteBinder wraps newRouteBinder with the proper closures needed for accessing TCPRoutes and their config entries. +func (b *Binder) newTCPRouteBinder(tracker referenceTracker, services map[types.NamespacedName]api.CatalogService, meshServices map[types.NamespacedName]v1alpha1.MeshService) *routeBinder[*gwv1alpha2.TCPRoute, *api.TCPRouteConfigEntry] { + return newRouteBinder( + b.isGatewayDeleted(), + &b.config.Gateway, + b.gatewayRef(), + b.config.Namespaces, + services, + meshServices, + tracker, + b.config.Translator.ReferenceForTCPRoute, + b.consulTCPRouteFor, + func(t *api.TCPRouteConfigEntry) []api.ResourceReference { return t.Parents }, + func(t *api.TCPRouteConfigEntry, parents []api.ResourceReference) { t.Parents = parents }, + b.statusSetter.removeTCPRouteReferences, + func(t *gwv1alpha2.TCPRoute) []gwv1beta1.Hostname { return nil }, + func(t *gwv1alpha2.TCPRoute) []gwv1beta1.ParentReference { return t.Spec.ParentRefs }, + b.config.Translator.TCPRouteToTCPRoute, + b.statusSetter.setTCPRouteCondition, + func(t *gwv1alpha2.TCPRoute) []gwv1beta1.BackendRef { + refs := []gwv1beta1.BackendRef{} + for _, rule := range t.Spec.Rules { + refs = append(refs, rule.BackendRefs...) + } + return refs + }, + b.statusSetter.removeTCPStatuses, + ) +} + +// newHTTPRouteBinder wraps newRouteBinder with the proper closures needed for accessing HTTPRoutes and their config entries. +func (b *Binder) newHTTPRouteBinder(tracker referenceTracker, services map[types.NamespacedName]api.CatalogService, meshServices map[types.NamespacedName]v1alpha1.MeshService) *routeBinder[*gwv1beta1.HTTPRoute, *api.HTTPRouteConfigEntry] { + return newRouteBinder( + b.isGatewayDeleted(), + &b.config.Gateway, + b.gatewayRef(), + b.config.Namespaces, + services, + meshServices, + tracker, + b.config.Translator.ReferenceForHTTPRoute, + b.consulHTTPRouteFor, + func(t *api.HTTPRouteConfigEntry) []api.ResourceReference { return t.Parents }, + func(t *api.HTTPRouteConfigEntry, parents []api.ResourceReference) { t.Parents = parents }, + b.statusSetter.removeHTTPRouteReferences, + func(t *gwv1beta1.HTTPRoute) []gwv1beta1.Hostname { return t.Spec.Hostnames }, + func(t *gwv1beta1.HTTPRoute) []gwv1beta1.ParentReference { return t.Spec.ParentRefs }, + b.config.Translator.HTTPRouteToHTTPRoute, + b.statusSetter.setHTTPRouteCondition, + func(t *gwv1beta1.HTTPRoute) []gwv1beta1.BackendRef { + refs := []gwv1beta1.BackendRef{} + for _, rule := range t.Spec.Rules { + for _, ref := range rule.BackendRefs { + refs = append(refs, ref.BackendRef) + } + } + return refs + }, + b.statusSetter.removeHTTPStatuses, + ) +} + +// cleanRoute removes a gateway reference from the given route config entry +// and marks adds it to the snapshot if its mutated the entry at all. +func cleanRoute[T api.ConfigEntry]( + route T, + seenRoutes map[api.ResourceReference]struct{}, + snapshot Snapshot, + gatewayRef api.ResourceReference, + getParentsFunc func(T) []api.ResourceReference, + setParentsFunc func(T, []api.ResourceReference), +) Snapshot { + routeRef := translation.EntryToReference(route) + if _, ok := seenRoutes[routeRef]; !ok { + existingParents := getParentsFunc(route) + parents := parentsForRoute(gatewayRef, existingParents, nil) + if len(parents) == 0 { + // we can GC this now since we've dropped all refs from it + snapshot.Consul.Deletions = append(snapshot.Consul.Deletions, routeRef) + } else if len(existingParents) != len(parents) { + // we've mutated the length, which means this route needs an update + setParentsFunc(route, parents) + snapshot.Consul.Updates = append(snapshot.Consul.Updates, route) + } + } + return snapshot +} + +// cleanHTTPRoute wraps cleanRoute with the proper closures for HTTPRoute config entries. +func (b *Binder) cleanHTTPRoute(route *api.HTTPRouteConfigEntry, seenRoutes map[api.ResourceReference]struct{}, snapshot Snapshot) Snapshot { + return cleanRoute(route, seenRoutes, snapshot, b.gatewayRef(), + func(route *api.HTTPRouteConfigEntry) []api.ResourceReference { return route.Parents }, + func(route *api.HTTPRouteConfigEntry, parents []api.ResourceReference) { route.Parents = parents }, + ) +} + +// cleanTCPRoute wraps cleanRoute with the proper closures for TCPRoute config entries. +func (b *Binder) cleanTCPRoute(route *api.TCPRouteConfigEntry, seenRoutes map[api.ResourceReference]struct{}, snapshot Snapshot) Snapshot { + return cleanRoute(route, seenRoutes, snapshot, b.gatewayRef(), + func(route *api.TCPRouteConfigEntry) []api.ResourceReference { return route.Parents }, + func(route *api.TCPRouteConfigEntry, parents []api.ResourceReference) { route.Parents = parents }, + ) +} + +// parentsForRoute constructs a list of Consul route parent references based on what parents actually bound +// on a given route. This is necessary due to the fact that some additional validation in Kubernetes might +// require a route not to actually be accepted by a gateway, whereas we may have laxer logic inside of Consul +// itself. In these cases we want to just drop the parent reference in the Consul config entry we are going +// to write in order for it not to succeed in binding where Kubernetes failed to bind. +func parentsForRoute(ref api.ResourceReference, existing []api.ResourceReference, results parentBindResults) []api.ResourceReference { + // store all section names that bound + parentSet := map[string]struct{}{} + for _, result := range results { + for _, r := range result.results { + if r.err == nil { + parentSet[string(r.section)] = struct{}{} + } + } + } + + // first, filter out all of the parent refs that don't correspond to this gateway + parents := []api.ResourceReference{} + for _, parent := range existing { + if parent.Kind == api.APIGateway && + parent.Name == ref.Name && + parent.Namespace == ref.Namespace { + continue + } + parents = append(parents, parent) + } + + // now construct the bound set + for parent := range parentSet { + parents = append(parents, api.ResourceReference{ + Kind: api.APIGateway, + Name: ref.Name, + Namespace: ref.Namespace, + SectionName: parent, + }) + } + return parents +} + +// filterParentRefs returns the subset of parent references on a route that point to the given gateway. +func filterParentRefs(gateway types.NamespacedName, namespace string, refs []gwv1beta1.ParentReference) []gwv1beta1.ParentReference { + references := []gwv1beta1.ParentReference{} + for _, ref := range refs { + if nilOrEqual(ref.Group, betaGroup) && + nilOrEqual(ref.Kind, kindGateway) && + gateway.Namespace == valueOr(ref.Namespace, namespace) && + gateway.Name == string(ref.Name) { + references = append(references, ref) + } + } + + return references +} + +// listenersFor returns the listeners corresponding the given section name. If the section +// name is actually specified, the returned set should just have one listener, if it is +// unspecified, the all gatweway listeners should be returned. +func listenersFor(gateway *gwv1beta1.Gateway, name *gwv1beta1.SectionName) []gwv1beta1.Listener { + listeners := []gwv1beta1.Listener{} + for _, listener := range gateway.Spec.Listeners { + if name == nil { + listeners = append(listeners, listener) + continue + } + if listener.Name == *name { + listeners = append(listeners, listener) + } + } + return listeners +} diff --git a/control-plane/api-gateway/binding/setter.go b/control-plane/api-gateway/binding/setter.go new file mode 100644 index 0000000000..93727b37b2 --- /dev/null +++ b/control-plane/api-gateway/binding/setter.go @@ -0,0 +1,180 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package binding + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +// setter wraps the status setting logic for routes. +type setter struct { + controllerName string +} + +// newSetter constructs a status setter with the given controller name. +func newSetter(controllerName string) *setter { + return &setter{controllerName: controllerName} +} + +// setHTTPRouteCondition sets an HTTPRoute condition on its status with the given parent. +func (s *setter) setHTTPRouteCondition(route *gwv1beta1.HTTPRoute, parent *gwv1beta1.ParentReference, condition metav1.Condition) bool { + condition.LastTransitionTime = metav1.Now() + condition.ObservedGeneration = route.Generation + + status := s.getParentStatus(route.Status.Parents, parent) + conditions, modified := setCondition(status.Conditions, condition) + if modified { + status.Conditions = conditions + route.Status.Parents = s.setParentStatus(route.Status.Parents, status) + } + return modified +} + +// removeHTTPRouteReferences removes the given parent reference sections from an HTTPRoute's status. +func (s *setter) removeHTTPRouteReferences(route *gwv1beta1.HTTPRoute, refs []gwv1beta1.ParentReference) bool { + modified := false + for _, parent := range refs { + parents, removed := s.removeParentStatus(route.Status.Parents, parent) + route.Status.Parents = parents + if removed { + modified = true + } + } + return modified +} + +// setTCPRouteCondition sets a TCPRoute condition on its status with the given parent. +func (s *setter) setTCPRouteCondition(route *gwv1alpha2.TCPRoute, parent *gwv1beta1.ParentReference, condition metav1.Condition) bool { + condition.LastTransitionTime = metav1.Now() + condition.ObservedGeneration = route.Generation + + status := s.getParentStatus(route.Status.Parents, parent) + conditions, modified := setCondition(status.Conditions, condition) + if modified { + status.Conditions = conditions + route.Status.Parents = s.setParentStatus(route.Status.Parents, status) + } + return modified +} + +// removeTCPRouteReferences removes the given parent reference sections from a TCPRoute's status. +func (s *setter) removeTCPRouteReferences(route *gwv1alpha2.TCPRoute, refs []gwv1beta1.ParentReference) bool { + modified := false + for _, parent := range refs { + parents, removed := s.removeParentStatus(route.Status.Parents, parent) + route.Status.Parents = parents + if removed { + modified = true + } + } + return modified +} + +// removeHTTPStatuses removes all statuses set by the given controller from an HTTPRoute's status. +func (s *setter) removeHTTPStatuses(route *gwv1beta1.HTTPRoute) bool { + modified := false + filtered := []gwv1beta1.RouteParentStatus{} + for _, status := range route.Status.Parents { + if string(status.ControllerName) == s.controllerName { + modified = true + continue + } + filtered = append(filtered, status) + } + + if modified { + route.Status.Parents = filtered + } + return modified +} + +// removeTCPStatuses removes all statuses set by the given controller from a TCPRoute's status. +func (s *setter) removeTCPStatuses(route *gwv1alpha2.TCPRoute) bool { + modified := false + filtered := []gwv1beta1.RouteParentStatus{} + for _, status := range route.Status.Parents { + if string(status.ControllerName) == s.controllerName { + modified = true + continue + } + filtered = append(filtered, status) + } + + if modified { + route.Status.Parents = filtered + } + return modified +} + +// getParentStatus returns the section of a status referenced by the given parent reference. +func (s *setter) getParentStatus(statuses []gwv1beta1.RouteParentStatus, parent *gwv1beta1.ParentReference) gwv1beta1.RouteParentStatus { + var parentRef gwv1beta1.ParentReference + if parent != nil { + parentRef = *parent + } + + for _, status := range statuses { + if parentsEqual(status.ParentRef, parentRef) && string(status.ControllerName) == s.controllerName { + return status + } + } + return gwv1beta1.RouteParentStatus{ + ParentRef: parentRef, + ControllerName: gwv1beta1.GatewayController(s.controllerName), + } +} + +// removeParentStatus removes the section of a status referenced by the given parent reference. +func (s *setter) removeParentStatus(statuses []gwv1beta1.RouteParentStatus, parent gwv1beta1.ParentReference) ([]gwv1beta1.RouteParentStatus, bool) { + found := false + filtered := []gwv1beta1.RouteParentStatus{} + for _, status := range statuses { + if parentsEqual(status.ParentRef, parent) && string(status.ControllerName) == s.controllerName { + found = true + continue + } + filtered = append(filtered, status) + } + return filtered, found +} + +// setCondition overrides or appends a condition to the list of conditions, returning if a modification +// to the condition set was made or not. Modifications only occur if a field other than the observation +// timestamp is modified. +func setCondition(conditions []metav1.Condition, condition metav1.Condition) ([]metav1.Condition, bool) { + for i, existing := range conditions { + if existing.Type == condition.Type { + // no-op if we have the exact same thing + if condition.Reason == existing.Reason && condition.Message == existing.Message && condition.ObservedGeneration == existing.ObservedGeneration { + return conditions, false + } + + conditions[i] = condition + return conditions, true + } + } + return append(conditions, condition), true +} + +// setParentStatus updates or inserts the set of parent statuses with the newly modified parent. +func (s *setter) setParentStatus(statuses []gwv1beta1.RouteParentStatus, parent gwv1beta1.RouteParentStatus) []gwv1beta1.RouteParentStatus { + for i, status := range statuses { + if parentsEqual(status.ParentRef, parent.ParentRef) && status.ControllerName == parent.ControllerName { + statuses[i] = parent + return statuses + } + } + return append(statuses, parent) +} + +// parentsEqual checks for equality between two parent references. +func parentsEqual(one, two gwv1beta1.ParentReference) bool { + return bothNilOrEqual(one.Group, two.Group) && + bothNilOrEqual(one.Kind, two.Kind) && + bothNilOrEqual(one.SectionName, two.SectionName) && + bothNilOrEqual(one.Port, two.Port) && + one.Name == two.Name +} diff --git a/control-plane/api-gateway/binding/setter_test.go b/control-plane/api-gateway/binding/setter_test.go new file mode 100644 index 0000000000..a8eabfb55d --- /dev/null +++ b/control-plane/api-gateway/binding/setter_test.go @@ -0,0 +1,42 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package binding + +import ( + "testing" + + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +func TestSetter(t *testing.T) { + setter := newSetter("test") + parentRef := gwv1beta1.ParentReference{ + Name: "test", + } + parentRefDup := gwv1beta1.ParentReference{ + Name: "test", + } + condition := metav1.Condition{ + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + } + route := &gwv1beta1.HTTPRoute{ + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{parentRef}, + }, + }, + } + require.True(t, setter.setHTTPRouteCondition(route, &parentRef, condition)) + require.False(t, setter.setHTTPRouteCondition(route, &parentRefDup, condition)) + require.False(t, setter.setHTTPRouteCondition(route, &parentRefDup, condition)) + require.False(t, setter.setHTTPRouteCondition(route, &parentRefDup, condition)) + + require.Len(t, route.Status.Parents, 1) + require.Len(t, route.Status.Parents[0].Conditions, 1) +} diff --git a/control-plane/api-gateway/binding/snapshot.go b/control-plane/api-gateway/binding/snapshot.go new file mode 100644 index 0000000000..5eaf48abb3 --- /dev/null +++ b/control-plane/api-gateway/binding/snapshot.go @@ -0,0 +1,56 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package binding + +import ( + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul/api" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// KubernetesSnapshot contains all the operations +// required in Kubernetes to complete reconciliation. +type KubernetesSnapshot struct { + // Updates is the list of objects that need to have + // aspects of their metadata or spec updated in Kubernetes + // (i.e. for finalizers or annotations) + Updates []client.Object + // StatusUpdates is the list of objects that need + // to have their statuses updated in Kubernetes + StatusUpdates []client.Object +} + +// ConsulSnapshot contains all the operations required +// in Consul to complete reconciliation. +type ConsulSnapshot struct { + // Updates is the list of ConfigEntry objects that should + // either be updated or created in Consul + Updates []api.ConfigEntry + // Deletions is a list of references that ought to be + // deleted in Consul + Deletions []api.ResourceReference + // Registrations is a list of Consul services to make sure + // are registered in Consul + Registrations []api.CatalogRegistration + // Deregistrations is a list of Consul services to make sure + // are no longer registered in Consul + Deregistrations []api.CatalogDeregistration +} + +// Snapshot contains all Kubernetes and Consul operations +// needed to complete reconciliation. +type Snapshot struct { + // Kubernetes holds the snapshot of required Kubernetes operations + Kubernetes KubernetesSnapshot + // Consul holds the snapshot of required Consul operations + Consul ConsulSnapshot + // GatewayClassConfig is the configuration to use for determining + // a Gateway deployment, if it is not set, a deployment should be + // deleted instead of updated + GatewayClassConfig *v1alpha1.GatewayClassConfig + + // UpsertGatewayDeployment determines whether the gateway deployment + // objects should be updated, i.e. deployments, roles, services + UpsertGatewayDeployment bool +} diff --git a/control-plane/api-gateway/binding/utils.go b/control-plane/api-gateway/binding/utils.go new file mode 100644 index 0000000000..2ba2d01ce5 --- /dev/null +++ b/control-plane/api-gateway/binding/utils.go @@ -0,0 +1,105 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package binding + +import ( + "reflect" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// pointerTo is a convenience method for taking a pointer +// of an object without having to declare an intermediate variable. +// It's also useful for making sure we don't accidentally take +// the pointer of a range variable directly. +func pointerTo[T any](v T) *T { + return &v +} + +// isNil checks if the argument is nil. It's mainly used to +// check if a generic conforming to a nullable interface is +// actually nil. +func isNil(arg interface{}) bool { + return arg == nil || reflect.ValueOf(arg).IsNil() +} + +// bothNilOrEqual is used to determine if two pointers to comparable +// object are either nil or both point to the same value. +func bothNilOrEqual[T comparable](one, two *T) bool { + if one == nil && two == nil { + return true + } + if one == nil { + return false + } + if two == nil { + return false + } + return *one == *two +} + +// valueOr checks if a string-like pointer is nil, and if it is, +// returns the given value instead. +func valueOr[T ~string](v *T, fallback string) string { + if v == nil { + return fallback + } + return string(*v) +} + +// nilOrEqual checks if a string-like pointer is nil or if it is +// equal to the value provided. +func nilOrEqual[T ~string](v *T, check string) bool { + return v == nil || string(*v) == check +} + +// objectToMeta returns the NamespacedName for the given object. +func objectToMeta[T metav1.Object](object T) types.NamespacedName { + return types.NamespacedName{ + Namespace: object.GetNamespace(), + Name: object.GetName(), + } +} + +// isDeleted checks if the deletion timestamp is set for an object. +func isDeleted(object client.Object) bool { + return !object.GetDeletionTimestamp().IsZero() +} + +// ensureFinalizer ensures that our finalizer is set on an object +// returning whether or not it modified the object. +func ensureFinalizer(object client.Object) bool { + if !object.GetDeletionTimestamp().IsZero() { + return false + } + + finalizers := object.GetFinalizers() + for _, f := range finalizers { + if f == gatewayFinalizer { + return false + } + } + + object.SetFinalizers(append(finalizers, gatewayFinalizer)) + return true +} + +// removeFinalizer ensures that our finalizer is absent from an object +// returning whether or not it modified the object. +func removeFinalizer(object client.Object) bool { + found := false + filtered := []string{} + for _, f := range object.GetFinalizers() { + if f == gatewayFinalizer { + found = true + continue + } + filtered = append(filtered, f) + } + + object.SetFinalizers(filtered) + return found +} diff --git a/control-plane/api-gateway/binding/utils_test.go b/control-plane/api-gateway/binding/utils_test.go new file mode 100644 index 0000000000..a7393e6839 --- /dev/null +++ b/control-plane/api-gateway/binding/utils_test.go @@ -0,0 +1,239 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package binding + +import ( + "testing" + + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +func TestIsNil(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + value interface{} + expected bool + }{ + "nil pointer": { + value: (*string)(nil), + expected: true, + }, + } { + t.Run(name, func(t *testing.T) { + require.Equal(t, tt.expected, isNil(tt.value)) + }) + } +} + +func TestBothNilOrEqual(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + first *string + second *string + expected bool + }{ + "both nil": { + first: nil, + second: nil, + expected: true, + }, + "second nil": { + first: pointerTo(""), + second: nil, + expected: false, + }, + "first nil": { + first: nil, + second: pointerTo(""), + expected: false, + }, + "both equal": { + first: pointerTo(""), + second: pointerTo(""), + expected: true, + }, + "both not equal": { + first: pointerTo("1"), + second: pointerTo("2"), + expected: false, + }, + } { + t.Run(name, func(t *testing.T) { + require.Equal(t, tt.expected, bothNilOrEqual(tt.first, tt.second)) + }) + } +} + +func TestValueOr(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + value *string + or string + expected string + }{ + "nil value": { + value: nil, + or: "test", + expected: "test", + }, + "set value": { + value: pointerTo("value"), + or: "test", + expected: "value", + }, + } { + t.Run(name, func(t *testing.T) { + require.Equal(t, tt.expected, valueOr(tt.value, tt.or)) + }) + } +} + +func TestNilOrEqual(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + value *string + check string + expected bool + }{ + "nil value": { + value: nil, + check: "test", + expected: true, + }, + "equal values": { + value: pointerTo("test"), + check: "test", + expected: true, + }, + "unequal values": { + value: pointerTo("value"), + check: "test", + expected: false, + }, + } { + t.Run(name, func(t *testing.T) { + require.Equal(t, tt.expected, nilOrEqual(tt.value, tt.check)) + }) + } +} + +func TestObjectToMeta(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + object metav1.Object + expected types.NamespacedName + }{ + "gateway": { + object: &gwv1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{Namespace: "test", Name: "test"}}, + expected: types.NamespacedName{Namespace: "test", Name: "test"}, + }, + "secret": { + object: &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Namespace: "secret", Name: "secret"}}, + expected: types.NamespacedName{Namespace: "secret", Name: "secret"}, + }, + } { + t.Run(name, func(t *testing.T) { + require.Equal(t, tt.expected, objectToMeta(tt.object)) + }) + } +} + +func TestIsDeleted(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + object client.Object + expected bool + }{ + "deleted gateway": { + object: &gwv1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{DeletionTimestamp: pointerTo(metav1.Now())}}, + expected: true, + }, + "non-deleted http route": { + object: &gwv1beta1.HTTPRoute{}, + expected: false, + }, + } { + t.Run(name, func(t *testing.T) { + require.Equal(t, tt.expected, isDeleted(tt.object)) + }) + } +} + +func TestEnsureFinalizer(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + object client.Object + expected bool + finalizers []string + }{ + "gateway no finalizer": { + object: &gwv1beta1.Gateway{}, + expected: true, + finalizers: []string{gatewayFinalizer}, + }, + "gateway other finalizer": { + object: &gwv1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"other"}}}, + expected: true, + finalizers: []string{"other", gatewayFinalizer}, + }, + "gateway already has finalizer": { + object: &gwv1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{gatewayFinalizer}}}, + expected: false, + finalizers: []string{gatewayFinalizer}, + }, + } { + t.Run(name, func(t *testing.T) { + require.Equal(t, tt.expected, ensureFinalizer(tt.object)) + require.Equal(t, tt.finalizers, tt.object.GetFinalizers()) + }) + } +} + +func TestRemoveFinalizer(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + object client.Object + expected bool + finalizers []string + }{ + "gateway no finalizer": { + object: &gwv1beta1.Gateway{}, + expected: false, + finalizers: []string{}, + }, + "gateway other finalizer": { + object: &gwv1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"other"}}}, + expected: false, + finalizers: []string{"other"}, + }, + "gateway multiple finalizers": { + object: &gwv1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{gatewayFinalizer, gatewayFinalizer}}}, + expected: true, + finalizers: []string{}, + }, + "gateway mixed finalizers": { + object: &gwv1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"other", gatewayFinalizer}}}, + expected: true, + finalizers: []string{"other"}, + }, + } { + t.Run(name, func(t *testing.T) { + require.Equal(t, tt.expected, removeFinalizer(tt.object)) + require.Equal(t, tt.finalizers, tt.object.GetFinalizers()) + }) + } +} diff --git a/control-plane/api-gateway/binding/validation.go b/control-plane/api-gateway/binding/validation.go new file mode 100644 index 0000000000..c22726f2da --- /dev/null +++ b/control-plane/api-gateway/binding/validation.go @@ -0,0 +1,332 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package binding + +import ( + "strings" + + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul/api" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + klabels "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +// validateRefs validates backend references for a route, determining whether or +// not they were found in the list of known connect-injected services. +func validateRefs(namespace string, refs []gwv1beta1.BackendRef, services map[types.NamespacedName]api.CatalogService, meshServices map[types.NamespacedName]v1alpha1.MeshService) routeValidationResults { + var result routeValidationResults + for _, ref := range refs { + nsn := types.NamespacedName{ + Name: string(ref.BackendObjectReference.Name), + Namespace: valueOr(ref.BackendObjectReference.Namespace, namespace), + } + + // TODO: check reference grants + + backendRef := ref.BackendObjectReference + + isServiceRef := nilOrEqual(backendRef.Group, "") && nilOrEqual(backendRef.Kind, "Service") + isMeshServiceRef := derefEqual(backendRef.Group, v1alpha1.ConsulHashicorpGroup) && derefEqual(backendRef.Kind, v1alpha1.MeshServiceKind) + + if !isServiceRef && !isMeshServiceRef { + result = append(result, routeValidationResult{ + namespace: nsn.Namespace, + backend: ref, + err: errRouteInvalidKind, + }) + continue + } + + if isServiceRef { + if _, found := services[nsn]; !found { + result = append(result, routeValidationResult{ + namespace: nsn.Namespace, + backend: ref, + err: errRouteBackendNotFound, + }) + continue + } + } + + if isMeshServiceRef { + if _, found := meshServices[nsn]; !found { + result = append(result, routeValidationResult{ + namespace: nsn.Namespace, + backend: ref, + err: errRouteBackendNotFound, + }) + continue + } + } + + result = append(result, routeValidationResult{ + namespace: nsn.Namespace, + backend: ref, + }) + } + return result +} + +// validateGateway validates that a gateway is semantically valid given +// the set of features that we support. +func validateGateway(gateway gwv1beta1.Gateway, pods []corev1.Pod, consulGateway *api.APIGatewayConfigEntry) gatewayValidationResult { + var result gatewayValidationResult + + if len(gateway.Spec.Addresses) > 0 { + result.acceptedErr = errGatewayUnsupportedAddress + } + + if len(pods) == 0 { + result.programmedErr = errGatewayPending_Pods + } else if consulGateway == nil { + result.programmedErr = errGatewayPending_Consul + } + + return result +} + +// mergedListener associates a listener with its indexed position +// in the gateway spec, it's used to re-associate a status with +// a listener after we merge compatible listeners together and then +// validate their conflicts. +type mergedListener struct { + index int + listener gwv1beta1.Listener +} + +// mergedListeners is a set of a listeners that are considered "merged" +// due to referencing the same listener port. +type mergedListeners []mergedListener + +// validateProtocol validates that the protocols used across all merged +// listeners are compatible. +func (m mergedListeners) validateProtocol() error { + var protocol *gwv1beta1.ProtocolType + for _, l := range m { + if protocol == nil { + protocol = pointerTo(l.listener.Protocol) + } + if *protocol != l.listener.Protocol { + return errListenerProtocolConflict + } + } + return nil +} + +// validateHostname validates that the merged listeners don't use the same +// hostnames as per the spec. +func (m mergedListeners) validateHostname(index int, listener gwv1beta1.Listener) error { + for _, l := range m { + if l.index == index { + continue + } + if bothNilOrEqual(listener.Hostname, l.listener.Hostname) { + return errListenerHostnameConflict + } + } + return nil +} + +// validateTLS validates that the TLS configuration for a given listener is valid and that +// the certificates that it references exist. +func validateTLS(namespace string, tls *gwv1beta1.GatewayTLSConfig, certificates []corev1.Secret) (error, error) { + if tls == nil { + return nil, nil + } + + // TODO: Resource Grants + + var err error +MAIN_LOOP: + for _, ref := range tls.CertificateRefs { + // break on the first error + if !nilOrEqual(ref.Group, "") || !nilOrEqual(ref.Kind, "Secret") { + err = errListenerInvalidCertificateRef_NotSupported + break MAIN_LOOP + } + ns := valueOr(ref.Namespace, namespace) + + for _, secret := range certificates { + if secret.Namespace == ns && secret.Name == string(ref.Name) { + continue MAIN_LOOP + } + } + + // not found, set error + err = errListenerInvalidCertificateRef_NotFound + break MAIN_LOOP + } + + if tls.Mode != nil && *tls.Mode == gwv1beta1.TLSModePassthrough { + return errListenerNoTLSPassthrough, err + } + + // TODO: validate tls options + return nil, err +} + +// validateListeners validates the given listeners both internally and with respect to each +// other for purposes of setting "Conflicted" status conditions. +func validateListeners(namespace string, listeners []gwv1beta1.Listener, secrets []corev1.Secret) listenerValidationResults { + var results listenerValidationResults + merged := make(map[gwv1beta1.PortNumber]mergedListeners) + for i, listener := range listeners { + merged[listener.Port] = append(merged[listener.Port], mergedListener{ + index: i, + listener: listener, + }) + } + + for i, listener := range listeners { + var result listenerValidationResult + + err, refErr := validateTLS(namespace, listener.TLS, secrets) + result.refErr = refErr + if err != nil { + result.acceptedErr = err + } else { + _, supported := supportedKindsForProtocol[listener.Protocol] + if !supported { + result.acceptedErr = errListenerUnsupportedProtocol + } else if listener.Port == 20000 { //admin port + result.acceptedErr = errListenerPortUnavailable + } + } + + if err := merged[listener.Port].validateProtocol(); err != nil { + result.conflictedErr = err + } else { + result.conflictedErr = merged[listener.Port].validateHostname(i, listener) + } + + results = append(results, result) + } + return results +} + +// routeAllowedForListenerNamespaces determines whether the route is allowed +// to bind to the Gateway based on the AllowedRoutes namespace selectors. +func routeAllowedForListenerNamespaces(gatewayNamespace string, allowedRoutes *gwv1beta1.AllowedRoutes, namespace corev1.Namespace) bool { + var namespaceSelector *gwv1beta1.RouteNamespaces + if allowedRoutes != nil { + // check gateway namespace + namespaceSelector = allowedRoutes.Namespaces + } + + // set default if namespace selector is nil + from := gwv1beta1.NamespacesFromSame + if namespaceSelector != nil && namespaceSelector.From != nil && *namespaceSelector.From != "" { + from = *namespaceSelector.From + } + + switch from { + case gwv1beta1.NamespacesFromAll: + return true + case gwv1beta1.NamespacesFromSame: + return gatewayNamespace == namespace.Name + case gwv1beta1.NamespacesFromSelector: + namespaceSelector, err := metav1.LabelSelectorAsSelector(namespaceSelector.Selector) + if err != nil { + // log the error here, the label selector is invalid + return false + } + + return namespaceSelector.Matches(toNamespaceSet(namespace.GetName(), namespace.GetLabels())) + default: + return false + } +} + +// routeAllowedForListenerHostname checks that a hostname specified on a route and the hostname specified +// on the gateway listener are compatible. +func routeAllowedForListenerHostname(hostname *gwv1beta1.Hostname, hostnames []gwv1beta1.Hostname) bool { + if hostname == nil || len(hostnames) == 0 { + return true + } + + for _, name := range hostnames { + if hostnamesMatch(name, *hostname) { + return true + } + } + return false +} + +// hostnameMatch checks that an individual hostname matches another hostname for +// compatibility. +func hostnamesMatch(a gwv1alpha2.Hostname, b gwv1beta1.Hostname) bool { + if a == "" || a == "*" || b == "" || b == "*" { + // any wildcard always matches + return true + } + + if strings.HasPrefix(string(a), "*.") || strings.HasPrefix(string(b), "*.") { + aLabels, bLabels := strings.Split(string(a), "."), strings.Split(string(b), ".") + if len(aLabels) != len(bLabels) { + return false + } + + for i := 1; i < len(aLabels); i++ { + if !strings.EqualFold(aLabels[i], bLabels[i]) { + return false + } + } + return true + } + + return string(a) == string(b) +} + +// routeKindIsAllowedForListener checks that the given route kind is present in the allowed set. +func routeKindIsAllowedForListener(kinds []gwv1beta1.RouteGroupKind, gk schema.GroupKind) bool { + if kinds == nil { + return true + } + + for _, kind := range kinds { + if string(kind.Kind) == gk.Kind && nilOrEqual(kind.Group, gk.Group) { + return true + } + } + + return false +} + +// routeKindIsAllowedForListenerExplicit checks that a route is allowed by the kinds specified explicitly +// on the listener. +func routeKindIsAllowedForListenerExplicit(allowedRoutes *gwv1alpha2.AllowedRoutes, gk schema.GroupKind) bool { + if allowedRoutes == nil { + return true + } + + return routeKindIsAllowedForListener(allowedRoutes.Kinds, gk) +} + +// toNamespaceSet constructs a list of labels used to match a Namespace. +func toNamespaceSet(name string, labels map[string]string) klabels.Labels { + // If namespace label is not set, implicitly insert it to support older Kubernetes versions + if labels[namespaceNameLabel] == name { + // Already set, avoid copies + return klabels.Set(labels) + } + // First we need a copy to not modify the underlying object + ret := make(map[string]string, len(labels)+1) + for k, v := range labels { + ret[k] = v + } + ret[namespaceNameLabel] = name + return klabels.Set(ret) +} + +func derefEqual[T ~string](v *T, check string) bool { + if v == nil { + return false + } + return string(*v) == check +} diff --git a/control-plane/api-gateway/binding/validation_test.go b/control-plane/api-gateway/binding/validation_test.go new file mode 100644 index 0000000000..eaf4388413 --- /dev/null +++ b/control-plane/api-gateway/binding/validation_test.go @@ -0,0 +1,672 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package binding + +import ( + "testing" + + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul/api" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +func TestValidateRefs(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + namespace string + refs []gwv1beta1.BackendObjectReference + services map[types.NamespacedName]api.CatalogService + meshServices map[types.NamespacedName]v1alpha1.MeshService + expectedErrors []error + }{ + "all pass no namespaces": { + namespace: "test", + refs: []gwv1beta1.BackendObjectReference{{Name: "1"}, {Name: "2"}}, + services: map[types.NamespacedName]api.CatalogService{ + {Name: "1", Namespace: "test"}: {}, + {Name: "2", Namespace: "test"}: {}, + {Name: "3", Namespace: "test"}: {}, + }, + meshServices: map[types.NamespacedName]v1alpha1.MeshService{}, + expectedErrors: []error{nil, nil}, + }, + "all pass namespaces": { + namespace: "test", + refs: []gwv1beta1.BackendObjectReference{ + {Name: "1", Namespace: pointerTo[gwv1beta1.Namespace]("other")}, + {Name: "2", Namespace: pointerTo[gwv1beta1.Namespace]("other")}, + }, + services: map[types.NamespacedName]api.CatalogService{ + {Name: "1", Namespace: "other"}: {}, + {Name: "2", Namespace: "other"}: {}, + {Name: "3", Namespace: "other"}: {}, + }, + meshServices: map[types.NamespacedName]v1alpha1.MeshService{}, + expectedErrors: []error{nil, nil}, + }, + "all pass mixed": { + namespace: "test", + refs: []gwv1beta1.BackendObjectReference{ + {Name: "1", Namespace: pointerTo[gwv1beta1.Namespace]("other")}, + {Name: "2"}, + }, + services: map[types.NamespacedName]api.CatalogService{ + {Name: "1", Namespace: "other"}: {}, + {Name: "2", Namespace: "test"}: {}, + {Name: "3", Namespace: "other"}: {}, + }, + meshServices: map[types.NamespacedName]v1alpha1.MeshService{}, + expectedErrors: []error{nil, nil}, + }, + "all fail mixed": { + namespace: "test", + refs: []gwv1beta1.BackendObjectReference{ + {Name: "1"}, + {Name: "2", Namespace: pointerTo[gwv1beta1.Namespace]("other")}, + }, + services: map[types.NamespacedName]api.CatalogService{ + {Name: "1", Namespace: "other"}: {}, + {Name: "2", Namespace: "test"}: {}, + {Name: "3", Namespace: "other"}: {}, + }, + meshServices: map[types.NamespacedName]v1alpha1.MeshService{}, + expectedErrors: []error{errRouteBackendNotFound, errRouteBackendNotFound}, + }, + "all fail no namespaces": { + namespace: "test", + refs: []gwv1beta1.BackendObjectReference{ + {Name: "1"}, + {Name: "2"}, + }, + services: map[types.NamespacedName]api.CatalogService{ + {Name: "1", Namespace: "other"}: {}, + {Name: "2", Namespace: "other"}: {}, + {Name: "3", Namespace: "other"}: {}, + }, + meshServices: map[types.NamespacedName]v1alpha1.MeshService{}, + expectedErrors: []error{errRouteBackendNotFound, errRouteBackendNotFound}, + }, + "all fail namespaces": { + namespace: "test", + refs: []gwv1beta1.BackendObjectReference{ + {Name: "1", Namespace: pointerTo[gwv1beta1.Namespace]("other")}, + {Name: "2", Namespace: pointerTo[gwv1beta1.Namespace]("other")}, + }, + services: map[types.NamespacedName]api.CatalogService{ + {Name: "1", Namespace: "test"}: {}, + {Name: "2", Namespace: "test"}: {}, + {Name: "3", Namespace: "test"}: {}, + }, + meshServices: map[types.NamespacedName]v1alpha1.MeshService{}, + expectedErrors: []error{errRouteBackendNotFound, errRouteBackendNotFound}, + }, + "type failures": { + namespace: "test", + refs: []gwv1beta1.BackendObjectReference{ + {Name: "1", Group: pointerTo[gwv1beta1.Group]("test")}, + {Name: "2"}, + }, + services: map[types.NamespacedName]api.CatalogService{ + {Name: "1", Namespace: "test"}: {}, + {Name: "2", Namespace: "test"}: {}, + {Name: "3", Namespace: "test"}: {}, + }, + meshServices: map[types.NamespacedName]v1alpha1.MeshService{}, + expectedErrors: []error{errRouteInvalidKind, nil}, + }, + "mesh services": { + namespace: "test", + refs: []gwv1beta1.BackendObjectReference{ + { + Name: "1", + Group: pointerTo(gwv1beta1.Group(v1alpha1.ConsulHashicorpGroup)), + Kind: pointerTo(gwv1beta1.Kind(v1alpha1.MeshServiceKind)), + }, + }, + meshServices: map[types.NamespacedName]v1alpha1.MeshService{ + {Name: "1", Namespace: "test"}: {}, + {Name: "2", Namespace: "test"}: {}, + {Name: "3", Namespace: "test"}: {}, + }, + expectedErrors: []error{nil}, + }, + } { + t.Run(name, func(t *testing.T) { + refs := make([]gwv1beta1.BackendRef, len(tt.refs)) + for i, ref := range tt.refs { + refs[i] = gwv1beta1.BackendRef{BackendObjectReference: ref} + } + + actual := validateRefs(tt.namespace, refs, tt.services, tt.meshServices) + require.Equal(t, len(actual), len(tt.refs)) + require.Equal(t, len(actual), len(tt.expectedErrors)) + for i, err := range tt.expectedErrors { + require.Equal(t, err, actual[i].err) + } + }) + } +} + +func TestValidateGateway(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + object gwv1beta1.Gateway + expected error + }{ + "valid": { + object: gwv1beta1.Gateway{}, + expected: nil, + }, + "invalid": { + object: gwv1beta1.Gateway{Spec: gwv1beta1.GatewaySpec{Addresses: []gwv1beta1.GatewayAddress{ + {Value: "1"}, + }}}, + expected: errGatewayUnsupportedAddress, + }, + } { + t.Run(name, func(t *testing.T) { + require.Equal(t, tt.expected, validateGateway(tt.object, nil, nil).acceptedErr) + }) + } +} + +func TestMergedListeners_ValidateProtocol(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + mergedListeners mergedListeners + expected error + }{ + "valid": { + mergedListeners: []mergedListener{ + {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, + {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, + {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, + {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, + }, + expected: nil, + }, + "invalid": { + mergedListeners: []mergedListener{ + {listener: gwv1beta1.Listener{Protocol: gwv1beta1.TCPProtocolType}}, + {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, + {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, + {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, + }, + expected: errListenerProtocolConflict, + }, + "big list": { + mergedListeners: []mergedListener{ + {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, + {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, + {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, + {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, + {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, + {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, + {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, + {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPSProtocolType}}, + {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, + {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, + {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, + {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, + }, + expected: errListenerProtocolConflict, + }, + } { + t.Run(name, func(t *testing.T) { + require.Equal(t, tt.expected, tt.mergedListeners.validateProtocol()) + }) + } +} + +func TestMergedListeners_ValidateHostname(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + mergedListeners mergedListeners + expected error + }{ + "valid": { + mergedListeners: []mergedListener{ + {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("1")}}, + {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("2")}}, + {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("3")}}, + {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("4")}}, + {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("5")}}, + {}, + }, + expected: nil, + }, + "invalid nil": { + mergedListeners: []mergedListener{ + {}, + {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("1")}}, + {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("2")}}, + {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("3")}}, + {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("4")}}, + {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("5")}}, + {}, + }, + expected: errListenerHostnameConflict, + }, + "invalid set": { + mergedListeners: []mergedListener{ + {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("1")}}, + {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("2")}}, + {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("3")}}, + {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("4")}}, + {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("5")}}, + {}, + {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("1")}}, + }, + expected: errListenerHostnameConflict, + }, + } { + t.Run(name, func(t *testing.T) { + for i, l := range tt.mergedListeners { + l.index = i + tt.mergedListeners[i] = l + } + + require.Equal(t, tt.expected, tt.mergedListeners.validateHostname(0, tt.mergedListeners[0].listener)) + }) + } +} + +func TestValidateTLS(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + namespace string + tls *gwv1beta1.GatewayTLSConfig + certificates []corev1.Secret + expectedResolvedRefsErr error + expectedAcceptedErr error + }{ + "no tls": { + namespace: "test", + tls: nil, + certificates: nil, + expectedResolvedRefsErr: nil, + expectedAcceptedErr: nil, + }, + "not supported certificate": { + namespace: "test", + tls: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{ + {Name: "foo", Namespace: pointerTo[gwv1beta1.Namespace]("other"), Group: pointerTo[gwv1beta1.Group]("test")}, + }, + }, + certificates: []corev1.Secret{ + {ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "other"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "other"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "other"}}, + }, + expectedResolvedRefsErr: errListenerInvalidCertificateRef_NotSupported, + expectedAcceptedErr: nil, + }, + "not found certificate": { + namespace: "test", + tls: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{ + {Name: "zoiks", Namespace: pointerTo[gwv1beta1.Namespace]("other")}, + }, + }, + certificates: []corev1.Secret{ + {ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "other"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "other"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "other"}}, + }, + expectedResolvedRefsErr: errListenerInvalidCertificateRef_NotFound, + expectedAcceptedErr: nil, + }, + "not found certificate mismatched namespace": { + namespace: "test", + tls: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{ + {Name: "foo", Namespace: pointerTo[gwv1beta1.Namespace]("1")}, + }, + }, + certificates: []corev1.Secret{ + {ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "other"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "other"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "other"}}, + }, + expectedResolvedRefsErr: errListenerInvalidCertificateRef_NotFound, + expectedAcceptedErr: nil, + }, + "passthrough mode": { + namespace: "test", + tls: &gwv1beta1.GatewayTLSConfig{ + Mode: pointerTo(gwv1beta1.TLSModePassthrough), + }, + certificates: nil, + expectedResolvedRefsErr: nil, + expectedAcceptedErr: errListenerNoTLSPassthrough, + }, + "valid targeted namespace": { + namespace: "test", + tls: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{ + {Name: "foo", Namespace: pointerTo[gwv1beta1.Namespace]("1")}, + {Name: "bar", Namespace: pointerTo[gwv1beta1.Namespace]("2")}, + {Name: "baz", Namespace: pointerTo[gwv1beta1.Namespace]("3")}, + }, + }, + certificates: []corev1.Secret{ + {ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "1"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "2"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "3"}}, + }, + expectedResolvedRefsErr: nil, + expectedAcceptedErr: nil, + }, + "valid same namespace": { + namespace: "test", + tls: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{ + {Name: "foo"}, + {Name: "bar"}, + {Name: "baz"}, + }, + }, + certificates: []corev1.Secret{ + {ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "test"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test"}}, + }, + expectedResolvedRefsErr: nil, + expectedAcceptedErr: nil, + }, + "valid empty certs": { + namespace: "test", + tls: &gwv1beta1.GatewayTLSConfig{}, + certificates: nil, + expectedResolvedRefsErr: nil, + expectedAcceptedErr: nil, + }, + } { + t.Run(name, func(t *testing.T) { + actualAcceptedError, actualResolvedRefsError := validateTLS(tt.namespace, tt.tls, tt.certificates) + require.Equal(t, tt.expectedResolvedRefsErr, actualResolvedRefsError) + require.Equal(t, tt.expectedAcceptedErr, actualAcceptedError) + }) + } +} + +func TestValidateListeners(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + listeners []gwv1beta1.Listener + expectedAcceptedErr error + }{ + "valid protocol HTTP": { + listeners: []gwv1beta1.Listener{ + {Protocol: gwv1beta1.HTTPProtocolType}, + }, + expectedAcceptedErr: nil, + }, + "valid protocol HTTPS": { + listeners: []gwv1beta1.Listener{ + {Protocol: gwv1beta1.HTTPSProtocolType}, + }, + expectedAcceptedErr: nil, + }, + "valid protocol TCP": { + listeners: []gwv1beta1.Listener{ + {Protocol: gwv1beta1.TCPProtocolType}, + }, + expectedAcceptedErr: nil, + }, + "invalid protocol UDP": { + listeners: []gwv1beta1.Listener{ + {Protocol: gwv1beta1.UDPProtocolType}, + }, + expectedAcceptedErr: errListenerUnsupportedProtocol, + }, + "invalid port": { + listeners: []gwv1beta1.Listener{ + {Protocol: gwv1beta1.TCPProtocolType, Port: 20000}, + }, + expectedAcceptedErr: errListenerPortUnavailable, + }, + } { + t.Run(name, func(t *testing.T) { + require.Equal(t, tt.expectedAcceptedErr, validateListeners("", tt.listeners, nil)[0].acceptedErr) + }) + } +} + +func TestRouteAllowedForListenerNamespaces(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + allowedRoutes *gwv1beta1.AllowedRoutes + gatewayNamespace string + routeNamespace corev1.Namespace + expected bool + }{ + "default same namespace allowed": { + allowedRoutes: nil, + gatewayNamespace: "test", + routeNamespace: corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test"}}, + expected: true, + }, + "default same namespace not allowed": { + allowedRoutes: nil, + gatewayNamespace: "test", + routeNamespace: corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "other"}}, + expected: false, + }, + "explicit same namespace allowed": { + allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{From: pointerTo(gwv1beta1.NamespacesFromSame)}}, + gatewayNamespace: "test", + routeNamespace: corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test"}}, + expected: true, + }, + "explicit same namespace not allowed": { + allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{From: pointerTo(gwv1beta1.NamespacesFromSame)}}, + gatewayNamespace: "test", + routeNamespace: corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "other"}}, + expected: false, + }, + "all namespace allowed": { + allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{From: pointerTo(gwv1beta1.NamespacesFromAll)}}, + gatewayNamespace: "test", + routeNamespace: corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "other"}}, + expected: true, + }, + "invalid namespace from not allowed": { + allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{From: pointerTo[gwv1beta1.FromNamespaces]("other")}}, + gatewayNamespace: "test", + routeNamespace: corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test"}}, + expected: false, + }, + "labeled namespace allowed": { + allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{ + From: pointerTo(gwv1beta1.NamespacesFromSelector), + Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, + }}, + gatewayNamespace: "test", + routeNamespace: corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "other", Labels: map[string]string{ + "foo": "bar", + }}}, + expected: true, + }, + "labeled namespace not allowed": { + allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{ + From: pointerTo(gwv1beta1.NamespacesFromSelector), + Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, + }}, + gatewayNamespace: "test", + routeNamespace: corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "other", Labels: map[string]string{ + "foo": "baz", + }}}, + expected: false, + }, + "invalid labeled namespace": { + allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{ + From: pointerTo(gwv1beta1.NamespacesFromSelector), + Selector: &metav1.LabelSelector{MatchExpressions: []metav1.LabelSelectorRequirement{ + {Key: "foo", Operator: "junk", Values: []string{"1"}}, + }}, + }}, + gatewayNamespace: "test", + routeNamespace: corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "other", Labels: map[string]string{ + "foo": "bar", + }}}, + expected: false, + }, + } { + t.Run(name, func(t *testing.T) { + require.Equal(t, tt.expected, routeAllowedForListenerNamespaces(tt.gatewayNamespace, tt.allowedRoutes, tt.routeNamespace)) + }) + } +} + +func TestRouteAllowedForListenerHostname(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + hostname *gwv1beta1.Hostname + hostnames []gwv1beta1.Hostname + expected bool + }{ + "empty hostnames": { + hostname: nil, + hostnames: []gwv1beta1.Hostname{"foo", "bar"}, + expected: true, + }, + "empty hostname": { + hostname: pointerTo[gwv1beta1.Hostname]("foo"), + hostnames: nil, + expected: true, + }, + "any hostname match": { + hostname: pointerTo[gwv1beta1.Hostname]("foo"), + hostnames: []gwv1beta1.Hostname{"foo", "bar"}, + expected: true, + }, + "no match": { + hostname: pointerTo[gwv1beta1.Hostname]("foo"), + hostnames: []gwv1beta1.Hostname{"bar"}, + expected: false, + }, + } { + t.Run(name, func(t *testing.T) { + require.Equal(t, tt.expected, routeAllowedForListenerHostname(tt.hostname, tt.hostnames)) + }) + } +} + +func TestHostnamesMatch(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + one gwv1beta1.Hostname + two gwv1beta1.Hostname + expected bool + }{ + "wildcard one": { + one: "*", + two: "foo", + expected: true, + }, + "wildcard two": { + one: "foo", + two: "*", + expected: true, + }, + "empty one": { + one: "", + two: "foo", + expected: true, + }, + "empty two": { + one: "foo", + two: "", + expected: true, + }, + "subdomain one": { + one: "*.foo", + two: "sub.foo", + expected: true, + }, + "subdomain two": { + one: "sub.foo", + two: "*.foo", + expected: true, + }, + "exact match": { + one: "foo", + two: "foo", + expected: true, + }, + "no match": { + one: "foo", + two: "bar", + expected: false, + }, + } { + t.Run(name, func(t *testing.T) { + require.Equal(t, tt.expected, hostnamesMatch(tt.one, tt.two)) + }) + } +} + +func TestRouteKindIsAllowedForListener(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + kinds []gwv1beta1.RouteGroupKind + gk schema.GroupKind + expected bool + }{ + "empty kinds": { + kinds: nil, + gk: schema.GroupKind{Group: "a", Kind: "b"}, + expected: true, + }, + "group specified": { + kinds: []gwv1beta1.RouteGroupKind{ + {Group: pointerTo[gwv1beta1.Group]("a"), Kind: "b"}, + }, + gk: schema.GroupKind{Group: "a", Kind: "b"}, + expected: true, + }, + "group unspecified": { + kinds: []gwv1beta1.RouteGroupKind{ + {Kind: "b"}, + }, + gk: schema.GroupKind{Group: "a", Kind: "b"}, + expected: true, + }, + "kind mismatch": { + kinds: []gwv1beta1.RouteGroupKind{ + {Kind: "b"}, + }, + gk: schema.GroupKind{Group: "a", Kind: "c"}, + expected: false, + }, + "group mismatch": { + kinds: []gwv1beta1.RouteGroupKind{ + {Group: pointerTo[gwv1beta1.Group]("a"), Kind: "b"}, + }, + gk: schema.GroupKind{Group: "d", Kind: "b"}, + expected: false, + }, + } { + t.Run(name, func(t *testing.T) { + require.Equal(t, tt.expected, routeKindIsAllowedForListener(tt.kinds, tt.gk)) + }) + } +} diff --git a/control-plane/api-gateway/cache/consul.go b/control-plane/api-gateway/cache/consul.go new file mode 100644 index 0000000000..7b66067123 --- /dev/null +++ b/control-plane/api-gateway/cache/consul.go @@ -0,0 +1,897 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package cache + +import ( + "context" + "fmt" + "strings" + "sync" + "time" + + "github.com/go-logr/logr" + "github.com/google/go-cmp/cmp" + "golang.org/x/exp/slices" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/event" + + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/translation" + "github.com/hashicorp/consul-k8s/control-plane/consul" + "github.com/hashicorp/consul/api" +) + +const ( + namespaceWildcard = "*" + apiTimeout = 5 * time.Minute +) + +var Kinds = []string{api.APIGateway, api.HTTPRoute, api.TCPRoute, api.InlineCertificate} + +type Config struct { + ConsulClientConfig *consul.Config + ConsulServerConnMgr consul.ServerConnectionManager + NamespacesEnabled bool + PeeringEnabled bool + Logger logr.Logger +} + +type resourceCache map[api.ResourceReference]api.ConfigEntry + +func (oldCache resourceCache) diff(newCache resourceCache) []api.ConfigEntry { + diffs := make([]api.ConfigEntry, 0) + + for ref, entry := range newCache { + oldRef, ok := oldCache[ref] + // ref from the new cache doesn't exist in the old one + // this means a resource was added + if !ok { + diffs = append(diffs, entry) + continue + } + + // the entry in the old cache has an older modify index than the ref + // from the new cache + if oldRef.GetModifyIndex() < entry.GetModifyIndex() { + diffs = append(diffs, entry) + } + } + + // get all deleted entries, these are entries present in the old cache + // that are not present in the new + for ref, entry := range oldCache { + if _, ok := newCache[ref]; !ok { + diffs = append(diffs, entry) + } + } + + return diffs +} + +type serviceCache map[api.ResourceReference]*api.CatalogService + +func (oldCache serviceCache) diff(newCache serviceCache) []*api.CatalogService { + diffs := make([]*api.CatalogService, 0) + + for ref, entry := range newCache { + oldRef, ok := oldCache[ref] + // ref from the new cache doesn't exist in the old one + // this means a resource was added + if !ok { + diffs = append(diffs, entry) + continue + } + + // the entry in the old cache has an older modify index than the ref + // from the new cache + if oldRef.ModifyIndex < entry.ModifyIndex { + diffs = append(diffs, entry) + } + } + + // get all deleted entries, these are entries present in the old cache + // that are not present in the new + for ref, entry := range oldCache { + if _, ok := newCache[ref]; !ok { + diffs = append(diffs, entry) + } + } + return diffs +} + +type peeringCache map[api.ResourceReference]*api.Peering + +func (oldCache peeringCache) diff(newCache peeringCache) []*api.Peering { + diffs := make([]*api.Peering, 0) + + for ref, entry := range newCache { + oldRef, ok := oldCache[ref] + // ref from the new cache doesn't exist in the old one + // this means a resource was added + if !ok { + diffs = append(diffs, entry) + continue + } + + // the entry in the old cache has an older modify index than the ref + // from the new cache + if oldRef.ModifyIndex < entry.ModifyIndex { + diffs = append(diffs, entry) + } + } + + // get all deleted entries, these are entries present in the old cache + // that are not present in the new + for ref, entry := range oldCache { + if _, ok := newCache[ref]; !ok { + diffs = append(diffs, entry) + } + } + return diffs +} + +// configEntryObject is used for generic k8s events so we maintain the consul name/namespace. +type configEntryObject struct { + client.Object // embed so we fufill the object interface + + Namespace string + Name string +} + +func (c *configEntryObject) GetNamespace() string { + return c.Namespace +} + +func (c *configEntryObject) GetName() string { + return c.Name +} + +func newConfigEntryObject(namespacedName types.NamespacedName) *configEntryObject { + return &configEntryObject{ + Namespace: namespacedName.Namespace, + Name: namespacedName.Name, + } +} + +// Subscription represents a watcher for events on a specific kind. +type Subscription struct { + translator translation.TranslatorFn + ctx context.Context + cancelCtx context.CancelFunc + events chan event.GenericEvent +} + +func (s *Subscription) Cancel() { + s.cancelCtx() +} + +func (s *Subscription) Events() chan event.GenericEvent { + return s.events +} + +type ServiceTranslatorFn func(*api.CatalogService) []types.NamespacedName + +// ServiceSubscription represents a watcher for events on a specific kind. +type ServiceSubscription struct { + translator ServiceTranslatorFn + ctx context.Context + cancelCtx context.CancelFunc + events chan event.GenericEvent +} + +func (s *ServiceSubscription) Cancel() { + s.cancelCtx() +} + +func (s *ServiceSubscription) Events() chan event.GenericEvent { + return s.events +} + +type PeeringTranslatorFn func(*api.Peering) []types.NamespacedName + +// PeeringsSubscription represents a watcher for events on a specific kind. +type PeeringsSubscription struct { + translator PeeringTranslatorFn + ctx context.Context + cancelCtx context.CancelFunc + events chan event.GenericEvent +} + +func (s *PeeringsSubscription) Cancel() { + s.cancelCtx() +} + +func (s *PeeringsSubscription) Events() chan event.GenericEvent { + return s.events +} + +// Cache subscribes to and caches Consul objects, it also responsible for mainting subscriptions to +// resources that it caches. +type Cache struct { + config *consul.Config + serverMgr consul.ServerConnectionManager + logger logr.Logger + + cache map[string]resourceCache + serviceCache serviceCache + peeringCache peeringCache + cacheMutex *sync.Mutex + + subscribers map[string][]*Subscription + serviceSubscribers []*ServiceSubscription + peeringsSubscribers []*PeeringsSubscription + subscriberMutex *sync.Mutex + + namespacesEnabled bool + peeringsEnabled bool + + synced chan struct{} + + kinds []string +} + +func New(config Config) *Cache { + cache := make(map[string]resourceCache, len(Kinds)) + for _, kind := range Kinds { + cache[kind] = make(resourceCache) + } + config.ConsulClientConfig.APITimeout = apiTimeout + + return &Cache{ + config: config.ConsulClientConfig, + serverMgr: config.ConsulServerConnMgr, + namespacesEnabled: config.NamespacesEnabled, + peeringsEnabled: config.PeeringEnabled, + cache: cache, + serviceCache: make(serviceCache), + peeringCache: make(peeringCache), + cacheMutex: &sync.Mutex{}, + subscribers: make(map[string][]*Subscription), + serviceSubscribers: make([]*ServiceSubscription, 0), + peeringsSubscribers: make([]*PeeringsSubscription, 0), + subscriberMutex: &sync.Mutex{}, + kinds: Kinds, + // we make a buffered channel that is the length of the kinds which + // are subscribed to + 2 for services + peerings + synced: make(chan struct{}, len(Kinds)+2), + logger: config.Logger, + } +} + +// WaitSynced is used to coordinate with the caller when the cache is initially filled. +func (c *Cache) WaitSynced(ctx context.Context) { + for range c.kinds { + select { + case <-c.synced: + case <-ctx.Done(): + return + } + } + // one more for service subscribers + select { + case <-c.synced: + case <-ctx.Done(): + return + } + if c.peeringsEnabled { + // and one more for peerings subscribers + select { + case <-c.synced: + case <-ctx.Done(): + return + } + } +} + +// Subscribe handles adding a new subscription for resources of a given kind. +func (c *Cache) Subscribe(ctx context.Context, kind string, translator translation.TranslatorFn) *Subscription { + c.subscriberMutex.Lock() + defer c.subscriberMutex.Unlock() + + // check that kind is registered with cache + if !slices.Contains(c.kinds, kind) { + return &Subscription{} + } + + subscribers, ok := c.subscribers[kind] + if !ok { + subscribers = []*Subscription{} + } + + ctx, cancel := context.WithCancel(ctx) + events := make(chan event.GenericEvent) + sub := &Subscription{ + translator: translator, + ctx: ctx, + cancelCtx: cancel, + events: events, + } + + subscribers = append(subscribers, sub) + + c.subscribers[kind] = subscribers + + return sub +} + +// SubscribeServices handles adding a new subscription for resources of a given kind. +func (c *Cache) SubscribeServices(ctx context.Context, translator ServiceTranslatorFn) *ServiceSubscription { + c.subscriberMutex.Lock() + defer c.subscriberMutex.Unlock() + + ctx, cancel := context.WithCancel(ctx) + events := make(chan event.GenericEvent) + sub := &ServiceSubscription{ + translator: translator, + ctx: ctx, + cancelCtx: cancel, + events: events, + } + + c.serviceSubscribers = append(c.serviceSubscribers, sub) + return sub +} + +// SubscribeServices handles adding a new subscription for resources of a given kind. +func (c *Cache) SubscribePeerings(ctx context.Context, translator PeeringTranslatorFn) *PeeringsSubscription { + c.subscriberMutex.Lock() + defer c.subscriberMutex.Unlock() + + ctx, cancel := context.WithCancel(ctx) + events := make(chan event.GenericEvent) + sub := &PeeringsSubscription{ + translator: translator, + ctx: ctx, + cancelCtx: cancel, + events: events, + } + + c.peeringsSubscribers = append(c.peeringsSubscribers, sub) + return sub +} + +// Run starts the cache watch cycle, on the first call it will fill the cache with existing resources. +func (c *Cache) Run(ctx context.Context) { + wg := &sync.WaitGroup{} + + for i := range c.kinds { + kind := c.kinds[i] + + wg.Add(1) + go func() { + defer wg.Done() + c.subscribeToConsul(ctx, kind) + }() + } + + wg.Add(1) + go func() { + defer wg.Done() + c.subscribeToConsulServices(ctx) + }() + + if c.peeringsEnabled { + wg.Add(1) + go func() { + defer wg.Done() + c.subscribeToConsulPeerings(ctx) + }() + } + + wg.Wait() +} + +func (c *Cache) subscribeToConsul(ctx context.Context, kind string) { + once := &sync.Once{} + + opts := &api.QueryOptions{} + if c.namespacesEnabled { + opts.Namespace = namespaceWildcard + } + + for { + select { + case <-ctx.Done(): + return + default: + } + + client, err := consul.NewClientFromConnMgr(c.config, c.serverMgr) + if err != nil { + c.logger.Error(err, "error initializing consul client") + continue + } + + entries, meta, err := client.ConfigEntries().List(kind, opts.WithContext(ctx)) + if err != nil { + c.logger.Error(err, fmt.Sprintf("error fetching config entries for kind: %s", kind)) + continue + } + + opts.WaitIndex = meta.LastIndex + + c.updateAndNotify(ctx, once, kind, entries) + + select { + case <-ctx.Done(): + return + default: + continue + } + } +} + +func (c *Cache) subscribeToConsulServices(ctx context.Context) { + once := &sync.Once{} + + opts := &api.QueryOptions{Connect: true} + + // we need a second set of opts to make sure we don't + // block on the secondary list operations + serviceListOpts := &api.QueryOptions{Connect: true} + +MAIN_LOOP: + for { + select { + case <-ctx.Done(): + return + default: + } + + client, err := consul.NewClientFromConnMgr(c.config, c.serverMgr) + if err != nil { + c.logger.Error(err, "error initializing consul client") + continue + } + + services, meta, err := client.Catalog().Services(opts.WithContext(ctx)) + if err != nil { + c.logger.Error(err, "error fetching services") + continue + } + + flattened := []*api.CatalogService{} + for service := range services { + serviceList, _, err := client.Catalog().Service(service, "", serviceListOpts.WithContext(ctx)) + if err != nil { + c.logger.Error(err, fmt.Sprintf("error fetching service: %s", service)) + continue MAIN_LOOP + } + flattened = append(flattened, serviceList...) + } + + opts.WaitIndex = meta.LastIndex + c.updateAndNotifyServices(ctx, once, flattened) + + select { + case <-ctx.Done(): + return + default: + continue + } + } +} + +func (c *Cache) subscribeToConsulPeerings(ctx context.Context) { + once := &sync.Once{} + + opts := &api.QueryOptions{Connect: true} + if c.namespacesEnabled { + opts.Namespace = namespaceWildcard + } + + for { + select { + case <-ctx.Done(): + return + default: + } + + client, err := consul.NewClientFromConnMgr(c.config, c.serverMgr) + if err != nil { + c.logger.Error(err, "error initializing consul client") + continue + } + + peerings, meta, err := client.Peerings().List(ctx, opts.WithContext(ctx)) + if err != nil { + c.logger.Error(err, "error fetching services") + continue + } + + opts.WaitIndex = meta.LastIndex + c.updateAndNotifyPeerings(ctx, once, peerings) + + select { + case <-ctx.Done(): + return + default: + continue + } + } +} + +func (c *Cache) updateAndNotify(ctx context.Context, once *sync.Once, kind string, entries []api.ConfigEntry) { + c.cacheMutex.Lock() + + cache := make(resourceCache) + + for _, entry := range entries { + cache[translation.EntryToReference(entry)] = entry + } + + diffs := c.cache[kind].diff(cache) + + c.cache[kind] = cache + + // we run this the first time the cache is filled to notify the waiter + once.Do(func() { + c.logger.Info("sync mark for " + kind) + c.synced <- struct{}{} + }) + + c.cacheMutex.Unlock() + + // now notify all subscribers + c.notifySubscribers(ctx, kind, diffs) +} + +func (c *Cache) updateAndNotifyServices(ctx context.Context, once *sync.Once, services []*api.CatalogService) { + c.cacheMutex.Lock() + + cache := make(serviceCache) + + for _, service := range services { + cache[api.ResourceReference{Name: service.ServiceName, Namespace: service.Namespace, Partition: service.Partition}] = service + } + + diffs := c.serviceCache.diff(cache) + + c.serviceCache = cache + + // we run this the first time the cache is filled to notify the waiter + once.Do(func() { + c.logger.Info("sync mark for services") + c.synced <- struct{}{} + }) + + c.cacheMutex.Unlock() + + // now notify all subscribers + c.notifyServiceSubscribers(ctx, diffs) +} + +func (c *Cache) updateAndNotifyPeerings(ctx context.Context, once *sync.Once, peerings []*api.Peering) { + c.cacheMutex.Lock() + + cache := make(peeringCache) + + for _, peering := range peerings { + cache[api.ResourceReference{Name: peering.Name, Namespace: peering.ID, Partition: peering.Partition}] = peering + } + + diffs := c.peeringCache.diff(cache) + + c.peeringCache = cache + + // we run this the first time the cache is filled to notify the waiter + once.Do(func() { + c.logger.Info("sync mark for peerings") + c.synced <- struct{}{} + }) + + c.cacheMutex.Unlock() + + // now notify all peering subscribers + c.notifyPeeringSubscribers(ctx, diffs) +} + +// notifyServiceSubscribers notifies each subscriber for a given kind on changes to a config entry of that kind. It also +// handles removing any subscribers that have marked themselves as done. +func (c *Cache) notifyServiceSubscribers(ctx context.Context, services []*api.CatalogService) { + c.subscriberMutex.Lock() + defer c.subscriberMutex.Unlock() + + for _, service := range services { + // this will hold the new list of current subscribers after we finish notifying + subscribers := make([]*ServiceSubscription, 0, len(c.serviceSubscribers)) + for _, subscriber := range c.serviceSubscribers { + addSubscriber := false + + for _, namespaceName := range subscriber.translator(service) { + event := event.GenericEvent{ + Object: newConfigEntryObject(namespaceName), + } + + select { + case <-ctx.Done(): + return + case <-subscriber.ctx.Done(): + // don't add this subscriber to current list because it is done + addSubscriber = false + case subscriber.events <- event: + // keep this one since we can send events to it + addSubscriber = true + } + } + + if addSubscriber { + subscribers = append(subscribers, subscriber) + } + } + c.serviceSubscribers = subscribers + } +} + +// notifyPeeringSubscribers notifies each subscriber for a given kind on changes to a config entry of that kind. It also +// handles removing any subscribers that have marked themselves as done. +func (c *Cache) notifyPeeringSubscribers(ctx context.Context, peerings []*api.Peering) { + c.subscriberMutex.Lock() + defer c.subscriberMutex.Unlock() + + for _, peering := range peerings { + // this will hold the new list of current subscribers after we finish notifying + subscribers := make([]*PeeringsSubscription, 0, len(c.peeringsSubscribers)) + for _, subscriber := range c.peeringsSubscribers { + addSubscriber := false + + for _, namespaceName := range subscriber.translator(peering) { + event := event.GenericEvent{ + Object: newConfigEntryObject(namespaceName), + } + + select { + case <-ctx.Done(): + return + case <-subscriber.ctx.Done(): + // don't add this subscriber to current list because it is done + addSubscriber = false + case subscriber.events <- event: + // keep this one since we can send events to it + addSubscriber = true + } + } + + if addSubscriber { + subscribers = append(subscribers, subscriber) + } + } + c.peeringsSubscribers = subscribers + } +} + +// notifySubscribers notifies each subscriber for a given kind on changes to a config entry of that kind. It also +// handles removing any subscribers that have marked themselves as done. +func (c *Cache) notifySubscribers(ctx context.Context, kind string, entries []api.ConfigEntry) { + c.subscriberMutex.Lock() + defer c.subscriberMutex.Unlock() + + for _, entry := range entries { + // this will hold the new list of current subscribers after we finish notifying + subscribers := make([]*Subscription, 0, len(c.subscribers[kind])) + for _, subscriber := range c.subscribers[kind] { + addSubscriber := false + + for _, namespaceName := range subscriber.translator(entry) { + event := event.GenericEvent{ + Object: newConfigEntryObject(namespaceName), + } + + select { + case <-ctx.Done(): + return + case <-subscriber.ctx.Done(): + // don't add this subscriber to current list because it is done + addSubscriber = false + case subscriber.events <- event: + // keep this one since we can send events to it + addSubscriber = true + } + } + + if addSubscriber { + subscribers = append(subscribers, subscriber) + } + } + c.subscribers[kind] = subscribers + } +} + +// Write handles writing the config entry back to Consul, if the current reference of the +// config entry is stale then it returns an error. +func (c *Cache) Write(ctx context.Context, entry api.ConfigEntry) error { + c.cacheMutex.Lock() + defer c.cacheMutex.Unlock() + + entryMap, ok := c.cache[entry.GetKind()] + if !ok { + return nil + } + + ref := translation.EntryToReference(entry) + + old, ok := entryMap[ref] + if ok { + if cmp.Equal(old, entry, cmp.FilterPath(func(p cmp.Path) bool { + path := p.String() + return strings.HasSuffix(path, "Status") || strings.HasSuffix(path, "ModifyIndex") || strings.HasSuffix(path, "CreateIndex") + }, cmp.Ignore())) { + return nil + } + } + + client, err := consul.NewClientFromConnMgr(c.config, c.serverMgr) + if err != nil { + return err + } + + options := &api.WriteOptions{} + + _, _, err = client.ConfigEntries().Set(entry, options.WithContext(ctx)) + if err != nil { + return err + } + + return nil +} + +// Get returns a config entry from the cache that corresponds to the given resource reference. +func (c *Cache) Get(ref api.ResourceReference) api.ConfigEntry { + c.cacheMutex.Lock() + defer c.cacheMutex.Unlock() + + entryMap, ok := c.cache[ref.Kind] + if !ok { + return nil + } + + entry, ok := entryMap[ref] + if !ok { + return nil + } + + return entry +} + +// Delete handles deleting the config entry from consul, if the current reference of the config entry is stale then +// it returns an error. +func (c *Cache) Delete(ctx context.Context, ref api.ResourceReference) error { + c.cacheMutex.Lock() + defer c.cacheMutex.Unlock() + + entryMap, ok := c.cache[ref.Kind] + if !ok { + return nil + } + _, ok = entryMap[ref] + if !ok { + c.logger.Info("cached object not found, not deleting") + return nil + } + + client, err := consul.NewClientFromConnMgr(c.config, c.serverMgr) + if err != nil { + return err + } + + options := &api.WriteOptions{} + + _, err = client.ConfigEntries().Delete(ref.Kind, ref.Name, options.WithContext(ctx)) + return err +} + +// List returns a list of config entries from the cache that corresponds to the given kind. +func (c *Cache) List(kind string) []api.ConfigEntry { + c.cacheMutex.Lock() + defer c.cacheMutex.Unlock() + + entryMap, ok := c.cache[kind] + if !ok { + return nil + } + entries := make([]api.ConfigEntry, 0, len(entryMap)) + for _, entry := range entryMap { + entries = append(entries, entry) + } + + return entries +} + +// ListServices returns a list of services from the cache that corresponds to the given kind. +func (c *Cache) ListServices() []api.CatalogService { + c.cacheMutex.Lock() + defer c.cacheMutex.Unlock() + + entries := make([]api.CatalogService, 0, len(c.serviceCache)) + for _, service := range c.serviceCache { + entries = append(entries, *service) + } + + return entries +} + +// LinkPolicy links a mesh write policy to a token associated with the service. +func (c *Cache) LinkPolicy(ctx context.Context, name, namespace string) error { + client, err := consul.NewClientFromConnMgr(c.config, c.serverMgr) + if err != nil { + return err + } + + options := &api.QueryOptions{} + + if c.namespacesEnabled { + options.Namespace = namespace + } + + policies, _, err := client.ACL().PolicyList(options.WithContext(ctx)) + if err != nil { + return ignoreACLsDisabled(err) + } + + links := []*api.ACLLink{} + for _, policy := range policies { + if strings.HasPrefix(policy.Name, "connect-inject-policy") { + links = append(links, &api.ACLLink{ + Name: policy.Name, + }) + } + } + + tokens, _, err := client.ACL().TokenList(options.WithContext(ctx)) + if err != nil { + return ignoreACLsDisabled(err) + } + + for _, token := range tokens { + for _, identity := range token.ServiceIdentities { + if identity.ServiceName == name { + token, _, err := client.ACL().TokenRead(token.AccessorID, options.WithContext(ctx)) + if err != nil { + return ignoreACLsDisabled(err) + } + token.Policies = links + + _, _, err = client.ACL().TokenUpdate(token, &api.WriteOptions{}) + if err != nil { + return ignoreACLsDisabled(err) + } + } + } + } + + return nil +} + +// Register registers a service in Consul. +func (c *Cache) Register(ctx context.Context, registration api.CatalogRegistration) error { + client, err := consul.NewClientFromConnMgr(c.config, c.serverMgr) + if err != nil { + return err + } + + options := &api.WriteOptions{} + + _, err = client.Catalog().Register(®istration, options.WithContext(ctx)) + return err +} + +// Deregister deregisters a service in Consul. +func (c *Cache) Deregister(ctx context.Context, deregistration api.CatalogDeregistration) error { + client, err := consul.NewClientFromConnMgr(c.config, c.serverMgr) + if err != nil { + return err + } + + options := &api.WriteOptions{} + + _, err = client.Catalog().Deregister(&deregistration, options.WithContext(ctx)) + return err +} + +func ignoreACLsDisabled(err error) error { + if err.Error() == "Unexpected response code: 401 (ACL support disabled)" { + return nil + } + return err +} diff --git a/control-plane/api-gateway/cache/consul_test.go b/control-plane/api-gateway/cache/consul_test.go new file mode 100644 index 0000000000..256aa6b31d --- /dev/null +++ b/control-plane/api-gateway/cache/consul_test.go @@ -0,0 +1,2028 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package cache + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "strconv" + "testing" + + "github.com/go-logr/logr" + logrtest "github.com/go-logr/logr/testing" + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/event" + + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/translation" + "github.com/hashicorp/consul-k8s/control-plane/consul" + "github.com/hashicorp/consul-k8s/control-plane/helper/test" + "github.com/hashicorp/consul/api" +) + +func Test_resourceCache_diff(t *testing.T) { + t.Parallel() + type args struct { + newCache resourceCache + } + tests := []struct { + name string + oldCache resourceCache + args args + want []api.ConfigEntry + }{ + { + name: "no difference", + oldCache: resourceCache{ + api.ResourceReference{ + Kind: api.HTTPRoute, + Name: "my route", + }: api.ConfigEntry(&api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, + Name: "my route", + Parents: []api.ResourceReference{ + { + Kind: api.APIGateway, + Name: "api-gw", + SectionName: "listener-1", + Namespace: "ns", + }, + }, + Rules: []api.HTTPRouteRule{ + { + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "add it on": "the value", + }, + Remove: []string{"time to go"}, + Set: map[string]string{ + "Magic": "v2", + "Another One": "dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{Path: "v1"}, + }, + Matches: []api.HTTPMatch{ + { + Headers: []api.HTTPHeaderMatch{ + { + Match: api.HTTPHeaderMatchExact, + Name: "my header match", + Value: "the value", + }, + }, + Method: api.HTTPMatchMethodGet, + Path: api.HTTPPathMatch{ + Match: api.HTTPPathMatchPrefix, + Value: "/v1", + }, + Query: []api.HTTPQueryMatch{ + { + Match: api.HTTPQueryMatchExact, + Name: "search", + Value: "term", + }, + }, + }, + }, + Services: []api.HTTPService{ + { + Name: "service one", + Weight: 45, + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "svc - add it on": "svc - the value", + }, + Remove: []string{"svc - time to go"}, + Set: map[string]string{ + "svc - Magic": "svc - v2", + "svc - Another One": "svc - dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{ + Path: "path", + }, + }, + Namespace: "some ns", + }, + }, + }, + }, + Hostnames: []string{"hostname.com"}, + Meta: map[string]string{}, + Status: api.ConfigEntryStatus{}, + }), + }, + args: args{ + newCache: resourceCache{ + api.ResourceReference{ + Kind: api.HTTPRoute, + Name: "my route", + }: api.ConfigEntry(&api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, + Name: "my route", + Parents: []api.ResourceReference{ + { + Kind: api.APIGateway, + Name: "api-gw", + SectionName: "listener-1", + Namespace: "ns", + }, + }, + Rules: []api.HTTPRouteRule{ + { + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "add it on": "the value", + }, + Remove: []string{"time to go"}, + Set: map[string]string{ + "Magic": "v2", + "Another One": "dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{Path: "v1"}, + }, + Matches: []api.HTTPMatch{ + { + Headers: []api.HTTPHeaderMatch{ + { + Match: api.HTTPHeaderMatchExact, + Name: "my header match", + Value: "the value", + }, + }, + Method: api.HTTPMatchMethodGet, + Path: api.HTTPPathMatch{ + Match: api.HTTPPathMatchPrefix, + Value: "/v1", + }, + Query: []api.HTTPQueryMatch{ + { + Match: api.HTTPQueryMatchExact, + Name: "search", + Value: "term", + }, + }, + }, + }, + Services: []api.HTTPService{ + { + Name: "service one", + Weight: 45, + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "svc - add it on": "svc - the value", + }, + Remove: []string{"svc - time to go"}, + Set: map[string]string{ + "svc - Magic": "svc - v2", + "svc - Another One": "svc - dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{ + Path: "path", + }, + }, + Namespace: "some ns", + }, + }, + }, + }, + Hostnames: []string{"hostname.com"}, + Meta: map[string]string{}, + Status: api.ConfigEntryStatus{}, + }), + }, + }, + want: []api.ConfigEntry{}, + }, + { + name: "resource exists in old cache but not new one", + oldCache: resourceCache{ + api.ResourceReference{ + Kind: api.HTTPRoute, + Name: "my route", + }: api.ConfigEntry(&api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, + Name: "my route", + Parents: []api.ResourceReference{ + { + Kind: api.APIGateway, + Name: "api-gw", + SectionName: "listener-1", + Namespace: "ns", + }, + }, + Rules: []api.HTTPRouteRule{ + { + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "add it on": "the value", + }, + Remove: []string{"time to go"}, + Set: map[string]string{ + "Magic": "v2", + "Another One": "dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{Path: "v1"}, + }, + Matches: []api.HTTPMatch{ + { + Headers: []api.HTTPHeaderMatch{ + { + Match: api.HTTPHeaderMatchExact, + Name: "my header match", + Value: "the value", + }, + }, + Method: api.HTTPMatchMethodGet, + Path: api.HTTPPathMatch{ + Match: api.HTTPPathMatchPrefix, + Value: "/v1", + }, + Query: []api.HTTPQueryMatch{ + { + Match: api.HTTPQueryMatchExact, + Name: "search", + Value: "term", + }, + }, + }, + }, + Services: []api.HTTPService{ + { + Name: "service one", + Weight: 45, + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "svc - add it on": "svc - the value", + }, + Remove: []string{"svc - time to go"}, + Set: map[string]string{ + "svc - Magic": "svc - v2", + "svc - Another One": "svc - dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{ + Path: "path", + }, + }, + Namespace: "some ns", + }, + }, + }, + }, + Hostnames: []string{"hostname.com"}, + Meta: map[string]string{}, + Status: api.ConfigEntryStatus{}, + }), + api.ResourceReference{ + Kind: api.HTTPRoute, + Name: "my route 2", + }: api.ConfigEntry(&api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, + Name: "my route 2", + Parents: []api.ResourceReference{ + { + Kind: api.APIGateway, + Name: "api-gw", + SectionName: "listener-2", + Namespace: "ns", + }, + }, + Rules: []api.HTTPRouteRule{ + { + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "add it on": "the value", + }, + Remove: []string{"time to go"}, + Set: map[string]string{ + "Magic": "v2", + "Another One": "dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{Path: "v1"}, + }, + Matches: []api.HTTPMatch{ + { + Headers: []api.HTTPHeaderMatch{ + { + Match: api.HTTPHeaderMatchExact, + Name: "my header match", + Value: "the value", + }, + }, + Method: api.HTTPMatchMethodGet, + Path: api.HTTPPathMatch{ + Match: api.HTTPPathMatchPrefix, + Value: "/v1", + }, + Query: []api.HTTPQueryMatch{ + { + Match: api.HTTPQueryMatchExact, + Name: "search", + Value: "term", + }, + }, + }, + }, + Services: []api.HTTPService{ + { + Name: "service one", + Weight: 45, + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "svc - add it on": "svc - the value", + }, + Remove: []string{"svc - time to go"}, + Set: map[string]string{ + "svc - Magic": "svc - v2", + "svc - Another One": "svc - dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{ + Path: "path", + }, + }, + Namespace: "some ns", + }, + }, + }, + }, + Hostnames: []string{"hostname.com"}, + Meta: map[string]string{}, + Status: api.ConfigEntryStatus{}, + }), + }, + args: args{ + newCache: resourceCache{ + api.ResourceReference{ + Kind: api.HTTPRoute, + Name: "my route", + }: api.ConfigEntry(&api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, + Name: "my route", + Parents: []api.ResourceReference{ + { + Kind: api.APIGateway, + Name: "api-gw", + SectionName: "listener-1", + Namespace: "ns", + }, + }, + Rules: []api.HTTPRouteRule{ + { + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "add it on": "the value", + }, + Remove: []string{"time to go"}, + Set: map[string]string{ + "Magic": "v2", + "Another One": "dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{Path: "v1"}, + }, + Matches: []api.HTTPMatch{ + { + Headers: []api.HTTPHeaderMatch{ + { + Match: api.HTTPHeaderMatchExact, + Name: "my header match", + Value: "the value", + }, + }, + Method: api.HTTPMatchMethodGet, + Path: api.HTTPPathMatch{ + Match: api.HTTPPathMatchPrefix, + Value: "/v1", + }, + Query: []api.HTTPQueryMatch{ + { + Match: api.HTTPQueryMatchExact, + Name: "search", + Value: "term", + }, + }, + }, + }, + Services: []api.HTTPService{ + { + Name: "service one", + Weight: 45, + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "svc - add it on": "svc - the value", + }, + Remove: []string{"svc - time to go"}, + Set: map[string]string{ + "svc - Magic": "svc - v2", + "svc - Another One": "svc - dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{ + Path: "path", + }, + }, + Namespace: "some ns", + }, + }, + }, + }, + Hostnames: []string{"hostname.com"}, + Meta: map[string]string{}, + Status: api.ConfigEntryStatus{}, + }), + }, + }, + want: []api.ConfigEntry{ + api.ConfigEntry(&api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, + Name: "my route 2", + Parents: []api.ResourceReference{ + { + Kind: api.APIGateway, + Name: "api-gw", + SectionName: "listener-2", + Namespace: "ns", + }, + }, + Rules: []api.HTTPRouteRule{ + { + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "add it on": "the value", + }, + Remove: []string{"time to go"}, + Set: map[string]string{ + "Magic": "v2", + "Another One": "dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{Path: "v1"}, + }, + Matches: []api.HTTPMatch{ + { + Headers: []api.HTTPHeaderMatch{ + { + Match: api.HTTPHeaderMatchExact, + Name: "my header match", + Value: "the value", + }, + }, + Method: api.HTTPMatchMethodGet, + Path: api.HTTPPathMatch{ + Match: api.HTTPPathMatchPrefix, + Value: "/v1", + }, + Query: []api.HTTPQueryMatch{ + { + Match: api.HTTPQueryMatchExact, + Name: "search", + Value: "term", + }, + }, + }, + }, + Services: []api.HTTPService{ + { + Name: "service one", + Weight: 45, + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "svc - add it on": "svc - the value", + }, + Remove: []string{"svc - time to go"}, + Set: map[string]string{ + "svc - Magic": "svc - v2", + "svc - Another One": "svc - dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{ + Path: "path", + }, + }, + Namespace: "some ns", + }, + }, + }, + }, + Hostnames: []string{"hostname.com"}, + Meta: map[string]string{}, + Status: api.ConfigEntryStatus{}, + }), + }, + }, + { + name: "resource exists in new cache but not old one", + oldCache: resourceCache{ + api.ResourceReference{ + Kind: api.HTTPRoute, + Name: "my route", + }: api.ConfigEntry(&api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, + Name: "my route", + Parents: []api.ResourceReference{ + { + Kind: api.APIGateway, + Name: "api-gw", + SectionName: "listener-1", + Namespace: "ns", + }, + }, + Rules: []api.HTTPRouteRule{ + { + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "add it on": "the value", + }, + Remove: []string{"time to go"}, + Set: map[string]string{ + "Magic": "v2", + "Another One": "dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{Path: "v1"}, + }, + Matches: []api.HTTPMatch{ + { + Headers: []api.HTTPHeaderMatch{ + { + Match: api.HTTPHeaderMatchExact, + Name: "my header match", + Value: "the value", + }, + }, + Method: api.HTTPMatchMethodGet, + Path: api.HTTPPathMatch{ + Match: api.HTTPPathMatchPrefix, + Value: "/v1", + }, + Query: []api.HTTPQueryMatch{ + { + Match: api.HTTPQueryMatchExact, + Name: "search", + Value: "term", + }, + }, + }, + }, + Services: []api.HTTPService{ + { + Name: "service one", + Weight: 45, + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "svc - add it on": "svc - the value", + }, + Remove: []string{"svc - time to go"}, + Set: map[string]string{ + "svc - Magic": "svc - v2", + "svc - Another One": "svc - dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{ + Path: "path", + }, + }, + Namespace: "some ns", + }, + }, + }, + }, + Hostnames: []string{"hostname.com"}, + Meta: map[string]string{}, + Status: api.ConfigEntryStatus{}, + }), + }, + args: args{ + newCache: resourceCache{ + api.ResourceReference{ + Kind: api.HTTPRoute, + Name: "my route", + }: api.ConfigEntry(&api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, + Name: "my route", + Parents: []api.ResourceReference{ + { + Kind: api.APIGateway, + Name: "api-gw", + SectionName: "listener-1", + Namespace: "ns", + }, + }, + Rules: []api.HTTPRouteRule{ + { + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "add it on": "the value", + }, + Remove: []string{"time to go"}, + Set: map[string]string{ + "Magic": "v2", + "Another One": "dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{Path: "v1"}, + }, + Matches: []api.HTTPMatch{ + { + Headers: []api.HTTPHeaderMatch{ + { + Match: api.HTTPHeaderMatchExact, + Name: "my header match", + Value: "the value", + }, + }, + Method: api.HTTPMatchMethodGet, + Path: api.HTTPPathMatch{ + Match: api.HTTPPathMatchPrefix, + Value: "/v1", + }, + Query: []api.HTTPQueryMatch{ + { + Match: api.HTTPQueryMatchExact, + Name: "search", + Value: "term", + }, + }, + }, + }, + Services: []api.HTTPService{ + { + Name: "service one", + Weight: 45, + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "svc - add it on": "svc - the value", + }, + Remove: []string{"svc - time to go"}, + Set: map[string]string{ + "svc - Magic": "svc - v2", + "svc - Another One": "svc - dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{ + Path: "path", + }, + }, + Namespace: "some ns", + }, + }, + }, + }, + Hostnames: []string{"hostname.com"}, + Meta: map[string]string{}, + Status: api.ConfigEntryStatus{}, + }), + + api.ResourceReference{ + Kind: api.HTTPRoute, + Name: "my route 2", + }: api.ConfigEntry(&api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, + Name: "my route 2", + Parents: []api.ResourceReference{ + { + Kind: api.APIGateway, + Name: "api-gw", + SectionName: "listener-2", + Namespace: "ns", + }, + }, + Rules: []api.HTTPRouteRule{ + { + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "add it on": "the value", + }, + Remove: []string{"time to go"}, + Set: map[string]string{ + "Magic": "v2", + "Another One": "dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{Path: "v1"}, + }, + Matches: []api.HTTPMatch{ + { + Headers: []api.HTTPHeaderMatch{ + { + Match: api.HTTPHeaderMatchExact, + Name: "my header match", + Value: "the value", + }, + }, + Method: api.HTTPMatchMethodGet, + Path: api.HTTPPathMatch{ + Match: api.HTTPPathMatchPrefix, + Value: "/v1", + }, + Query: []api.HTTPQueryMatch{ + { + Match: api.HTTPQueryMatchExact, + Name: "search", + Value: "term", + }, + }, + }, + }, + Services: []api.HTTPService{ + { + Name: "service one", + Weight: 45, + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "svc - add it on": "svc - the value", + }, + Remove: []string{"svc - time to go"}, + Set: map[string]string{ + "svc - Magic": "svc - v2", + "svc - Another One": "svc - dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{ + Path: "path", + }, + }, + Namespace: "some ns", + }, + }, + }, + }, + Hostnames: []string{"hostname.com"}, + Meta: map[string]string{}, + Status: api.ConfigEntryStatus{}, + }), + }, + }, + want: []api.ConfigEntry{ + api.ConfigEntry(&api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, + Name: "my route 2", + Parents: []api.ResourceReference{ + { + Kind: api.APIGateway, + Name: "api-gw", + SectionName: "listener-2", + Namespace: "ns", + }, + }, + Rules: []api.HTTPRouteRule{ + { + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "add it on": "the value", + }, + Remove: []string{"time to go"}, + Set: map[string]string{ + "Magic": "v2", + "Another One": "dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{Path: "v1"}, + }, + Matches: []api.HTTPMatch{ + { + Headers: []api.HTTPHeaderMatch{ + { + Match: api.HTTPHeaderMatchExact, + Name: "my header match", + Value: "the value", + }, + }, + Method: api.HTTPMatchMethodGet, + Path: api.HTTPPathMatch{ + Match: api.HTTPPathMatchPrefix, + Value: "/v1", + }, + Query: []api.HTTPQueryMatch{ + { + Match: api.HTTPQueryMatchExact, + Name: "search", + Value: "term", + }, + }, + }, + }, + Services: []api.HTTPService{ + { + Name: "service one", + Weight: 45, + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "svc - add it on": "svc - the value", + }, + Remove: []string{"svc - time to go"}, + Set: map[string]string{ + "svc - Magic": "svc - v2", + "svc - Another One": "svc - dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{ + Path: "path", + }, + }, + Namespace: "some ns", + }, + }, + }, + }, + Hostnames: []string{"hostname.com"}, + Meta: map[string]string{}, + Status: api.ConfigEntryStatus{}, + }), + }, + }, + { + name: "same ref new cache has a greater modify index", + oldCache: resourceCache{ + api.ResourceReference{ + Kind: api.HTTPRoute, + Name: "my route", + }: api.ConfigEntry(&api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, + Name: "my route", + ModifyIndex: 1, + Parents: []api.ResourceReference{ + { + Kind: api.APIGateway, + Name: "api-gw", + SectionName: "listener-1", + Namespace: "ns", + }, + }, + Rules: []api.HTTPRouteRule{ + { + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "add it on": "the value", + }, + Remove: []string{"time to go"}, + Set: map[string]string{ + "Magic": "v2", + "Another One": "dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{Path: "v1"}, + }, + Matches: []api.HTTPMatch{ + { + Headers: []api.HTTPHeaderMatch{ + { + Match: api.HTTPHeaderMatchExact, + Name: "my header match", + Value: "the value", + }, + }, + Method: api.HTTPMatchMethodGet, + Path: api.HTTPPathMatch{ + Match: api.HTTPPathMatchPrefix, + Value: "/v1", + }, + Query: []api.HTTPQueryMatch{ + { + Match: api.HTTPQueryMatchExact, + Name: "search", + Value: "term", + }, + }, + }, + }, + Services: []api.HTTPService{ + { + Name: "service one", + Weight: 45, + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "svc - add it on": "svc - the value", + }, + Remove: []string{"svc - time to go"}, + Set: map[string]string{ + "svc - Magic": "svc - v2", + "svc - Another One": "svc - dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{ + Path: "path", + }, + }, + Namespace: "some ns", + }, + }, + }, + }, + Hostnames: []string{"hostname.com"}, + Meta: map[string]string{}, + Status: api.ConfigEntryStatus{}, + }), + }, + args: args{ + newCache: resourceCache{ + api.ResourceReference{ + Kind: api.HTTPRoute, + Name: "my route", + }: api.ConfigEntry(&api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, + Name: "my route", + ModifyIndex: 10, + Parents: []api.ResourceReference{ + { + Kind: api.APIGateway, + Name: "api-gw", + SectionName: "listener-1", + Namespace: "ns", + }, + }, + Rules: []api.HTTPRouteRule{ + { + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "add it on": "the value", + }, + Remove: []string{"time to go"}, + Set: map[string]string{ + "Magic": "v2", + "Another One": "dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{Path: "v1"}, + }, + Matches: []api.HTTPMatch{ + { + Headers: []api.HTTPHeaderMatch{ + { + Match: api.HTTPHeaderMatchExact, + Name: "my header match", + Value: "the value", + }, + }, + Method: api.HTTPMatchMethodGet, + Path: api.HTTPPathMatch{ + Match: api.HTTPPathMatchPrefix, + Value: "/v1", + }, + Query: []api.HTTPQueryMatch{ + { + Match: api.HTTPQueryMatchExact, + Name: "search", + Value: "term", + }, + }, + }, + }, + Services: []api.HTTPService{ + { + Name: "service one", + Weight: 45, + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "svc - add it on": "svc - the value", + }, + Remove: []string{"svc - time to go"}, + Set: map[string]string{ + "svc - Magic": "svc - v2", + "svc - Another One": "svc - dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{ + Path: "path", + }, + }, + Namespace: "some ns", + }, + }, + }, + }, + Hostnames: []string{"hostname.com"}, + Meta: map[string]string{}, + Status: api.ConfigEntryStatus{}, + }), + }, + }, + want: []api.ConfigEntry{ + api.ConfigEntry(&api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, + Name: "my route", + ModifyIndex: 10, + Parents: []api.ResourceReference{ + { + Kind: api.APIGateway, + Name: "api-gw", + SectionName: "listener-1", + Namespace: "ns", + }, + }, + Rules: []api.HTTPRouteRule{ + { + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "add it on": "the value", + }, + Remove: []string{"time to go"}, + Set: map[string]string{ + "Magic": "v2", + "Another One": "dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{Path: "v1"}, + }, + Matches: []api.HTTPMatch{ + { + Headers: []api.HTTPHeaderMatch{ + { + Match: api.HTTPHeaderMatchExact, + Name: "my header match", + Value: "the value", + }, + }, + Method: api.HTTPMatchMethodGet, + Path: api.HTTPPathMatch{ + Match: api.HTTPPathMatchPrefix, + Value: "/v1", + }, + Query: []api.HTTPQueryMatch{ + { + Match: api.HTTPQueryMatchExact, + Name: "search", + Value: "term", + }, + }, + }, + }, + Services: []api.HTTPService{ + { + Name: "service one", + Weight: 45, + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "svc - add it on": "svc - the value", + }, + Remove: []string{"svc - time to go"}, + Set: map[string]string{ + "svc - Magic": "svc - v2", + "svc - Another One": "svc - dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{ + Path: "path", + }, + }, + Namespace: "some ns", + }, + }, + }, + }, + Hostnames: []string{"hostname.com"}, + Meta: map[string]string{}, + Status: api.ConfigEntryStatus{}, + }), + }, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + got := tt.oldCache.diff(tt.args.newCache) + if diff := cmp.Diff(got, tt.want); diff != "" { + t.Errorf("resourceCache.diff mismatch (-want +got):\n%s", diff) + } + }) + } +} + +func TestCache_Subscribe(t *testing.T) { + t.Parallel() + type args struct { + ctx context.Context + kind string + translator translation.TranslatorFn + } + tests := []struct { + name string + args args + subscribers map[string][]*Subscription + subscriberChange int + }{ + { + name: "new subscription added when there are no other subscribers of the same kind", + args: args{ + ctx: context.Background(), + kind: api.HTTPRoute, + translator: func(api.ConfigEntry) []types.NamespacedName { + return []types.NamespacedName{} + }, + }, + subscriberChange: 1, + }, + { + name: "new subscription added when there are existing subscribers of the same kind", + args: args{ + ctx: context.Background(), + kind: api.HTTPRoute, + translator: func(api.ConfigEntry) []types.NamespacedName { + return []types.NamespacedName{} + }, + }, + subscribers: map[string][]*Subscription{ + api.HTTPRoute: { + { + translator: func(api.ConfigEntry) []types.NamespacedName { + return []types.NamespacedName{} + }, + ctx: context.Background(), + cancelCtx: func() { + }, + events: make(chan event.GenericEvent), + }, + }, + }, + subscriberChange: 1, + }, + { + name: "subscription for kind that does not exist does not change any subscriber counts", + args: args{ + ctx: context.Background(), + kind: "UnknownKind", + translator: func(api.ConfigEntry) []types.NamespacedName { + return []types.NamespacedName{} + }, + }, + subscriberChange: 0, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := New(Config{ + ConsulClientConfig: &consul.Config{ + APIClientConfig: &api.Config{}, + HTTPPort: 0, + GRPCPort: 0, + APITimeout: 0, + }, + ConsulServerConnMgr: consul.NewMockServerConnectionManager(t), + NamespacesEnabled: false, + Logger: logr.Logger{}, + }) + + if len(tt.subscribers) > 0 { + c.subscribers = tt.subscribers + } + + kindSubscriberCounts := make(map[string]int) + for kind, subscribers := range c.subscribers { + kindSubscriberCounts[kind] = len(subscribers) + } + + c.Subscribe(tt.args.ctx, tt.args.kind, tt.args.translator) + + for kind, subscribers := range c.subscribers { + expectedSubscriberCount := kindSubscriberCounts[kind] + if kind == tt.args.kind { + expectedSubscriberCount += tt.subscriberChange + } + actualSubscriberCount := len(subscribers) + + if expectedSubscriberCount != actualSubscriberCount { + t.Errorf("Expected there to be %d subscribers, there were %d", expectedSubscriberCount, actualSubscriberCount) + } + } + }) + } +} + +func TestCache_Write(t *testing.T) { + t.Parallel() + testCases := []struct { + name string + responseFn func(w http.ResponseWriter) + expectedErr error + }{ + { + name: "write is successful", + responseFn: func(w http.ResponseWriter) { + w.WriteHeader(200) + fmt.Fprintln(w, `{updated: true}`) + }, + expectedErr: nil, + }, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + consulServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/v1/config": + tt.responseFn(w) + case "/v1/catalog/services": + fmt.Fprintln(w, `{}`) + default: + w.WriteHeader(500) + fmt.Fprintln(w, "Mock Server not configured for this route: "+r.URL.Path) + } + })) + defer consulServer.Close() + + serverURL, err := url.Parse(consulServer.URL) + require.NoError(t, err) + + port, err := strconv.Atoi(serverURL.Port()) + require.NoError(t, err) + + c := New(Config{ + ConsulClientConfig: &consul.Config{ + APIClientConfig: &api.Config{}, + HTTPPort: port, + GRPCPort: port, + APITimeout: 0, + }, + ConsulServerConnMgr: test.MockConnMgrForIPAndPort(serverURL.Hostname(), port), + NamespacesEnabled: false, + Logger: logr.Logger{}, + }) + + entry := &api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, + Name: "my route", + Parents: []api.ResourceReference{ + { + Kind: api.APIGateway, + Name: "api-gw", + SectionName: "listener-1", + Namespace: "ns", + }, + }, + Rules: []api.HTTPRouteRule{ + { + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "add it on": "the value", + }, + Remove: []string{"time to go"}, + Set: map[string]string{ + "Magic": "v2", + "Another One": "dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{Path: "v1"}, + }, + Matches: []api.HTTPMatch{ + { + Headers: []api.HTTPHeaderMatch{ + { + Match: api.HTTPHeaderMatchExact, + Name: "my header match", + Value: "the value", + }, + }, + Method: api.HTTPMatchMethodGet, + Path: api.HTTPPathMatch{ + Match: api.HTTPPathMatchPrefix, + Value: "/v1", + }, + Query: []api.HTTPQueryMatch{ + { + Match: api.HTTPQueryMatchExact, + Name: "search", + Value: "term", + }, + }, + }, + }, + Services: []api.HTTPService{ + { + Name: "service one", + Weight: 45, + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "svc - add it on": "svc - the value", + }, + Remove: []string{"svc - time to go"}, + Set: map[string]string{ + "svc - Magic": "svc - v2", + "svc - Another One": "svc - dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{ + Path: "path", + }, + }, + Namespace: "some ns", + }, + }, + }, + }, + Hostnames: []string{"hostname.com"}, + Meta: map[string]string{}, + Status: api.ConfigEntryStatus{}, + } + + err = c.Write(context.Background(), entry) + require.Equal(t, err, tt.expectedErr) + }) + } +} + +func TestCache_Get(t *testing.T) { + t.Parallel() + type args struct { + ref api.ResourceReference + } + tests := []struct { + name string + args args + want api.ConfigEntry + cache map[string]resourceCache + }{ + { + name: "entry exists", + args: args{ + ref: api.ResourceReference{ + Kind: api.APIGateway, + Name: "api-gw", + }, + }, + want: &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "api-gw", + Meta: map[string]string{}, + }, + cache: map[string]resourceCache{ + api.APIGateway: { + api.ResourceReference{ + Kind: api.APIGateway, + Name: "api-gw", + }: &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "api-gw", + Meta: map[string]string{}, + }, + api.ResourceReference{ + Kind: api.APIGateway, + Name: "api-gw-2", + }: &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "api-gw-2", + Meta: map[string]string{}, + }, + }, + }, + }, + { + name: "entry does not exist", + args: args{ + ref: api.ResourceReference{ + Kind: api.APIGateway, + Name: "api-gw-4", + }, + }, + want: nil, + cache: map[string]resourceCache{ + api.APIGateway: { + api.ResourceReference{ + Kind: api.APIGateway, + Name: "api-gw", + }: &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "api-gw", + Meta: map[string]string{}, + }, + api.ResourceReference{ + Kind: api.APIGateway, + Name: "api-gw-2", + }: &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "api-gw-2", + Meta: map[string]string{}, + }, + }, + }, + }, + { + name: "kind key does not exist", + args: args{ + ref: api.ResourceReference{ + Kind: api.APIGateway, + Name: "api-gw-4", + }, + }, + want: nil, + cache: map[string]resourceCache{ + api.HTTPRoute: { + api.ResourceReference{ + Kind: api.HTTPRoute, + Name: "api-gw", + }: &api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, + Name: "route", + Meta: map[string]string{}, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := New(Config{ + ConsulClientConfig: &consul.Config{ + APIClientConfig: &api.Config{}, + }, + }) + c.cache = tt.cache + + got := c.Get(tt.args.ref) + + if diff := cmp.Diff(got, tt.want); diff != "" { + t.Errorf("Cache.Get mismatch (-want +got):\n%s", diff) + } + }) + } +} + +func Test_Run(t *testing.T) { + t.Parallel() + // setup httproutes + httpRouteOne, httpRouteTwo := setupHTTPRoutes() + httpRoutes := []*api.HTTPRouteConfigEntry{httpRouteOne, httpRouteTwo} + + // setup gateway + gw := setupGateway() + gateways := []*api.APIGatewayConfigEntry{gw} + + // setup TCPRoutes + tcpRoute := setupTCPRoute() + tcpRoutes := []*api.TCPRouteConfigEntry{tcpRoute} + + // setup inline certs + inlineCert := setupInlineCertificate() + certs := []*api.InlineCertificateConfigEntry{inlineCert} + + consulServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/v1/config/http-route": + val, err := json.Marshal(httpRoutes) + if err != nil { + w.WriteHeader(500) + fmt.Fprintln(w, err) + return + } + fmt.Fprintln(w, string(val)) + case "/v1/config/api-gateway": + val, err := json.Marshal(gateways) + if err != nil { + w.WriteHeader(500) + fmt.Fprintln(w, err) + return + } + fmt.Fprintln(w, string(val)) + case "/v1/config/tcp-route": + val, err := json.Marshal(tcpRoutes) + if err != nil { + w.WriteHeader(500) + fmt.Fprintln(w, err) + return + } + fmt.Fprintln(w, string(val)) + case "/v1/config/inline-certificate": + val, err := json.Marshal(certs) + if err != nil { + w.WriteHeader(500) + fmt.Fprintln(w, err) + return + } + fmt.Fprintln(w, string(val)) + case "/v1/catalog/services": + fmt.Fprintln(w, `{}`) + case "/v1/peerings": + fmt.Fprintln(w, `[]`) + default: + w.WriteHeader(500) + fmt.Fprintln(w, "Mock Server not configured for this route: "+r.URL.Path) + } + })) + defer consulServer.Close() + + serverURL, err := url.Parse(consulServer.URL) + require.NoError(t, err) + + port, err := strconv.Atoi(serverURL.Port()) + require.NoError(t, err) + + c := New(Config{ + ConsulClientConfig: &consul.Config{ + APIClientConfig: &api.Config{}, + HTTPPort: port, + GRPCPort: port, + APITimeout: 0, + }, + ConsulServerConnMgr: test.MockConnMgrForIPAndPort(serverURL.Hostname(), port), + NamespacesEnabled: false, + Logger: logrtest.NewTestLogger(t), + }) + prevCache := make(map[string]resourceCache) + for kind, cache := range c.cache { + resCache := make(resourceCache) + for resourceRef, entry := range cache { + resCache[resourceRef] = entry + } + prevCache[kind] = resCache + } + + expectedCache := map[string]resourceCache{ + api.APIGateway: { + {Kind: api.APIGateway, Name: gw.Name}: gw, + }, + api.TCPRoute: { + {Kind: api.TCPRoute, Name: tcpRoute.Name}: tcpRoute, + }, + api.HTTPRoute: { + {Kind: api.HTTPRoute, Name: httpRouteOne.Name}: httpRouteOne, + {Kind: api.HTTPRoute, Name: httpRouteTwo.Name}: httpRouteTwo, + }, + api.InlineCertificate: { + {Kind: api.InlineCertificate, Name: inlineCert.Name}: inlineCert, + }, + } + + ctx, cancelFn := context.WithCancel(context.Background()) + + httpRouteOneNsn := types.NamespacedName{ + Name: httpRouteOne.Name, + Namespace: httpRouteOne.Namespace, + } + + httpRouteTwoNsn := types.NamespacedName{ + Name: httpRouteTwo.Name, + Namespace: httpRouteTwo.Namespace, + } + + httpRouteSubscriber := c.Subscribe(ctx, api.HTTPRoute, func(cfe api.ConfigEntry) []types.NamespacedName { + return []types.NamespacedName{ + {Name: cfe.GetName(), Namespace: cfe.GetNamespace()}, + } + }) + + canceledSub := c.Subscribe(ctx, api.HTTPRoute, func(cfe api.ConfigEntry) []types.NamespacedName { + return []types.NamespacedName{ + {Name: cfe.GetName(), Namespace: cfe.GetNamespace()}, + } + }) + + gwNsn := types.NamespacedName{ + Name: gw.Name, + Namespace: gw.Namespace, + } + + gwSubscriber := c.Subscribe(ctx, api.APIGateway, func(cfe api.ConfigEntry) []types.NamespacedName { + return []types.NamespacedName{ + {Name: cfe.GetName(), Namespace: cfe.GetNamespace()}, + } + }) + + tcpRouteNsn := types.NamespacedName{ + Name: tcpRoute.Name, + Namespace: tcpRoute.Namespace, + } + + tcpRouteSubscriber := c.Subscribe(ctx, api.TCPRoute, func(cfe api.ConfigEntry) []types.NamespacedName { + return []types.NamespacedName{ + {Name: cfe.GetName(), Namespace: cfe.GetNamespace()}, + } + }) + + certNsn := types.NamespacedName{ + Name: inlineCert.Name, + Namespace: inlineCert.Namespace, + } + + certSubscriber := c.Subscribe(ctx, api.InlineCertificate, func(cfe api.ConfigEntry) []types.NamespacedName { + return []types.NamespacedName{ + {Name: cfe.GetName(), Namespace: cfe.GetNamespace()}, + } + }) + + c.SubscribeServices(ctx, func(cs *api.CatalogService) []types.NamespacedName { return nil }).Cancel() + c.SubscribePeerings(ctx, func(cs *api.Peering) []types.NamespacedName { return nil }).Cancel() + + // mark this subscription as ended + canceledSub.Cancel() + + go c.Run(ctx) + + // Check subscribers + httpRouteExpectedEvents := []event.GenericEvent{{Object: newConfigEntryObject(httpRouteOneNsn)}, {Object: newConfigEntryObject(httpRouteTwoNsn)}} + gwExpectedEvent := event.GenericEvent{Object: newConfigEntryObject(gwNsn)} + tcpExpectedEvent := event.GenericEvent{Object: newConfigEntryObject(tcpRouteNsn)} + certExpectedEvent := event.GenericEvent{Object: newConfigEntryObject(certNsn)} + + // 2 http routes + 1 gw + 1 tcp route + 1 cert = 5 + i := 5 + for { + if i == 0 { + break + } + select { + case actualHTTPRouteEvent := <-httpRouteSubscriber.Events(): + require.Contains(t, httpRouteExpectedEvents, actualHTTPRouteEvent) + case actualGWEvent := <-gwSubscriber.Events(): + require.Equal(t, gwExpectedEvent, actualGWEvent) + case actualTCPRouteEvent := <-tcpRouteSubscriber.Events(): + require.Equal(t, tcpExpectedEvent, actualTCPRouteEvent) + case actualCertExpectedEvent := <-certSubscriber.Events(): + require.Equal(t, certExpectedEvent, actualCertExpectedEvent) + } + i -= 1 + } + + // the canceled Subscription should not receive any events + require.Zero(t, len(canceledSub.Events())) + c.WaitSynced(ctx) + + // cancel the context so the Run function exits + cancelFn() + + // Check cache + // expect the cache to have changed + if diff := cmp.Diff(prevCache, c.cache); diff == "" { + t.Error("Expect cache to have changed but it did not") + } + + if diff := cmp.Diff(expectedCache, c.cache); diff != "" { + t.Errorf("Cache.cache mismatch (-want +got):\n%s", diff) + } +} + +func setupHTTPRoutes() (*api.HTTPRouteConfigEntry, *api.HTTPRouteConfigEntry) { + routeOne := &api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, + Name: "my route", + Parents: []api.ResourceReference{ + { + Kind: api.APIGateway, + Name: "api-gw", + SectionName: "listener-1", + Namespace: "ns", + }, + }, + Rules: []api.HTTPRouteRule{ + { + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "add it on": "the value", + }, + Remove: []string{"time to go"}, + Set: map[string]string{ + "Magic": "v2", + "Another One": "dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{Path: "v1"}, + }, + Matches: []api.HTTPMatch{ + { + Headers: []api.HTTPHeaderMatch{ + { + Match: api.HTTPHeaderMatchExact, + Name: "my header match", + Value: "the value", + }, + }, + Method: api.HTTPMatchMethodGet, + Path: api.HTTPPathMatch{ + Match: api.HTTPPathMatchPrefix, + Value: "/v1", + }, + Query: []api.HTTPQueryMatch{ + { + Match: api.HTTPQueryMatchExact, + Name: "search", + Value: "term", + }, + }, + }, + }, + Services: []api.HTTPService{ + { + Name: "service one", + Weight: 45, + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "svc - add it on": "svc - the value", + }, + Remove: []string{"svc - time to go"}, + Set: map[string]string{ + "svc - Magic": "svc - v2", + "svc - Another One": "svc - dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{ + Path: "path", + }, + }, + Namespace: "some ns", + }, + }, + }, + }, + Hostnames: []string{"hostname.com"}, + Meta: map[string]string{ + "metaKey": "metaVal", + }, + Status: api.ConfigEntryStatus{}, + } + routeTwo := &api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, + Name: "my route 2", + Parents: []api.ResourceReference{ + { + Kind: api.APIGateway, + Name: "api-gw", + SectionName: "listener-2", + Namespace: "ns", + }, + }, + Rules: []api.HTTPRouteRule{ + { + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "add it on": "the value", + }, + Remove: []string{"time to go"}, + Set: map[string]string{ + "Magic": "v2", + "Another One": "dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{Path: "v1"}, + }, + Matches: []api.HTTPMatch{ + { + Headers: []api.HTTPHeaderMatch{ + { + Match: api.HTTPHeaderMatchExact, + Name: "my header match", + Value: "the value", + }, + }, + Method: api.HTTPMatchMethodGet, + Path: api.HTTPPathMatch{ + Match: api.HTTPPathMatchPrefix, + Value: "/v1", + }, + Query: []api.HTTPQueryMatch{ + { + Match: api.HTTPQueryMatchExact, + Name: "search", + Value: "term", + }, + }, + }, + }, + Services: []api.HTTPService{ + { + Name: "service one", + Weight: 45, + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{ + "svc - add it on": "svc - the value", + }, + Remove: []string{"svc - time to go"}, + Set: map[string]string{ + "svc - Magic": "svc - v2", + "svc - Another One": "svc - dj khaled", + }, + }, + }, + URLRewrite: &api.URLRewrite{ + Path: "path", + }, + }, + Namespace: "some ns", + }, + }, + }, + }, + Hostnames: []string{"hostname.com"}, + Meta: map[string]string{ + "metakey": "meta val", + }, + } + return routeOne, routeTwo +} + +func setupGateway() *api.APIGatewayConfigEntry { + return &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "api-gw", + Meta: map[string]string{ + "metakey": "meta val", + }, + Listeners: []api.APIGatewayListener{ + { + Name: "listener one", + Hostname: "hostname.com", + Port: 3350, + Protocol: "https", + TLS: api.APIGatewayTLSConfiguration{}, + }, + }, + } +} + +func setupTCPRoute() *api.TCPRouteConfigEntry { + return &api.TCPRouteConfigEntry{ + Kind: api.TCPRoute, + Name: "tcp route", + Parents: []api.ResourceReference{ + { + Kind: api.APIGateway, + Name: "api-gw", + SectionName: "listener two", + }, + }, + Services: []api.TCPService{ + { + Name: "tcp service", + }, + }, + Meta: map[string]string{ + "metakey": "meta val", + }, + Status: api.ConfigEntryStatus{}, + } +} + +func setupInlineCertificate() *api.InlineCertificateConfigEntry { + return &api.InlineCertificateConfigEntry{ + Kind: api.InlineCertificate, + Name: "inline-cert", + Certificate: "cert", + PrivateKey: "super secret", + Meta: map[string]string{ + "metaKey": "meta val", + }, + } +} + +func TestCache_Delete(t *testing.T) { + t.Parallel() + testCases := []struct { + name string + responseFn func(w http.ResponseWriter) + expectedErr error + }{ + { + name: "delete is successful", + responseFn: func(w http.ResponseWriter) { + w.WriteHeader(200) + fmt.Fprintln(w, `{deleted: true}`) + }, + expectedErr: nil, + }, + } + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + ref := api.ResourceReference{ + Name: "my-route", + Kind: api.HTTPRoute, + } + consulServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case fmt.Sprintf("/v1/config/%s/%s", ref.Kind, ref.Name): + tt.responseFn(w) + default: + w.WriteHeader(500) + fmt.Fprintln(w, "Mock Server not configured for this route: "+r.URL.Path) + } + })) + defer consulServer.Close() + + serverURL, err := url.Parse(consulServer.URL) + require.NoError(t, err) + + port, err := strconv.Atoi(serverURL.Port()) + require.NoError(t, err) + + c := New(Config{ + ConsulClientConfig: &consul.Config{ + APIClientConfig: &api.Config{}, + HTTPPort: port, + GRPCPort: port, + APITimeout: 0, + }, + ConsulServerConnMgr: test.MockConnMgrForIPAndPort(serverURL.Hostname(), port), + NamespacesEnabled: false, + Logger: logrtest.NewTestLogger(t), + }) + + err = c.Delete(context.Background(), ref) + require.ErrorIs(t, err, tt.expectedErr) + }) + } +} diff --git a/control-plane/api-gateway/controllers/finalizer.go b/control-plane/api-gateway/controllers/finalizer.go new file mode 100644 index 0000000000..c12f5f29e7 --- /dev/null +++ b/control-plane/api-gateway/controllers/finalizer.go @@ -0,0 +1,44 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package controllers + +import ( + "context" + + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// EnsureFinalizer ensures that the given object has the given finalizer. +func EnsureFinalizer(ctx context.Context, client client.Client, object client.Object, finalizer string) (didUpdate bool, err error) { + finalizers := object.GetFinalizers() + for _, f := range finalizers { + if f == finalizer { + return false, nil + } + } + object.SetFinalizers(append(finalizers, finalizer)) + if err := client.Update(ctx, object); err != nil { + return false, err + } + + return true, nil +} + +// RemoveFinalizer removes the given finalizer from the given object. +func RemoveFinalizer(ctx context.Context, client client.Client, object client.Object, finalizer string) (didUpdate bool, err error) { + finalizers := object.GetFinalizers() + + for i, f := range finalizers { + if f == finalizer { + finalizers = append(finalizers[:i], finalizers[i+1:]...) + object.SetFinalizers(finalizers) + if err := client.Update(ctx, object); err != nil { + return false, err + } + return true, nil + } + } + + return false, nil +} diff --git a/control-plane/api-gateway/controllers/finalizer_test.go b/control-plane/api-gateway/controllers/finalizer_test.go new file mode 100644 index 0000000000..dc265ef6ca --- /dev/null +++ b/control-plane/api-gateway/controllers/finalizer_test.go @@ -0,0 +1,84 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package controllers + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +func TestEnsureFinalizer(t *testing.T) { + t.Parallel() + + finalizer := "test-finalizer" + + cases := map[string]struct { + initialFinalizers []string + finalizerToAdd string + expectedDidUpdate bool + }{ + "should update": {[]string{}, finalizer, true}, + "should not update": {[]string{finalizer}, finalizer, false}, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + // It doesn't matter what the object is, as long as it implements client.Object. + // A Pod was as good as any other object here. + testObj := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-obj", + Finalizers: tc.initialFinalizers, + }, + } + + client := fake.NewClientBuilder().WithObjects(testObj).Build() + + didUpdate, err := EnsureFinalizer(context.Background(), client, testObj, tc.finalizerToAdd) + + require.NoError(t, err) + require.Equal(t, tc.expectedDidUpdate, didUpdate) + }) + } +} + +func TestRemoveFinalizer(t *testing.T) { + t.Parallel() + + finalizer := "test-finalizer" + + cases := map[string]struct { + initialFinalizers []string + finalizerToRemove string + expectedDidUpdate bool + }{ + "should update": {[]string{finalizer}, finalizer, true}, + "should not update": {[]string{}, finalizer, false}, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + // It doesn't matter what the object is, as long as it implements client.Object. + // A Pod was as good as any other object here. + testObj := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-obj", + Finalizers: tc.initialFinalizers, + }, + } + + client := fake.NewClientBuilder().WithObjects(testObj).Build() + + didUpdate, err := RemoveFinalizer(context.Background(), client, testObj, tc.finalizerToRemove) + + require.NoError(t, err) + require.Equal(t, tc.expectedDidUpdate, didUpdate) + }) + } +} diff --git a/control-plane/api-gateway/controllers/gateway_class_config_controller.go b/control-plane/api-gateway/controllers/gateway_class_config_controller.go new file mode 100644 index 0000000000..3889778348 --- /dev/null +++ b/control-plane/api-gateway/controllers/gateway_class_config_controller.go @@ -0,0 +1,130 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package controllers + +import ( + "context" + "time" + + "github.com/go-logr/logr" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" +) + +const ( + gatewayClassConfigFinalizer = "gateway-class-exists-finalizer.consul.hashicorp.com" +) + +// The GatewayClassConfigController manages the state of GatewayClassConfigs. +type GatewayClassConfigController struct { + client.Client + + Log logr.Logger +} + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.8.3/pkg/reconcile +func (r *GatewayClassConfigController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := r.Log.WithValues("gatewayClassConfig", req.NamespacedName.Name) + log.Info("Reconciling GatewayClassConfig ") + + gcc := &v1alpha1.GatewayClassConfig{} + if err := r.Client.Get(ctx, req.NamespacedName, gcc); err != nil { + if k8serrors.IsNotFound(err) { + return ctrl.Result{}, nil + } + r.Log.Error(err, "failed to get gateway class config") + return ctrl.Result{}, err + } + + if !gcc.ObjectMeta.DeletionTimestamp.IsZero() { + // We have a deletion, ensure we're not in use. + used, err := gatewayClassConfigInUse(ctx, r.Client, gcc) + if err != nil { + r.Log.Error(err, "failed to check if the gateway class config is still in use") + return ctrl.Result{}, err + } + if used { + r.Log.Info("gateway class config still in use") + // Requeue as to not block the reconciliation loop. + return ctrl.Result{RequeueAfter: 10 * time.Second}, nil + } + // gcc is no longer in use. + if _, err := RemoveFinalizer(ctx, r.Client, gcc, gatewayClassConfigFinalizer); err != nil { + r.Log.Error(err, "error removing gateway class config finalizer") + return ctrl.Result{}, err + } + return ctrl.Result{}, nil + } + + if _, err := EnsureFinalizer(ctx, r.Client, gcc, gatewayClassConfigFinalizer); err != nil { + r.Log.Error(err, "error adding gateway class config finalizer") + return ctrl.Result{}, err + } + + return ctrl.Result{}, nil +} + +// gatewayClassUsesConfig determines whether a given GatewayClass references a +// given GatewayClassConfig. Since these resources are scoped to the cluster, +// namespace is not considered. +func gatewayClassUsesConfig(gc gwv1beta1.GatewayClass, gcc *v1alpha1.GatewayClassConfig) bool { + parameterRef := gc.Spec.ParametersRef + return parameterRef != nil && + string(parameterRef.Group) == v1alpha1.ConsulHashicorpGroup && + parameterRef.Kind == v1alpha1.GatewayClassConfigKind && + parameterRef.Name == gcc.Name +} + +// GatewayClassConfigInUse determines whether any GatewayClass in the cluster +// references the provided GatewayClassConfig. +func gatewayClassConfigInUse(ctx context.Context, k8sClient client.Client, gcc *v1alpha1.GatewayClassConfig) (bool, error) { + list := &gwv1beta1.GatewayClassList{} + if err := k8sClient.List(ctx, list); err != nil { + return false, err + } + + for _, gc := range list.Items { + if gatewayClassUsesConfig(gc, gcc) { + return true, nil + } + } + + return false, nil +} + +func (r *GatewayClassConfigController) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&v1alpha1.GatewayClassConfig{}). + // Watch for changes to GatewayClass objects associated with this config for purposes of finalizer removal. + Watches(source.NewKindWithCache(&gwv1beta1.GatewayClass{}, mgr.GetCache()), r.transformGatewayClassToGatewayClassConfig(ctx)). + Complete(r) +} + +func (r *GatewayClassConfigController) transformGatewayClassToGatewayClassConfig(ctx context.Context) handler.EventHandler { + return handler.EnqueueRequestsFromMapFunc(func(o client.Object) []reconcile.Request { + gc := o.(*gwv1beta1.GatewayClass) + + pr := gc.Spec.ParametersRef + if pr != nil && pr.Kind == v1alpha1.GatewayClassConfigKind { + return []reconcile.Request{{ + NamespacedName: types.NamespacedName{ + Name: pr.Name, + }, + }} + } + + return nil + }) +} diff --git a/control-plane/api-gateway/controllers/gateway_class_config_controller_test.go b/control-plane/api-gateway/controllers/gateway_class_config_controller_test.go new file mode 100644 index 0000000000..40023d498f --- /dev/null +++ b/control-plane/api-gateway/controllers/gateway_class_config_controller_test.go @@ -0,0 +1,123 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package controllers + +import ( + "context" + "testing" + "time" + + logrtest "github.com/go-logr/logr/testr" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/stretchr/testify/require" + meta "k8s.io/apimachinery/pkg/apis/meta/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +func TestGatewayClassConfigReconcile(t *testing.T) { + t.Parallel() + deletionTimestamp := meta.Now() + cases := []struct { + name string + k8sObjects func() []runtime.Object + expErr string + requeue bool + requeueAfter time.Duration + }{ + { + name: "Successfully reconcile without any changes", + k8sObjects: func() []runtime.Object { + gatewayClassConfig := v1alpha1.GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-api-gateway", + }, + } + return []runtime.Object{&gatewayClassConfig} + }, + }, + { + name: "GatewayClassConfig Does Not Exist", + k8sObjects: func() []runtime.Object { + return []runtime.Object{} + }, + }, + { + name: "Remove not-in-use GatewayClassConfig", + k8sObjects: func() []runtime.Object { + gatewayClassConfig := v1alpha1.GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-api-gateway", + DeletionTimestamp: &deletionTimestamp, + }, + } + return []runtime.Object{&gatewayClassConfig} + }, + }, + { + name: "Try to remove in-use GatewayClassConfig", + k8sObjects: func() []runtime.Object { + gatewayClassConfig := v1alpha1.GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-api-gateway", + DeletionTimestamp: &deletionTimestamp, + }, + } + gatewayClass := gwv1beta1.GatewayClass{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-api-gateway-class", + }, + Spec: gwv1beta1.GatewayClassSpec{ + ParametersRef: &gwv1beta1.ParametersReference{ + Group: gwv1beta1.Group(v1alpha1.ConsulHashicorpGroup), + Kind: v1alpha1.GatewayClassConfigKind, + Name: gatewayClassConfig.ObjectMeta.Name, + Namespace: nil, + }, + }, + Status: gwv1beta1.GatewayClassStatus{}, + } + return []runtime.Object{&gatewayClassConfig, &gatewayClass} + }, + requeueAfter: time.Second * 10, + }, + } + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + s := runtime.NewScheme() + require.NoError(t, clientgoscheme.AddToScheme(s)) + require.NoError(t, gwv1alpha2.Install(s)) + require.NoError(t, gwv1beta1.Install(s)) + require.NoError(t, v1alpha1.AddToScheme(s)) + + fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(tt.k8sObjects()...).Build() + + // Create the gateway class config controller. + gcc := &GatewayClassConfigController{ + Client: fakeClient, + Log: logrtest.New(t), + } + + resp, err := gcc.Reconcile(context.Background(), ctrl.Request{ + NamespacedName: types.NamespacedName{ + Namespace: "", + Name: "consul-api-gateway", + }, + }) + if tt.expErr != "" { + require.EqualError(t, err, tt.expErr) + } else { + require.NoError(t, err) + } + require.Equal(t, tt.requeue, resp.Requeue) + }) + } +} diff --git a/control-plane/api-gateway/controllers/gateway_controller.go b/control-plane/api-gateway/controllers/gateway_controller.go new file mode 100644 index 0000000000..e29e40b28e --- /dev/null +++ b/control-plane/api-gateway/controllers/gateway_controller.go @@ -0,0 +1,779 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package controllers + +import ( + "context" + "reflect" + + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" + + "github.com/go-logr/logr" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" + "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + + apigateway "github.com/hashicorp/consul-k8s/control-plane/api-gateway" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/binding" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/cache" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/gatekeeper" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/translation" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul-k8s/control-plane/consul" + "github.com/hashicorp/consul/api" +) + +const ( + gatewayFinalizer = "gateway-finalizer.consul.hashicorp.com" + + kindGateway = "Gateway" +) + +// GatewayControllerConfig holds the values necessary for configuring the GatewayController. +type GatewayControllerConfig struct { + HelmConfig apigateway.HelmConfig + ConsulClientConfig *consul.Config + ConsulServerConnMgr consul.ServerConnectionManager + NamespacesEnabled bool + Partition string +} + +// GatewayController reconciles a Gateway object. +// The Gateway is responsible for defining the behavior of API gateways. +type GatewayController struct { + HelmConfig apigateway.HelmConfig + Log logr.Logger + Translator translation.K8sToConsulTranslator + cache *cache.Cache + client.Client +} + +// Reconcile handles the reconciliation loop for Gateway objects. +func (r *GatewayController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := r.Log.WithValues("gateway", req.NamespacedName) + log.Info("Reconciling Gateway") + + // If gateway does not exist, log an error. + var gw gwv1beta1.Gateway + err := r.Client.Get(ctx, req.NamespacedName, &gw) + if err != nil { + if k8serrors.IsNotFound(err) { + return ctrl.Result{}, nil + } + log.Error(err, "unable to get Gateway") + return ctrl.Result{}, err + } + + // If gateway class on the gateway does not exist, log an error. + gwc := &gwv1beta1.GatewayClass{} + err = r.Client.Get(ctx, types.NamespacedName{Name: string(gw.Spec.GatewayClassName)}, gwc) + if err != nil { + if !k8serrors.IsNotFound(err) { + log.Error(err, "unable to get GatewayClass") + return ctrl.Result{}, err + } + gwc = nil + } + + gwcc, err := getConfigForGatewayClass(ctx, r.Client, gwc) + if err != nil { + log.Error(err, "error fetching the gateway class config") + return ctrl.Result{}, err + } + + // fetch all namespaces + namespaceList := &corev1.NamespaceList{} + if err := r.Client.List(ctx, namespaceList); err != nil { + log.Error(err, "unable to list Namespaces") + return ctrl.Result{}, err + } + namespaces := map[string]corev1.Namespace{} + for _, namespace := range namespaceList.Items { + namespaces[namespace.Name] = namespace + } + + // fetch all gateways we control for reference counting + gwcList := &gwv1beta1.GatewayClassList{} + if err := r.Client.List(ctx, gwcList, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(GatewayClass_ControllerNameIndex, GatewayClassControllerName), + }); err != nil { + log.Error(err, "unable to list GatewayClasses") + return ctrl.Result{}, err + } + + // fetch related gateway pods + labels := apigateway.LabelsForGateway(&gw) + podList := &corev1.PodList{} + if err := r.Client.List(ctx, podList, client.MatchingLabels(labels)); err != nil { + log.Error(err, "unable to list Pods for Gateway") + return ctrl.Result{}, err + } + + // fetch related gateway services + service := &corev1.Service{} + // we use the implicit association of a service name/namespace with a corresponding + // gateway + if err := r.Client.Get(ctx, req.NamespacedName, service); err != nil { + if !k8serrors.IsNotFound(err) { + log.Error(err, "unable to fetch service for Gateway") + return ctrl.Result{}, err + } + // if we got a 404, then nil out the service + service = nil + } + + gwList := &gwv1beta1.GatewayList{} + if err := r.Client.List(ctx, gwList); err != nil { + log.Error(err, "unable to list Gateways") + return ctrl.Result{}, err + } + + controlled := map[types.NamespacedName]gwv1beta1.Gateway{} + for _, gwc := range gwcList.Items { + for _, gw := range gwList.Items { + if string(gw.Spec.GatewayClassName) == gwc.Name { + controlled[types.NamespacedName{Namespace: gw.Namespace, Name: gw.Name}] = gw + } + } + } + + // fetch all MeshServices + meshServiceList := &v1alpha1.MeshServiceList{} + if err := r.Client.List(ctx, meshServiceList); err != nil { + log.Error(err, "unable to list MeshServices") + return ctrl.Result{}, err + } + + // fetch all secrets referenced by this gateway + secretList := &corev1.SecretList{} + if err := r.Client.List(ctx, secretList); err != nil { + log.Error(err, "unable to list Secrets") + return ctrl.Result{}, err + } + + listenerCerts := make(map[types.NamespacedName]struct{}) + for _, listener := range gw.Spec.Listeners { + if listener.TLS != nil { + for _, ref := range listener.TLS.CertificateRefs { + if nilOrEqual(ref.Group, "") && nilOrEqual(ref.Kind, "Secret") { + listenerCerts[indexedNamespacedNameWithDefault(ref.Name, ref.Namespace, gw.Namespace)] = struct{}{} + } + } + } + } + + filteredSecrets := []corev1.Secret{} + for _, secret := range secretList.Items { + namespacedName := types.NamespacedName{Namespace: secret.Namespace, Name: secret.Name} + if _, ok := listenerCerts[namespacedName]; ok { + filteredSecrets = append(filteredSecrets, secret) + } + } + + // fetch all http routes referencing this gateway + httpRouteList := &gwv1beta1.HTTPRouteList{} + if err := r.Client.List(ctx, httpRouteList, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(HTTPRoute_GatewayIndex, req.String()), + }); err != nil { + log.Error(err, "unable to list HTTPRoutes") + return ctrl.Result{}, err + } + + // fetch all tcp routes referencing this gateway + tcpRouteList := &gwv1alpha2.TCPRouteList{} + if err := r.Client.List(ctx, tcpRouteList, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(TCPRoute_GatewayIndex, req.String()), + }); err != nil { + log.Error(err, "unable to list TCPRoutes") + return ctrl.Result{}, err + } + + configEntry := r.cache.Get(r.Translator.ReferenceForGateway(&gw)) + + var consulGateway *api.APIGatewayConfigEntry + if configEntry != nil { + consulGateway = configEntry.(*api.APIGatewayConfigEntry) + } + httpRoutes := r.cache.List(api.HTTPRoute) + tcpRoutes := r.cache.List(api.TCPRoute) + inlineCertificates := r.cache.List(api.InlineCertificate) + services := r.cache.ListServices() + + binder := binding.NewBinder(binding.BinderConfig{ + Translator: r.Translator, + ControllerName: GatewayClassControllerName, + GatewayClassConfig: gwcc, + GatewayClass: gwc, + Gateway: gw, + Pods: podList.Items, + Service: service, + HTTPRoutes: httpRouteList.Items, + TCPRoutes: tcpRouteList.Items, + MeshServices: meshServiceList.Items, + Secrets: filteredSecrets, + ConsulGateway: consulGateway, + ConsulHTTPRoutes: derefAll(configEntriesTo[*api.HTTPRouteConfigEntry](httpRoutes)), + ConsulTCPRoutes: derefAll(configEntriesTo[*api.TCPRouteConfigEntry](tcpRoutes)), + ConsulInlineCertificates: derefAll(configEntriesTo[*api.InlineCertificateConfigEntry](inlineCertificates)), + ConnectInjectedServices: services, + GatewayServices: consulServicesForGateway(gw, services), + Namespaces: namespaces, + ControlledGateways: controlled, + }) + + updates := binder.Snapshot() + + if updates.UpsertGatewayDeployment { + log.Info("updating gatekeeper") + err := r.updateGatekeeperResources(ctx, log, &gw, gwcc) + if err != nil { + log.Error(err, "unable to update gateway resources") + return ctrl.Result{}, err + } + } else { + log.Info("deleting gatekeeper") + err := r.deleteGatekeeperResources(ctx, log, &gw) + if err != nil { + log.Error(err, "unable to delete gateway resources") + return ctrl.Result{}, err + } + } + + for _, deletion := range updates.Consul.Deletions { + log.Info("deleting from Consul", "kind", deletion.Kind, "namespace", deletion.Namespace, "name", deletion.Name) + if err := r.cache.Delete(ctx, deletion); err != nil { + log.Error(err, "error deleting config entry") + return ctrl.Result{}, err + } + } + + for _, update := range updates.Consul.Updates { + log.Info("updating in Consul", "kind", update.GetKind(), "namespace", update.GetNamespace(), "name", update.GetName()) + if err := r.cache.Write(ctx, update); err != nil { + log.Error(err, "error updating config entry") + return ctrl.Result{}, err + } + } + + for _, registration := range updates.Consul.Registrations { + log.Info("registering service in Consul", "service", registration.Service.Service, "id", registration.Service.ID) + if err := r.cache.Register(ctx, registration); err != nil { + log.Error(err, "error registering service") + return ctrl.Result{}, err + } + } + + for _, deregistration := range updates.Consul.Deregistrations { + log.Info("deregistering service in Consul", "id", deregistration.ServiceID) + if err := r.cache.Deregister(ctx, deregistration); err != nil { + log.Error(err, "error deregistering service") + return ctrl.Result{}, err + } + } + + for _, update := range updates.Kubernetes.Updates { + log.Info("update in Kubernetes", "kind", update.GetObjectKind().GroupVersionKind().Kind, "namespace", update.GetNamespace(), "name", update.GetName()) + if err := r.updateAndResetStatus(ctx, update); err != nil { + log.Error(err, "error updating object") + return ctrl.Result{}, err + } + } + + for _, update := range updates.Kubernetes.StatusUpdates { + log.Info("update status in Kubernetes", "kind", update.GetObjectKind().GroupVersionKind().Kind, "namespace", update.GetNamespace(), "name", update.GetName()) + if err := r.Client.Status().Update(ctx, update); err != nil { + log.Error(err, "error updating status") + return ctrl.Result{}, err + } + } + + // link up policy - TODO: this is really a nasty hack to inject a known policy with + // mesh == read on the provisioned gateway token if needed, figure out some other + // way of handling it. + if updates.UpsertGatewayDeployment { + reference := r.Translator.ReferenceForGateway(&gw) + if err := r.cache.LinkPolicy(ctx, reference.Name, reference.Namespace); err != nil { + log.Error(err, "error linking token policy") + return ctrl.Result{}, err + } + } + + /* TODO: + 1.ReferenceGrants + */ + + return ctrl.Result{}, nil +} + +func (r *GatewayController) updateAndResetStatus(ctx context.Context, o client.Object) error { + // we create a copy so that we can re-update its status if need be + status := reflect.ValueOf(o.DeepCopyObject()).Elem().FieldByName("Status") + if err := r.Client.Update(ctx, o); err != nil { + return err + } + // reset the status in case it needs to be updated below + reflect.ValueOf(o).Elem().FieldByName("Status").Set(status) + return nil +} + +func derefAll[T any](vs []*T) []T { + e := make([]T, len(vs)) + for _, v := range vs { + e = append(e, *v) + } + return e +} + +func configEntriesTo[T api.ConfigEntry](entries []api.ConfigEntry) []T { + es := []T{} + for _, e := range entries { + es = append(es, e.(T)) + } + return es +} + +func (r *GatewayController) deleteGatekeeperResources(ctx context.Context, log logr.Logger, gw *gwv1beta1.Gateway) error { + gk := gatekeeper.New(log, r.Client) + err := gk.Delete(ctx, types.NamespacedName{ + Namespace: gw.Namespace, + Name: gw.Name, + }) + if err != nil { + return err + } + + return nil +} + +func (r *GatewayController) updateGatekeeperResources(ctx context.Context, log logr.Logger, gw *gwv1beta1.Gateway, gwcc *v1alpha1.GatewayClassConfig) error { + gk := gatekeeper.New(log, r.Client) + err := gk.Upsert(ctx, *gw, *gwcc, r.HelmConfig) + if err != nil { + return err + } + + return nil +} + +// SetupWithGatewayControllerManager registers the controller with the given manager. +func SetupGatewayControllerWithManager(ctx context.Context, mgr ctrl.Manager, config GatewayControllerConfig) (*cache.Cache, error) { + c := cache.New(cache.Config{ + ConsulClientConfig: config.ConsulClientConfig, + ConsulServerConnMgr: config.ConsulServerConnMgr, + NamespacesEnabled: config.NamespacesEnabled, + PeeringEnabled: config.HelmConfig.PeeringEnabled, + Logger: mgr.GetLogger(), + }) + + translator := translation.NewConsulToNamespaceNameTranslator(c) + + r := &GatewayController{ + Client: mgr.GetClient(), + Log: mgr.GetLogger(), + HelmConfig: config.HelmConfig, + cache: c, + } + + return c, ctrl.NewControllerManagedBy(mgr). + For(&gwv1beta1.Gateway{}). + Owns(&appsv1.Deployment{}). + Owns(&corev1.Service{}). + Owns(&corev1.Pod{}). + Watches( + source.NewKindWithCache(&gwv1beta1.GatewayClass{}, mgr.GetCache()), + handler.EnqueueRequestsFromMapFunc(r.transformGatewayClass(ctx)), + ). + Watches( + source.NewKindWithCache(&gwv1beta1.HTTPRoute{}, mgr.GetCache()), + handler.EnqueueRequestsFromMapFunc(r.transformHTTPRoute(ctx)), + ). + Watches( + source.NewKindWithCache(&gwv1alpha2.TCPRoute{}, mgr.GetCache()), + handler.EnqueueRequestsFromMapFunc(r.transformTCPRoute(ctx)), + ). + Watches( + source.NewKindWithCache(&corev1.Secret{}, mgr.GetCache()), + handler.EnqueueRequestsFromMapFunc(r.transformSecret(ctx)), + ). + Watches( + source.NewKindWithCache(&gwv1beta1.ReferenceGrant{}, mgr.GetCache()), + handler.EnqueueRequestsFromMapFunc(r.transformReferenceGrant(ctx)), + ). + Watches( + source.NewKindWithCache(&v1alpha1.MeshService{}, mgr.GetCache()), + handler.EnqueueRequestsFromMapFunc(r.transformMeshService(ctx)), + ). + Watches( + // Subscribe to changes from Consul Connect Services + &source.Channel{Source: c.SubscribeServices(ctx, r.transformConsulService(ctx)).Events()}, + &handler.EnqueueRequestForObject{}, + ). + Watches( + // Subscribe to changes from Consul Peering Services + &source.Channel{Source: c.SubscribePeerings(ctx, r.transformConsulPeering(ctx)).Events()}, + &handler.EnqueueRequestForObject{}, + ). + Watches( + // Subscribe to changes from Consul for APIGateways + &source.Channel{Source: c.Subscribe(ctx, api.APIGateway, translator.BuildConsulGatewayTranslator(ctx)).Events()}, + &handler.EnqueueRequestForObject{}, + ). + Watches( + // Subscribe to changes from Consul for HTTPRoutes + &source.Channel{Source: c.Subscribe(ctx, api.HTTPRoute, translator.BuildConsulHTTPRouteTranslator(ctx)).Events()}, + &handler.EnqueueRequestForObject{}, + ). + Watches( + // Subscribe to changes from Consul for TCPRoutes + &source.Channel{Source: c.Subscribe(ctx, api.TCPRoute, translator.BuildConsulTCPRouteTranslator(ctx)).Events()}, + &handler.EnqueueRequestForObject{}, + ). + Watches( + // Subscribe to changes from Consul for InlineCertificates + &source.Channel{Source: c.Subscribe(ctx, api.InlineCertificate, translator.BuildConsulInlineCertificateTranslator(ctx, r.transformSecret)).Events()}, + &handler.EnqueueRequestForObject{}, + ).Complete(r) +} + +func serviceToNamespacedName(s *api.CatalogService) types.NamespacedName { + return types.NamespacedName{ + Namespace: s.ServiceMeta[constants.MetaKeyKubeNS], + Name: s.ServiceMeta[constants.MetaKeyKubeServiceName], + } +} + +// transformGatewayClass will check the list of GatewayClass objects for a matching +// class, then return a list of reconcile Requests for it. +func (r *GatewayController) transformGatewayClass(ctx context.Context) func(o client.Object) []reconcile.Request { + return func(o client.Object) []reconcile.Request { + gatewayClass := o.(*gwv1beta1.GatewayClass) + gatewayList := &gwv1beta1.GatewayList{} + if err := r.Client.List(ctx, gatewayList, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(Gateway_GatewayClassIndex, gatewayClass.Name), + }); err != nil { + return nil + } + return objectsToRequests(pointersOf(gatewayList.Items)) + } +} + +// transformHTTPRoute will check the HTTPRoute object for a matching +// class, then return a list of reconcile Requests for Gateways referring to it. +func (r *GatewayController) transformHTTPRoute(ctx context.Context) func(o client.Object) []reconcile.Request { + return func(o client.Object) []reconcile.Request { + route := o.(*gwv1beta1.HTTPRoute) + return refsToRequests(parentRefs(gwv1beta1.GroupVersion.Group, kindGateway, route.Namespace, route.Spec.ParentRefs)) + } +} + +// transformTCPRoute will check the TCPRoute object for a matching +// class, then return a list of reconcile Requests for Gateways referring to it. +func (r *GatewayController) transformTCPRoute(ctx context.Context) func(o client.Object) []reconcile.Request { + return func(o client.Object) []reconcile.Request { + route := o.(*gwv1alpha2.TCPRoute) + return refsToRequests(parentRefs(gwv1beta1.GroupVersion.Group, kindGateway, route.Namespace, route.Spec.ParentRefs)) + } +} + +// transformSecret will check the Secret object for a matching +// class, then return a list of reconcile Requests for Gateways referring to it. +func (r *GatewayController) transformSecret(ctx context.Context) func(o client.Object) []reconcile.Request { + return func(o client.Object) []reconcile.Request { + secret := o.(*corev1.Secret) + gatewayList := &gwv1beta1.GatewayList{} + if err := r.Client.List(ctx, gatewayList, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(Secret_GatewayIndex, secret.Name), + }); err != nil { + return nil + } + return objectsToRequests(pointersOf(gatewayList.Items)) + } +} + +// transformReferenceGrant will check the ReferenceGrant object for a matching +// class, then return a list of reconcile Requests for Gateways referring to it. +func (r *GatewayController) transformReferenceGrant(ctx context.Context) func(o client.Object) []reconcile.Request { + return func(o client.Object) []reconcile.Request { + // just reconcile all gateways within the namespace + grant := o.(*gwv1beta1.ReferenceGrant) + gatewayList := &gwv1beta1.GatewayList{} + if err := r.Client.List(ctx, gatewayList, &client.ListOptions{ + Namespace: grant.Namespace, + }); err != nil { + return nil + } + return objectsToRequests(pointersOf(gatewayList.Items)) + } +} + +// transformConsulService will return a list of gateways that are referenced +// by a TCPRoute or HTTPRoute that references the Consul service. +func (r *GatewayController) transformConsulService(ctx context.Context) func(service *api.CatalogService) []types.NamespacedName { + return func(service *api.CatalogService) []types.NamespacedName { + nsn := serviceToNamespacedName(service) + + if nsn.Namespace != "" && nsn.Name != "" { + key := nsn.String() + + requestSet := make(map[types.NamespacedName]struct{}) + tcpRouteList := &gwv1alpha2.TCPRouteList{} + if err := r.Client.List(ctx, tcpRouteList, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(TCPRoute_ServiceIndex, key), + }); err != nil { + r.Log.Error(err, "unable to list TCPRoutes") + } + for _, route := range tcpRouteList.Items { + for _, ref := range parentRefs(gwv1beta1.GroupVersion.Group, kindGateway, route.Namespace, route.Spec.ParentRefs) { + requestSet[ref] = struct{}{} + } + } + + httpRouteList := &gwv1alpha2.HTTPRouteList{} + if err := r.Client.List(ctx, httpRouteList, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(HTTPRoute_ServiceIndex, key), + }); err != nil { + r.Log.Error(err, "unable to list HTTPRoutes") + } + for _, route := range httpRouteList.Items { + for _, ref := range parentRefs(gwv1beta1.GroupVersion.Group, kindGateway, route.Namespace, route.Spec.ParentRefs) { + requestSet[ref] = struct{}{} + } + } + + requests := []types.NamespacedName{} + for request := range requestSet { + requests = append(requests, request) + } + return requests + } + + return nil + } +} + +// transformConsulPeering will return a list of gateways that are referenced +// by a TCPRoute or HTTPRoute that references the Consul peering. +func (r *GatewayController) transformConsulPeering(ctx context.Context) func(service *api.Peering) []types.NamespacedName { + return func(peering *api.Peering) []types.NamespacedName { + meshServiceList := &v1alpha1.MeshServiceList{} + + if err := r.Client.List(ctx, meshServiceList, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(MeshService_PeerIndex, peering.Name), + }); err != nil { + r.Log.Error(err, "unable to list TCPRoutes") + } + + flattened := []types.NamespacedName{} + for _, meshService := range meshServiceList.Items { + for _, request := range r.transformMeshService(ctx)(&meshService) { + flattened = append(flattened, request.NamespacedName) + } + } + + return flattened + } +} + +// transformMeshService will return a list of gateways that are referenced +// by a TCPRoute or HTTPRoute that references the mesh service. +func (r *GatewayController) transformMeshService(ctx context.Context) func(o client.Object) []reconcile.Request { + return func(o client.Object) []reconcile.Request { + service := o.(*v1alpha1.MeshService) + key := client.ObjectKeyFromObject(service).String() + + requestSet := make(map[types.NamespacedName]struct{}) + + tcpRouteList := &gwv1alpha2.TCPRouteList{} + if err := r.Client.List(ctx, tcpRouteList, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(TCPRoute_MeshServiceIndex, key), + }); err != nil { + r.Log.Error(err, "unable to list TCPRoutes") + } + for _, route := range tcpRouteList.Items { + for _, ref := range parentRefs(gwv1beta1.GroupVersion.Group, kindGateway, route.Namespace, route.Spec.ParentRefs) { + requestSet[ref] = struct{}{} + } + } + + httpRouteList := &gwv1beta1.HTTPRouteList{} + if err := r.Client.List(ctx, httpRouteList, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(HTTPRoute_MeshServiceIndex, key), + }); err != nil { + r.Log.Error(err, "unable to list HTTPRoutes") + } + for _, route := range httpRouteList.Items { + for _, ref := range parentRefs(gwv1beta1.GroupVersion.Group, kindGateway, route.Namespace, route.Spec.ParentRefs) { + requestSet[ref] = struct{}{} + } + } + + requests := []reconcile.Request{} + for request := range requestSet { + requests = append(requests, reconcile.Request{NamespacedName: request}) + } + return requests + } +} + +// objectsToRequests takes a list of objects and returns a list of +// reconcile Requests. +func objectsToRequests[T metav1.Object](objects []T) []reconcile.Request { + requests := make([]reconcile.Request, 0, len(objects)) + + // TODO: is it possible to receive empty objects? + for _, object := range objects { + requests = append(requests, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: object.GetNamespace(), + Name: object.GetName(), + }, + }) + } + return requests +} + +// pointersOf returns a list of pointers to the list of objects passed in. +func pointersOf[T any](objects []T) []*T { + pointers := make([]*T, 0, len(objects)) + for _, object := range objects { + pointers = append(pointers, pointerTo(object)) + } + return pointers +} + +// pointerTo returns a pointer to the object type passed in. +func pointerTo[T any](v T) *T { + return &v +} + +// refsToRequests takes a list of NamespacedName objects and returns a list of +// reconcile Requests. +func refsToRequests(objects []types.NamespacedName) []reconcile.Request { + requests := make([]reconcile.Request, 0, len(objects)) + for _, object := range objects { + requests = append(requests, reconcile.Request{ + NamespacedName: object, + }) + } + return requests +} + +// parentRefs takes a list of ParentReference objects and returns a list of NamespacedName objects. +func parentRefs(group, kind, namespace string, refs []gwv1beta1.ParentReference) []types.NamespacedName { + indexed := make([]types.NamespacedName, 0, len(refs)) + for _, parent := range refs { + if nilOrEqual(parent.Group, group) && nilOrEqual(parent.Kind, kind) { + indexed = append(indexed, indexedNamespacedNameWithDefault(parent.Name, parent.Namespace, namespace)) + } + } + return indexed +} + +func nilOrEqual[T ~string](v *T, check string) bool { + return v == nil || string(*v) == check +} + +func indexedNamespacedNameWithDefault[T ~string, U ~string, V ~string](t T, u *U, v V) types.NamespacedName { + return types.NamespacedName{ + Namespace: derefStringOr(u, v), + Name: string(t), + } +} + +func derefStringOr[T ~string, U ~string](v *T, val U) string { + if v == nil { + return string(val) + } + return string(*v) +} + +func (r *GatewayController) getAllRefsForGateway(ctx context.Context, gw *gwv1beta1.Gateway) ([]metav1.Object, error) { + objs := make([]metav1.Object, 0) + + // handle http routes + httpRouteList := &gwv1beta1.HTTPRouteList{} + err := r.Client.List(ctx, httpRouteList, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(HTTPRoute_GatewayIndex, types.NamespacedName{Name: gw.Name, Namespace: gw.Namespace}.String()), + }) + if err != nil { + return nil, err + } + for _, route := range httpRouteList.Items { + objs = append(objs, &route) + } + // handle tcp routes + tcpRouteList := &v1alpha2.TCPRouteList{} + err = r.Client.List(ctx, tcpRouteList, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(TCPRoute_GatewayIndex, types.NamespacedName{Name: gw.Name, Namespace: gw.Namespace}.String()), + }) + if err != nil { + return nil, err + } + for _, route := range tcpRouteList.Items { + objs = append(objs, &route) + } + + // handle secrets + for _, listener := range gw.Spec.Listeners { + for _, secret := range listener.TLS.CertificateRefs { + secretObj := &corev1.Secret{} + err = r.Client.Get(ctx, indexedNamespacedNameWithDefault(secret.Name, secret.Namespace, gw.Namespace), secretObj) + if err != nil { + continue + } + objs = append(objs, secretObj) + } + } + + return objs, nil +} + +// getConfigForGatewayClass returns the relevant GatewayClassConfig for the GatewayClass. +func getConfigForGatewayClass(ctx context.Context, client client.Client, gwc *gwv1beta1.GatewayClass) (*v1alpha1.GatewayClassConfig, error) { + if gwc == nil { + // if we don't have a gateway class we can't fetch the corresponding config + return nil, nil + } + + config := &v1alpha1.GatewayClassConfig{} + if ref := gwc.Spec.ParametersRef; ref != nil { + if string(ref.Group) != v1alpha1.GroupVersion.Group || + ref.Kind != v1alpha1.GatewayClassConfigKind || + gwc.Spec.ControllerName != GatewayClassControllerName { + // we don't have supported params, so return nil + return nil, nil + } + + err := client.Get(ctx, types.NamespacedName{Name: ref.Name}, config) + if err != nil { + if k8serrors.IsNotFound(err) { + return nil, nil + } + return nil, err + } + } + return config, nil +} + +func consulServicesForGateway(gateway gwv1beta1.Gateway, services []api.CatalogService) []api.CatalogService { + filtered := []api.CatalogService{} + for _, service := range services { + kubeService := serviceToNamespacedName(&service) + if gateway.Name == kubeService.Name && gateway.Namespace == kubeService.Namespace { + filtered = append(filtered, service) + } + } + return filtered +} diff --git a/control-plane/api-gateway/controllers/gateway_controller_test.go b/control-plane/api-gateway/controllers/gateway_controller_test.go new file mode 100644 index 0000000000..d04a0b2e44 --- /dev/null +++ b/control-plane/api-gateway/controllers/gateway_controller_test.go @@ -0,0 +1,464 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package controllers + +import ( + "context" + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "strconv" + "testing" + + appsv1 "k8s.io/api/apps/v1" + rbac "k8s.io/api/rbac/v1" + + logrtest "github.com/go-logr/logr/testr" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/cache" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul-k8s/control-plane/consul" + "github.com/hashicorp/consul-k8s/control-plane/helper/test" + "github.com/hashicorp/consul/api" +) + +const ( + TestGatewayClassConfigName = "test-gateway-class-config" + TestAnnotationConfigKey = "api-gateway.consul.hashicorp.com/config" + TestGatewayClassName = "test-gateway-class" + TestGatewayName = "test-gateway" + TestNamespace = "test-namespace" +) + +func stubConsulCache(t *testing.T) *cache.Cache { + t.Helper() + + consulServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/v1/acl/policies": + fmt.Fprintln(w, `[]`) + case "/v1/acl/tokens": + fmt.Fprintln(w, `[]`) + case "/v1/config": + fmt.Fprintln(w, `[]`) + case "/v1/catalog/services": + fmt.Fprintln(w, `{}`) + default: + w.WriteHeader(500) + fmt.Fprintln(w, "Mock Server not configured for this route: "+r.URL.Path) + } + })) + t.Cleanup(consulServer.Close) + + serverURL, err := url.Parse(consulServer.URL) + require.NoError(t, err) + + port, err := strconv.Atoi(serverURL.Port()) + require.NoError(t, err) + + return cache.New(cache.Config{ + ConsulClientConfig: &consul.Config{ + APIClientConfig: &api.Config{}, + HTTPPort: port, + GRPCPort: port, + APITimeout: 0, + }, + ConsulServerConnMgr: test.MockConnMgrForIPAndPort(serverURL.Hostname(), port), + NamespacesEnabled: false, + Logger: logrtest.New(t), + }) +} + +func TestGatewayReconcileGatekeeperUpdates(t *testing.T) { + t.Parallel() + + namespace := "test-namespace" + name := "test-gateway" + + req := ctrl.Request{ + NamespacedName: types.NamespacedName{ + Namespace: namespace, + Name: name, + }, + } + + basicGatewayClass, basicGatewayClassConfig := getBasicGatewayClassAndConfig() + + cases := map[string]struct { + gateway *gwv1beta1.Gateway + k8sObjects []runtime.Object + expectedError error + }{ + "successful update of gateway": { + gateway: &gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + Finalizers: []string{gatewayFinalizer}, + Annotations: map[string]string{ + TestAnnotationConfigKey: `{"serviceType":"serviceType"}`, + }, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: TestGatewayClassName, + }, + }, + k8sObjects: []runtime.Object{ + &basicGatewayClass, + &basicGatewayClassConfig, + }, + expectedError: nil, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + s := runtime.NewScheme() + require.NoError(t, gwv1beta1.Install(s)) + require.NoError(t, v1alpha1.AddToScheme(s)) + require.NoError(t, gwv1alpha2.AddToScheme(s)) + require.NoError(t, rbac.AddToScheme(s)) + require.NoError(t, corev1.AddToScheme(s)) + require.NoError(t, appsv1.AddToScheme(s)) + + objs := tc.k8sObjects + if tc.gateway != nil { + objs = append(objs, tc.gateway) + } + + fakeClient := registerFieldIndexersForTest(fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(objs...)).Build() + + r := &GatewayController{ + cache: stubConsulCache(t), + Client: fakeClient, + Log: logrtest.New(t), + } + + _, err := r.Reconcile(context.Background(), req) + + require.Equal(t, tc.expectedError, err) + deployment := appsv1.Deployment{} + r.Client.Get(context.TODO(), types.NamespacedName{ + Namespace: TestNamespace, + Name: TestGatewayName, + }, &deployment) + require.NotEmpty(t, deployment) + require.Equal(t, TestGatewayName, deployment.ObjectMeta.Name) + }) + } +} + +func TestGatewayReconcileGatekeeperDeletes(t *testing.T) { + t.Parallel() + + req := ctrl.Request{ + NamespacedName: types.NamespacedName{ + Namespace: TestNamespace, + Name: TestGatewayName, + }, + } + + basicGatewayClass, basicGatewayClassConfig := getBasicGatewayClassAndConfig() + cases := map[string]struct { + gateway *gwv1beta1.Gateway + k8sObjects []runtime.Object + expectedError error + }{ + "successful change of gatewayclass on gateway": { + gateway: &gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: TestNamespace, + Name: TestGatewayName, + Finalizers: []string{gatewayFinalizer}, + Annotations: map[string]string{ + TestAnnotationConfigKey: `{"serviceType":"serviceType"}`, + }, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: TestGatewayClassName, + }, + }, + k8sObjects: []runtime.Object{ + &basicGatewayClass, + &basicGatewayClassConfig, + }, + expectedError: nil, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + s := runtime.NewScheme() + require.NoError(t, gwv1beta1.Install(s)) + require.NoError(t, v1alpha1.AddToScheme(s)) + require.NoError(t, gwv1alpha2.AddToScheme(s)) + require.NoError(t, rbac.AddToScheme(s)) + require.NoError(t, corev1.AddToScheme(s)) + require.NoError(t, appsv1.AddToScheme(s)) + + objs := tc.k8sObjects + if tc.gateway != nil { + objs = append(objs, tc.gateway) + } + + fakeClient := registerFieldIndexersForTest(fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(objs...)).Build() + + r := &GatewayController{ + cache: stubConsulCache(t), + Client: fakeClient, + Log: logrtest.New(t), + } + + _, err := r.Reconcile(context.Background(), req) + + require.Equal(t, tc.expectedError, err) + deployment := appsv1.Deployment{} + r.Client.Get(context.TODO(), types.NamespacedName{ + Namespace: TestNamespace, + Name: TestGatewayName, + }, &deployment) + require.NotEmpty(t, deployment) + require.Equal(t, TestGatewayName, deployment.ObjectMeta.Name) + }) + } +} + +func TestObjectsToRequests(t *testing.T) { + t.Parallel() + + name := "test-gatewayclass" + + namespacedName := types.NamespacedName{ + Namespace: TestNamespace, + Name: name, + } + + cases := map[string]struct { + objects []metav1.Object + expectedResult []reconcile.Request + }{ + "successful conversion of gateway to request": { + objects: []metav1.Object{ + &gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: TestNamespace, + Name: name, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: GatewayClassControllerName, + }, + }, + }, + expectedResult: []reconcile.Request{ + { + NamespacedName: namespacedName, + }, + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + requests := objectsToRequests(tc.objects) + + require.Equal(t, tc.expectedResult, requests) + }) + } +} + +func TestGatewayController_getAllRefsForGateway(t *testing.T) { + t.Parallel() + s := runtime.NewScheme() + require.NoError(t, gwv1beta1.Install(s)) + require.NoError(t, gwv1alpha2.Install(s)) + require.NoError(t, corev1.AddToScheme(s)) + require.NoError(t, v1alpha1.AddToScheme(s)) + + secret := &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "secret squirrel", + }, + } + gw := &gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-gw", + Annotations: map[string]string{}, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: "", + Listeners: []gwv1beta1.Listener{ + { + Name: "l1", + TLS: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{ + { + Kind: pointerTo(gwv1beta1.Kind("Secret")), + Name: "secret squirrel", + }, + }, + Options: map[gwv1beta1.AnnotationKey]gwv1beta1.AnnotationValue{}, + }, + AllowedRoutes: &gwv1beta1.AllowedRoutes{}, + }, + }, + Addresses: []gwv1beta1.GatewayAddress{}, + }, + Status: gwv1beta1.GatewayStatus{}, + } + gwc := &gwv1beta1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-gw-class", + }, + Spec: gwv1beta1.GatewayClassSpec{ + ControllerName: "", + ParametersRef: &gwv1beta1.ParametersReference{ + Group: Group, + Kind: v1alpha1.GatewayClassConfigKind, + Name: "the config", + }, + Description: new(string), + }, + } + gwcConfig := &v1alpha1.GatewayClassConfig{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "the config", + }, + Spec: v1alpha1.GatewayClassConfigSpec{ + ServiceType: pointerTo(corev1.ServiceType("serviceType")), + NodeSelector: map[string]string{ + "selector": "of node", + }, + Tolerations: []v1.Toleration{ + { + Key: "key", + Operator: "op", + Value: "120", + Effect: "to the moon", + TolerationSeconds: new(int64), + }, + }, + CopyAnnotations: v1alpha1.CopyAnnotationsSpec{ + Service: []string{"service"}, + }, + }, + } + + httpRouteOnGateway := &gwv1beta1.HTTPRoute{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "route 1", + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + { + Name: gwv1beta1.ObjectName(gw.Name), + }, + }, + }, + }, + } + + httpRouteNotOnGateway := &gwv1beta1.HTTPRoute{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "route not on gateway", + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + { + Name: gwv1beta1.ObjectName("not on the gateway"), + }, + }, + }, + }, + Status: gwv1beta1.HTTPRouteStatus{}, + } + + tcpRoute := &gwv1alpha2.TCPRoute{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "tcp route", + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + { + Name: gwv1beta1.ObjectName(gw.Name), + }, + }, + }, + }, + } + + objs := []runtime.Object{gw, gwc, gwcConfig, httpRouteOnGateway, httpRouteNotOnGateway, tcpRoute, secret} + + fakeClient := registerFieldIndexersForTest(fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(objs...)).Build() + controller := GatewayController{ + Client: fakeClient, + } + + ctx := context.Background() + + actual, err := controller.getAllRefsForGateway(ctx, gw) + + require.NoError(t, err) + expectedEntries := []metav1.Object{httpRouteOnGateway, tcpRoute, secret} + + require.ElementsMatch(t, expectedEntries, actual) +} + +func getBasicGatewayClassAndConfig() (gwv1beta1.GatewayClass, v1alpha1.GatewayClassConfig) { + serviceType := corev1.ServiceType("NodePort") + basicGatewayClass := gwv1beta1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "", + Name: TestGatewayClassName, + Finalizers: []string{ + gatewayClassFinalizer, + }, + }, + Spec: gwv1beta1.GatewayClassSpec{ + ControllerName: GatewayClassControllerName, + ParametersRef: &gwv1beta1.ParametersReference{ + Group: "consul.hashicorp.com", + Kind: "GatewayClassConfig", + Name: TestGatewayClassConfigName, + Namespace: nil, + }, + }, + } + + basicGatewayClassConfig := v1alpha1.GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: TestGatewayClassConfigName, + }, + Spec: v1alpha1.GatewayClassConfigSpec{ + ServiceType: &serviceType, + CopyAnnotations: v1alpha1.CopyAnnotationsSpec{ + Service: []string{"serviceType"}, + }, + }, + } + + return basicGatewayClass, basicGatewayClassConfig +} diff --git a/control-plane/api-gateway/controllers/gatewayclass_controller.go b/control-plane/api-gateway/controllers/gatewayclass_controller.go new file mode 100644 index 0000000000..4180157616 --- /dev/null +++ b/control-plane/api-gateway/controllers/gatewayclass_controller.go @@ -0,0 +1,259 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package controllers + +import ( + "context" + "fmt" + + "github.com/go-logr/logr" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +const ( + GatewayClassControllerName = "hashicorp.com/consul-api-gateway-controller" + + gatewayClassFinalizer = "gateway-exists-finalizer.consul.hashicorp.com" + + // GatewayClass status fields. + accepted = "Accepted" + invalidParameters = "InvalidParameters" +) + +// GatewayClassController reconciles a GatewayClass object. +// The GatewayClass is responsible for defining the behavior of API gateways +// which reference the given class. +type GatewayClassController struct { + ControllerName string + Log logr.Logger + + client.Client +} + +// Reconcile handles the reconciliation loop for GatewayClass objects. +func (r *GatewayClassController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := r.Log.WithValues("gatewayClass", req.NamespacedName.Name) + log.Info("Reconciling GatewayClass") + + gc := &gwv1beta1.GatewayClass{} + + err := r.Client.Get(ctx, req.NamespacedName, gc) + if err != nil { + if k8serrors.IsNotFound(err) { + return ctrl.Result{}, nil + } + log.Error(err, "unable to get GatewayClass") + return ctrl.Result{}, err + } + + if string(gc.Spec.ControllerName) != r.ControllerName { + // This GatewayClass is not for this controller. + _, err := RemoveFinalizer(ctx, r.Client, gc, gatewayClassFinalizer) + if err != nil { + log.Error(err, "unable to remove finalizer") + } + + return ctrl.Result{}, err + } + + if !gc.ObjectMeta.DeletionTimestamp.IsZero() { + // We have a deletion request. Ensure we are not in use. + used, err := r.isGatewayClassInUse(ctx, gc) + if err != nil { + log.Error(err, "unable to check if GatewayClass is in use") + return ctrl.Result{}, err + } + if used { + log.Info("GatewayClass is in use, cannot delete") + return ctrl.Result{}, nil + } + // Remove our finalizer. + if _, err := RemoveFinalizer(ctx, r.Client, gc, gatewayClassFinalizer); err != nil { + log.Error(err, "unable to remove finalizer") + return ctrl.Result{}, err + } + return ctrl.Result{}, nil + } + + // We are creating or updating the GatewayClass. + didUpdate, err := EnsureFinalizer(ctx, r.Client, gc, gatewayClassFinalizer) + if err != nil { + log.Error(err, "unable to add finalizer") + return ctrl.Result{}, err + } + if didUpdate { + // We updated the GatewayClass, requeue to avoid another update. + return ctrl.Result{}, nil + } + + didUpdate, err = r.validateParametersRef(ctx, gc, log) + if didUpdate { + if err := r.Client.Status().Update(ctx, gc); err != nil { + log.Error(err, "unable to update GatewayClass") + return ctrl.Result{}, err + } + return ctrl.Result{}, nil + } + if err != nil { + log.Error(err, "unable to validate ParametersRef") + } + + return ctrl.Result{}, err +} + +// SetupWithManager registers the controller with the given manager. +func (r *GatewayClassController) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&gwv1beta1.GatewayClass{}). + // Watch for changes to GatewayClassConfig objects. + Watches(source.NewKindWithCache(&v1alpha1.GatewayClassConfig{}, mgr.GetCache()), r.gatewayClassConfigFieldIndexEventHandler(ctx)). + // Watch for changes to Gateway objects that reference this GatewayClass. + Watches(source.NewKindWithCache(&gwv1beta1.Gateway{}, mgr.GetCache()), r.gatewayFieldIndexEventHandler(ctx)). + Complete(r) +} + +// isGatewayClassInUse returns true if the given GatewayClass is referenced by any Gateway objects. +func (r *GatewayClassController) isGatewayClassInUse(ctx context.Context, gc *gwv1beta1.GatewayClass) (bool, error) { + list := &gwv1beta1.GatewayList{} + if err := r.Client.List(ctx, list, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(Gateway_GatewayClassIndex, gc.Name), + }); err != nil { + return false, err + } + + return len(list.Items) != 0, nil +} + +// validateParametersRef validates the ParametersRef field of the given GatewayClass +// if it is set, ensuring that the referenced object is a GatewayClassConfig that exists. +func (r *GatewayClassController) validateParametersRef(ctx context.Context, gc *gwv1beta1.GatewayClass, log logr.Logger) (didUpdate bool, err error) { + parametersRef := gc.Spec.ParametersRef + if parametersRef != nil { + if parametersRef.Kind != v1alpha1.GatewayClassConfigKind { + didUpdate = r.setCondition(gc, metav1.Condition{ + Type: accepted, + Status: metav1.ConditionFalse, + Reason: invalidParameters, + Message: fmt.Sprintf("Incorrect type for parametersRef. Expected GatewayClassConfig, got %q.", parametersRef.Kind), + }) + return didUpdate, nil + } + + err = r.Client.Get(ctx, types.NamespacedName{Name: parametersRef.Name}, &v1alpha1.GatewayClassConfig{}) + if k8serrors.IsNotFound(err) { + didUpdate := r.setCondition(gc, metav1.Condition{ + Type: accepted, + Status: metav1.ConditionFalse, + Reason: invalidParameters, + Message: fmt.Sprintf("GatewayClassConfig not found %q.", parametersRef.Name), + }) + return didUpdate, nil + } + if err != nil { + log.Error(err, "unable to fetch GatewayClassConfig") + return false, err + } + } + + didUpdate = r.setCondition(gc, metav1.Condition{ + Type: accepted, + Status: metav1.ConditionTrue, + Reason: accepted, + Message: "GatewayClass Accepted", + }) + + return didUpdate, err +} + +// setCondition sets the given condition on the given GatewayClass. +func (r *GatewayClassController) setCondition(gc *gwv1beta1.GatewayClass, condition metav1.Condition) (didUpdate bool) { + condition.LastTransitionTime = metav1.Now() + condition.ObservedGeneration = gc.GetGeneration() + + // Set the condition if it already exists. + for i, c := range gc.Status.Conditions { + if c.Type == condition.Type { + // The condition already exists and is up to date. + if equalConditions(condition, c) { + return false + } + + gc.Status.Conditions[i] = condition + + return true + } + } + + // Append the condition if it does not exist. + gc.Status.Conditions = append(gc.Status.Conditions, condition) + + return true +} + +// gatewayClassConfigFieldIndexEventHandler returns an EventHandler that will enqueue +// reconcile.Requests for GatewayClass objects that reference the GatewayClassConfig +// object that triggered the event. +func (r *GatewayClassController) gatewayClassConfigFieldIndexEventHandler(ctx context.Context) handler.EventHandler { + return handler.EnqueueRequestsFromMapFunc(func(o client.Object) []reconcile.Request { + requests := []reconcile.Request{} + + // Get all GatewayClass objects from the field index of the GatewayClassConfig which triggered the event. + var gcList gwv1beta1.GatewayClassList + err := r.Client.List(ctx, &gcList, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(GatewayClass_GatewayClassConfigIndex, o.GetName()), + }) + if err != nil { + r.Log.Error(err, "unable to list gateway classes") + } + + // Create a reconcile request for each GatewayClass. + for _, gc := range gcList.Items { + requests = append(requests, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: gc.Name, + }, + }) + } + + return requests + }) +} + +// gatewayFieldIndexEventHandler returns an EventHandler that will enqueue +// reconcile.Requests for GatewayClass objects from Gateways which reference the GatewayClass +// when those Gateways are updated. +func (r *GatewayClassController) gatewayFieldIndexEventHandler(ctx context.Context) handler.EventHandler { + return handler.EnqueueRequestsFromMapFunc(func(o client.Object) []reconcile.Request { + // Get the Gateway object that triggered the event. + g := o.(*gwv1beta1.Gateway) + + // Return a slice with the single reconcile.Request for the GatewayClass + // that the Gateway references. + return []reconcile.Request{ + { + NamespacedName: types.NamespacedName{ + Name: string(g.Spec.GatewayClassName), + }, + }, + } + }) +} + +func equalConditions(a, b metav1.Condition) bool { + return a.Type == b.Type && + a.Status == b.Status && + a.Reason == b.Reason && + a.Message == b.Message && + a.ObservedGeneration == b.ObservedGeneration +} diff --git a/control-plane/api-gateway/controllers/gatewayclass_controller_test.go b/control-plane/api-gateway/controllers/gatewayclass_controller_test.go new file mode 100644 index 0000000000..ac5be25205 --- /dev/null +++ b/control-plane/api-gateway/controllers/gatewayclass_controller_test.go @@ -0,0 +1,275 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package controllers + +import ( + "context" + "fmt" + "testing" + + logrtest "github.com/go-logr/logr/testr" + "github.com/stretchr/testify/require" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + "sigs.k8s.io/gateway-api/apis/v1beta1" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" +) + +func TestGatewayClassReconciler(t *testing.T) { + t.Parallel() + + namespace := "" // GatewayClass is cluster-scoped. + name := "test-gatewayclass" + + req := ctrl.Request{ + NamespacedName: types.NamespacedName{ + Namespace: namespace, + Name: name, + }, + } + + deletionTimestamp := metav1.Now() + + cases := map[string]struct { + gatewayClass *gwv1beta1.GatewayClass + k8sObjects []runtime.Object + expectedResult ctrl.Result + expectedError error + expectedFinalizers []string + expectedIsDeleted bool + expectedConditions []metav1.Condition + }{ + "successful reconcile with no change": { + gatewayClass: &gwv1beta1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + Finalizers: []string{gatewayClassFinalizer}, + }, + Spec: gwv1beta1.GatewayClassSpec{ + ControllerName: GatewayClassControllerName, + }, + }, + expectedResult: ctrl.Result{}, + expectedError: nil, + expectedFinalizers: []string{gatewayClassFinalizer}, + expectedIsDeleted: false, + expectedConditions: []metav1.Condition{ + { + Type: accepted, + Status: metav1.ConditionTrue, + Reason: accepted, + Message: "GatewayClass Accepted", + }, + }, + }, + "successful reconcile that adds finalizer": { + gatewayClass: &gwv1beta1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + Finalizers: []string{}, + }, + Spec: gwv1beta1.GatewayClassSpec{ + ControllerName: GatewayClassControllerName, + }, + }, + expectedResult: ctrl.Result{}, + expectedError: nil, + expectedFinalizers: []string{gatewayClassFinalizer}, + expectedConditions: []metav1.Condition{}, + }, + "attempt to reconcile a GatewayClass with a different controller name": { + gatewayClass: &gwv1beta1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + Finalizers: []string{}, + }, + Spec: gwv1beta1.GatewayClassSpec{ + ControllerName: "foo", + }, + }, + expectedResult: ctrl.Result{}, + expectedError: nil, + expectedConditions: []metav1.Condition{}, + }, + "attempt to reconcile a GatewayClass with a different controller name removing our finalizer": { + gatewayClass: &gwv1beta1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + Finalizers: []string{gatewayClassFinalizer}, + }, + Spec: gwv1beta1.GatewayClassSpec{ + ControllerName: "foo", + }, + }, + expectedResult: ctrl.Result{}, + expectedError: nil, + expectedConditions: []metav1.Condition{}, + }, + "attempt to reconcile a GatewayClass with an incorrect parametersRef type": { + gatewayClass: &gwv1beta1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + Finalizers: []string{gatewayClassFinalizer}, + }, + Spec: gwv1beta1.GatewayClassSpec{ + ControllerName: GatewayClassControllerName, + ParametersRef: &gwv1beta1.ParametersReference{ + Kind: "some-nonsense", + }, + }, + }, + expectedResult: ctrl.Result{}, + expectedError: nil, + expectedFinalizers: []string{gatewayClassFinalizer}, + expectedConditions: []metav1.Condition{ + { + Type: accepted, + Status: metav1.ConditionFalse, + Reason: invalidParameters, + Message: fmt.Sprintf("Incorrect type for parametersRef. Expected GatewayClassConfig, got %q.", "some-nonsense"), + }, + }, + }, + "attempt to reconcile a GatewayClass with a GatewayClassConfig that does not exist": { + gatewayClass: &gwv1beta1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + Finalizers: []string{gatewayClassFinalizer}, + }, + Spec: gwv1beta1.GatewayClassSpec{ + ControllerName: GatewayClassControllerName, + ParametersRef: &gwv1beta1.ParametersReference{ + Kind: v1alpha1.GatewayClassConfigKind, + Name: "does-not-exist", + }, + }, + }, + expectedResult: ctrl.Result{}, + expectedError: nil, + expectedFinalizers: []string{gatewayClassFinalizer}, + expectedConditions: []metav1.Condition{ + { + Type: accepted, + Status: metav1.ConditionFalse, + Reason: invalidParameters, + Message: fmt.Sprintf("GatewayClassConfig not found %q.", "does-not-exist"), + }, + }, + }, + "attempt to reconcile a non-existent object": { + k8sObjects: []runtime.Object{}, + expectedResult: ctrl.Result{}, + expectedError: nil, + expectedConditions: []metav1.Condition{}, + }, + "attempt to remove a GatewayClass that is not in use": { + gatewayClass: &gwv1beta1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + Finalizers: []string{ + gatewayClassFinalizer, + }, + DeletionTimestamp: &deletionTimestamp, + }, + Spec: gwv1beta1.GatewayClassSpec{ + ControllerName: GatewayClassControllerName, + }, + }, + expectedResult: ctrl.Result{}, + expectedError: nil, + expectedFinalizers: []string{}, + expectedIsDeleted: true, + }, + "attempt to remove a GatewayClass that is in use": { + gatewayClass: &gwv1beta1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + Finalizers: []string{ + gatewayClassFinalizer, + }, + DeletionTimestamp: &deletionTimestamp, + }, + Spec: gwv1beta1.GatewayClassSpec{ + ControllerName: GatewayClassControllerName, + }, + }, + k8sObjects: []runtime.Object{ + &gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "test-gateway", + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: v1beta1.ObjectName(name), + }, + }, + }, + expectedResult: ctrl.Result{}, + expectedError: nil, + expectedFinalizers: []string{gatewayClassFinalizer}, + }, + // */ + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + s := runtime.NewScheme() + require.NoError(t, clientgoscheme.AddToScheme(s)) + require.NoError(t, gwv1alpha2.Install(s)) + require.NoError(t, gwv1beta1.Install(s)) + require.NoError(t, v1alpha1.AddToScheme(s)) + + objs := tc.k8sObjects + if tc.gatewayClass != nil { + objs = append(objs, tc.gatewayClass) + } + + fakeClient := registerFieldIndexersForTest(fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(objs...)).Build() + + r := &GatewayClassController{ + Client: fakeClient, + ControllerName: GatewayClassControllerName, + Log: logrtest.New(t), + } + result, err := r.Reconcile(context.Background(), req) + + require.Equal(t, tc.expectedResult, result) + require.Equal(t, tc.expectedError, err) + + // Check the GatewayClass after reconciliation. + gc := &gwv1beta1.GatewayClass{} + err = r.Client.Get(context.Background(), req.NamespacedName, gc) + + if tc.gatewayClass == nil || tc.expectedIsDeleted { + // There shouldn't be a GatewayClass to check. + require.True(t, apierrors.IsNotFound(err)) + return + } + + require.NoError(t, client.IgnoreNotFound(err)) + require.Equal(t, tc.expectedFinalizers, gc.ObjectMeta.Finalizers) + require.Equal(t, len(tc.expectedConditions), len(gc.Status.Conditions), "expected %+v, got %+v", tc.expectedConditions, gc.Status.Conditions) + for i, expectedCondition := range tc.expectedConditions { + require.True(t, equalConditions(expectedCondition, gc.Status.Conditions[i]), "expected %+v, got %+v", expectedCondition, gc.Status.Conditions[i]) + } + }) + } +} diff --git a/control-plane/api-gateway/controllers/index.go b/control-plane/api-gateway/controllers/index.go new file mode 100644 index 0000000000..3ef0e65c81 --- /dev/null +++ b/control-plane/api-gateway/controllers/index.go @@ -0,0 +1,262 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package controllers + +import ( + "context" + + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" +) + +const ( + // Naming convention: TARGET_REFERENCE. + GatewayClass_GatewayClassConfigIndex = "__gatewayclass_referencing_gatewayclassconfig" + GatewayClass_ControllerNameIndex = "__gatewayclass_controller_name" + Gateway_GatewayClassIndex = "__gateway_referencing_gatewayclass" + HTTPRoute_GatewayIndex = "__httproute_referencing_gateway" + HTTPRoute_ServiceIndex = "__httproute_referencing_service" + HTTPRoute_MeshServiceIndex = "__httproute_referencing_mesh_service" + TCPRoute_GatewayIndex = "__tcproute_referencing_gateway" + TCPRoute_ServiceIndex = "__tcproute_referencing_service" + TCPRoute_MeshServiceIndex = "__tcproute_referencing_mesh_service" + MeshService_PeerIndex = "__meshservice_referencing_peer" + Secret_GatewayIndex = "__secret_referencing_gateway" +) + +// RegisterFieldIndexes registers all of the field indexes for the API gateway controllers. +// These indexes are similar to indexes used in databases to speed up queries. +// They allow us to quickly find objects based on a field value. +func RegisterFieldIndexes(ctx context.Context, mgr ctrl.Manager) error { + for _, index := range indexes { + if err := mgr.GetFieldIndexer().IndexField(ctx, index.target, index.name, index.indexerFunc); err != nil { + return err + } + } + return nil +} + +type index struct { + name string + target client.Object + indexerFunc client.IndexerFunc +} + +var indexes = []index{ + { + name: GatewayClass_GatewayClassConfigIndex, + target: &gwv1beta1.GatewayClass{}, + indexerFunc: gatewayClassConfigForGatewayClass, + }, + { + name: GatewayClass_ControllerNameIndex, + target: &gwv1beta1.GatewayClass{}, + indexerFunc: gatewayClassControllerName, + }, + { + name: Gateway_GatewayClassIndex, + target: &gwv1beta1.Gateway{}, + indexerFunc: gatewayClassForGateway, + }, + { + name: Secret_GatewayIndex, + target: &gwv1beta1.Gateway{}, + indexerFunc: gatewayForSecret, + }, + { + name: HTTPRoute_GatewayIndex, + target: &gwv1beta1.HTTPRoute{}, + indexerFunc: gatewaysForHTTPRoute, + }, + { + name: HTTPRoute_ServiceIndex, + target: &gwv1beta1.HTTPRoute{}, + indexerFunc: servicesForHTTPRoute, + }, + { + name: HTTPRoute_MeshServiceIndex, + target: &gwv1beta1.HTTPRoute{}, + indexerFunc: meshServicesForHTTPRoute, + }, + { + name: TCPRoute_GatewayIndex, + target: &gwv1alpha2.TCPRoute{}, + indexerFunc: gatewaysForTCPRoute, + }, + { + name: TCPRoute_ServiceIndex, + target: &gwv1alpha2.TCPRoute{}, + indexerFunc: servicesForTCPRoute, + }, + { + name: TCPRoute_MeshServiceIndex, + target: &gwv1alpha2.TCPRoute{}, + indexerFunc: meshServicesForTCPRoute, + }, + { + name: MeshService_PeerIndex, + target: &v1alpha1.MeshService{}, + indexerFunc: peersForMeshService, + }, +} + +// gatewayClassConfigForGatewayClass creates an index of every GatewayClassConfig referenced by a GatewayClass. +func gatewayClassConfigForGatewayClass(o client.Object) []string { + gc := o.(*gwv1beta1.GatewayClass) + + pr := gc.Spec.ParametersRef + if pr != nil && pr.Kind == v1alpha1.GatewayClassConfigKind { + return []string{pr.Name} + } + + return []string{} +} + +func gatewayClassControllerName(o client.Object) []string { + gc := o.(*gwv1beta1.GatewayClass) + + if gc.Spec.ControllerName != "" { + return []string{string(gc.Spec.ControllerName)} + } + + return []string{} +} + +// gatewayClassForGateway creates an index of every GatewayClass referenced by a Gateway. +func gatewayClassForGateway(o client.Object) []string { + g := o.(*gwv1beta1.Gateway) + return []string{string(g.Spec.GatewayClassName)} +} + +func peersForMeshService(o client.Object) []string { + m := o.(*v1alpha1.MeshService) + if m.Spec.Peer != nil { + return []string{string(*m.Spec.Peer)} + } + return nil +} + +func gatewayForSecret(o client.Object) []string { + gateway := o.(*gwv1beta1.Gateway) + var secretReferences []string + for _, listener := range gateway.Spec.Listeners { + if listener.TLS == nil || *listener.TLS.Mode != gwv1beta1.TLSModeTerminate { + continue + } + for _, cert := range listener.TLS.CertificateRefs { + if nilOrEqual(cert.Group, "") && nilOrEqual(cert.Kind, "Secret") { + // If an explicit Secret namespace is not provided, use the Gateway namespace to lookup the provided Secret Name. + secretReferences = append(secretReferences, indexedNamespacedNameWithDefault(cert.Name, cert.Namespace, gateway.Namespace).String()) + } + } + } + return secretReferences +} + +func gatewaysForHTTPRoute(o client.Object) []string { + route := o.(*gwv1beta1.HTTPRoute) + return gatewaysForRoute(route.Namespace, route.Spec.ParentRefs) +} + +func gatewaysForTCPRoute(o client.Object) []string { + route := o.(*gwv1alpha2.TCPRoute) + return gatewaysForRoute(route.Namespace, route.Spec.ParentRefs) +} + +func servicesForHTTPRoute(o client.Object) []string { + route := o.(*gwv1beta1.HTTPRoute) + refs := []string{} + for _, rule := range route.Spec.Rules { + BACKEND_LOOP: + for _, ref := range rule.BackendRefs { + if nilOrEqual(ref.Group, "") && nilOrEqual(ref.Kind, "Service") { + backendRef := indexedNamespacedNameWithDefault(ref.Name, ref.Namespace, route.Namespace).String() + for _, member := range refs { + if member == backendRef { + continue BACKEND_LOOP + } + } + refs = append(refs, backendRef) + } + } + } + return refs +} + +func meshServicesForHTTPRoute(o client.Object) []string { + route := o.(*gwv1beta1.HTTPRoute) + refs := []string{} + for _, rule := range route.Spec.Rules { + BACKEND_LOOP: + for _, ref := range rule.BackendRefs { + if ref.Group != nil && string(*ref.Group) == v1alpha1.ConsulHashicorpGroup && + ref.Kind != nil && string(*ref.Kind) == v1alpha1.MeshServiceKind { + backendRef := indexedNamespacedNameWithDefault(ref.Name, ref.Namespace, route.Namespace).String() + for _, member := range refs { + if member == backendRef { + continue BACKEND_LOOP + } + } + refs = append(refs, backendRef) + } + } + } + return refs +} + +func servicesForTCPRoute(o client.Object) []string { + route := o.(*gwv1alpha2.TCPRoute) + refs := []string{} + for _, rule := range route.Spec.Rules { + BACKEND_LOOP: + for _, ref := range rule.BackendRefs { + if nilOrEqual(ref.Group, "") && nilOrEqual(ref.Kind, "Service") { + backendRef := indexedNamespacedNameWithDefault(ref.Name, ref.Namespace, route.Namespace).String() + for _, member := range refs { + if member == backendRef { + continue BACKEND_LOOP + } + } + refs = append(refs, backendRef) + } + } + } + return refs +} + +func meshServicesForTCPRoute(o client.Object) []string { + route := o.(*gwv1alpha2.TCPRoute) + refs := []string{} + for _, rule := range route.Spec.Rules { + BACKEND_LOOP: + for _, ref := range rule.BackendRefs { + if ref.Group != nil && string(*ref.Group) == v1alpha1.ConsulHashicorpGroup && + ref.Kind != nil && string(*ref.Kind) == v1alpha1.MeshServiceKind { + backendRef := indexedNamespacedNameWithDefault(ref.Name, ref.Namespace, route.Namespace).String() + for _, member := range refs { + if member == backendRef { + continue BACKEND_LOOP + } + } + refs = append(refs, backendRef) + } + } + } + return refs +} + +func gatewaysForRoute(namespace string, refs []gwv1beta1.ParentReference) []string { + var references []string + for _, parent := range refs { + if nilOrEqual(parent.Group, gwv1beta1.GroupVersion.Group) && nilOrEqual(parent.Kind, "Gateway") { + // If an explicit Gateway namespace is not provided, use the Route namespace to lookup the provided Gateway Namespace. + references = append(references, indexedNamespacedNameWithDefault(parent.Name, parent.Namespace, namespace).String()) + } + } + return references +} diff --git a/control-plane/api-gateway/controllers/index_test.go b/control-plane/api-gateway/controllers/index_test.go new file mode 100644 index 0000000000..5655a3c3da --- /dev/null +++ b/control-plane/api-gateway/controllers/index_test.go @@ -0,0 +1,13 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package controllers + +import "sigs.k8s.io/controller-runtime/pkg/client/fake" + +func registerFieldIndexersForTest(clientBuilder *fake.ClientBuilder) *fake.ClientBuilder { + for _, index := range indexes { + clientBuilder = clientBuilder.WithIndex(index.target, index.name, index.indexerFunc) + } + return clientBuilder +} diff --git a/control-plane/api-gateway/controllers/reference_validator.go b/control-plane/api-gateway/controllers/reference_validator.go new file mode 100644 index 0000000000..91b4c0ea51 --- /dev/null +++ b/control-plane/api-gateway/controllers/reference_validator.go @@ -0,0 +1,177 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package controllers + +import ( + "context" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +type ReferenceValidator struct { + client.Client +} + +func NewReferenceValidator(client client.Client) *ReferenceValidator { + return &ReferenceValidator{ + client, + } +} + +func (rv *ReferenceValidator) GatewayCanReferenceSecret(ctx context.Context, gateway gwv1beta1.Gateway, secretRef gwv1beta1.SecretObjectReference) (bool, error) { + fromNS := gateway.GetNamespace() + fromGK := metav1.GroupKind{ + Group: gateway.GroupVersionKind().Group, + Kind: gateway.GroupVersionKind().Kind, + } + + // Kind should default to Secret if not set + // https://github.com/kubernetes-sigs/gateway-api/blob/v0.6.2/apis/v1beta1/object_reference_types.go#LL59C21-L59C21 + toNS, toGK := createValuesFromRef(secretRef.Namespace, secretRef.Group, secretRef.Kind, "Secret") + + return referenceAllowed(ctx, fromGK, fromNS, toGK, toNS, string(secretRef.Name), rv.Client) +} + +func (rv *ReferenceValidator) HTTPRouteCanReferenceGateway(ctx context.Context, httproute gwv1beta1.HTTPRoute, parentRef gwv1beta1.ParentReference) (bool, error) { + fromNS := httproute.GetNamespace() + fromGK := metav1.GroupKind{ + Group: httproute.GroupVersionKind().Group, + Kind: httproute.GroupVersionKind().Kind, + } + + // Kind should default to Gateway if not set + // https://github.com/kubernetes-sigs/gateway-api/blob/v0.6.2/apis/v1beta1/shared_types.go#L48 + toNS, toGK := createValuesFromRef(parentRef.Namespace, parentRef.Group, parentRef.Kind, "Gateway") + + return referenceAllowed(ctx, fromGK, fromNS, toGK, toNS, string(parentRef.Name), rv.Client) +} + +func (rv *ReferenceValidator) HTTPRouteCanReferenceBackend(ctx context.Context, httproute gwv1beta1.HTTPRoute, backendRef gwv1beta1.BackendRef) (bool, error) { + fromNS := httproute.GetNamespace() + fromGK := metav1.GroupKind{ + Group: httproute.GroupVersionKind().Group, + Kind: httproute.GroupVersionKind().Kind, + } + + // Kind should default to Service if not set + // https://github.com/kubernetes-sigs/gateway-api/blob/v0.6.2/apis/v1beta1/object_reference_types.go#L106 + toNS, toGK := createValuesFromRef(backendRef.Namespace, backendRef.Group, backendRef.Kind, "Service") + + return referenceAllowed(ctx, fromGK, fromNS, toGK, toNS, string(backendRef.Name), rv.Client) + +} + +func (rv *ReferenceValidator) TCPRouteCanReferenceGateway(ctx context.Context, tcpRoute gwv1alpha2.TCPRoute, parentRef gwv1beta1.ParentReference) (bool, error) { + fromNS := tcpRoute.GetNamespace() + fromGK := metav1.GroupKind{ + Group: tcpRoute.GroupVersionKind().Group, + Kind: tcpRoute.GroupVersionKind().Kind, + } + + // Kind should default to Gateway if not set + // https://github.com/kubernetes-sigs/gateway-api/blob/v0.6.2/apis/v1beta1/shared_types.go#L48 + toNS, toGK := createValuesFromRef(parentRef.Namespace, parentRef.Group, parentRef.Kind, "Gateway") + + return referenceAllowed(ctx, fromGK, fromNS, toGK, toNS, string(parentRef.Name), rv.Client) +} + +func (rv *ReferenceValidator) TCPRouteCanReferenceBackend(ctx context.Context, tcpRoute gwv1alpha2.TCPRoute, backendRef gwv1beta1.BackendRef) (bool, error) { + fromNS := tcpRoute.GetNamespace() + fromGK := metav1.GroupKind{ + Group: tcpRoute.GroupVersionKind().Group, + Kind: tcpRoute.GroupVersionKind().Kind, + } + + // Kind should default to Service if not set + // https://github.com/kubernetes-sigs/gateway-api/blob/v0.6.2/apis/v1beta1/object_reference_types.go#L106 + toNS, toGK := createValuesFromRef(backendRef.Namespace, backendRef.Group, backendRef.Kind, "Service") + + return referenceAllowed(ctx, fromGK, fromNS, toGK, toNS, string(backendRef.Name), rv.Client) + +} + +func createValuesFromRef(ns *gwv1beta1.Namespace, group *gwv1beta1.Group, kind *gwv1beta1.Kind, defaultKind string) (string, metav1.GroupKind) { + toNS := "" + if ns != nil { + toNS = string(*ns) + } + + gk := metav1.GroupKind{ + Kind: defaultKind, + } + if group != nil { + gk.Group = string(*group) + } + if kind != nil { + gk.Kind = string(*kind) + } + + return toNS, gk +} + +// referenceAllowed checks to see if a reference between resources is allowed. +// In particular, references from one namespace to a resource in a different namespace +// require an applicable ReferenceGrant be found in the namespace containing the resource +// being referred to. +// +// For example, a Gateway in namespace "foo" may only reference a Secret in namespace "bar" +// if a ReferenceGrant in namespace "bar" allows references from namespace "foo". +func referenceAllowed(ctx context.Context, fromGK metav1.GroupKind, fromNamespace string, toGK metav1.GroupKind, toNamespace, toName string, c client.Client) (bool, error) { + // Reference does not cross namespaces + if toNamespace == "" || toNamespace == fromNamespace { + return true, nil + } + + // Fetch all ReferenceGrants in the referenced namespace + refGrants, err := getReferenceGrantsInNamespace(ctx, toNamespace, c) + if err != nil || len(refGrants) == 0 { + return false, err + } + + for _, refGrant := range refGrants { + // Check for a From that applies + fromMatch := false + for _, from := range refGrant.Spec.From { + if fromGK.Group == string(from.Group) && fromGK.Kind == string(from.Kind) && fromNamespace == string(from.Namespace) { + fromMatch = true + break + } + } + + if !fromMatch { + continue + } + + // Check for a To that applies + for _, to := range refGrant.Spec.To { + if toGK.Group == string(to.Group) && toGK.Kind == string(to.Kind) { + if to.Name == nil || *to.Name == "" { + // No name specified is treated as a wildcard within the namespace + return true, nil + } + + if gwv1beta1.ObjectName(toName) == *to.Name { + // The ReferenceGrant specifically targets this object + return true, nil + } + } + } + } + + // No ReferenceGrant was found which allows this cross-namespace reference + return false, nil +} + +// This function will get all reference grants in the given namespace. +func getReferenceGrantsInNamespace(ctx context.Context, namespace string, c client.Client) ([]gwv1beta1.ReferenceGrant, error) { + refGrantList := &gwv1beta1.ReferenceGrantList{} + if err := c.List(ctx, refGrantList, client.InNamespace(namespace)); err != nil { + return nil, err + } + refGrants := refGrantList.Items + + return refGrants, nil +} diff --git a/control-plane/api-gateway/controllers/reference_validator_test.go b/control-plane/api-gateway/controllers/reference_validator_test.go new file mode 100644 index 0000000000..e507568ae1 --- /dev/null +++ b/control-plane/api-gateway/controllers/reference_validator_test.go @@ -0,0 +1,649 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package controllers + +import ( + "context" + "k8s.io/apimachinery/pkg/runtime" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + "testing" + + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +const ( + ToNamespace = "toNamespace" + FromNamespace = "fromNamespace" + InvalidNamespace = "invalidNamespace" + Group = "gateway.networking.k8s.io" + V1Beta1 = "/v1beta1" + V1Alpha2 = "/v1alpha2" + HTTPRouteKind = "HTTPRoute" + TCPRouteKind = "TCPRoute" + GatewayKind = "Gateway" + BackendRefKind = "Service" + SecretKind = "Secret" +) + +func TestGatewayCanReferenceSecret(t *testing.T) { + t.Parallel() + + objName := gwv1beta1.ObjectName("mysecret") + + basicValidReferenceGrant := &gwv1beta1.ReferenceGrant{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Namespace: ToNamespace, + }, + Spec: gwv1beta1.ReferenceGrantSpec{ + From: []gwv1beta1.ReferenceGrantFrom{ + { + Group: Group, + Kind: GatewayKind, + Namespace: FromNamespace, + }, + }, + To: []gwv1beta1.ReferenceGrantTo{ + { + Group: Group, + Kind: SecretKind, + Name: &objName, + }, + }, + }, + } + + secretRefGroup := gwv1beta1.Group(Group) + secretRefKind := gwv1beta1.Kind(SecretKind) + secretRefNamespace := gwv1beta1.Namespace(ToNamespace) + + cases := map[string]struct { + canReference bool + err error + ctx context.Context + gateway gwv1beta1.Gateway + secret gwv1beta1.SecretObjectReference + k8sReferenceGrants []runtime.Object + }{ + "gateway allowed to secret": { + canReference: true, + err: nil, + ctx: context.TODO(), + gateway: gwv1beta1.Gateway{ + TypeMeta: metav1.TypeMeta{ + Kind: GatewayKind, + APIVersion: Group + V1Beta1, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: FromNamespace, + }, + Spec: gwv1beta1.GatewaySpec{}, + Status: gwv1beta1.GatewayStatus{}, + }, + secret: gwv1beta1.SecretObjectReference{ + Group: &secretRefGroup, + Kind: &secretRefKind, + Namespace: &secretRefNamespace, + Name: objName, + }, + k8sReferenceGrants: []runtime.Object{ + basicValidReferenceGrant, + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + s := runtime.NewScheme() + require.NoError(t, gwv1beta1.Install(s)) + require.NoError(t, gwv1beta1.AddToScheme(s)) + + client := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(tc.k8sReferenceGrants...).Build() + rv := NewReferenceValidator(client) + canReference, err := rv.GatewayCanReferenceSecret(tc.ctx, tc.gateway, tc.secret) + + require.Equal(t, tc.err, err) + require.Equal(t, tc.canReference, canReference) + }) + } +} + +func TestHTTPRouteCanReferenceGateway(t *testing.T) { + t.Parallel() + + objName := gwv1beta1.ObjectName("mygateway") + + basicValidReferenceGrant := &gwv1beta1.ReferenceGrant{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Namespace: ToNamespace, + }, + Spec: gwv1beta1.ReferenceGrantSpec{ + From: []gwv1beta1.ReferenceGrantFrom{ + { + Group: Group, + Kind: HTTPRouteKind, + Namespace: FromNamespace, + }, + }, + To: []gwv1beta1.ReferenceGrantTo{ + { + Group: Group, + Kind: GatewayKind, + Name: &objName, + }, + }, + }, + } + + gatewayRefGroup := gwv1beta1.Group(Group) + gatewayRefKind := gwv1beta1.Kind(GatewayKind) + gatewayRefNamespace := gwv1beta1.Namespace(ToNamespace) + + cases := map[string]struct { + canReference bool + err error + ctx context.Context + httpRoute gwv1beta1.HTTPRoute + gatewayRef gwv1beta1.ParentReference + k8sReferenceGrants []runtime.Object + }{ + "httproute allowed to gateway": { + canReference: true, + err: nil, + ctx: context.TODO(), + httpRoute: gwv1beta1.HTTPRoute{ + TypeMeta: metav1.TypeMeta{ + Kind: HTTPRouteKind, + APIVersion: Group + V1Beta1, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: FromNamespace, + }, + Spec: gwv1beta1.HTTPRouteSpec{}, + Status: gwv1beta1.HTTPRouteStatus{}, + }, + gatewayRef: gwv1beta1.ParentReference{ + Group: &gatewayRefGroup, + Kind: &gatewayRefKind, + Namespace: &gatewayRefNamespace, + Name: objName, + SectionName: nil, + Port: nil, + }, + k8sReferenceGrants: []runtime.Object{ + basicValidReferenceGrant, + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + s := runtime.NewScheme() + require.NoError(t, gwv1beta1.Install(s)) + require.NoError(t, gwv1beta1.AddToScheme(s)) + + client := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(tc.k8sReferenceGrants...).Build() + rv := NewReferenceValidator(client) + canReference, err := rv.HTTPRouteCanReferenceGateway(tc.ctx, tc.httpRoute, tc.gatewayRef) + + require.Equal(t, tc.err, err) + require.Equal(t, tc.canReference, canReference) + }) + } +} + +func TestHTTPRouteCanReferenceBackend(t *testing.T) { + t.Parallel() + + objName := gwv1beta1.ObjectName("myBackendRef") + + basicValidReferenceGrant := &gwv1beta1.ReferenceGrant{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Namespace: ToNamespace, + }, + Spec: gwv1beta1.ReferenceGrantSpec{ + From: []gwv1beta1.ReferenceGrantFrom{ + { + Group: Group, + Kind: HTTPRouteKind, + Namespace: FromNamespace, + }, + }, + To: []gwv1beta1.ReferenceGrantTo{ + { + Group: Group, + Kind: BackendRefKind, + Name: &objName, + }, + }, + }, + } + + backendRefGroup := gwv1beta1.Group(Group) + backendRefKind := gwv1beta1.Kind(BackendRefKind) + backendRefNamespace := gwv1beta1.Namespace(ToNamespace) + + cases := map[string]struct { + canReference bool + err error + ctx context.Context + httpRoute gwv1beta1.HTTPRoute + backendRef gwv1beta1.BackendRef + k8sReferenceGrants []runtime.Object + }{ + "httproute allowed to gateway": { + canReference: true, + err: nil, + ctx: context.TODO(), + httpRoute: gwv1beta1.HTTPRoute{ + TypeMeta: metav1.TypeMeta{ + Kind: HTTPRouteKind, + APIVersion: Group + V1Beta1, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: FromNamespace, + }, + Spec: gwv1beta1.HTTPRouteSpec{}, + Status: gwv1beta1.HTTPRouteStatus{}, + }, + backendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Group: &backendRefGroup, + Kind: &backendRefKind, + Name: objName, + Namespace: &backendRefNamespace, + Port: nil, + }, + Weight: nil, + }, + k8sReferenceGrants: []runtime.Object{ + basicValidReferenceGrant, + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + s := runtime.NewScheme() + require.NoError(t, gwv1beta1.Install(s)) + require.NoError(t, gwv1beta1.AddToScheme(s)) + + client := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(tc.k8sReferenceGrants...).Build() + rv := NewReferenceValidator(client) + canReference, err := rv.HTTPRouteCanReferenceBackend(tc.ctx, tc.httpRoute, tc.backendRef) + + require.Equal(t, tc.err, err) + require.Equal(t, tc.canReference, canReference) + }) + } +} + +func TestTCPRouteCanReferenceGateway(t *testing.T) { + t.Parallel() + + objName := gwv1beta1.ObjectName("mygateway") + + basicValidReferenceGrant := &gwv1beta1.ReferenceGrant{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Namespace: ToNamespace, + }, + Spec: gwv1beta1.ReferenceGrantSpec{ + From: []gwv1beta1.ReferenceGrantFrom{ + { + Group: Group, + Kind: TCPRouteKind, + Namespace: FromNamespace, + }, + }, + To: []gwv1beta1.ReferenceGrantTo{ + { + Group: Group, + Kind: GatewayKind, + Name: &objName, + }, + }, + }, + } + + gatewayRefGroup := gwv1beta1.Group(Group) + gatewayRefKind := gwv1beta1.Kind(GatewayKind) + gatewayRefNamespace := gwv1beta1.Namespace(ToNamespace) + + cases := map[string]struct { + canReference bool + err error + ctx context.Context + tcpRoute gwv1alpha2.TCPRoute + gatewayRef gwv1beta1.ParentReference + k8sReferenceGrants []runtime.Object + }{ + "tcpRoute allowed to gateway": { + canReference: true, + err: nil, + ctx: context.TODO(), + tcpRoute: gwv1alpha2.TCPRoute{ + TypeMeta: metav1.TypeMeta{ + Kind: TCPRouteKind, + APIVersion: Group + V1Alpha2, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: FromNamespace, + }, + Spec: gwv1alpha2.TCPRouteSpec{}, + Status: gwv1alpha2.TCPRouteStatus{}, + }, + gatewayRef: gwv1beta1.ParentReference{ + Group: &gatewayRefGroup, + Kind: &gatewayRefKind, + Namespace: &gatewayRefNamespace, + Name: objName, + SectionName: nil, + Port: nil, + }, + k8sReferenceGrants: []runtime.Object{ + basicValidReferenceGrant, + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + s := runtime.NewScheme() + require.NoError(t, gwv1beta1.Install(s)) + require.NoError(t, gwv1beta1.AddToScheme(s)) + + client := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(tc.k8sReferenceGrants...).Build() + rv := NewReferenceValidator(client) + canReference, err := rv.TCPRouteCanReferenceGateway(tc.ctx, tc.tcpRoute, tc.gatewayRef) + + require.Equal(t, tc.err, err) + require.Equal(t, tc.canReference, canReference) + }) + } +} + +func TestTCPRouteCanReferenceBackend(t *testing.T) { + t.Parallel() + + objName := gwv1beta1.ObjectName("myBackendRef") + + basicValidReferenceGrant := &gwv1beta1.ReferenceGrant{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Namespace: ToNamespace, + }, + Spec: gwv1beta1.ReferenceGrantSpec{ + From: []gwv1beta1.ReferenceGrantFrom{ + { + Group: Group, + Kind: TCPRouteKind, + Namespace: FromNamespace, + }, + }, + To: []gwv1beta1.ReferenceGrantTo{ + { + Group: Group, + Kind: BackendRefKind, + Name: &objName, + }, + }, + }, + } + + backendRefGroup := gwv1beta1.Group(Group) + backendRefKind := gwv1beta1.Kind(BackendRefKind) + backendRefNamespace := gwv1beta1.Namespace(ToNamespace) + + cases := map[string]struct { + canReference bool + err error + ctx context.Context + tcpRoute gwv1alpha2.TCPRoute + backendRef gwv1beta1.BackendRef + k8sReferenceGrants []runtime.Object + }{ + "tcpRoute allowed to gateway": { + canReference: true, + err: nil, + ctx: context.TODO(), + tcpRoute: gwv1alpha2.TCPRoute{ + TypeMeta: metav1.TypeMeta{ + Kind: TCPRouteKind, + APIVersion: Group + V1Alpha2, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: FromNamespace, + }, + Spec: gwv1alpha2.TCPRouteSpec{}, + Status: gwv1alpha2.TCPRouteStatus{}, + }, + backendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Group: &backendRefGroup, + Kind: &backendRefKind, + Name: objName, + Namespace: &backendRefNamespace, + Port: nil, + }, + Weight: nil, + }, + k8sReferenceGrants: []runtime.Object{ + basicValidReferenceGrant, + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + s := runtime.NewScheme() + require.NoError(t, gwv1beta1.Install(s)) + require.NoError(t, gwv1beta1.AddToScheme(s)) + + client := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(tc.k8sReferenceGrants...).Build() + rv := NewReferenceValidator(client) + canReference, err := rv.TCPRouteCanReferenceBackend(tc.ctx, tc.tcpRoute, tc.backendRef) + + require.Equal(t, tc.err, err) + require.Equal(t, tc.canReference, canReference) + }) + } +} + +func TestReferenceAllowed(t *testing.T) { + t.Parallel() + + objName := gwv1beta1.ObjectName("myObject") + + basicValidReferenceGrant := &gwv1beta1.ReferenceGrant{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Namespace: ToNamespace, + }, + Spec: gwv1beta1.ReferenceGrantSpec{ + From: []gwv1beta1.ReferenceGrantFrom{ + { + Group: Group, + Kind: HTTPRouteKind, + Namespace: FromNamespace, + }, + }, + To: []gwv1beta1.ReferenceGrantTo{ + { + Group: Group, + Kind: GatewayKind, + Name: &objName, + }, + }, + }, + } + + cases := map[string]struct { + refAllowed bool + err error + ctx context.Context + fromGK metav1.GroupKind + fromNamespace string + toGK metav1.GroupKind + toNamespace string + toName string + k8sReferenceGrants []runtime.Object + }{ + "same namespace": { + refAllowed: true, + err: nil, + ctx: context.TODO(), + fromGK: metav1.GroupKind{ + Group: Group, + Kind: HTTPRouteKind, + }, + fromNamespace: FromNamespace, + toGK: metav1.GroupKind{ + Group: Group, + Kind: GatewayKind, + }, + toNamespace: FromNamespace, + toName: string(objName), + k8sReferenceGrants: []runtime.Object{ + &gwv1beta1.ReferenceGrant{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Namespace: FromNamespace, + }, + Spec: gwv1beta1.ReferenceGrantSpec{ + From: []gwv1beta1.ReferenceGrantFrom{ + { + Group: Group, + Kind: HTTPRouteKind, + Namespace: FromNamespace, + }, + }, + To: []gwv1beta1.ReferenceGrantTo{ + { + Group: Group, + Kind: GatewayKind, + Name: &objName, + }, + }, + }, + }, + }, + }, + "reference allowed": { + refAllowed: true, + err: nil, + ctx: context.TODO(), + fromGK: metav1.GroupKind{ + Group: Group, + Kind: HTTPRouteKind, + }, + fromNamespace: FromNamespace, + toGK: metav1.GroupKind{ + Group: Group, + Kind: GatewayKind, + }, + toNamespace: ToNamespace, + toName: string(objName), + k8sReferenceGrants: []runtime.Object{ + basicValidReferenceGrant, + }, + }, + "reference not allowed": { + refAllowed: false, + err: nil, + ctx: context.TODO(), + fromGK: metav1.GroupKind{ + Group: Group, + Kind: HTTPRouteKind, + }, + fromNamespace: InvalidNamespace, + toGK: metav1.GroupKind{ + Group: Group, + Kind: GatewayKind, + }, + toNamespace: ToNamespace, + toName: string(objName), + k8sReferenceGrants: []runtime.Object{ + basicValidReferenceGrant, + }, + }, + "no reference grant defined in namespace": { + refAllowed: false, + err: nil, + ctx: context.TODO(), + fromGK: metav1.GroupKind{ + Group: Group, + Kind: HTTPRouteKind, + }, + fromNamespace: FromNamespace, + toGK: metav1.GroupKind{ + Group: Group, + Kind: GatewayKind, + }, + toNamespace: ToNamespace, + toName: string(objName), + k8sReferenceGrants: nil, + }, + "reference allowed to all objects in namespace": { + refAllowed: true, + err: nil, + ctx: context.TODO(), + fromGK: metav1.GroupKind{ + Group: Group, + Kind: HTTPRouteKind, + }, + fromNamespace: FromNamespace, + toGK: metav1.GroupKind{ + Group: Group, + Kind: GatewayKind, + }, + toNamespace: ToNamespace, + toName: string(objName), + k8sReferenceGrants: []runtime.Object{ + &gwv1beta1.ReferenceGrant{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Namespace: ToNamespace, + }, + Spec: gwv1beta1.ReferenceGrantSpec{ + From: []gwv1beta1.ReferenceGrantFrom{ + { + Group: Group, + Kind: HTTPRouteKind, + Namespace: FromNamespace, + }, + }, + To: []gwv1beta1.ReferenceGrantTo{ + { + Group: Group, + Kind: GatewayKind, + Name: nil, + }, + }, + }, + }, + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + s := runtime.NewScheme() + require.NoError(t, gwv1beta1.Install(s)) + require.NoError(t, gwv1beta1.AddToScheme(s)) + + client := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(tc.k8sReferenceGrants...).Build() + + refAllowed, err := referenceAllowed(tc.ctx, tc.fromGK, tc.fromNamespace, tc.toGK, tc.toNamespace, tc.toName, client) + + require.Equal(t, tc.err, err) + require.Equal(t, tc.refAllowed, refAllowed) + }) + } +} diff --git a/control-plane/api-gateway/gatekeeper/dataplane.go b/control-plane/api-gateway/gatekeeper/dataplane.go new file mode 100644 index 0000000000..6ef7cc04ec --- /dev/null +++ b/control-plane/api-gateway/gatekeeper/dataplane.go @@ -0,0 +1,172 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package gatekeeper + +import ( + "fmt" + "strconv" + + corev1 "k8s.io/api/core/v1" + "k8s.io/utils/pointer" + + apigateway "github.com/hashicorp/consul-k8s/control-plane/api-gateway" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" + "github.com/hashicorp/consul-k8s/control-plane/namespaces" + "k8s.io/apimachinery/pkg/util/intstr" +) + +const ( + consulDataplaneDNSBindHost = "127.0.0.1" + consulDataplaneDNSBindPort = 8600 + sidecarUserAndGroupID = 5995 + defaultPrometheusScrapePath = "/metrics" + defaultEnvoyProxyConcurrency = 1 + volumeName = "consul-connect-inject-data" +) + +func consulDataplaneContainer(config apigateway.HelmConfig, name, namespace string) (corev1.Container, error) { + // Extract the service account token's volume mount. + var ( + err error + bearerTokenFile string + ) + + if config.AuthMethod != "" { + bearerTokenFile = "/var/run/secrets/kubernetes.io/serviceaccount/token" + } + + args, err := getDataplaneArgs(namespace, config, bearerTokenFile, name) + if err != nil { + return corev1.Container{}, err + } + + probe := &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Port: intstr.FromInt(constants.ProxyDefaultHealthPort), + Path: "/ready", + }, + }, + InitialDelaySeconds: 1, + } + + container := corev1.Container{ + Name: name, + Image: config.ImageDataplane, + + // We need to set tmp dir to an ephemeral volume that we're mounting so that + // consul-dataplane can write files to it. Otherwise, it wouldn't be able to + // because we set file system to be read-only. + Env: []corev1.EnvVar{ + { + Name: "TMPDIR", + Value: "/consul/connect-inject", + }, + { + Name: "NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "DP_SERVICE_NODE_NAME", + Value: "$(NODE_NAME)-virtual", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: volumeName, + MountPath: "/consul/connect-inject", + }, + }, + Args: args, + ReadinessProbe: probe, + } + + // Configure the Readiness Address for the proxy's health check to be the Pod IP. + container.Env = append(container.Env, corev1.EnvVar{ + Name: "DP_ENVOY_READY_BIND_ADDRESS", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{FieldPath: "status.podIP"}, + }, + }) + // Configure the port on which the readiness probe will query the proxy for its health. + container.Ports = append(container.Ports, corev1.ContainerPort{ + Name: "proxy-health", + ContainerPort: int32(constants.ProxyDefaultHealthPort), + }) + + // If not running in an OpenShift environment, + // skip setting the security context and let OpenShift set it for us. + if !config.EnableOpenShift { + container.SecurityContext = &corev1.SecurityContext{ + RunAsUser: pointer.Int64(sidecarUserAndGroupID), + RunAsGroup: pointer.Int64(sidecarUserAndGroupID), + RunAsNonRoot: pointer.Bool(true), + ReadOnlyRootFilesystem: pointer.Bool(true), + } + } + + return container, nil +} + +func getDataplaneArgs(namespace string, config apigateway.HelmConfig, bearerTokenFile string, name string) ([]string, error) { + proxyIDFileName := "/consul/connect-inject/proxyid" + envoyConcurrency := defaultEnvoyProxyConcurrency + + args := []string{ + "-addresses", config.ConsulConfig.Address, + "-grpc-port=" + strconv.Itoa(config.ConsulConfig.GRPCPort), + "-proxy-service-id-path=" + proxyIDFileName, + "-log-level=" + config.LogLevel, + "-log-json=" + strconv.FormatBool(config.LogJSON), + "-envoy-concurrency=" + strconv.Itoa(envoyConcurrency), + } + + consulNamespace := namespaces.ConsulNamespace(namespace, config.EnableNamespaces, config.ConsulDestinationNamespace, config.EnableNamespaceMirroring, config.NamespaceMirroringPrefix) + + if config.AuthMethod != "" { + args = append(args, + "-credential-type=login", + "-login-auth-method="+config.AuthMethod, + "-login-bearer-token-path="+bearerTokenFile, + "-login-meta="+fmt.Sprintf("gateway=%s/%s", namespace, name), + ) + if config.EnableNamespaces { + args = append(args, "-login-namespace="+consulNamespace) + } + if config.ConsulPartition != "" { + args = append(args, "-login-partition="+config.ConsulPartition) + } + } + if config.EnableNamespaces { + args = append(args, "-service-namespace="+consulNamespace) + } + if config.ConsulPartition != "" { + args = append(args, "-service-partition="+config.ConsulPartition) + } + if config.TLSEnabled { + if config.ConsulTLSServerName != "" { + args = append(args, "-tls-server-name="+config.ConsulTLSServerName) + } + if config.ConsulCACert != "" { + args = append(args, "-ca-certs="+constants.ConsulCAFile) + } + } else { + args = append(args, "-tls-disabled") + } + + // Configure the readiness port on the dataplane sidecar if proxy health checks are enabled. + args = append(args, fmt.Sprintf("%s=%d", "-envoy-ready-bind-port", constants.ProxyDefaultHealthPort)) + + args = append(args, fmt.Sprintf("-envoy-admin-bind-port=%d", 19000)) + + // Set a default scrape path that can be overwritten by the annotation. + prometheusScrapePath := defaultPrometheusScrapePath + args = append(args, "-telemetry-prom-scrape-path="+prometheusScrapePath) + + return args, nil +} diff --git a/control-plane/api-gateway/gatekeeper/deployment.go b/control-plane/api-gateway/gatekeeper/deployment.go new file mode 100644 index 0000000000..0c46dbb0a5 --- /dev/null +++ b/control-plane/api-gateway/gatekeeper/deployment.go @@ -0,0 +1,234 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package gatekeeper + +import ( + "context" + + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "k8s.io/apimachinery/pkg/types" + + apigateway "github.com/hashicorp/consul-k8s/control-plane/api-gateway" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +const ( + defaultInstances int32 = 1 +) + +func (g *Gatekeeper) upsertDeployment(ctx context.Context, gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config apigateway.HelmConfig) error { + // Get Deployment if it exists. + existingDeployment := &appsv1.Deployment{} + exists := false + + err := g.Client.Get(ctx, g.namespacedName(gateway), existingDeployment) + if err != nil && !k8serrors.IsNotFound(err) { + return err + } else if k8serrors.IsNotFound(err) { + exists = false + } else { + exists = true + } + + var currentReplicas *int32 + if exists { + currentReplicas = existingDeployment.Spec.Replicas + } + + deployment, err := g.deployment(gateway, gcc, config, currentReplicas) + if err != nil { + return err + } + + if exists { + g.Log.Info("Existing Gateway Deployment found.") + + // If the user has set the number of replicas, let's respect that. + deployment.Spec.Replicas = existingDeployment.Spec.Replicas + } + + mutated := deployment.DeepCopy() + mutator := newDeploymentMutator(deployment, mutated, gcc, gateway, g.Client.Scheme()) + + result, err := controllerutil.CreateOrUpdate(ctx, g.Client, mutated, mutator) + if err != nil { + return err + } + + switch result { + case controllerutil.OperationResultCreated: + g.Log.Info("Created Deployment") + case controllerutil.OperationResultUpdated: + g.Log.Info("Updated Deployment") + case controllerutil.OperationResultNone: + g.Log.Info("No change to deployment") + } + + return nil +} + +func (g *Gatekeeper) deleteDeployment(ctx context.Context, nsname types.NamespacedName) error { + err := g.Client.Delete(ctx, &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: nsname.Name, Namespace: nsname.Namespace}}) + if k8serrors.IsNotFound(err) { + return nil + } + + return err +} + +func (g *Gatekeeper) deployment(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config apigateway.HelmConfig, currentReplicas *int32) (*appsv1.Deployment, error) { + initContainer, err := initContainer(config, gateway.Name, gateway.Namespace) + if err != nil { + return nil, err + } + + container, err := consulDataplaneContainer(config, gateway.Name, gateway.Namespace) + if err != nil { + return nil, err + } + + return &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: gateway.Name, + Namespace: gateway.Namespace, + Labels: apigateway.LabelsForGateway(&gateway), + }, + Spec: appsv1.DeploymentSpec{ + Replicas: deploymentReplicas(gcc, currentReplicas), + Selector: &metav1.LabelSelector{ + MatchLabels: apigateway.LabelsForGateway(&gateway), + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: apigateway.LabelsForGateway(&gateway), + Annotations: map[string]string{ + "consul.hashicorp.com/connect-inject": "false", + }, + }, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{Medium: corev1.StorageMediumMemory}, + }, + }, + }, + InitContainers: []corev1.Container{ + initContainer, + }, + Containers: []corev1.Container{ + container, + }, + Affinity: &corev1.Affinity{ + PodAntiAffinity: &corev1.PodAntiAffinity{ + PreferredDuringSchedulingIgnoredDuringExecution: []corev1.WeightedPodAffinityTerm{ + { + Weight: 1, + PodAffinityTerm: corev1.PodAffinityTerm{ + LabelSelector: &metav1.LabelSelector{ + MatchLabels: apigateway.LabelsForGateway(&gateway), + }, + TopologyKey: "kubernetes.io/hostname", + }, + }, + }, + }, + }, + NodeSelector: gcc.Spec.NodeSelector, + Tolerations: gcc.Spec.Tolerations, + ServiceAccountName: g.serviceAccountName(gateway, config), + }, + }, + }, + }, nil +} + +func mergeDeployments(gcc v1alpha1.GatewayClassConfig, a, b *appsv1.Deployment) *appsv1.Deployment { + if !compareDeployments(a, b) { + b.Spec.Template = a.Spec.Template + b.Spec.Replicas = deploymentReplicas(gcc, a.Spec.Replicas) + } + + return b +} + +func compareDeployments(a, b *appsv1.Deployment) bool { + // since K8s adds a bunch of defaults when we create a deployment, check that + // they don't differ by the things that we may actually change, namely container + // ports + if len(b.Spec.Template.Spec.Containers) != len(a.Spec.Template.Spec.Containers) { + return false + } + for i, container := range a.Spec.Template.Spec.Containers { + otherPorts := b.Spec.Template.Spec.Containers[i].Ports + if len(container.Ports) != len(otherPorts) { + return false + } + for j, port := range container.Ports { + otherPort := otherPorts[j] + if port.ContainerPort != otherPort.ContainerPort { + return false + } + if port.Protocol != otherPort.Protocol { + return false + } + } + } + + if b.Spec.Replicas == nil && a.Spec.Replicas == nil { + return true + } else if b.Spec.Replicas == nil { + return false + } else if a.Spec.Replicas == nil { + return false + } + + return *b.Spec.Replicas == *a.Spec.Replicas +} + +func newDeploymentMutator(deployment, mutated *appsv1.Deployment, gcc v1alpha1.GatewayClassConfig, gateway gwv1beta1.Gateway, scheme *runtime.Scheme) resourceMutator { + return func() error { + mutated = mergeDeployments(gcc, deployment, mutated) + return ctrl.SetControllerReference(&gateway, mutated, scheme) + } +} + +func deploymentReplicas(gcc v1alpha1.GatewayClassConfig, currentReplicas *int32) *int32 { + instanceValue := defaultInstances + + //if currentReplicas is not nil use current value when building deployment + if currentReplicas != nil { + instanceValue = *currentReplicas + } else if gcc.Spec.DeploymentSpec.DefaultInstances != nil { + // otherwise use the default value on the GatewayClassConfig if set + instanceValue = *gcc.Spec.DeploymentSpec.DefaultInstances + } + + if gcc.Spec.DeploymentSpec.MaxInstances != nil { + + //check if over maximum and lower to maximum + maxValue := *gcc.Spec.DeploymentSpec.MaxInstances + if instanceValue > maxValue { + instanceValue = maxValue + } + } + + if gcc.Spec.DeploymentSpec.MinInstances != nil { + //check if less than minimum and raise to minimum + minValue := *gcc.Spec.DeploymentSpec.MinInstances + if instanceValue < minValue { + instanceValue = minValue + } + + } + return &instanceValue +} diff --git a/control-plane/api-gateway/gatekeeper/gatekeeper.go b/control-plane/api-gateway/gatekeeper/gatekeeper.go new file mode 100644 index 0000000000..e333b6a8ea --- /dev/null +++ b/control-plane/api-gateway/gatekeeper/gatekeeper.go @@ -0,0 +1,91 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package gatekeeper + +import ( + "context" + "fmt" + + "github.com/go-logr/logr" + apigateway "github.com/hashicorp/consul-k8s/control-plane/api-gateway" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +// Gatekeeper is used to manage the lifecycle of Gateway deployments and services. +type Gatekeeper struct { + Log logr.Logger + Client client.Client +} + +// New creates a new Gatekeeper from the Config. +func New(log logr.Logger, client client.Client) *Gatekeeper { + return &Gatekeeper{ + Log: log, + Client: client, + } +} + +// Upsert creates or updates the resources for handling routing of network traffic. +func (g *Gatekeeper) Upsert(ctx context.Context, gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config apigateway.HelmConfig) error { + g.Log.Info(fmt.Sprintf("Upsert Gateway Deployment %s/%s", gateway.Namespace, gateway.Name)) + + if err := g.upsertRole(ctx, gateway, gcc, config); err != nil { + return err + } + + if err := g.upsertServiceAccount(ctx, gateway, config); err != nil { + return err + } + + if err := g.upsertService(ctx, gateway, gcc, config); err != nil { + return err + } + + if err := g.upsertDeployment(ctx, gateway, gcc, config); err != nil { + return err + } + + return nil +} + +// Delete removes the resources for handling routing of network traffic. +func (g *Gatekeeper) Delete(ctx context.Context, nsname types.NamespacedName) error { + if err := g.deleteRole(ctx, nsname); err != nil { + return err + } + + if err := g.deleteServiceAccount(ctx, nsname); err != nil { + return err + } + + if err := g.deleteService(ctx, nsname); err != nil { + return err + } + + if err := g.deleteDeployment(ctx, nsname); err != nil { + return err + } + + return nil +} + +// resourceMutator is passed to create or update functions to mutate Kubernetes resources. +type resourceMutator = func() error + +func (g Gatekeeper) namespacedName(gateway gwv1beta1.Gateway) types.NamespacedName { + return types.NamespacedName{ + Namespace: gateway.Namespace, + Name: gateway.Name, + } +} + +func (g Gatekeeper) serviceAccountName(gateway gwv1beta1.Gateway, config apigateway.HelmConfig) string { + if config.AuthMethod == "" { + return "" + } + return gateway.Name +} diff --git a/control-plane/api-gateway/gatekeeper/gatekeeper_test.go b/control-plane/api-gateway/gatekeeper/gatekeeper_test.go new file mode 100644 index 0000000000..92a92d66f2 --- /dev/null +++ b/control-plane/api-gateway/gatekeeper/gatekeeper_test.go @@ -0,0 +1,896 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package gatekeeper + +import ( + "context" + "fmt" + "testing" + + logrtest "github.com/go-logr/logr/testr" + apigateway "github.com/hashicorp/consul-k8s/control-plane/api-gateway" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/stretchr/testify/require" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + rbac "k8s.io/api/rbac/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +var ( + createdAtLabelKey = "gateway.consul.hashicorp.com/created" + createdAtLabelValue = "101010" + name = "test" + namespace = "default" + labels = map[string]string{ + "gateway.consul.hashicorp.com/name": name, + "gateway.consul.hashicorp.com/namespace": namespace, + createdAtLabelKey: createdAtLabelValue, + "gateway.consul.hashicorp.com/managed": "true", + } + listeners = []gwv1beta1.Listener{ + { + Name: "Listener 1", + Port: 8080, + Protocol: "TCP", + }, + { + Name: "Listener 2", + Port: 8081, + Protocol: "UDP", + }, + } +) + +type testCase struct { + gateway gwv1beta1.Gateway + gatewayClassConfig v1alpha1.GatewayClassConfig + helmConfig apigateway.HelmConfig + + initialResources resources + finalResources resources +} + +type resources struct { + deployments []*appsv1.Deployment + roles []*rbac.Role + services []*corev1.Service + serviceAccounts []*corev1.ServiceAccount +} + +func TestUpsert(t *testing.T) { + t.Parallel() + + cases := map[string]testCase{ + "create a new gateway deployment with only Deployment": { + gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: gwv1beta1.GatewaySpec{ + Listeners: listeners, + }, + }, + gatewayClassConfig: v1alpha1.GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-gatewayclassconfig", + }, + Spec: v1alpha1.GatewayClassConfigSpec{ + DeploymentSpec: v1alpha1.DeploymentSpec{ + DefaultInstances: ptrTo(int32(3)), + MaxInstances: ptrTo(int32(3)), + MinInstances: ptrTo(int32(1)), + }, + CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, + ServiceType: (*corev1.ServiceType)(ptrTo("NodePort")), + }, + }, + helmConfig: apigateway.HelmConfig{}, + initialResources: resources{}, + finalResources: resources{ + deployments: []*appsv1.Deployment{ + configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), + }, + roles: []*rbac.Role{}, + services: []*corev1.Service{}, + serviceAccounts: []*corev1.ServiceAccount{}, + }, + }, + "create a new gateway deployment with managed Service": { + gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: gwv1beta1.GatewaySpec{ + Listeners: listeners, + }, + }, + gatewayClassConfig: v1alpha1.GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-gatewayclassconfig", + }, + Spec: v1alpha1.GatewayClassConfigSpec{ + DeploymentSpec: v1alpha1.DeploymentSpec{ + DefaultInstances: ptrTo(int32(3)), + MaxInstances: ptrTo(int32(3)), + MinInstances: ptrTo(int32(1)), + }, + CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, + ServiceType: (*corev1.ServiceType)(ptrTo("NodePort")), + }, + }, + helmConfig: apigateway.HelmConfig{}, + initialResources: resources{}, + finalResources: resources{ + deployments: []*appsv1.Deployment{ + configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), + }, + roles: []*rbac.Role{}, + services: []*corev1.Service{ + configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ + { + Name: "Listener 1", + Protocol: "TCP", + Port: 8080, + }, + { + Name: "Listener 2", + Protocol: "UDP", + Port: 8081, + }, + }, "1"), + }, + serviceAccounts: []*corev1.ServiceAccount{}, + }, + }, + "create a new gateway deployment with managed Service and ACLs": { + gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: gwv1beta1.GatewaySpec{ + Listeners: listeners, + }, + }, + gatewayClassConfig: v1alpha1.GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-gatewayclassconfig", + }, + Spec: v1alpha1.GatewayClassConfigSpec{ + DeploymentSpec: v1alpha1.DeploymentSpec{ + DefaultInstances: ptrTo(int32(3)), + MaxInstances: ptrTo(int32(3)), + MinInstances: ptrTo(int32(1)), + }, + CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, + ServiceType: (*corev1.ServiceType)(ptrTo("NodePort")), + }, + }, + helmConfig: apigateway.HelmConfig{ + AuthMethod: "method", + }, + initialResources: resources{}, + finalResources: resources{ + deployments: []*appsv1.Deployment{ + configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), + }, + roles: []*rbac.Role{ + configureRole(name, namespace, labels, "1"), + }, + services: []*corev1.Service{ + configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ + { + Name: "Listener 1", + Protocol: "TCP", + Port: 8080, + }, + { + Name: "Listener 2", + Protocol: "UDP", + Port: 8081, + }, + }, "1"), + }, + serviceAccounts: []*corev1.ServiceAccount{ + configureServiceAccount(name, namespace, labels, "1"), + }, + }, + }, + "update a gateway, adding a listener to a service": { + gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: gwv1beta1.GatewaySpec{ + Listeners: listeners, + }, + }, + gatewayClassConfig: v1alpha1.GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-gatewayclassconfig", + }, + Spec: v1alpha1.GatewayClassConfigSpec{ + DeploymentSpec: v1alpha1.DeploymentSpec{ + DefaultInstances: ptrTo(int32(3)), + MaxInstances: ptrTo(int32(3)), + MinInstances: ptrTo(int32(1)), + }, + CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, + ServiceType: (*corev1.ServiceType)(ptrTo("NodePort")), + }, + }, + helmConfig: apigateway.HelmConfig{ + AuthMethod: "method", + }, + initialResources: resources{ + deployments: []*appsv1.Deployment{ + configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), + }, + roles: []*rbac.Role{ + configureRole(name, namespace, labels, "1"), + }, + services: []*corev1.Service{ + configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ + { + Name: "Listener 1", + Protocol: "TCP", + Port: 8080, + }, + }, "1"), + }, + serviceAccounts: []*corev1.ServiceAccount{ + configureServiceAccount(name, namespace, labels, "1"), + }, + }, + finalResources: resources{ + deployments: []*appsv1.Deployment{ + configureDeployment(name, namespace, labels, 3, nil, nil, "", "2"), + }, + roles: []*rbac.Role{ + configureRole(name, namespace, labels, "1"), + }, + services: []*corev1.Service{ + configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ + { + Name: "Listener 1", + Protocol: "TCP", + Port: 8080, + }, + { + Name: "Listener 2", + Protocol: "UDP", + Port: 8081, + }, + }, "2"), + }, + serviceAccounts: []*corev1.ServiceAccount{ + configureServiceAccount(name, namespace, labels, "1"), + }, + }, + }, + "update a gateway, removing a listener from a service": { + gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: gwv1beta1.GatewaySpec{ + Listeners: []gwv1beta1.Listener{ + listeners[0], + }, + }, + }, + gatewayClassConfig: v1alpha1.GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-gatewayclassconfig", + }, + Spec: v1alpha1.GatewayClassConfigSpec{ + DeploymentSpec: v1alpha1.DeploymentSpec{ + DefaultInstances: ptrTo(int32(3)), + MaxInstances: ptrTo(int32(3)), + MinInstances: ptrTo(int32(1)), + }, + CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, + ServiceType: (*corev1.ServiceType)(ptrTo("NodePort")), + }, + }, + helmConfig: apigateway.HelmConfig{ + AuthMethod: "method", + }, + initialResources: resources{ + deployments: []*appsv1.Deployment{ + configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), + }, + roles: []*rbac.Role{ + configureRole(name, namespace, labels, "1"), + }, + services: []*corev1.Service{ + configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ + { + Name: "Listener 1", + Protocol: "TCP", + Port: 8080, + }, + { + Name: "Listener 2", + Protocol: "UDP", + Port: 8081, + }, + }, "1"), + }, + serviceAccounts: []*corev1.ServiceAccount{ + configureServiceAccount(name, namespace, labels, "1"), + }, + }, + finalResources: resources{ + deployments: []*appsv1.Deployment{ + configureDeployment(name, namespace, labels, 3, nil, nil, "", "2"), + }, + roles: []*rbac.Role{ + configureRole(name, namespace, labels, "1"), + }, + services: []*corev1.Service{ + configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ + { + Name: "Listener 1", + Protocol: "TCP", + Port: 8080, + }, + }, "2"), + }, + serviceAccounts: []*corev1.ServiceAccount{ + configureServiceAccount(name, namespace, labels, "1"), + }, + }, + }, + "updating a gateway deployment respects the number of replicas a user has set": { + gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: gwv1beta1.GatewaySpec{ + Listeners: listeners, + }, + }, + gatewayClassConfig: v1alpha1.GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-gatewayclassconfig", + }, + Spec: v1alpha1.GatewayClassConfigSpec{ + DeploymentSpec: v1alpha1.DeploymentSpec{ + DefaultInstances: ptrTo(int32(5)), + MaxInstances: ptrTo(int32(7)), + MinInstances: ptrTo(int32(1)), + }, + CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, + ServiceType: (*corev1.ServiceType)(ptrTo("NodePort")), + }, + }, + helmConfig: apigateway.HelmConfig{}, + initialResources: resources{ + deployments: []*appsv1.Deployment{ + configureDeployment(name, namespace, labels, 5, nil, nil, "", "1"), + }, + }, + finalResources: resources{ + deployments: []*appsv1.Deployment{ + configureDeployment(name, namespace, labels, 5, nil, nil, "", "1"), + }, + roles: []*rbac.Role{}, + services: []*corev1.Service{}, + serviceAccounts: []*corev1.ServiceAccount{}, + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + s := runtime.NewScheme() + require.NoError(t, gwv1beta1.Install(s)) + require.NoError(t, v1alpha1.AddToScheme(s)) + require.NoError(t, rbac.AddToScheme(s)) + require.NoError(t, corev1.AddToScheme(s)) + require.NoError(t, appsv1.AddToScheme(s)) + + log := logrtest.New(t) + + objs := append(joinResources(tc.initialResources), &tc.gateway, &tc.gatewayClassConfig) + client := fake.NewClientBuilder().WithScheme(s).WithObjects(objs...).Build() + + gatekeeper := New(log, client) + + err := gatekeeper.Upsert(context.Background(), tc.gateway, tc.gatewayClassConfig, tc.helmConfig) + require.NoError(t, err) + require.NoError(t, validateResourcesExist(t, client, tc.finalResources)) + }) + } +} + +func TestDelete(t *testing.T) { + t.Parallel() + + cases := map[string]testCase{ + "delete a gateway deployment with only Deployment": { + gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: gwv1beta1.GatewaySpec{ + Listeners: listeners, + }, + }, + gatewayClassConfig: v1alpha1.GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-gatewayclassconfig", + }, + Spec: v1alpha1.GatewayClassConfigSpec{ + DeploymentSpec: v1alpha1.DeploymentSpec{ + DefaultInstances: ptrTo(int32(3)), + MaxInstances: ptrTo(int32(3)), + MinInstances: ptrTo(int32(1)), + }, + CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, + ServiceType: (*corev1.ServiceType)(ptrTo("NodePort")), + }, + }, + helmConfig: apigateway.HelmConfig{}, + initialResources: resources{ + deployments: []*appsv1.Deployment{ + configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), + }, + }, + finalResources: resources{ + deployments: []*appsv1.Deployment{}, + roles: []*rbac.Role{}, + services: []*corev1.Service{}, + serviceAccounts: []*corev1.ServiceAccount{}, + }, + }, + "delete a gateway deployment with a managed Service": { + gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: gwv1beta1.GatewaySpec{ + Listeners: listeners, + }, + }, + gatewayClassConfig: v1alpha1.GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-gatewayclassconfig", + }, + Spec: v1alpha1.GatewayClassConfigSpec{ + DeploymentSpec: v1alpha1.DeploymentSpec{ + DefaultInstances: ptrTo(int32(3)), + MaxInstances: ptrTo(int32(3)), + MinInstances: ptrTo(int32(1)), + }, + CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, + ServiceType: (*corev1.ServiceType)(ptrTo("NodePort")), + }, + }, + helmConfig: apigateway.HelmConfig{}, + initialResources: resources{ + deployments: []*appsv1.Deployment{ + configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), + }, + roles: []*rbac.Role{}, + services: []*corev1.Service{ + configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ + { + Name: "Listener 1", + Protocol: "TCP", + Port: 8080, + }, + { + Name: "Listener 2", + Protocol: "UDP", + Port: 8081, + }, + }, "1"), + }, + serviceAccounts: []*corev1.ServiceAccount{}, + }, + finalResources: resources{ + deployments: []*appsv1.Deployment{}, + roles: []*rbac.Role{}, + services: []*corev1.Service{}, + serviceAccounts: []*corev1.ServiceAccount{}, + }, + }, + "delete a gateway deployment with managed Service and ACLs": { + gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: gwv1beta1.GatewaySpec{ + Listeners: listeners, + }, + }, + gatewayClassConfig: v1alpha1.GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-gatewayclassconfig", + }, + Spec: v1alpha1.GatewayClassConfigSpec{ + DeploymentSpec: v1alpha1.DeploymentSpec{ + DefaultInstances: ptrTo(int32(3)), + MaxInstances: ptrTo(int32(3)), + MinInstances: ptrTo(int32(1)), + }, + CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, + ServiceType: (*corev1.ServiceType)(ptrTo("NodePort")), + }, + }, + helmConfig: apigateway.HelmConfig{ + AuthMethod: "method", + }, + initialResources: resources{ + deployments: []*appsv1.Deployment{ + configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), + }, + roles: []*rbac.Role{ + configureRole(name, namespace, labels, "1"), + }, + services: []*corev1.Service{ + configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ + { + Name: "Listener 1", + Protocol: "TCP", + Port: 8080, + }, + { + Name: "Listener 2", + Protocol: "UDP", + Port: 8081, + }, + }, "1"), + }, + serviceAccounts: []*corev1.ServiceAccount{ + configureServiceAccount(name, namespace, labels, "1"), + }, + }, + finalResources: resources{ + deployments: []*appsv1.Deployment{}, + roles: []*rbac.Role{}, + services: []*corev1.Service{}, + serviceAccounts: []*corev1.ServiceAccount{}, + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + s := runtime.NewScheme() + require.NoError(t, gwv1beta1.Install(s)) + require.NoError(t, v1alpha1.AddToScheme(s)) + require.NoError(t, rbac.AddToScheme(s)) + require.NoError(t, corev1.AddToScheme(s)) + require.NoError(t, appsv1.AddToScheme(s)) + + log := logrtest.New(t) + + objs := append(joinResources(tc.initialResources), &tc.gateway, &tc.gatewayClassConfig) + client := fake.NewClientBuilder().WithScheme(s).WithObjects(objs...).Build() + + gatekeeper := New(log, client) + + err := gatekeeper.Delete(context.Background(), types.NamespacedName{ + Namespace: tc.gateway.Namespace, + Name: tc.gateway.Name, + }) + require.NoError(t, err) + require.NoError(t, validateResourcesExist(t, client, tc.finalResources)) + require.NoError(t, validateResourcesAreDeleted(t, client, tc.initialResources)) + }) + } +} + +func joinResources(resources resources) (objs []client.Object) { + for _, deployment := range resources.deployments { + objs = append(objs, deployment) + } + + for _, role := range resources.roles { + objs = append(objs, role) + } + + for _, service := range resources.services { + objs = append(objs, service) + } + + for _, serviceAccount := range resources.serviceAccounts { + objs = append(objs, serviceAccount) + } + + return objs +} + +func validateResourcesExist(t *testing.T, client client.Client, resources resources) error { + t.Helper() + + for _, expected := range resources.deployments { + actual := &appsv1.Deployment{} + err := client.Get(context.Background(), types.NamespacedName{ + Name: expected.Name, + Namespace: expected.Namespace, + }, actual) + if err != nil { + return err + } + + // Patch the createdAt label + actual.Labels[createdAtLabelKey] = createdAtLabelValue + actual.Spec.Selector.MatchLabels[createdAtLabelKey] = createdAtLabelValue + actual.Spec.Template.ObjectMeta.Labels[createdAtLabelKey] = createdAtLabelValue + + require.Equal(t, expected.Name, actual.Name) + require.Equal(t, expected.Namespace, actual.Namespace) + require.Equal(t, expected.APIVersion, actual.APIVersion) + require.Equal(t, expected.Labels, actual.Labels) + if expected.Spec.Replicas != nil { + require.NotNil(t, actual.Spec.Replicas) + require.EqualValues(t, *expected.Spec.Replicas, *actual.Spec.Replicas) + } + } + + for _, expected := range resources.roles { + actual := &rbac.Role{} + err := client.Get(context.Background(), types.NamespacedName{ + Name: expected.Name, + Namespace: expected.Namespace, + }, actual) + if err != nil { + return err + } + + // Patch the createdAt label + actual.Labels[createdAtLabelKey] = createdAtLabelValue + + require.Equal(t, expected, actual) + } + + for _, expected := range resources.services { + actual := &corev1.Service{} + err := client.Get(context.Background(), types.NamespacedName{ + Name: expected.Name, + Namespace: expected.Namespace, + }, actual) + if err != nil { + return err + } + + // Patch the createdAt label + actual.Labels[createdAtLabelKey] = createdAtLabelValue + actual.Spec.Selector[createdAtLabelKey] = createdAtLabelValue + + require.Equal(t, expected, actual) + } + + for _, expected := range resources.serviceAccounts { + actual := &corev1.ServiceAccount{} + err := client.Get(context.Background(), types.NamespacedName{ + Name: expected.Name, + Namespace: expected.Namespace, + }, actual) + if err != nil { + return err + } + + // Patch the createdAt label + actual.Labels[createdAtLabelKey] = createdAtLabelValue + + require.Equal(t, expected, actual) + } + + return nil +} + +func validateResourcesAreDeleted(t *testing.T, client client.Client, resources resources) error { + t.Helper() + + for _, expected := range resources.deployments { + actual := &appsv1.Deployment{} + err := client.Get(context.Background(), types.NamespacedName{ + Name: expected.Name, + Namespace: expected.Namespace, + }, actual) + if !k8serrors.IsNotFound(err) { + return fmt.Errorf("expected deployment %s to be deleted", expected.Name) + } + require.Error(t, err) + } + + for _, expected := range resources.roles { + actual := &rbac.Role{} + err := client.Get(context.Background(), types.NamespacedName{ + Name: expected.Name, + Namespace: expected.Namespace, + }, actual) + if !k8serrors.IsNotFound(err) { + return fmt.Errorf("expected role %s to be deleted", expected.Name) + } + require.Error(t, err) + } + + for _, expected := range resources.services { + actual := &corev1.Service{} + err := client.Get(context.Background(), types.NamespacedName{ + Name: expected.Name, + Namespace: expected.Namespace, + }, actual) + if !k8serrors.IsNotFound(err) { + return fmt.Errorf("expected service %s to be deleted", expected.Name) + } + require.Error(t, err) + } + + for _, expected := range resources.serviceAccounts { + actual := &corev1.ServiceAccount{} + err := client.Get(context.Background(), types.NamespacedName{ + Name: expected.Name, + Namespace: expected.Namespace, + }, actual) + if !k8serrors.IsNotFound(err) { + return fmt.Errorf("expected service account %s to be deleted", expected.Name) + } + require.Error(t, err) + } + + return nil +} + +func configureDeployment(name, namespace string, labels map[string]string, replicas int32, nodeSelector map[string]string, tolerations []corev1.Toleration, serviceAccoutName, resourceVersion string) *appsv1.Deployment { + return &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "apps/v1", + Kind: "Deployment", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Labels: labels, + ResourceVersion: resourceVersion, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: "gateway.networking.k8s.io/v1beta1", + Kind: "Gateway", + Name: name, + Controller: ptrTo(true), + BlockOwnerDeletion: ptrTo(true), + }, + }, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + Selector: &metav1.LabelSelector{ + MatchLabels: labels, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: labels, + Annotations: map[string]string{ + "consul.hashicorp.com/connect-inject": "false", + }, + }, + Spec: corev1.PodSpec{ + Affinity: &corev1.Affinity{ + PodAntiAffinity: &corev1.PodAntiAffinity{ + PreferredDuringSchedulingIgnoredDuringExecution: []corev1.WeightedPodAffinityTerm{ + { + Weight: 1, + PodAffinityTerm: corev1.PodAffinityTerm{ + LabelSelector: &metav1.LabelSelector{ + MatchLabels: labels, + }, + TopologyKey: "kubernetes.io/hostname", + }, + }, + }, + }, + }, + NodeSelector: nodeSelector, + Tolerations: tolerations, + ServiceAccountName: serviceAccoutName, + }, + }, + }, + } +} + +func configureRole(name, namespace string, labels map[string]string, resourceVersion string) *rbac.Role { + return &rbac.Role{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "rbac.authorization.k8s.io/v1", + Kind: "Role", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Labels: labels, + ResourceVersion: resourceVersion, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: "gateway.networking.k8s.io/v1beta1", + Kind: "Gateway", + Name: name, + Controller: ptrTo(true), + BlockOwnerDeletion: ptrTo(true), + }, + }, + }, + Rules: []rbac.PolicyRule{}, + } +} + +func configureService(name, namespace string, labels, annotations map[string]string, serviceType corev1.ServiceType, ports []corev1.ServicePort, resourceVersion string) *corev1.Service { + return &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Labels: labels, + Annotations: annotations, + ResourceVersion: resourceVersion, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: "gateway.networking.k8s.io/v1beta1", + Kind: "Gateway", + Name: name, + Controller: ptrTo(true), + BlockOwnerDeletion: ptrTo(true), + }, + }, + }, + Spec: corev1.ServiceSpec{ + Selector: labels, + Type: serviceType, + Ports: ports, + }, + } +} + +func configureServiceAccount(name, namespace string, labels map[string]string, resourceVersion string) *corev1.ServiceAccount { + return &corev1.ServiceAccount{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ServiceAccount", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Labels: labels, + ResourceVersion: resourceVersion, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: "gateway.networking.k8s.io/v1beta1", + Kind: "Gateway", + Name: name, + Controller: ptrTo(true), + BlockOwnerDeletion: ptrTo(true), + }, + }, + }, + } +} + +func ptrTo[T any](t T) *T { + return &t +} diff --git a/control-plane/api-gateway/gatekeeper/init.go b/control-plane/api-gateway/gatekeeper/init.go new file mode 100644 index 0000000000..4f53769c11 --- /dev/null +++ b/control-plane/api-gateway/gatekeeper/init.go @@ -0,0 +1,199 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package gatekeeper + +import ( + "bytes" + "strconv" + "strings" + "text/template" + + corev1 "k8s.io/api/core/v1" + + apigateway "github.com/hashicorp/consul-k8s/control-plane/api-gateway" + "github.com/hashicorp/consul-k8s/control-plane/namespaces" + "k8s.io/utils/pointer" +) + +const ( + injectInitContainerName = "consul-connect-inject-init" + initContainersUserAndGroupID = 5996 +) + +type initContainerCommandData struct { + ServiceName string + ServiceAccountName string + AuthMethod string + + // Log settings for the connect-init command. + LogLevel string + LogJSON bool +} + +// containerInit returns the init container spec for connect-init that polls for the service and the connect proxy service to be registered +// so that it can save the proxy service id to the shared volume and boostrap Envoy with the proxy-id. +func initContainer(config apigateway.HelmConfig, name, namespace string) (corev1.Container, error) { + data := initContainerCommandData{ + AuthMethod: config.AuthMethod, + LogLevel: config.LogLevel, + LogJSON: config.LogJSON, + ServiceName: name, + ServiceAccountName: name, + } + + // Create expected volume mounts + volMounts := []corev1.VolumeMount{ + { + Name: volumeName, + MountPath: "/consul/connect-inject", + }, + } + + var bearerTokenFile string + if config.AuthMethod != "" { + bearerTokenFile = "/var/run/secrets/kubernetes.io/serviceaccount/token" + } + + // Render the command + var buf bytes.Buffer + tpl := template.Must(template.New("root").Parse(strings.TrimSpace(initContainerCommandTpl))) + + if err := tpl.Execute(&buf, &data); err != nil { + return corev1.Container{}, err + } + + consulNamespace := namespaces.ConsulNamespace(namespace, config.EnableNamespaces, config.ConsulDestinationNamespace, config.EnableNamespaceMirroring, config.NamespaceMirroringPrefix) + + initContainerName := injectInitContainerName + container := corev1.Container{ + Name: initContainerName, + Image: config.ImageConsulK8S, + + Env: []corev1.EnvVar{ + { + Name: "POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{FieldPath: "metadata.name"}, + }, + }, + { + Name: "POD_NAMESPACE", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{FieldPath: "metadata.namespace"}, + }, + }, + { + Name: "NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "CONSUL_ADDRESSES", + Value: config.ConsulConfig.Address, + }, + { + Name: "CONSUL_GRPC_PORT", + Value: strconv.Itoa(config.ConsulConfig.GRPCPort), + }, + { + Name: "CONSUL_HTTP_PORT", + Value: strconv.Itoa(config.ConsulConfig.HTTPPort), + }, + { + Name: "CONSUL_API_TIMEOUT", + Value: config.ConsulConfig.APITimeout.String(), + }, + { + Name: "CONSUL_NODE_NAME", + Value: "$(NODE_NAME)-virtual", + }, + }, + VolumeMounts: volMounts, + Command: []string{"/bin/sh", "-ec", buf.String()}, + } + + if config.TLSEnabled { + container.Env = append(container.Env, + corev1.EnvVar{ + Name: "CONSUL_USE_TLS", + Value: "true", + }, + corev1.EnvVar{ + Name: "CONSUL_CACERT_PEM", + Value: config.ConsulCACert, + }, + corev1.EnvVar{ + Name: "CONSUL_TLS_SERVER_NAME", + Value: config.ConsulTLSServerName, + }) + } + + if config.AuthMethod != "" { + container.Env = append(container.Env, + corev1.EnvVar{ + Name: "CONSUL_LOGIN_AUTH_METHOD", + Value: config.AuthMethod, + }, + corev1.EnvVar{ + Name: "CONSUL_LOGIN_BEARER_TOKEN_FILE", + Value: bearerTokenFile, + }, + corev1.EnvVar{ + Name: "CONSUL_LOGIN_META", + Value: "pod=$(POD_NAMESPACE)/$(POD_NAME)", + }) + + container.Env = append(container.Env, corev1.EnvVar{ + Name: "CONSUL_LOGIN_NAMESPACE", + Value: consulNamespace, + }) + + if config.ConsulPartition != "" { + container.Env = append(container.Env, corev1.EnvVar{ + Name: "CONSUL_LOGIN_PARTITION", + Value: config.ConsulPartition, + }) + } + } + container.Env = append(container.Env, + corev1.EnvVar{ + Name: "CONSUL_NAMESPACE", + Value: consulNamespace, + }) + + if config.ConsulPartition != "" { + container.Env = append(container.Env, + corev1.EnvVar{ + Name: "CONSUL_PARTITION", + Value: config.ConsulPartition, + }) + } + + container.SecurityContext = &corev1.SecurityContext{ + RunAsUser: pointer.Int64(initContainersUserAndGroupID), + RunAsGroup: pointer.Int64(initContainersUserAndGroupID), + RunAsNonRoot: pointer.Bool(true), + Privileged: pointer.Bool(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"ALL"}, + }, + } + + return container, nil +} + +// initContainerCommandTpl is the template for the command executed by +// the init container. +const initContainerCommandTpl = ` +consul-k8s-control-plane connect-init -pod-name=${POD_NAME} -pod-namespace=${POD_NAMESPACE} \ + -gateway-kind="api-gateway" \ + -log-json={{ .LogJSON }} \ + {{- if .AuthMethod }} + -service-account-name="{{ .ServiceAccountName }}" \ + {{- end }} + -service-name="{{ .ServiceName }}" +` diff --git a/control-plane/api-gateway/gatekeeper/role.go b/control-plane/api-gateway/gatekeeper/role.go new file mode 100644 index 0000000000..604c503be5 --- /dev/null +++ b/control-plane/api-gateway/gatekeeper/role.go @@ -0,0 +1,90 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package gatekeeper + +import ( + "context" + "errors" + + "k8s.io/apimachinery/pkg/types" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + + apigateway "github.com/hashicorp/consul-k8s/control-plane/api-gateway" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + rbac "k8s.io/api/rbac/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrl "sigs.k8s.io/controller-runtime" +) + +func (g *Gatekeeper) upsertRole(ctx context.Context, gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config apigateway.HelmConfig) error { + if config.AuthMethod == "" { + return g.deleteRole(ctx, types.NamespacedName{Namespace: gateway.Namespace, Name: gateway.Name}) + } + + role := &rbac.Role{} + exists := false + + // Get ServiceAccount + err := g.Client.Get(ctx, g.namespacedName(gateway), role) + if err != nil && !k8serrors.IsNotFound(err) { + return err + } else if k8serrors.IsNotFound(err) { + exists = false + } else { + exists = true + } + + if exists { + // Ensure we own the Role. + for _, ref := range role.GetOwnerReferences() { + if ref.UID == gateway.GetUID() && ref.Name == gateway.GetName() { + // We found ourselves! + return nil + } + } + return errors.New("Role not owned by controller") + } + + role = g.role(gateway, gcc) + if err := ctrl.SetControllerReference(&gateway, role, g.Client.Scheme()); err != nil { + return err + } + if err := g.Client.Create(ctx, role); err != nil { + return err + } + + return nil +} + +func (g *Gatekeeper) deleteRole(ctx context.Context, nsname types.NamespacedName) error { + if err := g.Client.Delete(ctx, &rbac.Role{ObjectMeta: metav1.ObjectMeta{Name: nsname.Name, Namespace: nsname.Namespace}}); err != nil { + if k8serrors.IsNotFound(err) { + return nil + } + return err + } + + return nil +} + +func (g *Gatekeeper) role(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig) *rbac.Role { + role := &rbac.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: gateway.Name, + Namespace: gateway.Namespace, + Labels: apigateway.LabelsForGateway(&gateway), + }, + Rules: []rbac.PolicyRule{}, + } + if gcc.Spec.PodSecurityPolicy != "" { + role.Rules = append(role.Rules, rbac.PolicyRule{ + APIGroups: []string{"policy"}, + Resources: []string{"podsecuritypolicies"}, + ResourceNames: []string{gcc.Spec.PodSecurityPolicy}, + Verbs: []string{"use"}, + }) + } + return role +} diff --git a/control-plane/api-gateway/gatekeeper/service.go b/control-plane/api-gateway/gatekeeper/service.go new file mode 100644 index 0000000000..1191879390 --- /dev/null +++ b/control-plane/api-gateway/gatekeeper/service.go @@ -0,0 +1,143 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package gatekeeper + +import ( + "context" + + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "k8s.io/apimachinery/pkg/types" + + apigateway "github.com/hashicorp/consul-k8s/control-plane/api-gateway" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/equality" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +var ( + defaultServiceAnnotations = []string{ + "external-dns.alpha.kubernetes.io/hostname", + } +) + +func (g *Gatekeeper) upsertService(ctx context.Context, gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config apigateway.HelmConfig) error { + if gcc.Spec.ServiceType == nil { + return g.deleteService(ctx, types.NamespacedName{Namespace: gateway.Namespace, Name: gateway.Name}) + } + + service := g.service(gateway, gcc) + + mutated := service.DeepCopy() + mutator := newServiceMutator(service, mutated, gateway, g.Client.Scheme()) + + result, err := controllerutil.CreateOrUpdate(ctx, g.Client, mutated, mutator) + if err != nil { + return err + } + + switch result { + case controllerutil.OperationResultCreated: + g.Log.Info("Created Service") + case controllerutil.OperationResultUpdated: + g.Log.Info("Updated Service") + case controllerutil.OperationResultNone: + g.Log.Info("No change to service") + } + + return nil +} + +func (g *Gatekeeper) deleteService(ctx context.Context, nsname types.NamespacedName) error { + if err := g.Client.Delete(ctx, &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: nsname.Name, Namespace: nsname.Namespace}}); err != nil { + if k8serrors.IsNotFound(err) { + return nil + } + return err + } + + return nil +} + +func (g *Gatekeeper) service(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig) *corev1.Service { + ports := []corev1.ServicePort{} + for _, listener := range gateway.Spec.Listeners { + ports = append(ports, corev1.ServicePort{ + Name: string(listener.Name), + Protocol: corev1.Protocol(listener.Protocol), + Port: int32(listener.Port), + }) + } + + // Copy annotations from the Gateway, filtered by those allowed by the GatewayClassConfig. + allowedAnnotations := gcc.Spec.CopyAnnotations.Service + if allowedAnnotations == nil { + allowedAnnotations = defaultServiceAnnotations + } + annotations := make(map[string]string) + for _, allowedAnnotation := range allowedAnnotations { + if value, found := gateway.Annotations[allowedAnnotation]; found { + annotations[allowedAnnotation] = value + } + } + + return &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: gateway.Name, + Namespace: gateway.Namespace, + Labels: apigateway.LabelsForGateway(&gateway), + Annotations: annotations, + }, + Spec: corev1.ServiceSpec{ + Selector: apigateway.LabelsForGateway(&gateway), + Type: *gcc.Spec.ServiceType, + Ports: ports, + }, + } +} + +// mergeService is used to keep annotations and ports from the `from` Service +// to the `to` service. This prevents an infinite reconciliation loop when +// Kubernetes adds this configuration back in. +func mergeService(from, to *corev1.Service) *corev1.Service { + if areServicesEqual(from, to) { + return to + } + + to.Annotations = from.Annotations + to.Spec.Ports = from.Spec.Ports + + return to +} + +func areServicesEqual(a, b *corev1.Service) bool { + if !equality.Semantic.DeepEqual(a.Annotations, b.Annotations) { + return false + } + if len(b.Spec.Ports) != len(a.Spec.Ports) { + return false + } + + for i, port := range a.Spec.Ports { + otherPort := b.Spec.Ports[i] + if port.Port != otherPort.Port { + return false + } + if port.Protocol != otherPort.Protocol { + return false + } + } + return true +} + +func newServiceMutator(service, mutated *corev1.Service, gateway gwv1beta1.Gateway, scheme *runtime.Scheme) resourceMutator { + return func() error { + mutated = mergeService(service, mutated) + return ctrl.SetControllerReference(&gateway, mutated, scheme) + } +} diff --git a/control-plane/api-gateway/gatekeeper/serviceaccount.go b/control-plane/api-gateway/gatekeeper/serviceaccount.go new file mode 100644 index 0000000000..207f4485c4 --- /dev/null +++ b/control-plane/api-gateway/gatekeeper/serviceaccount.go @@ -0,0 +1,80 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package gatekeeper + +import ( + "context" + "errors" + + "k8s.io/apimachinery/pkg/types" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + + apigateway "github.com/hashicorp/consul-k8s/control-plane/api-gateway" + corev1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrl "sigs.k8s.io/controller-runtime" +) + +func (g *Gatekeeper) upsertServiceAccount(ctx context.Context, gateway gwv1beta1.Gateway, config apigateway.HelmConfig) error { + if config.AuthMethod == "" { + return g.deleteServiceAccount(ctx, types.NamespacedName{Namespace: gateway.Namespace, Name: gateway.Name}) + } + + serviceAccount := &corev1.ServiceAccount{} + exists := false + + // Get ServiceAccount if it exists. + err := g.Client.Get(ctx, g.namespacedName(gateway), serviceAccount) + if err != nil && !k8serrors.IsNotFound(err) { + return err + } else if k8serrors.IsNotFound(err) { + exists = false + } else { + exists = true + } + + if exists { + // Ensure we own the ServiceAccount. + for _, ref := range serviceAccount.GetOwnerReferences() { + if ref.UID == gateway.GetUID() && ref.Name == gateway.GetName() { + // We found ourselves! + return nil + } + } + return errors.New("ServiceAccount not owned by controller") + } + + // Create the ServiceAccount. + serviceAccount = g.serviceAccount(gateway) + if err := ctrl.SetControllerReference(&gateway, serviceAccount, g.Client.Scheme()); err != nil { + return err + } + if err := g.Client.Create(ctx, serviceAccount); err != nil { + return err + } + + return nil +} + +func (g *Gatekeeper) deleteServiceAccount(ctx context.Context, nsname types.NamespacedName) error { + if err := g.Client.Delete(ctx, &corev1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: nsname.Name, Namespace: nsname.Namespace}}); err != nil { + if k8serrors.IsNotFound(err) { + return nil + } + return err + } + + return nil +} + +func (g *Gatekeeper) serviceAccount(gateway gwv1beta1.Gateway) *corev1.ServiceAccount { + return &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: gateway.Name, + Namespace: gateway.Namespace, + Labels: apigateway.LabelsForGateway(&gateway), + }, + } +} diff --git a/control-plane/api-gateway/helm_config.go b/control-plane/api-gateway/helm_config.go new file mode 100644 index 0000000000..ef7ab22df3 --- /dev/null +++ b/control-plane/api-gateway/helm_config.go @@ -0,0 +1,35 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package apigateway + +import "time" + +// HelmConfig is the configuration of gateways that comes in from the user's Helm values. +type HelmConfig struct { + // ImageDataplane is the Consul Dataplane image to use in gateway deployments. + ImageDataplane string + ImageConsulK8S string + ConsulDestinationNamespace string + NamespaceMirroringPrefix string + EnableNamespaces bool + EnableOpenShift bool + EnableNamespaceMirroring bool + AuthMethod string + // LogLevel is the logging level of the deployed Consul Dataplanes. + LogLevel string + ConsulPartition string + LogJSON bool + TLSEnabled bool + PeeringEnabled bool + ConsulTLSServerName string + ConsulCACert string + ConsulConfig ConsulConfig +} + +type ConsulConfig struct { + Address string + GRPCPort int + HTTPPort int + APITimeout time.Duration +} diff --git a/control-plane/api-gateway/labels.go b/control-plane/api-gateway/labels.go new file mode 100644 index 0000000000..3b4f7c5f1f --- /dev/null +++ b/control-plane/api-gateway/labels.go @@ -0,0 +1,27 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package apigateway + +import ( + "fmt" + + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +const ( + nameLabel = "gateway.consul.hashicorp.com/name" + namespaceLabel = "gateway.consul.hashicorp.com/namespace" + createdAtLabel = "gateway.consul.hashicorp.com/created" + managedLabel = "gateway.consul.hashicorp.com/managed" +) + +// LabelsForGateway formats the default labels that appear on objects managed by the controllers. +func LabelsForGateway(gateway *gwv1beta1.Gateway) map[string]string { + return map[string]string{ + nameLabel: gateway.Name, + namespaceLabel: gateway.Namespace, + createdAtLabel: fmt.Sprintf("%d", gateway.CreationTimestamp.Unix()), + managedLabel: "true", + } +} diff --git a/control-plane/api-gateway/translation/config_entry_translation.go b/control-plane/api-gateway/translation/config_entry_translation.go new file mode 100644 index 0000000000..9501859f8b --- /dev/null +++ b/control-plane/api-gateway/translation/config_entry_translation.go @@ -0,0 +1,516 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// Package translation handles translating resources between different types +package translation + +import ( + "strings" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul-k8s/control-plane/namespaces" + "github.com/hashicorp/consul/api" + capi "github.com/hashicorp/consul/api" +) + +const ( + metaKeyManagedBy = "managed-by" + metaValueManagedBy = "consul-k8s-gateway-controller" + metaKeyKubeNS = "k8s-namespace" + metaKeyKubeServiceName = "k8s-service-name" + metaKeyKubeName = "k8s-name" + + // AnnotationGateway is the annotation used to override the gateway name. + AnnotationGateway = "consul.hashicorp.com/gateway" + // AnnotationHTTPRoute is the annotation used to override the http route name. + AnnotationHTTPRoute = "consul.hashicorp.com/http-route" + // AnnotationTCPRoute is the annotation used to override the tcp route name. + AnnotationTCPRoute = "consul.hashicorp.com/tcp-route" + // AnnotationInlineCertificate is the annotation used to override the inline certificate name. + AnnotationInlineCertificate = "consul.hashicorp.com/inline-certificate" +) + +func translateListenerProtocol[T ~string](protocol T) string { + return strings.ToLower(string(protocol)) +} + +// K8sToConsulTranslator handles translating K8s resources into Consul config entries. +type K8sToConsulTranslator struct { + EnableConsulNamespaces bool + ConsulDestNamespace string + EnableK8sMirroring bool + MirroringPrefix string + ConsulPartition string +} + +// GatewayToAPIGateway translates a kuberenetes API gateway into a Consul APIGateway Config Entry. +func (t K8sToConsulTranslator) GatewayToAPIGateway(k8sGW gwv1beta1.Gateway, certs map[types.NamespacedName]api.ResourceReference) capi.APIGatewayConfigEntry { + listeners := make([]capi.APIGatewayListener, 0, len(k8sGW.Spec.Listeners)) + for _, listener := range k8sGW.Spec.Listeners { + var certificates []capi.ResourceReference + if listener.TLS != nil { + certificates = make([]capi.ResourceReference, 0, len(listener.TLS.CertificateRefs)) + for _, certificate := range listener.TLS.CertificateRefs { + k8sNS := "" + if certificate.Namespace != nil { + k8sNS = string(*certificate.Namespace) + } + nsn := types.NamespacedName{Name: string(certificate.Name), Namespace: k8sNS} + certRef, ok := certs[nsn] + if !ok { + // we don't have a ref for this certificate in consul + // drop the ref from the created gateway + continue + } + c := capi.ResourceReference{ + Kind: capi.InlineCertificate, + Name: certRef.Name, + Partition: certRef.Partition, + Namespace: certRef.Namespace, + } + certificates = append(certificates, c) + } + } + hostname := "" + if listener.Hostname != nil { + hostname = string(*listener.Hostname) + } + l := capi.APIGatewayListener{ + Name: string(listener.Name), + Hostname: hostname, + Port: int(listener.Port), + Protocol: translateListenerProtocol(listener.Protocol), + TLS: capi.APIGatewayTLSConfiguration{ + Certificates: certificates, + }, + } + + listeners = append(listeners, l) + } + gwName := k8sGW.Name + + if gwNameFromAnnotation, ok := k8sGW.Annotations[AnnotationGateway]; ok && gwNameFromAnnotation != "" && !strings.Contains(gwNameFromAnnotation, ",") { + gwName = gwNameFromAnnotation + } + + return capi.APIGatewayConfigEntry{ + Kind: capi.APIGateway, + Name: gwName, + Meta: map[string]string{ + metaKeyManagedBy: metaValueManagedBy, + metaKeyKubeNS: k8sGW.GetObjectMeta().GetNamespace(), + metaKeyKubeServiceName: k8sGW.GetObjectMeta().GetName(), + metaKeyKubeName: k8sGW.GetObjectMeta().GetName(), + }, + Listeners: listeners, + Partition: t.ConsulPartition, + Namespace: t.getConsulNamespace(k8sGW.GetObjectMeta().GetNamespace()), + } +} + +func (t K8sToConsulTranslator) ReferenceForGateway(k8sGW *gwv1beta1.Gateway) api.ResourceReference { + gwName := k8sGW.Name + if gwNameFromAnnotation, ok := k8sGW.Annotations[AnnotationGateway]; ok && gwNameFromAnnotation != "" && !strings.Contains(gwNameFromAnnotation, ",") { + gwName = gwNameFromAnnotation + } + return api.ResourceReference{ + Kind: api.APIGateway, + Name: gwName, + Namespace: t.getConsulNamespace(k8sGW.GetObjectMeta().GetNamespace()), + } +} + +// HTTPRouteToHTTPRoute translates a k8s HTTPRoute into a Consul HTTPRoute Config Entry. +func (t K8sToConsulTranslator) HTTPRouteToHTTPRoute(k8sHTTPRoute *gwv1beta1.HTTPRoute, parentRefs map[types.NamespacedName]api.ResourceReference, k8sServices map[types.NamespacedName]api.CatalogService, meshServices map[types.NamespacedName]v1alpha1.MeshService) *capi.HTTPRouteConfigEntry { + routeName := k8sHTTPRoute.Name + if routeNameFromAnnotation, ok := k8sHTTPRoute.Annotations[AnnotationHTTPRoute]; ok && routeNameFromAnnotation != "" && !strings.Contains(routeNameFromAnnotation, ",") { + routeName = routeNameFromAnnotation + } + + consulHTTPRoute := &capi.HTTPRouteConfigEntry{ + Kind: capi.HTTPRoute, + Name: routeName, + Meta: map[string]string{ + metaKeyManagedBy: metaValueManagedBy, + metaKeyKubeNS: k8sHTTPRoute.GetObjectMeta().GetNamespace(), + metaKeyKubeServiceName: k8sHTTPRoute.GetObjectMeta().GetName(), + metaKeyKubeName: k8sHTTPRoute.GetObjectMeta().GetName(), + }, + Partition: t.ConsulPartition, + + Namespace: t.getConsulNamespace(k8sHTTPRoute.GetObjectMeta().GetNamespace()), + } + + // translate hostnames + hostnames := make([]string, 0, len(k8sHTTPRoute.Spec.Hostnames)) + for _, k8Hostname := range k8sHTTPRoute.Spec.Hostnames { + hostnames = append(hostnames, string(k8Hostname)) + } + consulHTTPRoute.Hostnames = hostnames + + // translate parent refs + consulHTTPRoute.Parents = translateRouteParentRefs(k8sHTTPRoute.Spec.CommonRouteSpec.ParentRefs, parentRefs) + + // translate rules + consulHTTPRoute.Rules = t.translateHTTPRouteRules(k8sHTTPRoute.Namespace, k8sHTTPRoute.Spec.Rules, k8sServices, meshServices) + + return consulHTTPRoute +} + +func (t K8sToConsulTranslator) ReferenceForHTTPRoute(k8sHTTPRoute *gwv1beta1.HTTPRoute) api.ResourceReference { + routeName := k8sHTTPRoute.Name + if routeNameFromAnnotation, ok := k8sHTTPRoute.Annotations[AnnotationHTTPRoute]; ok && routeNameFromAnnotation != "" && !strings.Contains(routeNameFromAnnotation, ",") { + routeName = routeNameFromAnnotation + } + return api.ResourceReference{ + Kind: api.HTTPRoute, + Name: routeName, + Namespace: t.getConsulNamespace(k8sHTTPRoute.GetObjectMeta().GetNamespace()), + } +} + +// translates parent refs for Routes into Consul Resource References. +func translateRouteParentRefs(k8sParentRefs []gwv1beta1.ParentReference, parentRefs map[types.NamespacedName]api.ResourceReference) []capi.ResourceReference { + parents := make([]capi.ResourceReference, 0, len(k8sParentRefs)) + for _, k8sParentRef := range k8sParentRefs { + namespace := "" + if k8sParentRef.Namespace != nil { + namespace = string(*k8sParentRef.Namespace) + } + parentRef, ok := parentRefs[types.NamespacedName{Name: string(k8sParentRef.Name), Namespace: namespace}] + if !(ok && isRefAPIGateway(k8sParentRef)) { + // we drop any parent refs that consul does not know about + continue + } + sectionName := "" + if k8sParentRef.SectionName != nil { + sectionName = string(*k8sParentRef.SectionName) + } + ref := capi.ResourceReference{ + Kind: capi.APIGateway, // Will this ever not be a gateway? is that something we need to handle? + Name: parentRef.Name, + SectionName: sectionName, + Partition: parentRef.Partition, + Namespace: parentRef.Namespace, + } + parents = append(parents, ref) + } + return parents +} + +// isRefAPIGateway checks if the parent resource is an APIGateway. +func isRefAPIGateway(ref gwv1beta1.ParentReference) bool { + return ref.Kind != nil && *ref.Kind == gwv1beta1.Kind("Gateway") || ref.Group != nil && string(*ref.Group) == gwv1beta1.GroupName +} + +// translate the rules portion of a HTTPRoute. +func (t K8sToConsulTranslator) translateHTTPRouteRules(namespace string, k8sRules []gwv1beta1.HTTPRouteRule, k8sServices map[types.NamespacedName]api.CatalogService, meshServices map[types.NamespacedName]v1alpha1.MeshService) []capi.HTTPRouteRule { + rules := make([]capi.HTTPRouteRule, 0, len(k8sRules)) + for _, k8sRule := range k8sRules { + rule := capi.HTTPRouteRule{} + // translate matches + rule.Matches = translateHTTPMatches(k8sRule.Matches) + + // translate filters + rule.Filters = translateHTTPFilters(k8sRule.Filters) + + // translate services + rule.Services = t.translateHTTPServices(namespace, k8sRule.BackendRefs, k8sServices, meshServices) + + rules = append(rules, rule) + } + return rules +} + +var headerMatchTypeTranslation = map[gwv1beta1.HeaderMatchType]capi.HTTPHeaderMatchType{ + gwv1beta1.HeaderMatchExact: capi.HTTPHeaderMatchExact, + gwv1beta1.HeaderMatchRegularExpression: capi.HTTPHeaderMatchRegularExpression, +} + +var headerPathMatchTypeTranslation = map[gwv1beta1.PathMatchType]capi.HTTPPathMatchType{ + gwv1beta1.PathMatchExact: capi.HTTPPathMatchExact, + gwv1beta1.PathMatchPathPrefix: capi.HTTPPathMatchPrefix, + gwv1beta1.PathMatchRegularExpression: capi.HTTPPathMatchRegularExpression, +} + +var queryMatchTypeTranslation = map[gwv1beta1.QueryParamMatchType]capi.HTTPQueryMatchType{ + gwv1beta1.QueryParamMatchExact: capi.HTTPQueryMatchExact, + gwv1beta1.QueryParamMatchRegularExpression: capi.HTTPQueryMatchRegularExpression, +} + +// translate the http matches section. +func translateHTTPMatches(k8sMatches []gwv1beta1.HTTPRouteMatch) []capi.HTTPMatch { + matches := make([]capi.HTTPMatch, 0, len(k8sMatches)) + for _, k8sMatch := range k8sMatches { + // translate header matches + headers := make([]capi.HTTPHeaderMatch, 0, len(k8sMatch.Headers)) + for _, k8sHeader := range k8sMatch.Headers { + header := capi.HTTPHeaderMatch{ + Name: string(k8sHeader.Name), + Value: k8sHeader.Value, + } + if k8sHeader.Type != nil { + header.Match = headerMatchTypeTranslation[*k8sHeader.Type] + } + headers = append(headers, header) + } + + // translate query matches + queries := make([]capi.HTTPQueryMatch, 0, len(k8sMatch.QueryParams)) + for _, k8sQuery := range k8sMatch.QueryParams { + query := capi.HTTPQueryMatch{ + Name: k8sQuery.Name, + Value: k8sQuery.Value, + } + if k8sQuery.Type != nil { + query.Match = queryMatchTypeTranslation[*k8sQuery.Type] + } + queries = append(queries, query) + } + + match := capi.HTTPMatch{ + Headers: headers, + Query: queries, + } + if k8sMatch.Method != nil { + match.Method = capi.HTTPMatchMethod(*k8sMatch.Method) + } + if k8sMatch.Path != nil { + if k8sMatch.Path.Type != nil { + match.Path.Match = headerPathMatchTypeTranslation[*k8sMatch.Path.Type] + } + if k8sMatch.Path.Value != nil { + match.Path.Value = string(*k8sMatch.Path.Value) + } + } + matches = append(matches, match) + } + return matches +} + +// translate the http filters section. +func translateHTTPFilters(k8sFilters []gwv1beta1.HTTPRouteFilter) capi.HTTPFilters { + add := make(map[string]string) + set := make(map[string]string) + remove := make([]string, 0) + var urlRewrite *capi.URLRewrite + for _, k8sFilter := range k8sFilters { + for _, adder := range k8sFilter.RequestHeaderModifier.Add { + add[string(adder.Name)] = adder.Value + } + + for _, setter := range k8sFilter.RequestHeaderModifier.Set { + set[string(setter.Name)] = setter.Value + } + + remove = append(remove, k8sFilter.RequestHeaderModifier.Remove...) + + // we drop any path rewrites that are not prefix matches as we don't support those + if k8sFilter.URLRewrite != nil && k8sFilter.URLRewrite.Path.Type == gwv1beta1.PrefixMatchHTTPPathModifier { + urlRewrite = &capi.URLRewrite{Path: *k8sFilter.URLRewrite.Path.ReplacePrefixMatch} + } + + } + filter := capi.HTTPFilters{ + Headers: []capi.HTTPHeaderFilter{ + { + Add: add, + Remove: remove, + Set: set, + }, + }, + URLRewrite: urlRewrite, + } + + return filter +} + +// translate the backendrefs into services. +func (t K8sToConsulTranslator) translateHTTPServices(namespace string, k8sBackendRefs []gwv1beta1.HTTPBackendRef, k8sServices map[types.NamespacedName]api.CatalogService, meshServices map[types.NamespacedName]v1alpha1.MeshService) []capi.HTTPService { + services := make([]capi.HTTPService, 0, len(k8sBackendRefs)) + + for _, k8sRef := range k8sBackendRefs { + backendRef := k8sRef.BackendObjectReference + + nsn := types.NamespacedName{ + Name: string(backendRef.Name), + Namespace: valueOr(backendRef.Namespace, namespace), + } + + isServiceRef := nilOrEqual(backendRef.Group, "") && nilOrEqual(backendRef.Kind, "Service") + isMeshServiceRef := derefEqual(backendRef.Group, v1alpha1.ConsulHashicorpGroup) && derefEqual(backendRef.Kind, v1alpha1.MeshServiceKind) + + k8sService, k8sServiceFound := k8sServices[nsn] + meshService, meshServiceFound := meshServices[nsn] + + if isServiceRef && k8sServiceFound { + service := capi.HTTPService{ + Name: strings.TrimSuffix(k8sService.ServiceName, "-sidecar-proxy"), + Namespace: t.getConsulNamespace(k8sService.Namespace), + Filters: translateHTTPFilters(k8sRef.Filters), + } + if k8sRef.Weight != nil { + service.Weight = int(*k8sRef.Weight) + } + services = append(services, service) + } else if isMeshServiceRef && meshServiceFound { + service := capi.HTTPService{ + Name: meshService.Spec.Name, + Namespace: t.getConsulNamespace(meshService.Namespace), + Filters: translateHTTPFilters(k8sRef.Filters), + } + if k8sRef.Weight != nil { + service.Weight = int(*k8sRef.Weight) + } + services = append(services, service) + } + } + + return services +} + +// TCPRouteToTCPRoute translates a Kuberenetes TCPRoute into a Consul TCPRoute Config Entry. +func (t K8sToConsulTranslator) TCPRouteToTCPRoute(k8sRoute *gwv1alpha2.TCPRoute, parentRefs map[types.NamespacedName]api.ResourceReference, k8sServices map[types.NamespacedName]api.CatalogService, meshServices map[types.NamespacedName]v1alpha1.MeshService) *capi.TCPRouteConfigEntry { + routeName := k8sRoute.Name + if routeNameFromAnnotation, ok := k8sRoute.Annotations[AnnotationTCPRoute]; ok && routeNameFromAnnotation != "" && !strings.Contains(routeNameFromAnnotation, ",") { + routeName = routeNameFromAnnotation + } + + consulRoute := &capi.TCPRouteConfigEntry{ + Kind: capi.TCPRoute, + Name: routeName, + Meta: map[string]string{ + metaKeyManagedBy: metaValueManagedBy, + metaKeyKubeNS: k8sRoute.GetObjectMeta().GetNamespace(), + metaKeyKubeServiceName: k8sRoute.GetObjectMeta().GetName(), + metaKeyKubeName: k8sRoute.GetObjectMeta().GetName(), + }, + Partition: t.ConsulPartition, + + Namespace: t.getConsulNamespace(k8sRoute.GetObjectMeta().GetNamespace()), + } + + // translate parent refs + consulRoute.Parents = translateRouteParentRefs(k8sRoute.Spec.CommonRouteSpec.ParentRefs, parentRefs) + + // translate the services + consulRoute.Services = make([]capi.TCPService, 0) + for _, rule := range k8sRoute.Spec.Rules { + for _, k8sref := range rule.BackendRefs { + backendRef := k8sref.BackendObjectReference + + nsn := types.NamespacedName{ + Name: string(backendRef.Name), + Namespace: valueOr(backendRef.Namespace, k8sRoute.Namespace), + } + + isServiceRef := nilOrEqual(backendRef.Group, "") && nilOrEqual(backendRef.Kind, "Service") + isMeshServiceRef := derefEqual(backendRef.Group, v1alpha1.ConsulHashicorpGroup) && derefEqual(backendRef.Kind, v1alpha1.MeshServiceKind) + + k8sService, k8sServiceFound := k8sServices[nsn] + meshService, meshServiceFound := meshServices[nsn] + + if isServiceRef && k8sServiceFound { + service := capi.TCPService{ + Name: strings.TrimSuffix(k8sService.ServiceName, "-sidecar-proxy"), + Namespace: t.getConsulNamespace(k8sService.Namespace), + } + consulRoute.Services = append(consulRoute.Services, service) + } else if isMeshServiceRef && meshServiceFound { + service := capi.TCPService{ + Name: meshService.Spec.Name, + Namespace: t.getConsulNamespace(meshService.Namespace), + } + consulRoute.Services = append(consulRoute.Services, service) + } + } + } + + return consulRoute +} + +func (t K8sToConsulTranslator) ReferenceForTCPRoute(k8sTCPRoute *gwv1alpha2.TCPRoute) api.ResourceReference { + routeName := k8sTCPRoute.Name + if routeNameFromAnnotation, ok := k8sTCPRoute.Annotations[AnnotationTCPRoute]; ok && routeNameFromAnnotation != "" && !strings.Contains(routeNameFromAnnotation, ",") { + routeName = routeNameFromAnnotation + } + return api.ResourceReference{ + Kind: api.TCPRoute, + Name: routeName, + Namespace: t.getConsulNamespace(k8sTCPRoute.GetObjectMeta().GetNamespace()), + } +} + +// SecretToInlineCertificate translates a Kuberenetes Secret into a Consul Inline Certificate Config Entry. +func (t K8sToConsulTranslator) SecretToInlineCertificate(k8sSecret corev1.Secret) capi.InlineCertificateConfigEntry { + namespace := t.getConsulNamespace(k8sSecret.GetObjectMeta().GetNamespace()) + return capi.InlineCertificateConfigEntry{ + Kind: capi.InlineCertificate, + Namespace: namespace, + Name: k8sSecret.Name, + Certificate: k8sSecret.StringData[corev1.TLSCertKey], + PrivateKey: k8sSecret.StringData[corev1.TLSPrivateKeyKey], + Meta: map[string]string{ + metaKeyManagedBy: metaValueManagedBy, + metaKeyKubeNS: namespace, + metaKeyKubeServiceName: string(k8sSecret.Name), + metaKeyKubeName: string(k8sSecret.Name), + }, + } +} + +func (t K8sToConsulTranslator) ReferenceForSecret(k8sSecret corev1.Secret) api.ResourceReference { + return api.ResourceReference{ + Kind: api.InlineCertificate, + Name: k8sSecret.Name, + Namespace: t.getConsulNamespace(k8sSecret.GetObjectMeta().GetNamespace()), + } +} + +func EntryToNamespacedName(entry capi.ConfigEntry) types.NamespacedName { + meta := entry.GetMeta() + return types.NamespacedName{ + Name: meta[metaKeyKubeName], + Namespace: meta[metaKeyKubeNS], + } +} + +func (t K8sToConsulTranslator) getConsulNamespace(k8sNS string) string { + return namespaces.ConsulNamespace(k8sNS, t.EnableK8sMirroring, t.ConsulDestNamespace, t.EnableK8sMirroring, t.MirroringPrefix) +} + +func EntryToReference(entry capi.ConfigEntry) capi.ResourceReference { + return capi.ResourceReference{ + Kind: entry.GetKind(), + Name: entry.GetName(), + Partition: entry.GetPartition(), + Namespace: entry.GetNamespace(), + } +} + +func ptrTo[T any](v T) *T { + return &v +} + +func derefEqual[T ~string](v *T, check string) bool { + if v == nil { + return false + } + return string(*v) == check +} + +func nilOrEqual[T ~string](v *T, check string) bool { + return v == nil || string(*v) == check +} + +func valueOr[T ~string](v *T, fallback string) string { + if v == nil { + return fallback + } + return string(*v) +} diff --git a/control-plane/api-gateway/translation/config_entry_translation_test.go b/control-plane/api-gateway/translation/config_entry_translation_test.go new file mode 100644 index 0000000000..39abe4b613 --- /dev/null +++ b/control-plane/api-gateway/translation/config_entry_translation_test.go @@ -0,0 +1,1636 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package translation + +import ( + "testing" + "time" + + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul/api" + capi "github.com/hashicorp/consul/api" +) + +func TestTranslator_GatewayToAPIGateway(t *testing.T) { + t.Parallel() + k8sObjectName := "my-k8s-gw" + k8sNamespace := "my-k8s-namespace" + + // gw status + gwLastTransmissionTime := time.Now() + + // listener one configuration + listenerOneName := "listener-one" + listenerOneHostname := "*.consul.io" + listenerOnePort := 3366 + listenerOneProtocol := "https" + + // listener one tls config + listenerOneCertName := "one-cert" + listenerOneCertK8sNamespace := "one-cert-k8s-ns" + listenerOneCertConsulNamespace := "one-cert-consul-ns" + + // listener one status + listenerOneLastTransmissionTime := time.Now() + + // listener two configuration + listenerTwoName := "listener-two" + listenerTwoHostname := "*.consul.io" + listenerTwoPort := 5432 + listenerTwoProtocol := "https" + + // listener one tls config + listenerTwoCertName := "two-cert" + listenerTwoCertK8sNamespace := "two-cert-k8s-ns" + listenerTwoCertConsulNamespace := "two-cert-consul-ns" + + // listener two status + listenerTwoLastTransmissionTime := time.Now() + + testCases := map[string]struct { + annotations map[string]string + expectedGWName string + listenerOneK8sCertRefs []gwv1beta1.SecretObjectReference + }{ + "when gw name is not overriden by annotations": { + annotations: make(map[string]string), + expectedGWName: k8sObjectName, + listenerOneK8sCertRefs: []gwv1beta1.SecretObjectReference{ + { + Name: gwv1beta1.ObjectName(listenerOneCertName), + Namespace: ptrTo(gwv1beta1.Namespace(listenerOneCertK8sNamespace)), + }, + }, + }, + "when gw name is overriden by annotations": { + annotations: map[string]string{AnnotationGateway: "my-new-gw-name"}, + expectedGWName: "my-new-gw-name", + listenerOneK8sCertRefs: []gwv1beta1.SecretObjectReference{ + { + Name: gwv1beta1.ObjectName(listenerOneCertName), + Namespace: ptrTo(gwv1beta1.Namespace(listenerOneCertK8sNamespace)), + }, + }, + }, + "when k8s has certs that are not referenced in consul": { + annotations: make(map[string]string), + expectedGWName: k8sObjectName, + listenerOneK8sCertRefs: []gwv1beta1.SecretObjectReference{ + { + Name: gwv1beta1.ObjectName(listenerOneCertName), + Namespace: ptrTo(gwv1beta1.Namespace(listenerOneCertK8sNamespace)), + }, + { + Name: gwv1beta1.ObjectName("cert that won't exist in the translated type"), + Namespace: ptrTo(gwv1beta1.Namespace(listenerOneCertK8sNamespace)), + }, + }, + }, + } + + for name, tc := range testCases { + tc := tc + t.Run(name, func(t *testing.T) { + t.Parallel() + + input := gwv1beta1.Gateway{ + TypeMeta: metav1.TypeMeta{ + Kind: "Gateway", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: k8sObjectName, + Namespace: k8sNamespace, + Annotations: tc.annotations, + }, + Spec: gwv1beta1.GatewaySpec{ + Listeners: []gwv1beta1.Listener{ + { + Name: gwv1beta1.SectionName(listenerOneName), + Hostname: ptrTo(gwv1beta1.Hostname(listenerOneHostname)), + Port: gwv1beta1.PortNumber(listenerOnePort), + Protocol: gwv1beta1.ProtocolType(listenerOneProtocol), + TLS: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: tc.listenerOneK8sCertRefs, + }, + }, + { + Name: gwv1beta1.SectionName(listenerTwoName), + Hostname: ptrTo(gwv1beta1.Hostname(listenerTwoHostname)), + Port: gwv1beta1.PortNumber(listenerTwoPort), + Protocol: gwv1beta1.ProtocolType(listenerTwoProtocol), + TLS: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{ + { + Name: gwv1beta1.ObjectName(listenerTwoCertName), + Namespace: ptrTo(gwv1beta1.Namespace(listenerTwoCertK8sNamespace)), + }, + }, + }, + }, + }, + }, + Status: gwv1beta1.GatewayStatus{ + Conditions: []metav1.Condition{ + { + Type: string(gwv1beta1.GatewayConditionAccepted), + Status: metav1.ConditionTrue, + LastTransitionTime: metav1.Time{Time: gwLastTransmissionTime}, + Reason: string(gwv1beta1.GatewayReasonAccepted), + Message: "I'm accepted", + }, + }, + Listeners: []gwv1beta1.ListenerStatus{ + { + Name: gwv1beta1.SectionName(listenerOneName), + AttachedRoutes: 5, + Conditions: []metav1.Condition{ + { + Type: string(gwv1beta1.GatewayConditionReady), + Status: metav1.ConditionTrue, + LastTransitionTime: metav1.Time{Time: listenerOneLastTransmissionTime}, + Reason: string(gwv1beta1.GatewayConditionReady), + Message: "I'm ready", + }, + }, + }, + + { + Name: gwv1beta1.SectionName(listenerTwoName), + AttachedRoutes: 3, + Conditions: []metav1.Condition{ + { + Type: string(gwv1beta1.GatewayConditionReady), + Status: metav1.ConditionTrue, + LastTransitionTime: metav1.Time{Time: listenerTwoLastTransmissionTime}, + Reason: string(gwv1beta1.GatewayConditionReady), + Message: "I'm also ready", + }, + }, + }, + }, + }, + } + + expectedConfigEntry := capi.APIGatewayConfigEntry{ + Kind: capi.APIGateway, + Name: tc.expectedGWName, + Meta: map[string]string{ + metaKeyManagedBy: metaValueManagedBy, + metaKeyKubeNS: k8sNamespace, + metaKeyKubeServiceName: k8sObjectName, + metaKeyKubeName: k8sObjectName, + }, + Listeners: []capi.APIGatewayListener{ + { + Name: listenerOneName, + Hostname: listenerOneHostname, + Port: listenerOnePort, + Protocol: listenerOneProtocol, + TLS: capi.APIGatewayTLSConfiguration{ + Certificates: []capi.ResourceReference{ + { + Kind: capi.InlineCertificate, + Name: listenerOneCertName, + Namespace: listenerOneCertConsulNamespace, + }, + }, + }, + }, + { + Name: listenerTwoName, + Hostname: listenerTwoHostname, + Port: listenerTwoPort, + Protocol: listenerTwoProtocol, + TLS: capi.APIGatewayTLSConfiguration{ + Certificates: []capi.ResourceReference{ + { + Kind: capi.InlineCertificate, + Name: listenerTwoCertName, + Namespace: listenerTwoCertConsulNamespace, + }, + }, + }, + }, + }, + Status: capi.ConfigEntryStatus{}, + Namespace: k8sNamespace, + } + translator := K8sToConsulTranslator{ + EnableConsulNamespaces: true, + ConsulDestNamespace: "", + EnableK8sMirroring: true, + MirroringPrefix: "", + } + + certs := map[types.NamespacedName]api.ResourceReference{ + {Name: listenerOneCertName, Namespace: listenerOneCertK8sNamespace}: { + Name: listenerOneCertName, + Namespace: listenerOneCertConsulNamespace, + }, + {Name: listenerTwoCertName, Namespace: listenerTwoCertK8sNamespace}: { + Name: listenerTwoCertName, + Namespace: listenerTwoCertConsulNamespace, + }, + } + + actualConfigEntry := translator.GatewayToAPIGateway(input, certs) + + if diff := cmp.Diff(expectedConfigEntry, actualConfigEntry); diff != "" { + t.Errorf("Translator.GatewayToAPIGateway() mismatch (-want +got):\n%s", diff) + } + }) + } +} + +func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { + t.Parallel() + type args struct { + k8sHTTPRoute gwv1beta1.HTTPRoute + parentRefs map[types.NamespacedName]api.ResourceReference + services map[types.NamespacedName]api.CatalogService + meshServices map[types.NamespacedName]v1alpha1.MeshService + } + tests := map[string]struct { + args args + want capi.HTTPRouteConfigEntry + }{ + "base test": { + args: args{ + k8sHTTPRoute: gwv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "k8s-http-route", + Namespace: "k8s-ns", + Annotations: map[string]string{}, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + { + Namespace: ptrTo(gwv1beta1.Namespace("k8s-gw-ns")), + Name: gwv1beta1.ObjectName("api-gw"), + Kind: ptrTo(gwv1beta1.Kind("Gateway")), + SectionName: ptrTo(gwv1beta1.SectionName("listener-1")), + }, + }, + }, + Hostnames: []gwv1beta1.Hostname{ + "host-name.example.com", + "consul.io", + }, + Rules: []gwv1beta1.HTTPRouteRule{ + { + Matches: []gwv1beta1.HTTPRouteMatch{ + { + Path: &gwv1beta1.HTTPPathMatch{ + Type: ptrTo(gwv1beta1.PathMatchPathPrefix), + Value: ptrTo("/v1"), + }, + Headers: []gwv1beta1.HTTPHeaderMatch{ + { + Type: ptrTo(gwv1beta1.HeaderMatchExact), + Name: "my header match", + Value: "the value", + }, + }, + QueryParams: []gwv1beta1.HTTPQueryParamMatch{ + { + Type: ptrTo(gwv1beta1.QueryParamMatchExact), + Name: "search", + Value: "term", + }, + }, + Method: ptrTo(gwv1beta1.HTTPMethodGet), + }, + }, + Filters: []gwv1beta1.HTTPRouteFilter{ + { + RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ + Set: []gwv1beta1.HTTPHeader{ + { + Name: "Magic", + Value: "v2", + }, + { + Name: "Another One", + Value: "dj khaled", + }, + }, + Add: []gwv1beta1.HTTPHeader{ + { + Name: "add it on", + Value: "the value", + }, + }, + Remove: []string{"time to go"}, + }, + URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ + Path: &gwv1beta1.HTTPPathModifier{ + Type: gwv1beta1.PrefixMatchHTTPPathModifier, + ReplacePrefixMatch: ptrTo("v1"), + }, + }, + }, + }, + BackendRefs: []gwv1beta1.HTTPBackendRef{ + { + BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "service one", + Namespace: ptrTo(gwv1beta1.Namespace("some ns")), + }, + Weight: ptrTo(int32(45)), + }, + Filters: []gwv1beta1.HTTPRouteFilter{ + { + RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ + Set: []gwv1beta1.HTTPHeader{ + { + Name: "svc - Magic", + Value: "svc - v2", + }, + { + Name: "svc - Another One", + Value: "svc - dj khaled", + }, + }, + Add: []gwv1beta1.HTTPHeader{ + { + Name: "svc - add it on", + Value: "svc - the value", + }, + }, + Remove: []string{"svc - time to go"}, + }, + URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ + Path: &gwv1beta1.HTTPPathModifier{ + Type: gwv1beta1.PrefixMatchHTTPPathModifier, + ReplacePrefixMatch: ptrTo("path"), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + parentRefs: map[types.NamespacedName]api.ResourceReference{ + {Name: "api-gw", Namespace: "k8s-gw-ns"}: {Name: "api-gw", Partition: "part-1", Namespace: "ns"}, + }, + services: map[types.NamespacedName]api.CatalogService{ + {Name: "service one", Namespace: "some ns"}: {ServiceName: "service one", Namespace: "other"}, + }, + }, + want: capi.HTTPRouteConfigEntry{ + Kind: capi.HTTPRoute, + Name: "k8s-http-route", + Parents: []capi.ResourceReference{ + { + Kind: capi.APIGateway, + Name: "api-gw", + SectionName: "listener-1", + Partition: "part-1", + Namespace: "ns", + }, + }, + Rules: []capi.HTTPRouteRule{ + { + Filters: capi.HTTPFilters{ + Headers: []capi.HTTPHeaderFilter{ + { + Add: map[string]string{ + "add it on": "the value", + }, + Remove: []string{"time to go"}, + Set: map[string]string{ + "Magic": "v2", + "Another One": "dj khaled", + }, + }, + }, + URLRewrite: &capi.URLRewrite{Path: "v1"}, + }, + Matches: []capi.HTTPMatch{ + { + Headers: []capi.HTTPHeaderMatch{ + { + Match: capi.HTTPHeaderMatchExact, + Name: "my header match", + Value: "the value", + }, + }, + Method: capi.HTTPMatchMethodGet, + Path: capi.HTTPPathMatch{ + Match: capi.HTTPPathMatchPrefix, + Value: "/v1", + }, + Query: []capi.HTTPQueryMatch{ + { + Match: capi.HTTPQueryMatchExact, + Name: "search", + Value: "term", + }, + }, + }, + }, + Services: []capi.HTTPService{ + { + Name: "service one", + Weight: 45, + Filters: capi.HTTPFilters{ + Headers: []capi.HTTPHeaderFilter{ + { + Add: map[string]string{ + "svc - add it on": "svc - the value", + }, + Remove: []string{"svc - time to go"}, + Set: map[string]string{ + "svc - Magic": "svc - v2", + "svc - Another One": "svc - dj khaled", + }, + }, + }, + URLRewrite: &capi.URLRewrite{ + Path: "path", + }, + }, + Namespace: "other", + }, + }, + }, + }, + Hostnames: []string{ + "host-name.example.com", + "consul.io", + }, + Meta: map[string]string{ + metaKeyManagedBy: metaValueManagedBy, + metaKeyKubeNS: "k8s-ns", + metaKeyKubeServiceName: "k8s-http-route", + metaKeyKubeName: "k8s-http-route", + }, + Namespace: "k8s-ns", + }, + }, + "with httproute name override": { + args: args{ + k8sHTTPRoute: gwv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "k8s-http-route", + Namespace: "k8s-ns", + Annotations: map[string]string{ + AnnotationHTTPRoute: "overrrrride", + }, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + { + Namespace: ptrTo(gwv1beta1.Namespace("k8s-gw-ns")), + Name: gwv1beta1.ObjectName("api-gw"), + Kind: ptrTo(gwv1beta1.Kind("Gateway")), + SectionName: ptrTo(gwv1beta1.SectionName("listener-1")), + }, + }, + }, + Hostnames: []gwv1beta1.Hostname{ + "host-name.example.com", + "consul.io", + }, + Rules: []gwv1beta1.HTTPRouteRule{ + { + Matches: []gwv1beta1.HTTPRouteMatch{ + { + Path: &gwv1beta1.HTTPPathMatch{ + Type: ptrTo(gwv1beta1.PathMatchPathPrefix), + Value: ptrTo("/v1"), + }, + Headers: []gwv1beta1.HTTPHeaderMatch{ + { + Type: ptrTo(gwv1beta1.HeaderMatchExact), + Name: "my header match", + Value: "the value", + }, + }, + QueryParams: []gwv1beta1.HTTPQueryParamMatch{ + { + Type: ptrTo(gwv1beta1.QueryParamMatchExact), + Name: "search", + Value: "term", + }, + }, + Method: ptrTo(gwv1beta1.HTTPMethodGet), + }, + }, + Filters: []gwv1beta1.HTTPRouteFilter{ + { + RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ + Set: []gwv1beta1.HTTPHeader{ + { + Name: "Magic", + Value: "v2", + }, + { + Name: "Another One", + Value: "dj khaled", + }, + }, + Add: []gwv1beta1.HTTPHeader{ + { + Name: "add it on", + Value: "the value", + }, + }, + Remove: []string{"time to go"}, + }, + URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ + Path: &gwv1beta1.HTTPPathModifier{ + Type: gwv1beta1.PrefixMatchHTTPPathModifier, + ReplacePrefixMatch: ptrTo("v1"), + }, + }, + }, + }, + BackendRefs: []gwv1beta1.HTTPBackendRef{ + { + BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "service one", + Namespace: ptrTo(gwv1beta1.Namespace("some ns")), + }, + Weight: ptrTo(int32(45)), + }, + Filters: []gwv1beta1.HTTPRouteFilter{ + { + RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ + Set: []gwv1beta1.HTTPHeader{ + { + Name: "svc - Magic", + Value: "svc - v2", + }, + { + Name: "svc - Another One", + Value: "svc - dj khaled", + }, + }, + Add: []gwv1beta1.HTTPHeader{ + { + Name: "svc - add it on", + Value: "svc - the value", + }, + }, + Remove: []string{"svc - time to go"}, + }, + URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ + Path: &gwv1beta1.HTTPPathModifier{ + Type: gwv1beta1.PrefixMatchHTTPPathModifier, + ReplacePrefixMatch: ptrTo("path"), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + parentRefs: map[types.NamespacedName]api.ResourceReference{ + {Name: "api-gw", Namespace: "k8s-gw-ns"}: {Name: "api-gw", Partition: "part-1", Namespace: "ns"}, + }, + services: map[types.NamespacedName]api.CatalogService{ + {Name: "service one", Namespace: "some ns"}: {ServiceName: "service one", Namespace: "some ns"}, + }, + }, + want: capi.HTTPRouteConfigEntry{ + Kind: capi.HTTPRoute, + Name: "overrrrride", + Parents: []capi.ResourceReference{ + { + Kind: capi.APIGateway, + Name: "api-gw", + SectionName: "listener-1", + Partition: "part-1", + Namespace: "ns", + }, + }, + Rules: []capi.HTTPRouteRule{ + { + Filters: capi.HTTPFilters{ + Headers: []capi.HTTPHeaderFilter{ + { + Add: map[string]string{ + "add it on": "the value", + }, + Remove: []string{"time to go"}, + Set: map[string]string{ + "Magic": "v2", + "Another One": "dj khaled", + }, + }, + }, + URLRewrite: &capi.URLRewrite{Path: "v1"}, + }, + Matches: []capi.HTTPMatch{ + { + Headers: []capi.HTTPHeaderMatch{ + { + Match: capi.HTTPHeaderMatchExact, + Name: "my header match", + Value: "the value", + }, + }, + Method: capi.HTTPMatchMethodGet, + Path: capi.HTTPPathMatch{ + Match: capi.HTTPPathMatchPrefix, + Value: "/v1", + }, + Query: []capi.HTTPQueryMatch{ + { + Match: capi.HTTPQueryMatchExact, + Name: "search", + Value: "term", + }, + }, + }, + }, + Services: []capi.HTTPService{ + { + Name: "service one", + Weight: 45, + Filters: capi.HTTPFilters{ + Headers: []capi.HTTPHeaderFilter{ + { + Add: map[string]string{ + "svc - add it on": "svc - the value", + }, + Remove: []string{"svc - time to go"}, + Set: map[string]string{ + "svc - Magic": "svc - v2", + "svc - Another One": "svc - dj khaled", + }, + }, + }, + URLRewrite: &capi.URLRewrite{ + Path: "path", + }, + }, + Namespace: "some ns", + }, + }, + }, + }, + Hostnames: []string{ + "host-name.example.com", + "consul.io", + }, + Meta: map[string]string{ + metaKeyManagedBy: metaValueManagedBy, + metaKeyKubeNS: "k8s-ns", + metaKeyKubeServiceName: "k8s-http-route", + metaKeyKubeName: "k8s-http-route", + }, + Namespace: "k8s-ns", + }, + }, + "dropping path rewrites that are not prefix match": { + args: args{ + k8sHTTPRoute: gwv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "k8s-http-route", + Namespace: "k8s-ns", + Annotations: map[string]string{ + AnnotationHTTPRoute: "overrrrride", + }, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + { + Namespace: ptrTo(gwv1beta1.Namespace("k8s-gw-ns")), + Name: gwv1beta1.ObjectName("api-gw"), + SectionName: ptrTo(gwv1beta1.SectionName("listener-1")), + Kind: ptrTo(gwv1beta1.Kind("Gateway")), + }, + }, + }, + Hostnames: []gwv1beta1.Hostname{ + "host-name.example.com", + "consul.io", + }, + Rules: []gwv1beta1.HTTPRouteRule{ + { + Matches: []gwv1beta1.HTTPRouteMatch{ + { + Path: &gwv1beta1.HTTPPathMatch{ + Type: ptrTo(gwv1beta1.PathMatchPathPrefix), + Value: ptrTo("/v1"), + }, + Headers: []gwv1beta1.HTTPHeaderMatch{ + { + Type: ptrTo(gwv1beta1.HeaderMatchExact), + Name: "my header match", + Value: "the value", + }, + }, + QueryParams: []gwv1beta1.HTTPQueryParamMatch{ + { + Type: ptrTo(gwv1beta1.QueryParamMatchExact), + Name: "search", + Value: "term", + }, + }, + Method: ptrTo(gwv1beta1.HTTPMethodGet), + }, + }, + Filters: []gwv1beta1.HTTPRouteFilter{ + { + RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ + Set: []gwv1beta1.HTTPHeader{ + { + Name: "Magic", + Value: "v2", + }, + { + Name: "Another One", + Value: "dj khaled", + }, + }, + Add: []gwv1beta1.HTTPHeader{ + { + Name: "add it on", + Value: "the value", + }, + }, + Remove: []string{"time to go"}, + }, + // THIS IS THE CHANGE + URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ + Path: &gwv1beta1.HTTPPathModifier{ + Type: gwv1beta1.FullPathHTTPPathModifier, + ReplaceFullPath: ptrTo("v1"), + }, + }, + }, + }, + BackendRefs: []gwv1beta1.HTTPBackendRef{ + { + BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "service one", + Namespace: ptrTo(gwv1beta1.Namespace("some ns")), + }, + Weight: ptrTo(int32(45)), + }, + Filters: []gwv1beta1.HTTPRouteFilter{ + { + RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ + Set: []gwv1beta1.HTTPHeader{ + { + Name: "svc - Magic", + Value: "svc - v2", + }, + { + Name: "svc - Another One", + Value: "svc - dj khaled", + }, + }, + Add: []gwv1beta1.HTTPHeader{ + { + Name: "svc - add it on", + Value: "svc - the value", + }, + }, + Remove: []string{"svc - time to go"}, + }, + URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ + Path: &gwv1beta1.HTTPPathModifier{ + Type: gwv1beta1.PrefixMatchHTTPPathModifier, + ReplacePrefixMatch: ptrTo("path"), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + parentRefs: map[types.NamespacedName]api.ResourceReference{ + {Name: "api-gw", Namespace: "k8s-gw-ns"}: {Name: "api-gw", Partition: "part-1", Namespace: "ns"}, + }, + services: map[types.NamespacedName]api.CatalogService{ + {Name: "service one", Namespace: "some ns"}: {ServiceName: "service one", Namespace: "some ns"}, + }, + }, + want: capi.HTTPRouteConfigEntry{ + Kind: capi.HTTPRoute, + Name: "overrrrride", + Parents: []capi.ResourceReference{ + { + Kind: capi.APIGateway, + Name: "api-gw", + SectionName: "listener-1", + Partition: "part-1", + Namespace: "ns", + }, + }, + Rules: []capi.HTTPRouteRule{ + { + Filters: capi.HTTPFilters{ + Headers: []capi.HTTPHeaderFilter{ + { + Add: map[string]string{ + "add it on": "the value", + }, + Remove: []string{"time to go"}, + Set: map[string]string{ + "Magic": "v2", + "Another One": "dj khaled", + }, + }, + }, + }, + Matches: []capi.HTTPMatch{ + { + Headers: []capi.HTTPHeaderMatch{ + { + Match: capi.HTTPHeaderMatchExact, + Name: "my header match", + Value: "the value", + }, + }, + Method: capi.HTTPMatchMethodGet, + Path: capi.HTTPPathMatch{ + Match: capi.HTTPPathMatchPrefix, + Value: "/v1", + }, + Query: []capi.HTTPQueryMatch{ + { + Match: capi.HTTPQueryMatchExact, + Name: "search", + Value: "term", + }, + }, + }, + }, + Services: []capi.HTTPService{ + { + Name: "service one", + Weight: 45, + Filters: capi.HTTPFilters{ + Headers: []capi.HTTPHeaderFilter{ + { + Add: map[string]string{ + "svc - add it on": "svc - the value", + }, + Remove: []string{"svc - time to go"}, + Set: map[string]string{ + "svc - Magic": "svc - v2", + "svc - Another One": "svc - dj khaled", + }, + }, + }, + URLRewrite: &capi.URLRewrite{ + Path: "path", + }, + }, + Namespace: "some ns", + }, + }, + }, + }, + Hostnames: []string{ + "host-name.example.com", + "consul.io", + }, + Meta: map[string]string{ + metaKeyManagedBy: metaValueManagedBy, + metaKeyKubeNS: "k8s-ns", + metaKeyKubeServiceName: "k8s-http-route", + metaKeyKubeName: "k8s-http-route", + }, + Namespace: "k8s-ns", + }, + }, + + "parent ref that is not registered with consul is dropped": { + args: args{ + k8sHTTPRoute: gwv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "k8s-http-route", + Namespace: "k8s-ns", + Annotations: map[string]string{}, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + { + Namespace: ptrTo(gwv1beta1.Namespace("k8s-gw-ns")), + Name: gwv1beta1.ObjectName("api-gw"), + Kind: ptrTo(gwv1beta1.Kind("Gateway")), + SectionName: ptrTo(gwv1beta1.SectionName("listener-1")), + }, + + { + Namespace: ptrTo(gwv1beta1.Namespace("k8s-gw-ns")), + Name: gwv1beta1.ObjectName("consul don't know about me"), + Kind: ptrTo(gwv1beta1.Kind("Gateway")), + SectionName: ptrTo(gwv1beta1.SectionName("listener-1")), + }, + }, + }, + Hostnames: []gwv1beta1.Hostname{ + "host-name.example.com", + "consul.io", + }, + Rules: []gwv1beta1.HTTPRouteRule{ + { + Matches: []gwv1beta1.HTTPRouteMatch{ + { + Path: &gwv1beta1.HTTPPathMatch{ + Type: ptrTo(gwv1beta1.PathMatchPathPrefix), + Value: ptrTo("/v1"), + }, + Headers: []gwv1beta1.HTTPHeaderMatch{ + { + Type: ptrTo(gwv1beta1.HeaderMatchExact), + Name: "my header match", + Value: "the value", + }, + }, + QueryParams: []gwv1beta1.HTTPQueryParamMatch{ + { + Type: ptrTo(gwv1beta1.QueryParamMatchExact), + Name: "search", + Value: "term", + }, + }, + Method: ptrTo(gwv1beta1.HTTPMethodGet), + }, + }, + Filters: []gwv1beta1.HTTPRouteFilter{ + { + RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ + Set: []gwv1beta1.HTTPHeader{ + { + Name: "Magic", + Value: "v2", + }, + { + Name: "Another One", + Value: "dj khaled", + }, + }, + Add: []gwv1beta1.HTTPHeader{ + { + Name: "add it on", + Value: "the value", + }, + }, + Remove: []string{"time to go"}, + }, + URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ + Path: &gwv1beta1.HTTPPathModifier{ + Type: gwv1beta1.PrefixMatchHTTPPathModifier, + ReplacePrefixMatch: ptrTo("v1"), + }, + }, + }, + }, + BackendRefs: []gwv1beta1.HTTPBackendRef{ + { + BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "service one", + Namespace: ptrTo(gwv1beta1.Namespace("some ns")), + }, + Weight: ptrTo(int32(45)), + }, + Filters: []gwv1beta1.HTTPRouteFilter{ + { + RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ + Set: []gwv1beta1.HTTPHeader{ + { + Name: "svc - Magic", + Value: "svc - v2", + }, + { + Name: "svc - Another One", + Value: "svc - dj khaled", + }, + }, + Add: []gwv1beta1.HTTPHeader{ + { + Name: "svc - add it on", + Value: "svc - the value", + }, + }, + Remove: []string{"svc - time to go"}, + }, + URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ + Path: &gwv1beta1.HTTPPathModifier{ + Type: gwv1beta1.PrefixMatchHTTPPathModifier, + ReplacePrefixMatch: ptrTo("path"), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + parentRefs: map[types.NamespacedName]api.ResourceReference{ + {Name: "api-gw", Namespace: "k8s-gw-ns"}: {Name: "api-gw", Partition: "part-1", Namespace: "ns"}, + }, + services: map[types.NamespacedName]api.CatalogService{ + {Name: "service one", Namespace: "some ns"}: {ServiceName: "service one", Namespace: "some ns"}, + }, + }, + want: capi.HTTPRouteConfigEntry{ + Kind: capi.HTTPRoute, + Name: "k8s-http-route", + Parents: []capi.ResourceReference{ + { + Kind: capi.APIGateway, + Name: "api-gw", + SectionName: "listener-1", + Partition: "part-1", + Namespace: "ns", + }, + }, + Rules: []capi.HTTPRouteRule{ + { + Filters: capi.HTTPFilters{ + Headers: []capi.HTTPHeaderFilter{ + { + Add: map[string]string{ + "add it on": "the value", + }, + Remove: []string{"time to go"}, + Set: map[string]string{ + "Magic": "v2", + "Another One": "dj khaled", + }, + }, + }, + URLRewrite: &capi.URLRewrite{Path: "v1"}, + }, + Matches: []capi.HTTPMatch{ + { + Headers: []capi.HTTPHeaderMatch{ + { + Match: capi.HTTPHeaderMatchExact, + Name: "my header match", + Value: "the value", + }, + }, + Method: capi.HTTPMatchMethodGet, + Path: capi.HTTPPathMatch{ + Match: capi.HTTPPathMatchPrefix, + Value: "/v1", + }, + Query: []capi.HTTPQueryMatch{ + { + Match: capi.HTTPQueryMatchExact, + Name: "search", + Value: "term", + }, + }, + }, + }, + Services: []capi.HTTPService{ + { + Name: "service one", + Weight: 45, + Filters: capi.HTTPFilters{ + Headers: []capi.HTTPHeaderFilter{ + { + Add: map[string]string{ + "svc - add it on": "svc - the value", + }, + Remove: []string{"svc - time to go"}, + Set: map[string]string{ + "svc - Magic": "svc - v2", + "svc - Another One": "svc - dj khaled", + }, + }, + }, + URLRewrite: &capi.URLRewrite{ + Path: "path", + }, + }, + Namespace: "some ns", + }, + }, + }, + }, + Hostnames: []string{ + "host-name.example.com", + "consul.io", + }, + Meta: map[string]string{ + metaKeyManagedBy: metaValueManagedBy, + metaKeyKubeNS: "k8s-ns", + metaKeyKubeServiceName: "k8s-http-route", + metaKeyKubeName: "k8s-http-route", + }, + Namespace: "k8s-ns", + }, + }, + "when section name on apigw is not supplied": { + args: args{ + k8sHTTPRoute: gwv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "k8s-http-route", + Namespace: "k8s-ns", + Annotations: map[string]string{}, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + { + Namespace: ptrTo(gwv1beta1.Namespace("k8s-gw-ns")), + Name: gwv1beta1.ObjectName("api-gw"), + Kind: ptrTo(gwv1beta1.Kind("Gateway")), + }, + }, + }, + Hostnames: []gwv1beta1.Hostname{ + "host-name.example.com", + "consul.io", + }, + Rules: []gwv1beta1.HTTPRouteRule{ + { + Matches: []gwv1beta1.HTTPRouteMatch{ + { + Path: &gwv1beta1.HTTPPathMatch{ + Type: ptrTo(gwv1beta1.PathMatchPathPrefix), + Value: ptrTo("/v1"), + }, + Headers: []gwv1beta1.HTTPHeaderMatch{ + { + Type: ptrTo(gwv1beta1.HeaderMatchExact), + Name: "my header match", + Value: "the value", + }, + }, + QueryParams: []gwv1beta1.HTTPQueryParamMatch{ + { + Type: ptrTo(gwv1beta1.QueryParamMatchExact), + Name: "search", + Value: "term", + }, + }, + Method: ptrTo(gwv1beta1.HTTPMethodGet), + }, + }, + Filters: []gwv1beta1.HTTPRouteFilter{ + { + RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ + Set: []gwv1beta1.HTTPHeader{ + { + Name: "Magic", + Value: "v2", + }, + { + Name: "Another One", + Value: "dj khaled", + }, + }, + Add: []gwv1beta1.HTTPHeader{ + { + Name: "add it on", + Value: "the value", + }, + }, + Remove: []string{"time to go"}, + }, + URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ + Path: &gwv1beta1.HTTPPathModifier{ + Type: gwv1beta1.PrefixMatchHTTPPathModifier, + ReplacePrefixMatch: ptrTo("v1"), + }, + }, + }, + }, + BackendRefs: []gwv1beta1.HTTPBackendRef{ + { + // this ref should get dropped + BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "service two", + Namespace: ptrTo(gwv1beta1.Namespace("some ns")), + }, + }, + }, + { + BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "some-service-part-three", + Namespace: ptrTo(gwv1beta1.Namespace("svc-ns")), + Group: ptrTo(gwv1beta1.Group(v1alpha1.ConsulHashicorpGroup)), + Kind: ptrTo(gwv1beta1.Kind(v1alpha1.MeshServiceKind)), + }, + }, + }, + { + BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "service one", + Namespace: ptrTo(gwv1beta1.Namespace("some ns")), + }, + Weight: ptrTo(int32(45)), + }, + Filters: []gwv1beta1.HTTPRouteFilter{ + { + RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ + Set: []gwv1beta1.HTTPHeader{ + { + Name: "svc - Magic", + Value: "svc - v2", + }, + { + Name: "svc - Another One", + Value: "svc - dj khaled", + }, + }, + Add: []gwv1beta1.HTTPHeader{ + { + Name: "svc - add it on", + Value: "svc - the value", + }, + }, + Remove: []string{"svc - time to go"}, + }, + URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ + Path: &gwv1beta1.HTTPPathModifier{ + Type: gwv1beta1.PrefixMatchHTTPPathModifier, + ReplacePrefixMatch: ptrTo("path"), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + parentRefs: map[types.NamespacedName]api.ResourceReference{ + {Name: "api-gw", Namespace: "k8s-gw-ns"}: {Name: "api-gw", Partition: "part-1", Namespace: "ns"}, + }, + services: map[types.NamespacedName]api.CatalogService{ + {Name: "service one", Namespace: "some ns"}: {ServiceName: "service one", Namespace: "some ns"}, + }, + meshServices: map[types.NamespacedName]v1alpha1.MeshService{ + {Name: "some-service-part-three", Namespace: "svc-ns"}: {Spec: v1alpha1.MeshServiceSpec{Name: "some-service-part-three"}}, + }, + }, + want: capi.HTTPRouteConfigEntry{ + Kind: capi.HTTPRoute, + Name: "k8s-http-route", + Parents: []capi.ResourceReference{ + { + Kind: capi.APIGateway, + Name: "api-gw", + SectionName: "", + Partition: "part-1", + Namespace: "ns", + }, + }, + Rules: []capi.HTTPRouteRule{ + { + Filters: capi.HTTPFilters{ + Headers: []capi.HTTPHeaderFilter{ + { + Add: map[string]string{ + "add it on": "the value", + }, + Remove: []string{"time to go"}, + Set: map[string]string{ + "Magic": "v2", + "Another One": "dj khaled", + }, + }, + }, + URLRewrite: &capi.URLRewrite{Path: "v1"}, + }, + Matches: []capi.HTTPMatch{ + { + Headers: []capi.HTTPHeaderMatch{ + { + Match: capi.HTTPHeaderMatchExact, + Name: "my header match", + Value: "the value", + }, + }, + Method: capi.HTTPMatchMethodGet, + Path: capi.HTTPPathMatch{ + Match: capi.HTTPPathMatchPrefix, + Value: "/v1", + }, + Query: []capi.HTTPQueryMatch{ + { + Match: capi.HTTPQueryMatchExact, + Name: "search", + Value: "term", + }, + }, + }, + }, + Services: []capi.HTTPService{ + {Name: "some-service-part-three", Filters: capi.HTTPFilters{Headers: []capi.HTTPHeaderFilter{{Add: make(map[string]string), Remove: make([]string, 0), Set: make(map[string]string)}}}}, + { + Name: "service one", + Weight: 45, + Filters: capi.HTTPFilters{ + Headers: []capi.HTTPHeaderFilter{ + { + Add: map[string]string{ + "svc - add it on": "svc - the value", + }, + Remove: []string{"svc - time to go"}, + Set: map[string]string{ + "svc - Magic": "svc - v2", + "svc - Another One": "svc - dj khaled", + }, + }, + }, + URLRewrite: &capi.URLRewrite{ + Path: "path", + }, + }, + Namespace: "some ns", + }, + }, + }, + }, + Hostnames: []string{ + "host-name.example.com", + "consul.io", + }, + Meta: map[string]string{ + metaKeyManagedBy: metaValueManagedBy, + metaKeyKubeNS: "k8s-ns", + metaKeyKubeServiceName: "k8s-http-route", + metaKeyKubeName: "k8s-http-route", + }, + Namespace: "k8s-ns", + }, + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + tr := K8sToConsulTranslator{ + EnableConsulNamespaces: true, + EnableK8sMirroring: true, + } + got := tr.HTTPRouteToHTTPRoute(&tc.args.k8sHTTPRoute, tc.args.parentRefs, tc.args.services, tc.args.meshServices) + if diff := cmp.Diff(&tc.want, got); diff != "" { + t.Errorf("Translator.HTTPRouteToHTTPRoute() mismatch (-want +got):\n%s", diff) + } + }) + } +} + +func TestTranslator_TCPRouteToTCPRoute(t *testing.T) { + t.Parallel() + type args struct { + k8sRoute gwv1alpha2.TCPRoute + parentRefs map[types.NamespacedName]api.ResourceReference + services map[types.NamespacedName]api.CatalogService + meshServices map[types.NamespacedName]v1alpha1.MeshService + } + tests := map[string]struct { + args args + want capi.TCPRouteConfigEntry + }{ + "base test": { + args: args{ + k8sRoute: gwv1alpha2.TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tcp-route", + Namespace: "k8s-ns", + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + { + Namespace: ptrTo(gwv1beta1.Namespace("another-ns")), + Name: "mygw", + SectionName: ptrTo(gwv1beta1.SectionName("listener-one")), + Kind: ptrTo(gwv1beta1.Kind("Gateway")), + }, + }, + }, + Rules: []gwv1alpha2.TCPRouteRule{ + { + BackendRefs: []gwv1beta1.BackendRef{ + { + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "some-service", + Namespace: ptrTo(gwv1beta1.Namespace("svc-ns")), + }, + Weight: new(int32), + }, + }, + }, + { + BackendRefs: []gwv1beta1.BackendRef{ + { + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "some-service-part-two", + Namespace: ptrTo(gwv1beta1.Namespace("svc-ns")), + }, + Weight: new(int32), + }, + }, + }, + }, + }, + }, + parentRefs: map[types.NamespacedName]api.ResourceReference{ + { + Namespace: "another-ns", + Name: "mygw", + }: { + Name: "mygw", + Namespace: "another-ns", + Partition: "", + }, + }, + services: map[types.NamespacedName]api.CatalogService{ + {Name: "some-service", Namespace: "svc-ns"}: {ServiceName: "some-service", Namespace: "svc-ns"}, + {Name: "some-service-part-two", Namespace: "svc-ns"}: {ServiceName: "some-service-part-two", Namespace: "svc-ns"}, + }, + meshServices: map[types.NamespacedName]v1alpha1.MeshService{ + {Name: "some-service-part-three", Namespace: "svc-ns"}: {Spec: v1alpha1.MeshServiceSpec{Name: "some-service-part-three"}}, + }, + }, + want: capi.TCPRouteConfigEntry{ + Kind: capi.TCPRoute, + Name: "tcp-route", + Namespace: "k8s-ns", + Parents: []capi.ResourceReference{ + { + Kind: capi.APIGateway, + Name: "mygw", + SectionName: "listener-one", + Partition: "", + Namespace: "another-ns", + }, + }, + Services: []capi.TCPService{ + { + Name: "some-service", + Partition: "", + Namespace: "svc-ns", + }, + { + Name: "some-service-part-two", + Partition: "", + Namespace: "svc-ns", + }, + }, + Meta: map[string]string{ + metaKeyManagedBy: metaValueManagedBy, + metaKeyKubeNS: "k8s-ns", + metaKeyKubeServiceName: "tcp-route", + metaKeyKubeName: "tcp-route", + }, + }, + }, + + "overwrite the route name via annotaions": { + args: args{ + k8sRoute: gwv1alpha2.TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tcp-route", + Namespace: "k8s-ns", + Annotations: map[string]string{ + AnnotationTCPRoute: "replaced-name", + }, + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + { + Namespace: ptrTo(gwv1beta1.Namespace("another-ns")), + Name: "mygw", + SectionName: ptrTo(gwv1beta1.SectionName("listener-one")), + Kind: ptrTo(gwv1beta1.Kind("Gateway")), + }, + }, + }, + Rules: []gwv1alpha2.TCPRouteRule{ + { + BackendRefs: []gwv1beta1.BackendRef{ + { + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "some-service", + Namespace: ptrTo(gwv1beta1.Namespace("svc-ns")), + }, + Weight: new(int32), + }, + }, + }, + { + BackendRefs: []gwv1beta1.BackendRef{ + { + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "some-service-part-two", + Namespace: ptrTo(gwv1beta1.Namespace("svc-ns")), + }, + Weight: new(int32), + }, + }, + }, + { + BackendRefs: []gwv1beta1.BackendRef{ + { + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "some-service-part-three", + Namespace: ptrTo(gwv1beta1.Namespace("svc-ns")), + Group: ptrTo(gwv1beta1.Group(v1alpha1.ConsulHashicorpGroup)), + Kind: ptrTo(gwv1beta1.Kind(v1alpha1.MeshServiceKind)), + }, + Weight: new(int32), + }, + }, + }, + }, + }, + }, + parentRefs: map[types.NamespacedName]api.ResourceReference{ + { + Namespace: "another-ns", + Name: "mygw", + }: { + Name: "mygw", + Namespace: "another-ns", + Partition: "", + }, + }, + services: map[types.NamespacedName]api.CatalogService{ + {Name: "some-service", Namespace: "svc-ns"}: {ServiceName: "some-service", Namespace: "other"}, + }, + meshServices: map[types.NamespacedName]v1alpha1.MeshService{ + {Name: "some-service-part-three", Namespace: "svc-ns"}: {Spec: v1alpha1.MeshServiceSpec{Name: "some-service-part-three"}}, + }, + }, + want: capi.TCPRouteConfigEntry{ + Kind: capi.TCPRoute, + Name: "replaced-name", + Namespace: "k8s-ns", + Parents: []capi.ResourceReference{ + { + Kind: capi.APIGateway, + Name: "mygw", + SectionName: "listener-one", + Partition: "", + Namespace: "another-ns", + }, + }, + Services: []capi.TCPService{ + { + Name: "some-service", + Partition: "", + Namespace: "other", + }, + {Name: "some-service-part-three"}, + }, + Meta: map[string]string{ + metaKeyManagedBy: metaValueManagedBy, + metaKeyKubeNS: "k8s-ns", + metaKeyKubeServiceName: "tcp-route", + metaKeyKubeName: "tcp-route", + }, + }, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + tr := K8sToConsulTranslator{ + EnableConsulNamespaces: true, + EnableK8sMirroring: true, + } + + got := tr.TCPRouteToTCPRoute(&tt.args.k8sRoute, tt.args.parentRefs, tt.args.services, tt.args.meshServices) + if diff := cmp.Diff(&tt.want, got); diff != "" { + t.Errorf("Translator.TCPRouteToTCPRoute() mismatch (-want +got):\n%s", diff) + } + }) + } +} diff --git a/control-plane/api-gateway/translation/k8s_cache_translation.go b/control-plane/api-gateway/translation/k8s_cache_translation.go new file mode 100644 index 0000000000..88c0eca3b9 --- /dev/null +++ b/control-plane/api-gateway/translation/k8s_cache_translation.go @@ -0,0 +1,134 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package translation + +import ( + "context" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/hashicorp/consul/api" +) + +type TranslatorFn func(api.ConfigEntry) []types.NamespacedName + +type secretTransfomer func(context.Context) func(client.Object) []reconcile.Request + +type resourceGetter interface { + Get(api.ResourceReference) api.ConfigEntry +} + +// ConsulToNamespaceNameTranslator handles translating consul config entries to k8s namespaced names. +type ConsulToNamespaceNameTranslator struct { + cache resourceGetter +} + +// NewConsulToNamespaceNameTranslator creates an instance of the ConsulToNSNTranslator. +func NewConsulToNamespaceNameTranslator(cache resourceGetter) ConsulToNamespaceNameTranslator { + return ConsulToNamespaceNameTranslator{cache: cache} +} + +// BuildConsulGatewayTranslator creates a slice k8s types.NamespacedName from the meta fields of the api gateway config entry. +func (c ConsulToNamespaceNameTranslator) BuildConsulGatewayTranslator(ctx context.Context) TranslatorFn { + return func(config api.ConfigEntry) []types.NamespacedName { + meta, ok := metaToK8sNamespacedName(config) + if !ok { + return nil + } + + return []types.NamespacedName{meta} + } +} + +// BuildConsulHTTPRouteTranslator creates a slice of k8s types.NamespacedName from the meta fields of the http route parent refs. +func (c ConsulToNamespaceNameTranslator) BuildConsulHTTPRouteTranslator(ctx context.Context) TranslatorFn { + return func(config api.ConfigEntry) []types.NamespacedName { + route, ok := config.(*api.HTTPRouteConfigEntry) + if !ok { + return nil + } + + return consulRefsToNSN(c.cache, route.Parents) + } +} + +// BuildConsulTCPRouteTranslator creates a slice of k8s types.NamespacedName from the meta fields of the tcp route parent refs. +func (c ConsulToNamespaceNameTranslator) BuildConsulTCPRouteTranslator(ctx context.Context) TranslatorFn { + return func(config api.ConfigEntry) []types.NamespacedName { + route, ok := config.(*api.TCPRouteConfigEntry) + if !ok { + return nil + } + + return consulRefsToNSN(c.cache, route.Parents) + } +} + +// BuildConsulInlineCertificateTranslator creates a slice of k8s types.NamespacedName from the meta fields of the secret. It does this +// by using a secret transformer function to get a list of reconcile requests from k8s for the given secret and then converts +// those requests to the slice of NamespaceName. +func (c ConsulToNamespaceNameTranslator) BuildConsulInlineCertificateTranslator(ctx context.Context, secretTransformer secretTransfomer) TranslatorFn { + return func(config api.ConfigEntry) []types.NamespacedName { + meta, ok := metaToK8sNamespacedName(config) + if !ok { + return nil + } + + return requestsToRefs(secretTransformer(ctx)(&corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: meta.Name, + Namespace: meta.Namespace, + }, + })) + } +} + +func metaToK8sNamespacedName(config api.ConfigEntry) (types.NamespacedName, bool) { + meta := config.GetMeta() + + namespace, ok := meta[metaKeyKubeNS] + if !ok { + return types.NamespacedName{}, false + } + + name, ok := meta[metaKeyKubeName] + if !ok { + return types.NamespacedName{}, false + } + + return types.NamespacedName{ + Namespace: namespace, + Name: name, + }, true +} + +func consulRefsToNSN(cache resourceGetter, refs []api.ResourceReference) []types.NamespacedName { + nsnSet := make(map[types.NamespacedName]struct{}) + + for _, ref := range refs { + if parent := cache.Get(ref); parent != nil { + if k8sNSN, ok := metaToK8sNamespacedName(parent); ok { + nsnSet[k8sNSN] = struct{}{} + } + } + } + nsns := make([]types.NamespacedName, 0, len(nsnSet)) + + for nsn := range nsnSet { + nsns = append(nsns, nsn) + } + return nsns +} + +func requestsToRefs(objects []reconcile.Request) []types.NamespacedName { + var refs []types.NamespacedName + for _, object := range objects { + refs = append(refs, object.NamespacedName) + } + return refs +} diff --git a/control-plane/api-gateway/translation/k8s_cache_translation_test.go b/control-plane/api-gateway/translation/k8s_cache_translation_test.go new file mode 100644 index 0000000000..52f2ca0eb3 --- /dev/null +++ b/control-plane/api-gateway/translation/k8s_cache_translation_test.go @@ -0,0 +1,460 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package translation + +import ( + "context" + "sort" + "testing" + + "github.com/google/go-cmp/cmp" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/hashicorp/consul/api" +) + +func Test_ConsulToNamespaceNameTranslator_TranslateConsulGateway(t *testing.T) { + t.Parallel() + type args struct { + config *api.APIGatewayConfigEntry + } + tests := []struct { + name string + args args + want []types.NamespacedName + }{ + { + name: "when name and namespace are set", + args: args{ + config: &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "api-gw", + Meta: map[string]string{ + metaKeyKubeNS: "my-ns", + metaKeyKubeName: "api-gw-name", + }, + }, + }, + want: []types.NamespacedName{ + { + Namespace: "my-ns", + Name: "api-gw-name", + }, + }, + }, + { + name: "when name is not set and namespace is set", + args: args{ + config: &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "api-gw", + Meta: map[string]string{ + metaKeyKubeNS: "my-ns", + }, + }, + }, + want: nil, + }, + { + name: "when name is set and namespace is not set", + args: args{ + config: &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "api-gw", + Meta: map[string]string{ + metaKeyKubeName: "api-gw-name", + }, + }, + }, + want: nil, + }, + { + name: "when both name and namespace are not set", + args: args{ + config: &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "api-gw", + Meta: map[string]string{}, + }, + }, + want: nil, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + translator := ConsulToNamespaceNameTranslator{} + fn := translator.BuildConsulGatewayTranslator(context.Background()) + got := fn(tt.args.config) + if diff := cmp.Diff(got, tt.want, sortTransformer()); diff != "" { + t.Errorf("ConsulToNSNTranslator.TranslateConsulGateway() mismatch (-want +got):\n%s", diff) + } + }) + } +} + +func TestConsulToNamespaceNameTranslator_TranslateConsulHTTPRoute(t *testing.T) { + t.Parallel() + type fields struct { + cache resourceGetter + } + tests := []struct { + name string + fields fields + parentRefs []api.ResourceReference + want []types.NamespacedName + }{ + { + name: "all refs in cache", + fields: fields{ + cache: buildMockCache(map[api.ResourceReference]api.ConfigEntry{ + { + Kind: api.APIGateway, + Name: "api-gw-1", + Namespace: "ns", + }: api.ConfigEntry(&api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "api-gw-1", + Meta: map[string]string{ + metaKeyKubeNS: "ns", + metaKeyKubeName: "api-gw-1", + }, + Namespace: "ns", + }), + { + Kind: api.APIGateway, + Name: "api-gw-2", + Namespace: "ns", + }: api.ConfigEntry(&api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "api-gw-2", + Meta: map[string]string{ + metaKeyKubeNS: "ns", + metaKeyKubeName: "api-gw-2", + }, + Namespace: "ns", + }), + }), + }, + parentRefs: []api.ResourceReference{ + { + Kind: api.APIGateway, + Name: "api-gw-1", + Namespace: "ns", + }, + + { + Kind: api.APIGateway, + Name: "api-gw-2", + Namespace: "ns", + }, + }, + want: []types.NamespacedName{ + { + Namespace: "ns", + Name: "api-gw-1", + }, + { + Namespace: "ns", + Name: "api-gw-2", + }, + }, + }, + { + name: "some refs not in cache", + fields: fields{ + cache: buildMockCache(map[api.ResourceReference]api.ConfigEntry{ + { + Kind: api.APIGateway, + Name: "api-gw-1", + Namespace: "ns", + }: api.ConfigEntry(&api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "api-gw-1", + Meta: map[string]string{ + metaKeyKubeNS: "ns", + metaKeyKubeName: "api-gw-1", + }, + Namespace: "ns", + }), + }), + }, + parentRefs: []api.ResourceReference{ + { + Kind: api.APIGateway, + Name: "api-gw-1", + Namespace: "ns", + }, + { + Kind: api.APIGateway, + Name: "api-gw-2", + Namespace: "ns", + }, + }, + want: []types.NamespacedName{ + { + Namespace: "ns", + Name: "api-gw-1", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := ConsulToNamespaceNameTranslator{ + cache: tt.fields.cache, + } + config := &api.HTTPRouteConfigEntry{ + Parents: tt.parentRefs, + } + got := c.BuildConsulHTTPRouteTranslator(context.Background())(config) + if diff := cmp.Diff(got, tt.want, sortTransformer()); diff != "" { + t.Errorf("ConsulToNSNTranslator.TranslateConsulHTTPRoute() mismatch (-want +got):\n%s", diff) + } + }) + } +} + +func TestConsulToNamespaceNameTranslator_TranslateConsulTCPRoute(t *testing.T) { + t.Parallel() + type fields struct { + cache resourceGetter + } + tests := []struct { + name string + fields fields + parentRefs []api.ResourceReference + want []types.NamespacedName + }{ + { + name: "all refs in cache", + fields: fields{ + cache: buildMockCache(map[api.ResourceReference]api.ConfigEntry{ + { + Kind: api.APIGateway, + Name: "api-gw-1", + Namespace: "ns", + }: api.ConfigEntry(&api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "api-gw-1", + Meta: map[string]string{ + metaKeyKubeNS: "ns", + metaKeyKubeName: "api-gw-1", + }, + Namespace: "ns", + }), + { + Kind: api.APIGateway, + Name: "api-gw-2", + Namespace: "ns", + }: api.ConfigEntry(&api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "api-gw-2", + Meta: map[string]string{ + metaKeyKubeNS: "ns", + metaKeyKubeName: "api-gw-2", + }, + Namespace: "ns", + }), + }), + }, + parentRefs: []api.ResourceReference{ + { + Kind: api.APIGateway, + Name: "api-gw-1", + Namespace: "ns", + }, + + { + Kind: api.APIGateway, + Name: "api-gw-2", + Namespace: "ns", + }, + }, + want: []types.NamespacedName{ + { + Namespace: "ns", + Name: "api-gw-1", + }, + { + Namespace: "ns", + Name: "api-gw-2", + }, + }, + }, + { + name: "some refs not in cache", + fields: fields{ + cache: buildMockCache(map[api.ResourceReference]api.ConfigEntry{ + { + Kind: api.APIGateway, + Name: "api-gw-1", + Namespace: "ns", + }: api.ConfigEntry(&api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "api-gw-1", + Meta: map[string]string{ + metaKeyKubeNS: "ns", + metaKeyKubeName: "api-gw-1", + }, + Namespace: "ns", + }), + }), + }, + parentRefs: []api.ResourceReference{ + { + Kind: api.APIGateway, + Name: "api-gw-1", + Namespace: "ns", + }, + { + Kind: api.APIGateway, + Name: "api-gw-2", + Namespace: "ns", + }, + }, + want: []types.NamespacedName{ + { + Namespace: "ns", + Name: "api-gw-1", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := ConsulToNamespaceNameTranslator{ + cache: tt.fields.cache, + } + config := &api.TCPRouteConfigEntry{ + Parents: tt.parentRefs, + } + got := c.BuildConsulTCPRouteTranslator(context.Background())(config) + if diff := cmp.Diff(got, tt.want, sortTransformer()); diff != "" { + t.Errorf("ConsulToNSNTranslator.TranslateConsulTCPRoute() mismatch (-want +got):\n%s", diff) + } + }) + } +} + +func Test_ConsulToNamespaceNameTranslator_TranslateInlineCertificate(t *testing.T) { + t.Parallel() + type args struct { + config *api.InlineCertificateConfigEntry + } + tests := []struct { + name string + args args + want []types.NamespacedName + }{ + { + name: "when name and namespace are set", + args: args{ + config: &api.InlineCertificateConfigEntry{ + Kind: api.InlineCertificate, + Name: "secret", + Meta: map[string]string{ + metaKeyKubeNS: "my-ns", + metaKeyKubeName: "secret", + }, + }, + }, + want: []types.NamespacedName{ + { + Namespace: "my-ns", + Name: "secret", + }, + }, + }, + { + name: "when name is not set and namespace is set", + args: args{ + config: &api.InlineCertificateConfigEntry{ + Kind: api.InlineCertificate, + Name: "secret", + Meta: map[string]string{ + metaKeyKubeNS: "my-ns", + }, + }, + }, + want: nil, + }, + { + name: "when name is set and namespace is not set", + args: args{ + config: &api.InlineCertificateConfigEntry{ + Kind: api.APIGateway, + Name: "secret", + Meta: map[string]string{ + metaKeyKubeName: "secret", + }, + }, + }, + want: nil, + }, + { + name: "when both name and namespace are not set", + args: args{ + config: &api.InlineCertificateConfigEntry{ + Kind: api.InlineCertificate, + Name: "secret", + Meta: map[string]string{}, + }, + }, + want: nil, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + transformer := func(ctx context.Context) func(client.Object) []reconcile.Request { + return func(o client.Object) []reconcile.Request { + return []reconcile.Request{ + { + NamespacedName: types.NamespacedName{Name: o.GetName(), Namespace: o.GetNamespace()}, + }, + } + } + } + + translator := ConsulToNamespaceNameTranslator{} + fn := translator.BuildConsulInlineCertificateTranslator(context.Background(), transformer) + got := fn(tt.args.config) + if diff := cmp.Diff(got, tt.want, sortTransformer()); diff != "" { + t.Errorf("ConsulToNSNTranslator.TranslateConsulInlineCertificate() mismatch (-want +got):\n%s", diff) + } + }) + } +} + +func sortTransformer() cmp.Option { + return cmp.Transformer("Sort", func(in []types.NamespacedName) []types.NamespacedName { + sort.Slice(in, func(i int, j int) bool { + return in[i].Name < in[j].Name + }) + return in + }) +} + +type mockCache struct { + c map[api.ResourceReference]api.ConfigEntry +} + +func (m mockCache) Get(ref api.ResourceReference) api.ConfigEntry { + val, ok := m.c[ref] + if !ok { + return nil + } + return val +} + +func buildMockCache(c map[api.ResourceReference]api.ConfigEntry) mockCache { + return mockCache{c: c} +} diff --git a/control-plane/api/common/configentry_webhook_test.go b/control-plane/api/common/configentry_webhook_test.go index 07f24cff20..7d7139089d 100644 --- a/control-plane/api/common/configentry_webhook_test.go +++ b/control-plane/api/common/configentry_webhook_test.go @@ -115,7 +115,7 @@ func TestValidateConfigEntry(t *testing.T) { }, }, }, - logrtest.TestLogger{T: t}, + logrtest.NewTestLogger(t), lister, c.newResource, ConsulMeta{ diff --git a/control-plane/api/v1alpha1/api_gateway_types.go b/control-plane/api/v1alpha1/api_gateway_types.go new file mode 100644 index 0000000000..f9be4bdb47 --- /dev/null +++ b/control-plane/api/v1alpha1/api_gateway_types.go @@ -0,0 +1,135 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package v1alpha1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + GatewayClassConfigKind = "GatewayClassConfig" + MeshServiceKind = "MeshService" +) + +func init() { + SchemeBuilder.Register(&GatewayClassConfig{}, &GatewayClassConfigList{}) + SchemeBuilder.Register(&MeshService{}, &MeshServiceList{}) +} + +// +genclient +// +kubebuilder:object:root=true +// +kubebuilder:resource:scope=Cluster + +// GatewayClassConfig defines the values that may be set on a GatewayClass for Consul API Gateway. +type GatewayClassConfig struct { + // Standard Kubernetes resource metadata. + metav1.TypeMeta `json:",inline"` + + // Standard object's metadata. + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec defines the desired state of GatewayClassConfig. + Spec GatewayClassConfigSpec `json:"spec,omitempty"` +} + +// +k8s:deepcopy-gen=true + +// GatewayClassConfigSpec specifies the desired state of the Config CRD. +type GatewayClassConfigSpec struct { + + // +kubebuilder:validation:Enum=ClusterIP;NodePort;LoadBalancer + ServiceType *corev1.ServiceType `json:"serviceType,omitempty"` + + // NodeSelector is a selector which must be true for the pod to fit on a node. + // Selector which must match a node's labels for the pod to be scheduled on that node. + // More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + + // Tolerations allow the scheduler to schedule nodes with matching taints. + // More Info: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ + Tolerations []corev1.Toleration `json:"tolerations,omitempty"` + + // Deployment defines the deployment configuration for the gateway. + DeploymentSpec DeploymentSpec `json:"deployment,omitempty"` + + // Annotation Information to copy to services or deployments + CopyAnnotations CopyAnnotationsSpec `json:"copyAnnotations,omitempty"` + + // The name of an existing Kubernetes PodSecurityPolicy to bind to the managed ServiceAccount if ACLs are managed. + PodSecurityPolicy string `json:"podSecurityPolicy,omitempty"` +} + +// +k8s:deepcopy-gen=true + +type DeploymentSpec struct { + // +kubebuilder:default:=1 + // +kubebuilder:validation:Maximum=8 + // +kubebuilder:validation:Minimum=1 + // Number of gateway instances that should be deployed by default + DefaultInstances *int32 `json:"defaultInstances,omitempty"` + // +kubebuilder:default:=8 + // +kubebuilder:validation:Maximum=8 + // +kubebuilder:validation:Minimum=1 + // Max allowed number of gateway instances + MaxInstances *int32 `json:"maxInstances,omitempty"` + // +kubebuilder:default:=1 + // +kubebuilder:validation:Maximum=8 + // +kubebuilder:validation:Minimum=1 + // Minimum allowed number of gateway instances + MinInstances *int32 `json:"minInstances,omitempty"` +} + +//+kubebuilder:object:generate=true + +// CopyAnnotationsSpec defines the annotations that should be copied to the gateway service. +type CopyAnnotationsSpec struct { + // List of annotations to copy to the gateway service. + Service []string `json:"service,omitempty"` +} + +// +kubebuilder:object:root=true + +// GatewayClassConfigList is a list of Config resources. +type GatewayClassConfigList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + + // Items is the list of Configs. + Items []GatewayClassConfig `json:"items"` +} + +// +genclient +// +kubebuilder:object:root=true + +// MeshService holds a reference to an externally managed Consul Service Mesh service. +type MeshService struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec defines the desired state of MeshService. + Spec MeshServiceSpec `json:"spec,omitempty"` +} + +// +k8s:deepcopy-gen=true + +// MeshServiceSpec specifies the 'spec' of the MeshService CRD. +type MeshServiceSpec struct { + // Name holds the service name for a Consul service. + Name string `json:"name,omitempty"` + // Peer optionally specifies the name of the peer exporting the Consul service. + // If not specified, the Consul service is assumed to be in the local datacenter. + Peer *string `json:"peer,omitempty"` +} + +// +kubebuilder:object:root=true + +// MeshServiceList is a list of MeshService resources. +type MeshServiceList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + + Items []MeshService `json:"items"` +} diff --git a/control-plane/api/v1alpha1/api_gateway_types_test.go b/control-plane/api/v1alpha1/api_gateway_types_test.go new file mode 100644 index 0000000000..1f9d8ebef0 --- /dev/null +++ b/control-plane/api/v1alpha1/api_gateway_types_test.go @@ -0,0 +1,48 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package v1alpha1 + +import ( + "testing" + + "github.com/stretchr/testify/require" + core "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestGatewayClassConfigDeepCopy(t *testing.T) { + var nilConfig *GatewayClassConfig + require.Nil(t, nilConfig.DeepCopy()) + require.Nil(t, nilConfig.DeepCopyObject()) + lbType := core.ServiceTypeLoadBalancer + spec := GatewayClassConfigSpec{ + ServiceType: &lbType, + NodeSelector: map[string]string{ + "test": "test", + }, + } + config := &GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + Spec: spec, + } + copy := config.DeepCopy() + copyObject := config.DeepCopyObject() + require.Equal(t, copy, copyObject) + + var nilSpec *GatewayClassConfigSpec + require.Nil(t, nilSpec.DeepCopy()) + specCopy := (&spec).DeepCopy() + require.Equal(t, spec.NodeSelector, specCopy.NodeSelector) + + var nilConfigList *GatewayClassConfigList + require.Nil(t, nilConfigList.DeepCopyObject()) + configList := &GatewayClassConfigList{ + Items: []GatewayClassConfig{*config}, + } + copyConfigList := configList.DeepCopy() + copyConfigListObject := configList.DeepCopyObject() + require.Equal(t, copyConfigList, copyConfigListObject) +} diff --git a/control-plane/api/v1alpha1/exportedservices_webhook_test.go b/control-plane/api/v1alpha1/exportedservices_webhook_test.go index 735f938825..19c2b040bb 100644 --- a/control-plane/api/v1alpha1/exportedservices_webhook_test.go +++ b/control-plane/api/v1alpha1/exportedservices_webhook_test.go @@ -180,7 +180,7 @@ func TestValidateExportedServices(t *testing.T) { validator := &ExportedServicesWebhook{ Client: client, - Logger: logrtest.TestLogger{T: t}, + Logger: logrtest.NewTestLogger(t), decoder: decoder, ConsulMeta: c.consulMeta, } diff --git a/control-plane/api/v1alpha1/mesh_webhook_test.go b/control-plane/api/v1alpha1/mesh_webhook_test.go index def1ebf54f..bd22b02475 100644 --- a/control-plane/api/v1alpha1/mesh_webhook_test.go +++ b/control-plane/api/v1alpha1/mesh_webhook_test.go @@ -97,7 +97,7 @@ func TestValidateMesh(t *testing.T) { validator := &MeshWebhook{ Client: client, - Logger: logrtest.TestLogger{T: t}, + Logger: logrtest.NewTestLogger(t), decoder: decoder, } response := validator.Handle(ctx, admission.Request{ diff --git a/control-plane/api/v1alpha1/peeringacceptor_webhook_test.go b/control-plane/api/v1alpha1/peeringacceptor_webhook_test.go index b97f7cf97c..d0cd39d2d0 100644 --- a/control-plane/api/v1alpha1/peeringacceptor_webhook_test.go +++ b/control-plane/api/v1alpha1/peeringacceptor_webhook_test.go @@ -138,7 +138,7 @@ func TestValidatePeeringAcceptor(t *testing.T) { validator := &PeeringAcceptorWebhook{ Client: client, - Logger: logrtest.TestLogger{T: t}, + Logger: logrtest.NewTestLogger(t), decoder: decoder, } response := validator.Handle(ctx, admission.Request{ diff --git a/control-plane/api/v1alpha1/peeringdialer_webhook_test.go b/control-plane/api/v1alpha1/peeringdialer_webhook_test.go index 62b941d6a1..a6fcb79f9e 100644 --- a/control-plane/api/v1alpha1/peeringdialer_webhook_test.go +++ b/control-plane/api/v1alpha1/peeringdialer_webhook_test.go @@ -138,7 +138,7 @@ func TestValidatePeeringDialer(t *testing.T) { validator := &PeeringDialerWebhook{ Client: client, - Logger: logrtest.TestLogger{T: t}, + Logger: logrtest.NewTestLogger(t), decoder: decoder, } response := validator.Handle(ctx, admission.Request{ diff --git a/control-plane/api/v1alpha1/proxydefaults_webhook_test.go b/control-plane/api/v1alpha1/proxydefaults_webhook_test.go index 9ba6241e57..90e59f0bf8 100644 --- a/control-plane/api/v1alpha1/proxydefaults_webhook_test.go +++ b/control-plane/api/v1alpha1/proxydefaults_webhook_test.go @@ -122,7 +122,7 @@ func TestValidateProxyDefault(t *testing.T) { validator := &ProxyDefaultsWebhook{ Client: client, - Logger: logrtest.TestLogger{T: t}, + Logger: logrtest.NewTestLogger(t), decoder: decoder, } response := validator.Handle(ctx, admission.Request{ diff --git a/control-plane/api/v1alpha1/serviceintentions_webhook_test.go b/control-plane/api/v1alpha1/serviceintentions_webhook_test.go index c6934557c8..507d831e68 100644 --- a/control-plane/api/v1alpha1/serviceintentions_webhook_test.go +++ b/control-plane/api/v1alpha1/serviceintentions_webhook_test.go @@ -253,7 +253,7 @@ func TestHandle_ServiceIntentions_Create(t *testing.T) { validator := &ServiceIntentionsWebhook{ Client: client, - Logger: logrtest.TestLogger{T: t}, + Logger: logrtest.NewTestLogger(t), decoder: decoder, ConsulMeta: common.ConsulMeta{ NamespacesEnabled: true, @@ -442,7 +442,7 @@ func TestHandle_ServiceIntentions_Update(t *testing.T) { validator := &ServiceIntentionsWebhook{ Client: client, - Logger: logrtest.TestLogger{T: t}, + Logger: logrtest.NewTestLogger(t), decoder: decoder, ConsulMeta: common.ConsulMeta{ NamespacesEnabled: true, @@ -602,7 +602,7 @@ func TestHandle_ServiceIntentions_Patches(t *testing.T) { validator := &ServiceIntentionsWebhook{ Client: client, - Logger: logrtest.TestLogger{T: t}, + Logger: logrtest.NewTestLogger(t), decoder: decoder, ConsulMeta: common.ConsulMeta{ NamespacesEnabled: namespacesEnabled, diff --git a/control-plane/api/v1alpha1/zz_generated.deepcopy.go b/control-plane/api/v1alpha1/zz_generated.deepcopy.go index cedd78a1e5..a2e83491ab 100644 --- a/control-plane/api/v1alpha1/zz_generated.deepcopy.go +++ b/control-plane/api/v1alpha1/zz_generated.deepcopy.go @@ -7,7 +7,8 @@ package v1alpha1 import ( "encoding/json" - "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -79,6 +80,56 @@ func (in *CookieConfig) DeepCopy() *CookieConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CopyAnnotationsSpec) DeepCopyInto(out *CopyAnnotationsSpec) { + *out = *in + if in.Service != nil { + in, out := &in.Service, &out.Service + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CopyAnnotationsSpec. +func (in *CopyAnnotationsSpec) DeepCopy() *CopyAnnotationsSpec { + if in == nil { + return nil + } + out := new(CopyAnnotationsSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentSpec) DeepCopyInto(out *DeploymentSpec) { + *out = *in + if in.DefaultInstances != nil { + in, out := &in.DefaultInstances, &out.DefaultInstances + *out = new(int32) + **out = **in + } + if in.MaxInstances != nil { + in, out := &in.MaxInstances, &out.MaxInstances + *out = new(int32) + **out = **in + } + if in.MinInstances != nil { + in, out := &in.MinInstances, &out.MinInstances + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentSpec. +func (in *DeploymentSpec) DeepCopy() *DeploymentSpec { + if in == nil { + return nil + } + out := new(DeploymentSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EnvoyExtension) DeepCopyInto(out *EnvoyExtension) { *out = *in @@ -276,6 +327,100 @@ func (in *FailoverPolicy) DeepCopy() *FailoverPolicy { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GatewayClassConfig) DeepCopyInto(out *GatewayClassConfig) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayClassConfig. +func (in *GatewayClassConfig) DeepCopy() *GatewayClassConfig { + if in == nil { + return nil + } + out := new(GatewayClassConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *GatewayClassConfig) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GatewayClassConfigList) DeepCopyInto(out *GatewayClassConfigList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]GatewayClassConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayClassConfigList. +func (in *GatewayClassConfigList) DeepCopy() *GatewayClassConfigList { + if in == nil { + return nil + } + out := new(GatewayClassConfigList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *GatewayClassConfigList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GatewayClassConfigSpec) DeepCopyInto(out *GatewayClassConfigSpec) { + *out = *in + if in.ServiceType != nil { + in, out := &in.ServiceType, &out.ServiceType + *out = new(v1.ServiceType) + **out = **in + } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Tolerations != nil { + in, out := &in.Tolerations, &out.Tolerations + *out = make([]v1.Toleration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.DeploymentSpec.DeepCopyInto(&out.DeploymentSpec) + in.CopyAnnotations.DeepCopyInto(&out.CopyAnnotations) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayClassConfigSpec. +func (in *GatewayClassConfigSpec) DeepCopy() *GatewayClassConfigSpec { + if in == nil { + return nil + } + out := new(GatewayClassConfigSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GatewayServiceTLSConfig) DeepCopyInto(out *GatewayServiceTLSConfig) { *out = *in @@ -860,6 +1005,84 @@ func (in *MeshList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MeshService) DeepCopyInto(out *MeshService) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MeshService. +func (in *MeshService) DeepCopy() *MeshService { + if in == nil { + return nil + } + out := new(MeshService) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MeshService) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MeshServiceList) DeepCopyInto(out *MeshServiceList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]MeshService, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MeshServiceList. +func (in *MeshServiceList) DeepCopy() *MeshServiceList { + if in == nil { + return nil + } + out := new(MeshServiceList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MeshServiceList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MeshServiceSpec) DeepCopyInto(out *MeshServiceSpec) { + *out = *in + if in.Peer != nil { + in, out := &in.Peer, &out.Peer + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MeshServiceSpec. +func (in *MeshServiceSpec) DeepCopy() *MeshServiceSpec { + if in == nil { + return nil + } + out := new(MeshServiceSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MeshSpec) DeepCopyInto(out *MeshSpec) { *out = *in @@ -932,7 +1155,7 @@ func (in *PassiveHealthCheck) DeepCopyInto(out *PassiveHealthCheck) { } if in.BaseEjectionTime != nil { in, out := &in.BaseEjectionTime, &out.BaseEjectionTime - *out = new(v1.Duration) + *out = new(metav1.Duration) **out = **in } } diff --git a/control-plane/build-support/controller/README.md b/control-plane/build-support/controller/README.md deleted file mode 100644 index 0d24937531..0000000000 --- a/control-plane/build-support/controller/README.md +++ /dev/null @@ -1,5 +0,0 @@ -## Overview - -`boilerplate.go.txt` is a file required by `operator-sdk` when it performs code-generation. - -It's contents provide the headers to the generated files but as we do not require headers for the files we generate, it has been left intentionally blank. diff --git a/control-plane/build-support/controller/boilerplate.go.txt b/control-plane/build-support/controller/boilerplate.go.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/control-plane/catalog/to-consul/resource_test.go b/control-plane/catalog/to-consul/resource_test.go index 3475729950..3c01088c0d 100644 --- a/control-plane/catalog/to-consul/resource_test.go +++ b/control-plane/catalog/to-consul/resource_test.go @@ -1665,8 +1665,8 @@ func TestServiceResource_addIngress(t *testing.T) { }, }, Status: networkingv1.IngressStatus{ - LoadBalancer: corev1.LoadBalancerStatus{ - Ingress: []corev1.LoadBalancerIngress{{IP: "1.2.3.4"}}, + LoadBalancer: networkingv1.IngressLoadBalancerStatus{ + Ingress: []networkingv1.IngressLoadBalancerIngress{{IP: "1.2.3.4"}}, }, }, }, @@ -1712,8 +1712,8 @@ func TestServiceResource_addIngress(t *testing.T) { }, }, Status: networkingv1.IngressStatus{ - LoadBalancer: corev1.LoadBalancerStatus{ - Ingress: []corev1.LoadBalancerIngress{{IP: "1.2.3.4"}}, + LoadBalancer: networkingv1.IngressLoadBalancerStatus{ + Ingress: []networkingv1.IngressLoadBalancerIngress{{IP: "1.2.3.4"}}, }, }, }, diff --git a/control-plane/commands.go b/control-plane/commands.go index 0da29c938b..4e76abf00d 100644 --- a/control-plane/commands.go +++ b/control-plane/commands.go @@ -12,6 +12,7 @@ import ( cmdCreateFederationSecret "github.com/hashicorp/consul-k8s/control-plane/subcommand/create-federation-secret" cmdDeleteCompletedJob "github.com/hashicorp/consul-k8s/control-plane/subcommand/delete-completed-job" cmdFetchServerRegion "github.com/hashicorp/consul-k8s/control-plane/subcommand/fetch-server-region" + cmdGatewayCleanup "github.com/hashicorp/consul-k8s/control-plane/subcommand/gateway-cleanup" cmdGetConsulClientCA "github.com/hashicorp/consul-k8s/control-plane/subcommand/get-consul-client-ca" cmdGossipEncryptionAutogenerate "github.com/hashicorp/consul-k8s/control-plane/subcommand/gossip-encryption-autogenerate" cmdInjectConnect "github.com/hashicorp/consul-k8s/control-plane/subcommand/inject-connect" @@ -49,6 +50,10 @@ func init() { return &cmdConsulLogout.Command{UI: ui}, nil }, + "gateway-cleanup": func() (cli.Command, error) { + return &cmdGatewayCleanup.Command{UI: ui}, nil + }, + "server-acl-init": func() (cli.Command, error) { return &cmdServerACLInit.Command{UI: ui}, nil }, diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml index 0b6b969856..f066c90612 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml @@ -6,7 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: exportedservices.consul.hashicorp.com spec: @@ -134,9 +134,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml new file mode 100644 index 0000000000..a8393cd8fd --- /dev/null +++ b/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml @@ -0,0 +1,140 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: gatewayclassconfigs.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: GatewayClassConfig + listKind: GatewayClassConfigList + plural: gatewayclassconfigs + singular: gatewayclassconfig + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: GatewayClassConfig defines the values that may be set on a GatewayClass + for Consul API Gateway. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of GatewayClassConfig. + properties: + copyAnnotations: + description: Annotation Information to copy to services or deployments + properties: + service: + description: List of annotations to copy to the gateway service. + items: + type: string + type: array + type: object + deployment: + description: Deployment defines the deployment configuration for the + gateway. + properties: + defaultInstances: + default: 1 + description: Number of gateway instances that should be deployed + by default + format: int32 + maximum: 8 + minimum: 1 + type: integer + maxInstances: + default: 8 + description: Max allowed number of gateway instances + format: int32 + maximum: 8 + minimum: 1 + type: integer + minInstances: + default: 1 + description: Minimum allowed number of gateway instances + format: int32 + maximum: 8 + minimum: 1 + type: integer + type: object + nodeSelector: + additionalProperties: + type: string + description: 'NodeSelector is a selector which must be true for the + pod to fit on a node. Selector which must match a node''s labels + for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' + type: object + podSecurityPolicy: + description: The name of an existing Kubernetes PodSecurityPolicy + to bind to the managed ServiceAccount if ACLs are managed. + type: string + serviceType: + description: Service Type string describes ingress methods for a service + enum: + - ClusterIP + - NodePort + - LoadBalancer + type: string + tolerations: + description: 'Tolerations allow the scheduler to schedule nodes with + matching taints. More Info: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/' + items: + description: The pod this Toleration is attached to tolerates any + taint that matches the triple using the matching + operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty + means match all taint effects. When specified, allowed values + are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match all + values and all keys. + type: string + operator: + description: Operator represents a key's relationship to the + value. Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod + can tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time + the toleration (which must be of effect NoExecute, otherwise + this field is ignored) tolerates the taint. By default, it + is not set, which means tolerate the taint forever (do not + evict). Zero and negative values will be treated as 0 (evict + immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array + type: object + type: object + served: true + storage: true diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml index fd8ebc86ff..f7ccf205d9 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml @@ -6,7 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: ingressgateways.consul.hashicorp.com spec: @@ -364,9 +364,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml index adbb12bba6..bc46b6ab37 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml @@ -6,7 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: meshes.consul.hashicorp.com spec: @@ -202,9 +202,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml new file mode 100644 index 0000000000..0871fc32e5 --- /dev/null +++ b/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml @@ -0,0 +1,53 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: meshservices.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: MeshService + listKind: MeshServiceList + plural: meshservices + singular: meshservice + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: MeshService holds a reference to an externally managed Consul + Service Mesh service. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of MeshService. + properties: + name: + description: Name holds the service name for a Consul service. + type: string + peer: + description: Peer optionally specifies the name of the peer exporting + the Consul service. If not specified, the Consul service is assumed + to be in the local datacenter. + type: string + type: object + type: object + served: true + storage: true diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml index 50df179f04..f6f9eda72b 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml @@ -6,7 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: peeringacceptors.consul.hashicorp.com spec: @@ -141,9 +141,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml index 01e4363f14..7e0927c169 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml @@ -6,7 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: peeringdialers.consul.hashicorp.com spec: @@ -141,9 +141,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml index 7084980bf0..7396816f7e 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml @@ -6,7 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: proxydefaults.consul.hashicorp.com spec: @@ -250,9 +250,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml index c71a211f63..23de092485 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml @@ -6,7 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: samenessgroups.consul.hashicorp.com spec: @@ -124,9 +124,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml index ed4dbff857..0890c6323b 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml @@ -6,7 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: servicedefaults.consul.hashicorp.com spec: @@ -490,9 +490,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml index 35930a0e8a..fac2b31b97 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml @@ -6,7 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: serviceintentions.consul.hashicorp.com spec: @@ -231,9 +231,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml index 69084724a9..3cd3b37324 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml @@ -6,7 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: serviceresolvers.consul.hashicorp.com spec: @@ -329,9 +329,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml index 5b9c9d3c1f..25055b0951 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml @@ -6,7 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: servicerouters.consul.hashicorp.com spec: @@ -303,9 +303,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml index aa2b592c94..d5848ed6ec 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml @@ -6,7 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: servicesplitters.consul.hashicorp.com spec: @@ -181,9 +181,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml index b465cd9494..4910e42829 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml @@ -6,7 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: terminatinggateways.consul.hashicorp.com spec: @@ -132,9 +132,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/connect-inject/constants/constants.go b/control-plane/connect-inject/constants/constants.go index 30b7cddac0..48596f9d4b 100644 --- a/control-plane/connect-inject/constants/constants.go +++ b/control-plane/connect-inject/constants/constants.go @@ -16,6 +16,9 @@ const ( // MetaKeyKubeNS is the meta key name for Kubernetes namespace used for the Consul services. MetaKeyKubeNS = "k8s-namespace" + // MetaKeyKubeServiceName is the meta key name for Kubernetes service name used for the Consul services. + MetaKeyKubeServiceName = "k8s-service-name" + // MetaKeyPodName is the meta key name for Kubernetes pod name used for the Consul services. MetaKeyPodName = "pod-name" ) diff --git a/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks_test.go b/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks_test.go index 8c74b4f718..23bd39c1ed 100644 --- a/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks_test.go +++ b/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks_test.go @@ -241,7 +241,7 @@ func TestUpdateHealthCheckOnConsulClient(t *testing.T) { ctrl := Controller{ ConsulClientConfig: testClient.Cfg, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), } err := ctrl.updateHealthCheckOnConsulClient(testClient.Cfg.APIClientConfig, pod, endpoints, c.updateToStatus) diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go index abb84c7116..e0c7f034b1 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go @@ -226,7 +226,7 @@ func TestReconcileCreateEndpointWithNamespaces(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -487,7 +487,7 @@ func TestReconcileCreateGatewayWithNamespaces(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -1494,7 +1494,7 @@ func TestReconcileUpdateEndpointWithNamespaces(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -1780,7 +1780,7 @@ func TestReconcileDeleteEndpointWithNamespaces(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -2074,7 +2074,7 @@ func TestReconcileDeleteGatewayWithNamespaces(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go index e9ae243a12..871081073d 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go @@ -603,7 +603,7 @@ func TestProcessUpstreams(t *testing.T) { for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { ep := &Controller{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSetWith(), EnableConsulNamespaces: tt.consulNamespacesEnabled, @@ -902,7 +902,7 @@ func TestReconcileCreateEndpoint_MultiportService(t *testing.T) { // Create the endpoints controller ep := &Controller{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -2057,7 +2057,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -3377,7 +3377,7 @@ func TestReconcileUpdateEndpoint(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -3627,7 +3627,7 @@ func TestReconcileUpdateEndpoint_LegacyService(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -4001,7 +4001,7 @@ func TestReconcileDeleteEndpoint(t *testing.T) { // Create the endpoints controller ep := &Controller{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -4146,7 +4146,7 @@ func TestReconcileIgnoresServiceIgnoreLabel(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -4232,7 +4232,7 @@ func TestReconcile_podSpecifiesExplicitService(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -5061,7 +5061,7 @@ func TestCreateServiceRegistrations_withTransparentProxy(t *testing.T) { }, }, LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20300), }, @@ -5119,7 +5119,7 @@ func TestCreateServiceRegistrations_withTransparentProxy(t *testing.T) { }, }, LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20300), }, @@ -5176,7 +5176,7 @@ func TestCreateServiceRegistrations_withTransparentProxy(t *testing.T) { }, }, LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20300), }, @@ -5222,7 +5222,7 @@ func TestCreateServiceRegistrations_withTransparentProxy(t *testing.T) { }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20400), }, @@ -5279,7 +5279,7 @@ func TestCreateServiceRegistrations_withTransparentProxy(t *testing.T) { }, }, StartupProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20500), }, @@ -5336,21 +5336,21 @@ func TestCreateServiceRegistrations_withTransparentProxy(t *testing.T) { }, }, LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20300), }, }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20400), }, }, }, StartupProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20500), }, @@ -5415,21 +5415,21 @@ func TestCreateServiceRegistrations_withTransparentProxy(t *testing.T) { }, }, LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20300), }, }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20400), }, }, }, StartupProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20500), }, @@ -5449,21 +5449,21 @@ func TestCreateServiceRegistrations_withTransparentProxy(t *testing.T) { }, }, LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20300 + 1), }, }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20400 + 1), }, }, }, StartupProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20500 + 1), }, @@ -5549,7 +5549,7 @@ func TestCreateServiceRegistrations_withTransparentProxy(t *testing.T) { }, }, LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ TCPSocket: &corev1.TCPSocketAction{ Port: intstr.FromInt(8080), }, @@ -5601,21 +5601,21 @@ func TestCreateServiceRegistrations_withTransparentProxy(t *testing.T) { }, }, LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20300), }, }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20400), }, }, }, StartupProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20500), }, @@ -5711,7 +5711,7 @@ func TestCreateServiceRegistrations_withTransparentProxy(t *testing.T) { Client: fakeClient, EnableTransparentProxy: c.tproxyGlobalEnabled, TProxyOverwriteProbes: c.overwriteProbes, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), } serviceRegistration, proxyServiceRegistration, err := epCtrl.createServiceRegistrations(*pod, *endpoints, api.HealthPassing) diff --git a/control-plane/connect-inject/controllers/peering/peering_acceptor_controller_test.go b/control-plane/connect-inject/controllers/peering/peering_acceptor_controller_test.go index f3b3e6a844..af4d3b0f0a 100644 --- a/control-plane/connect-inject/controllers/peering/peering_acceptor_controller_test.go +++ b/control-plane/connect-inject/controllers/peering/peering_acceptor_controller_test.go @@ -520,7 +520,7 @@ func TestReconcile_CreateUpdatePeeringAcceptor(t *testing.T) { Client: fakeClient, ExposeServersServiceName: "test-expose-servers", ReleaseNamespace: "default", - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, Scheme: s, @@ -638,7 +638,7 @@ func TestReconcile_DeletePeeringAcceptor(t *testing.T) { // Create the peering acceptor controller. controller := &AcceptorController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, Scheme: s, @@ -782,7 +782,7 @@ func TestReconcile_VersionAnnotation(t *testing.T) { // Create the peering acceptor controller controller := &AcceptorController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, Scheme: s, @@ -1086,7 +1086,7 @@ func TestAcceptorUpdateStatus(t *testing.T) { // Create the peering acceptor controller. pac := &AcceptorController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), Scheme: s, } @@ -1198,7 +1198,7 @@ func TestAcceptorUpdateStatusError(t *testing.T) { // Create the peering acceptor controller. controller := &AcceptorController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), Scheme: s, } @@ -1481,7 +1481,7 @@ func TestAcceptor_RequestsForPeeringTokens(t *testing.T) { fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(tt.secret, &tt.acceptors).Build() controller := AcceptorController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), } result := controller.requestsForPeeringTokens(tt.secret) diff --git a/control-plane/connect-inject/controllers/peering/peering_dialer_controller_test.go b/control-plane/connect-inject/controllers/peering/peering_dialer_controller_test.go index e211fe856e..d1b9cba696 100644 --- a/control-plane/connect-inject/controllers/peering/peering_dialer_controller_test.go +++ b/control-plane/connect-inject/controllers/peering/peering_dialer_controller_test.go @@ -321,7 +321,7 @@ func TestReconcile_CreateUpdatePeeringDialer(t *testing.T) { // Create the peering dialer controller controller := &PeeringDialerController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, Scheme: s, @@ -531,7 +531,7 @@ func TestReconcile_VersionAnnotationPeeringDialer(t *testing.T) { // Create the peering dialer controller controller := &PeeringDialerController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), ConsulClientConfig: consulConfig, ConsulServerConnMgr: watcher, Scheme: s, @@ -755,7 +755,7 @@ func TestReconcileDeletePeeringDialer(t *testing.T) { // Create the peering dialer controller. pdc := &PeeringDialerController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, Scheme: s, @@ -887,7 +887,7 @@ func TestDialerUpdateStatus(t *testing.T) { // Create the peering dialer controller. controller := &PeeringDialerController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), Scheme: s, } @@ -999,7 +999,7 @@ func TestDialerUpdateStatusError(t *testing.T) { // Create the peering dialer controller. controller := &PeeringDialerController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), Scheme: s, } @@ -1282,7 +1282,7 @@ func TestDialer_RequestsForPeeringTokens(t *testing.T) { fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(tt.secret, &tt.dialers).Build() controller := PeeringDialerController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), } result := controller.requestsForPeeringTokens(tt.secret) diff --git a/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go b/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go index f5134f208f..fe37720b7d 100644 --- a/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go +++ b/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go @@ -55,7 +55,7 @@ func (w *MeshWebhook) consulDataplaneSidecar(namespace corev1.Namespace, pod cor // If using the proxy health check for a service, configure an HTTP handler // that queries the '/ready' endpoint of the proxy. probe = &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(constants.ProxyDefaultHealthPort + mpi.serviceIndex), Path: "/ready", @@ -65,7 +65,7 @@ func (w *MeshWebhook) consulDataplaneSidecar(namespace corev1.Namespace, pod cor } } else { probe = &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ TCPSocket: &corev1.TCPSocketAction{ Port: intstr.FromInt(constants.ProxyDefaultInboundPort + mpi.serviceIndex), }, diff --git a/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go b/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go index e127915218..0860293352 100644 --- a/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go +++ b/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go @@ -209,7 +209,7 @@ func TestHandlerConsulDataplaneSidecar(t *testing.T) { } expectedProbe := &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ TCPSocket: &corev1.TCPSocketAction{ Port: intstr.FromInt(constants.ProxyDefaultInboundPort), }, @@ -428,7 +428,7 @@ func TestHandlerConsulDataplaneSidecar_ProxyHealthCheck(t *testing.T) { } container, err := h.consulDataplaneSidecar(testNS, pod, multiPortInfo{}) expectedProbe := &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(21000), Path: "/ready", @@ -514,7 +514,7 @@ func TestHandlerConsulDataplaneSidecar_ProxyHealthCheck_Multiport(t *testing.T) } expectedProbe := []*corev1.Probe{ { - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(21000), Path: "/ready", @@ -523,7 +523,7 @@ func TestHandlerConsulDataplaneSidecar_ProxyHealthCheck_Multiport(t *testing.T) InitialDelaySeconds: 1, }, { - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(21001), Path: "/ready", @@ -672,7 +672,7 @@ func TestHandlerConsulDataplaneSidecar_Multiport(t *testing.T) { port := constants.ProxyDefaultInboundPort + i expectedProbe := &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ TCPSocket: &corev1.TCPSocketAction{ Port: intstr.FromInt(port), }, diff --git a/control-plane/connect-inject/webhook/mesh_webhook_ent_test.go b/control-plane/connect-inject/webhook/mesh_webhook_ent_test.go index e6da9b1f49..860694dcef 100644 --- a/control-plane/connect-inject/webhook/mesh_webhook_ent_test.go +++ b/control-plane/connect-inject/webhook/mesh_webhook_ent_test.go @@ -52,7 +52,7 @@ func TestHandler_MutateWithNamespaces(t *testing.T) { { Name: "single destination namespace 'default' from k8s 'default'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -74,7 +74,7 @@ func TestHandler_MutateWithNamespaces(t *testing.T) { { Name: "single destination namespace 'default' from k8s 'non-default'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -96,7 +96,7 @@ func TestHandler_MutateWithNamespaces(t *testing.T) { { Name: "single destination namespace 'dest' from k8s 'default'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -118,7 +118,7 @@ func TestHandler_MutateWithNamespaces(t *testing.T) { { Name: "single destination namespace 'dest' from k8s 'non-default'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -140,7 +140,7 @@ func TestHandler_MutateWithNamespaces(t *testing.T) { { Name: "mirroring from k8s 'default'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -163,7 +163,7 @@ func TestHandler_MutateWithNamespaces(t *testing.T) { { Name: "mirroring from k8s 'dest'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -186,7 +186,7 @@ func TestHandler_MutateWithNamespaces(t *testing.T) { { Name: "mirroring with prefix from k8s 'default'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -210,7 +210,7 @@ func TestHandler_MutateWithNamespaces(t *testing.T) { { Name: "mirroring with prefix from k8s 'dest'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -298,7 +298,7 @@ func TestHandler_MutateWithNamespaces_ACLs(t *testing.T) { { Name: "acls + single destination namespace 'default' from k8s 'default'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -321,7 +321,7 @@ func TestHandler_MutateWithNamespaces_ACLs(t *testing.T) { { Name: "acls + single destination namespace 'default' from k8s 'non-default'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -344,7 +344,7 @@ func TestHandler_MutateWithNamespaces_ACLs(t *testing.T) { { Name: "acls + single destination namespace 'dest' from k8s 'default'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -367,7 +367,7 @@ func TestHandler_MutateWithNamespaces_ACLs(t *testing.T) { { Name: "acls + single destination namespace 'dest' from k8s 'non-default'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -390,7 +390,7 @@ func TestHandler_MutateWithNamespaces_ACLs(t *testing.T) { { Name: "acls + mirroring from k8s 'default'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -414,7 +414,7 @@ func TestHandler_MutateWithNamespaces_ACLs(t *testing.T) { { Name: "acls + mirroring from k8s 'dest'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -438,7 +438,7 @@ func TestHandler_MutateWithNamespaces_ACLs(t *testing.T) { { Name: "acls + mirroring with prefix from k8s 'default'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -463,7 +463,7 @@ func TestHandler_MutateWithNamespaces_ACLs(t *testing.T) { { Name: "acls + mirroring with prefix from k8s 'dest'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -603,7 +603,7 @@ func TestHandler_MutateWithNamespaces_Annotation(t *testing.T) { require.NoError(t, err) webhook := MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, diff --git a/control-plane/connect-inject/webhook/mesh_webhook_test.go b/control-plane/connect-inject/webhook/mesh_webhook_test.go index 20c66bd57a..ddc03eaecc 100644 --- a/control-plane/connect-inject/webhook/mesh_webhook_test.go +++ b/control-plane/connect-inject/webhook/mesh_webhook_test.go @@ -56,7 +56,7 @@ func TestHandlerHandle(t *testing.T) { { "kube-system namespace", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -76,7 +76,7 @@ func TestHandlerHandle(t *testing.T) { { "already injected", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -100,7 +100,7 @@ func TestHandlerHandle(t *testing.T) { { "empty pod basic", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -142,7 +142,7 @@ func TestHandlerHandle(t *testing.T) { { "pod with upstreams specified", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -201,7 +201,7 @@ func TestHandlerHandle(t *testing.T) { { "empty pod with injection disabled", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -227,7 +227,7 @@ func TestHandlerHandle(t *testing.T) { { "empty pod with injection truthy", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -282,7 +282,7 @@ func TestHandlerHandle(t *testing.T) { { "pod with empty volume mount annotation", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -336,7 +336,7 @@ func TestHandlerHandle(t *testing.T) { { "pod with volume mount annotation", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -410,7 +410,7 @@ func TestHandlerHandle(t *testing.T) { { "pod with sidecar volume mount annotation", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -470,7 +470,7 @@ func TestHandlerHandle(t *testing.T) { { "pod with sidecar invalid volume mount annotation", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -501,7 +501,7 @@ func TestHandlerHandle(t *testing.T) { { "pod with service annotation", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -556,7 +556,7 @@ func TestHandlerHandle(t *testing.T) { { "pod with existing label", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -606,7 +606,7 @@ func TestHandlerHandle(t *testing.T) { { "tproxy with overwriteProbes is enabled", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableTransparentProxy: true, @@ -629,14 +629,14 @@ func TestHandlerHandle(t *testing.T) { { Name: "web", LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8080), }, }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8081), }, @@ -696,7 +696,7 @@ func TestHandlerHandle(t *testing.T) { { "multiport pod kube < 1.24 with AuthMethod, serviceaccount has secret ref", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -755,7 +755,7 @@ func TestHandlerHandle(t *testing.T) { { "multiport pod kube 1.24 with AuthMethod, serviceaccount does not have secret ref", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -814,7 +814,7 @@ func TestHandlerHandle(t *testing.T) { { "dns redirection enabled", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableTransparentProxy: true, @@ -888,7 +888,7 @@ func TestHandlerHandle(t *testing.T) { { "dns redirection only enabled if tproxy enabled", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableTransparentProxy: true, @@ -1602,7 +1602,7 @@ func TestOverwriteProbes(t *testing.T) { }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8080), }, @@ -1628,7 +1628,7 @@ func TestOverwriteProbes(t *testing.T) { }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8080), }, @@ -1655,7 +1655,7 @@ func TestOverwriteProbes(t *testing.T) { }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8080), }, @@ -1682,7 +1682,7 @@ func TestOverwriteProbes(t *testing.T) { }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8080), }, @@ -1709,7 +1709,7 @@ func TestOverwriteProbes(t *testing.T) { }, }, LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8081), }, @@ -1745,21 +1745,21 @@ func TestOverwriteProbes(t *testing.T) { }, }, LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8081), }, }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8080), }, }, }, StartupProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8082), }, @@ -1788,21 +1788,21 @@ func TestOverwriteProbes(t *testing.T) { }, }, LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8081), }, }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8080), }, }, }, StartupProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8080), }, @@ -1822,21 +1822,21 @@ func TestOverwriteProbes(t *testing.T) { }, }, LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8083), }, }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8082), }, }, }, StartupProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8082), }, diff --git a/control-plane/connect-inject/webhook/redirect_traffic_test.go b/control-plane/connect-inject/webhook/redirect_traffic_test.go index f94871fa96..df6b7a8559 100644 --- a/control-plane/connect-inject/webhook/redirect_traffic_test.go +++ b/control-plane/connect-inject/webhook/redirect_traffic_test.go @@ -48,7 +48,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "basic bare minimum pod", webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -78,7 +78,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "proxy health checks enabled", webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -111,7 +111,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "metrics enabled", webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -145,7 +145,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "metrics enabled with incorrect annotation", webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -180,7 +180,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "overwrite probes, transparent proxy annotation set", webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -199,7 +199,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { Name: "test", LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(exposedPathsLivenessPortsRangeStart), }, @@ -221,7 +221,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "exclude inbound ports", webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -254,7 +254,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "exclude outbound ports", webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -287,7 +287,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "exclude outbound CIDRs", webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -320,7 +320,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "exclude UIDs", webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -352,7 +352,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "exclude inbound ports, outbound ports, outbound CIDRs, and UIDs", webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, diff --git a/control-plane/controllers/configentry_controller.go b/control-plane/controllers/configentry_controller.go index f35d2e88e7..374af7451e 100644 --- a/control-plane/controllers/configentry_controller.go +++ b/control-plane/controllers/configentry_controller.go @@ -40,11 +40,11 @@ type Controller interface { // Update updates the state of the whole object. Update(context.Context, client.Object, ...client.UpdateOption) error // UpdateStatus updates the state of just the object's status. - UpdateStatus(context.Context, client.Object, ...client.UpdateOption) error + UpdateStatus(context.Context, client.Object, ...client.SubResourceUpdateOption) error // Get retrieves an obj for the given object key from the Kubernetes Cluster. // obj must be a struct pointer so that obj can be updated with the response // returned by the Server. - Get(ctx context.Context, key client.ObjectKey, obj client.Object) error + Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error // Logger returns a logger with values added for the specific controller // and request name. Logger(types.NamespacedName) logr.Logger diff --git a/control-plane/controllers/configentry_controller_ent_test.go b/control-plane/controllers/configentry_controller_ent_test.go index ef2aa6b7a4..ab6c70b9ad 100644 --- a/control-plane/controllers/configentry_controller_ent_test.go +++ b/control-plane/controllers/configentry_controller_ent_test.go @@ -108,7 +108,7 @@ func TestConfigEntryController_createsEntConfigEntry(t *testing.T) { req.True(written) } - r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.TestLogger{T: t}) + r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.NewTestLogger(t)) namespacedName := types.NamespacedName{ Namespace: kubeNS, Name: c.configEntryResource.KubernetesName(), @@ -235,7 +235,7 @@ func TestConfigEntryController_updatesEntConfigEntry(t *testing.T) { c.updateF(c.configEntryResource) err = fakeClient.Update(ctx, c.configEntryResource) req.NoError(err) - r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.TestLogger{T: t}) + r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.NewTestLogger(t)) resp, err := r.Reconcile(ctx, ctrl.Request{ NamespacedName: namespacedName, }) @@ -331,7 +331,7 @@ func TestConfigEntryController_deletesEntConfigEntry(t *testing.T) { Namespace: kubeNS, Name: c.configEntryResourceWithDeletion.KubernetesName(), } - r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.TestLogger{T: t}) + r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.NewTestLogger(t)) resp, err := r.Reconcile(context.Background(), ctrl.Request{ NamespacedName: namespacedName, }) @@ -516,7 +516,7 @@ func TestConfigEntryController_createsConfigEntry_consulNamespaces(tt *testing.T r := in.GetController( fakeClient, - logrtest.TestLogger{T: t}, + logrtest.NewTestLogger(t), s, &ConfigEntryController{ ConsulClientConfig: testClient.Cfg, @@ -778,7 +778,7 @@ func TestConfigEntryController_updatesConfigEntry_consulNamespaces(tt *testing.T r := in.GetControllerFunc( fakeClient, - logrtest.TestLogger{T: t}, + logrtest.NewTestLogger(t), s, &ConfigEntryController{ ConsulClientConfig: testClient.Cfg, @@ -1027,7 +1027,7 @@ func TestConfigEntryController_deletesConfigEntry_consulNamespaces(tt *testing.T r := in.GetControllerFunc( fakeClient, - logrtest.TestLogger{T: t}, + logrtest.NewTestLogger(t), s, &ConfigEntryController{ ConsulClientConfig: testClient.Cfg, diff --git a/control-plane/controllers/configentry_controller_test.go b/control-plane/controllers/configentry_controller_test.go index d548f78069..2404b59365 100644 --- a/control-plane/controllers/configentry_controller_test.go +++ b/control-plane/controllers/configentry_controller_test.go @@ -453,7 +453,7 @@ func TestConfigEntryControllers_createsConfigEntry(t *testing.T) { req.True(written) } - r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.TestLogger{T: t}) + r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.NewTestLogger(t)) namespacedName := types.NamespacedName{ Namespace: kubeNS, Name: c.configEntryResource.KubernetesName(), @@ -953,7 +953,7 @@ func TestConfigEntryControllers_updatesConfigEntry(t *testing.T) { c.updateF(c.configEntryResource) err = fakeClient.Update(ctx, c.configEntryResource) req.NoError(err) - r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.TestLogger{T: t}) + r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.NewTestLogger(t)) resp, err := r.Reconcile(ctx, ctrl.Request{ NamespacedName: namespacedName, }) @@ -1345,7 +1345,7 @@ func TestConfigEntryControllers_deletesConfigEntry(t *testing.T) { Namespace: kubeNS, Name: c.configEntryResourceWithDeletion.KubernetesName(), } - r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.TestLogger{T: t}) + r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.NewTestLogger(t)) resp, err := r.Reconcile(context.Background(), ctrl.Request{ NamespacedName: namespacedName, }) @@ -1392,7 +1392,7 @@ func TestConfigEntryControllers_errorUpdatesSyncStatus(t *testing.T) { reconciler := &ServiceDefaultsController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), ConfigEntryController: &ConfigEntryController{ ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, @@ -1459,7 +1459,7 @@ func TestConfigEntryControllers_setsSyncedToTrue(t *testing.T) { consulClient := testClient.APIClient reconciler := &ServiceDefaultsController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), ConfigEntryController: &ConfigEntryController{ ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, @@ -1551,7 +1551,7 @@ func TestConfigEntryControllers_doesNotCreateUnownedConfigEntry(t *testing.T) { // Attempt to create the entry in Kube and run reconcile. reconciler := ServiceDefaultsController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), ConfigEntryController: &ConfigEntryController{ ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, @@ -1615,7 +1615,7 @@ func TestConfigEntryControllers_doesNotDeleteUnownedConfig(t *testing.T) { consulClient := testClient.APIClient reconciler := &ServiceDefaultsController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), ConfigEntryController: &ConfigEntryController{ ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, @@ -1695,7 +1695,7 @@ func TestConfigEntryControllers_updatesStatusWhenDeleteFails(t *testing.T) { testClient := test.TestServerWithMockConnMgrWatcher(t, nil) testClient.TestServer.WaitForServiceIntentions(t) - logger := logrtest.TestLogger{T: t} + logger := logrtest.NewTestLogger(t) svcDefaultsReconciler := ServiceDefaultsController{ Client: fakeClient, @@ -1833,7 +1833,7 @@ func TestConfigEntryController_Migration(t *testing.T) { require.True(t, success, "config entry was not created") // Set up the reconciler. - logger := logrtest.TestLogger{T: t} + logger := logrtest.NewTestLogger(t) svcDefaultsReconciler := ServiceDefaultsController{ Client: fakeClient, Log: logger, @@ -2109,7 +2109,7 @@ func TestConfigEntryControllers_assignServiceVirtualIP(t *testing.T) { testClient.TestServer.WaitForLeader(t) consulClient := testClient.APIClient - ctrl := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.TestLogger{T: t}) + ctrl := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.NewTestLogger(t)) namespacedName := types.NamespacedName{ Namespace: kubeNS, Name: c.configEntryResource.KubernetesName(), diff --git a/control-plane/controllers/exportedservices_controller.go b/control-plane/controllers/exportedservices_controller.go index e72b743a1f..2e82ed0eae 100644 --- a/control-plane/controllers/exportedservices_controller.go +++ b/control-plane/controllers/exportedservices_controller.go @@ -34,7 +34,7 @@ func (r *ExportedServicesController) Logger(name types.NamespacedName) logr.Logg return r.Log.WithValues("request", name) } -func (r *ExportedServicesController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { +func (r *ExportedServicesController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { return r.Status().Update(ctx, obj, opts...) } diff --git a/control-plane/controllers/exportedservices_controller_ent_test.go b/control-plane/controllers/exportedservices_controller_ent_test.go index 40ab5d1e1e..94a605eab4 100644 --- a/control-plane/controllers/exportedservices_controller_ent_test.go +++ b/control-plane/controllers/exportedservices_controller_ent_test.go @@ -105,7 +105,7 @@ func TestExportedServicesController_createsExportedServices(tt *testing.T) { controller := &controllers.ExportedServicesController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), Scheme: s, ConfigEntryController: &controllers.ConfigEntryController{ ConsulClientConfig: testClient.Cfg, @@ -220,7 +220,7 @@ func TestExportedServicesController_updatesExportedServices(tt *testing.T) { controller := &controllers.ExportedServicesController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), Scheme: s, ConfigEntryController: &controllers.ConfigEntryController{ ConsulClientConfig: testClient.Cfg, @@ -358,7 +358,7 @@ func TestExportedServicesController_deletesExportedServices(tt *testing.T) { controller := &controllers.ExportedServicesController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.NewTestLogger(t), Scheme: s, ConfigEntryController: &controllers.ConfigEntryController{ ConsulClientConfig: testClient.Cfg, diff --git a/control-plane/controllers/ingressgateway_controller.go b/control-plane/controllers/ingressgateway_controller.go index faa728dd4f..5bcb39bc2a 100644 --- a/control-plane/controllers/ingressgateway_controller.go +++ b/control-plane/controllers/ingressgateway_controller.go @@ -34,7 +34,7 @@ func (r *IngressGatewayController) Logger(name types.NamespacedName) logr.Logger return r.Log.WithValues("request", name) } -func (r *IngressGatewayController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { +func (r *IngressGatewayController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { return r.Status().Update(ctx, obj, opts...) } diff --git a/control-plane/controllers/mesh_controller.go b/control-plane/controllers/mesh_controller.go index 92839d0104..ba78f0c144 100644 --- a/control-plane/controllers/mesh_controller.go +++ b/control-plane/controllers/mesh_controller.go @@ -34,7 +34,7 @@ func (r *MeshController) Logger(name types.NamespacedName) logr.Logger { return r.Log.WithValues("request", name) } -func (r *MeshController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { +func (r *MeshController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { return r.Status().Update(ctx, obj, opts...) } diff --git a/control-plane/controllers/proxydefaults_controller.go b/control-plane/controllers/proxydefaults_controller.go index 1415da8688..882843bf9e 100644 --- a/control-plane/controllers/proxydefaults_controller.go +++ b/control-plane/controllers/proxydefaults_controller.go @@ -34,7 +34,7 @@ func (r *ProxyDefaultsController) Logger(name types.NamespacedName) logr.Logger return r.Log.WithValues("request", name) } -func (r *ProxyDefaultsController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { +func (r *ProxyDefaultsController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { return r.Status().Update(ctx, obj, opts...) } diff --git a/control-plane/controllers/samenessgroups_controller.go b/control-plane/controllers/samenessgroups_controller.go index 1d768cc968..815b7e8ab8 100644 --- a/control-plane/controllers/samenessgroups_controller.go +++ b/control-plane/controllers/samenessgroups_controller.go @@ -5,6 +5,7 @@ package controllers import ( "context" + "k8s.io/apimachinery/pkg/types" "github.com/go-logr/logr" @@ -34,7 +35,7 @@ func (r *SamenessGroupController) Logger(name types.NamespacedName) logr.Logger return r.Log.WithValues("request", name) } -func (r *SamenessGroupController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { +func (r *SamenessGroupController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { return r.Status().Update(ctx, obj, opts...) } diff --git a/control-plane/controllers/servicedefaults_controller.go b/control-plane/controllers/servicedefaults_controller.go index 9c2dbe683d..0496788bb3 100644 --- a/control-plane/controllers/servicedefaults_controller.go +++ b/control-plane/controllers/servicedefaults_controller.go @@ -34,7 +34,7 @@ func (r *ServiceDefaultsController) Logger(name types.NamespacedName) logr.Logge return r.Log.WithValues("request", name) } -func (r *ServiceDefaultsController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { +func (r *ServiceDefaultsController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { return r.Status().Update(ctx, obj, opts...) } diff --git a/control-plane/controllers/serviceintentions_controller.go b/control-plane/controllers/serviceintentions_controller.go index 30dcb63f81..4461a82dd6 100644 --- a/control-plane/controllers/serviceintentions_controller.go +++ b/control-plane/controllers/serviceintentions_controller.go @@ -34,7 +34,7 @@ func (r *ServiceIntentionsController) Logger(name types.NamespacedName) logr.Log return r.Log.WithValues("request", name) } -func (r *ServiceIntentionsController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { +func (r *ServiceIntentionsController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { return r.Status().Update(ctx, obj, opts...) } diff --git a/control-plane/controllers/serviceresolver_controller.go b/control-plane/controllers/serviceresolver_controller.go index f82c4d42a2..cfc5c31ca3 100644 --- a/control-plane/controllers/serviceresolver_controller.go +++ b/control-plane/controllers/serviceresolver_controller.go @@ -34,7 +34,7 @@ func (r *ServiceResolverController) Logger(name types.NamespacedName) logr.Logge return r.Log.WithValues("request", name) } -func (r *ServiceResolverController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { +func (r *ServiceResolverController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { return r.Status().Update(ctx, obj, opts...) } diff --git a/control-plane/controllers/servicerouter_controller.go b/control-plane/controllers/servicerouter_controller.go index 831179eee9..a0b3bc0581 100644 --- a/control-plane/controllers/servicerouter_controller.go +++ b/control-plane/controllers/servicerouter_controller.go @@ -34,7 +34,7 @@ func (r *ServiceRouterController) Logger(name types.NamespacedName) logr.Logger return r.Log.WithValues("request", name) } -func (r *ServiceRouterController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { +func (r *ServiceRouterController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { return r.Status().Update(ctx, obj, opts...) } diff --git a/control-plane/controllers/servicesplitter_controller.go b/control-plane/controllers/servicesplitter_controller.go index 1dd89dc278..b733fb9cb5 100644 --- a/control-plane/controllers/servicesplitter_controller.go +++ b/control-plane/controllers/servicesplitter_controller.go @@ -34,7 +34,7 @@ func (r *ServiceSplitterController) Logger(name types.NamespacedName) logr.Logge return r.Log.WithValues("request", name) } -func (r *ServiceSplitterController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { +func (r *ServiceSplitterController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { return r.Status().Update(ctx, obj, opts...) } diff --git a/control-plane/controllers/terminatinggateway_controller.go b/control-plane/controllers/terminatinggateway_controller.go index 9550a2d04f..d73d4e043c 100644 --- a/control-plane/controllers/terminatinggateway_controller.go +++ b/control-plane/controllers/terminatinggateway_controller.go @@ -34,7 +34,7 @@ func (r *TerminatingGatewayController) Logger(name types.NamespacedName) logr.Lo return r.Log.WithValues("request", name) } -func (r *TerminatingGatewayController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { +func (r *TerminatingGatewayController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { return r.Status().Update(ctx, obj, opts...) } diff --git a/control-plane/go.mod b/control-plane/go.mod index da581c221f..9f81da1907 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -4,9 +4,9 @@ require ( github.com/cenkalti/backoff v2.2.1+incompatible github.com/containernetworking/cni v1.1.1 github.com/deckarep/golang-set v1.7.1 - github.com/fsnotify/fsnotify v1.5.4 - github.com/go-logr/logr v0.4.0 - github.com/google/go-cmp v0.5.8 + github.com/fsnotify/fsnotify v1.6.0 + github.com/go-logr/logr v1.2.3 + github.com/google/go-cmp v0.5.9 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83383d github.com/hashicorp/consul-server-connection-manager v0.1.2 @@ -26,21 +26,23 @@ require ( github.com/mitchellh/cli v1.1.0 github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/mapstructure v1.5.0 - github.com/stretchr/testify v1.7.2 - go.uber.org/zap v1.19.0 + github.com/stretchr/testify v1.8.1 + go.uber.org/zap v1.24.0 + golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 golang.org/x/text v0.7.0 - golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 + golang.org/x/time v0.3.0 gomodules.xyz/jsonpatch/v2 v2.2.0 - k8s.io/api v0.22.2 - k8s.io/apimachinery v0.22.2 - k8s.io/client-go v0.22.2 - k8s.io/klog/v2 v2.9.0 - k8s.io/utils v0.0.0-20220812165043-ad590609e2e5 - sigs.k8s.io/controller-runtime v0.10.2 + k8s.io/api v0.26.1 + k8s.io/apimachinery v0.26.1 + k8s.io/client-go v0.26.1 + k8s.io/klog/v2 v2.80.1 + k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 + sigs.k8s.io/controller-runtime v0.14.6 + sigs.k8s.io/gateway-api v0.6.2 ) require ( - cloud.google.com/go v0.81.0 // indirect + cloud.google.com/go v0.65.0 // indirect github.com/Azure/azure-sdk-for-go v44.0.0+incompatible // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.18 // indirect @@ -59,24 +61,29 @@ require ( github.com/bgentry/speakeasy v0.1.0 // indirect github.com/cenkalti/backoff/v3 v3.0.0 // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect - github.com/cespare/xxhash/v2 v2.1.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661 // indirect - github.com/digitalocean/godo v1.10.0 // indirect + github.com/digitalocean/godo v1.7.5 // indirect github.com/dimchansky/utfbom v1.1.0 // indirect + github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect + github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/fatih/color v1.13.0 // indirect github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect - github.com/go-logr/zapr v0.4.0 // indirect + github.com/go-logr/zapr v1.2.3 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.20.0 // indirect + github.com/go-openapi/swag v0.19.14 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/go-querystring v1.0.0 // indirect + github.com/google/gnostic v0.5.7-v3refs // indirect + github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 // indirect github.com/google/gofuzz v1.1.0 // indirect github.com/google/uuid v1.1.2 // indirect github.com/googleapis/gax-go/v2 v2.0.5 // indirect - github.com/googleapis/gnostic v0.5.5 // indirect github.com/gophercloud/gophercloud v0.1.0 // indirect github.com/hashicorp/consul/proto-public v0.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect @@ -97,18 +104,21 @@ require ( github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/linode/linodego v0.7.1 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/mailru/easyjson v0.7.6 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/go-testing-interface v1.0.0 // indirect github.com/mitchellh/pointerstructure v1.2.1 // indirect github.com/mitchellh/reflectwalk v1.0.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 // indirect github.com/oklog/run v1.0.0 // indirect github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c // indirect @@ -116,46 +126,46 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/posener/complete v1.2.3 // indirect - github.com/prometheus/client_golang v1.11.0 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.26.0 // indirect - github.com/prometheus/procfs v0.6.0 // indirect + github.com/prometheus/client_golang v1.14.0 // indirect + github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/common v0.37.0 // indirect + github.com/prometheus/procfs v0.8.0 // indirect github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/sirupsen/logrus v1.8.1 // indirect github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/objx v0.2.0 // indirect - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.658 // indirect - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.658 // indirect + github.com/stretchr/objx v0.5.0 // indirect + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.480 // indirect + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.480 // indirect github.com/vmware/govmomi v0.18.0 // indirect - go.opencensus.io v0.23.0 // indirect + go.opencensus.io v0.22.4 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.6.0 // indirect - golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect - golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect + golang.org/x/crypto v0.1.0 // indirect golang.org/x/mod v0.7.0 // indirect golang.org/x/net v0.7.0 // indirect - golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect + golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect golang.org/x/sync v0.1.0 // indirect golang.org/x/sys v0.5.0 // indirect golang.org/x/term v0.5.0 // indirect golang.org/x/tools v0.3.0 // indirect - google.golang.org/api v0.43.0 // indirect + google.golang.org/api v0.30.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect - google.golang.org/grpc v1.48.0 // indirect + google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect + google.golang.org/grpc v1.49.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/resty.v1 v1.12.0 // indirect gopkg.in/square/go-jose.v2 v2.5.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.22.2 // indirect - k8s.io/component-base v0.22.2 // indirect - k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect - sigs.k8s.io/yaml v1.2.0 // indirect + k8s.io/apiextensions-apiserver v0.26.1 // indirect + k8s.io/component-base v0.26.1 // indirect + k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect + sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) go 1.20 diff --git a/control-plane/go.sum b/control-plane/go.sum index 8dd50a5843..b915ac004b 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -12,13 +12,8 @@ cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bP cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -27,7 +22,6 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -40,8 +34,6 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v44.0.0+incompatible h1:e82Yv2HNpS0kuyeCrV29OPKvEiqfs2/uJHic3/3iKdg= github.com/Azure/azure-sdk-for-go v44.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.0/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= @@ -71,13 +63,7 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14= -github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -85,17 +71,14 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.44.262 h1:gyXpcJptWoNkK+DiAiaBltlreoWKQXjAIh6FRh60F+I= github.com/aws/aws-sdk-go v1.44.262/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= -github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -104,8 +87,6 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c= @@ -113,11 +94,9 @@ github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4r github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -125,32 +104,14 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= -github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/containernetworking/cni v1.1.1 h1:ky20T7c0MvKvbMOwS/FrlbNwjEoqJEUUYfsL4b0mc4k= github.com/containernetworking/cni v1.1.1/go.mod h1:sDpYKmGVENF3s6uvMvGgldDWeG8dMxakj/u+i9ht9vw= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11/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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -159,44 +120,38 @@ github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14y github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661 h1:lrWnAyy/F72MbxIxFUzKmcMCdt9Oi8RzpAxzTNQHD7o= github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/digitalocean/godo v1.10.0 h1:uW1/FcvZE/hoixnJcnlmIUvTVNdZCLjRLzmDtRi1xXY= -github.com/digitalocean/godo v1.10.0/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= +github.com/digitalocean/godo v1.7.5 h1:JOQbAO6QT1GGjor0doT0mXefX2FgUDPOpYh2RaXA+ko= +github.com/digitalocean/godo v1.7.5/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= +github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= 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.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= -github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= +github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/frankban/quicktest v1.13.0 h1:yNZif1OkDfNoDfb9zZa9aXIpejNR4F23Wely0c+Qdqk= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -204,32 +159,32 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/zapr v0.4.0 h1:uc1uML3hRYL9/ZZPdgHS/n8Nzo+eaYL/Efxkkamf7OM= -github.com/go-logr/zapr v0.4.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= +github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -242,7 +197,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -258,7 +212,6 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= @@ -266,7 +219,8 @@ github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= +github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -274,21 +228,18 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/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.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/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.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/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.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0= +github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -296,44 +247,26 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= -github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83383d h1:RJ1MZ8JKnfgKQ1kR3IBQAMpOpzXrdseZAYN/QR//MFM= github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83383d/go.mod h1:IHIHMzkoMwlv6rLsgwcoFBVYupR7/1pKEOHBMjD4L0k= github.com/hashicorp/consul-server-connection-manager v0.1.2 h1:tNVQHUPuMbd+cMdD8kd+qkZUYpmLmrHMAV/49f4L53I= github.com/hashicorp/consul-server-connection-manager v0.1.2/go.mod h1:NzQoVi1KcxGI2SangsDue8+ZPuXZWs+6BKAKrDNyg+w= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.10.1-0.20230512003852-bd0eb07ed3ca h1:5UPVYOlJg/HBEJ2q82rkkQ3ZLzeMnF5MOpGcw2kh+XU= github.com/hashicorp/consul/api v1.10.1-0.20230512003852-bd0eb07ed3ca/go.mod h1:tXfrC6o0yFTgAW46xd5Ic8STHc9oIBcRVBcwhX5KNCQ= github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3usQMXZF6ziK3SzXU= github.com/hashicorp/consul/proto-public v0.1.0/go.mod h1:vs2KkuWwtjkIgA5ezp4YKPzQp4GitV+q/+PvksrA92k= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.13.1 h1:EygWVWWMczTzXGpO93awkHFzfUka6hLYJ0qhETd+6lY= github.com/hashicorp/consul/sdk v0.13.1/go.mod h1:SW/mM4LbKfqmMvcFu8v+eiQQ7oitXEFeiBe9StxERb0= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -366,7 +299,6 @@ github.com/hashicorp/go-plugin v1.4.5/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHG github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 h1:cCRo8gK7oq6A2L6LICkUZ+/a5rLiRXFMf1Qd4xSwxTc= @@ -386,7 +318,6 @@ github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2I github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= @@ -394,13 +325,10 @@ github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uG github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/mdns v1.0.4 h1:sY0CMhFmjIPDMlTB+HfymFHCaYLhgifZ0QhjaYKD/UQ= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= github.com/hashicorp/vault/api v1.8.3 h1:cHQOLcMhBR+aVI0HzhPxO62w2+gJhIrKguQNONPzu6o= @@ -414,13 +342,8 @@ github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKe github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= -github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da h1:FjHUJJ7oBW4G/9j1KzlHaXL09LyMVM9rupS39lncbXk= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= @@ -428,11 +351,10 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f h1:ENpDacvnr8faw5ugQmEF1QYk+f/Y9lXFvuYmRxykago= -github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f/go.mod h1:KDSfL7qe5ZfQqvlDMkVjCztbmcpp/c8M77vhQP8ZPvk= +github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62 h1:JHCT6xuyPUrbbgAPE/3dqlvUKzRHMNuTBKKUb6OeR/k= +github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -442,11 +364,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -459,33 +378,27 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/linode/linodego v0.7.1 h1:4WZmMpSA2NRwlPZcc0+4Gyn7rr99Evk9bnr0B3gXRKE= github.com/linode/linodego v0.7.1/go.mod h1:ga11n3ivecUrPCHN0rANxKmfWBJVkOXfLMZinAbj2sY= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM= +github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= @@ -495,16 +408,12 @@ github.com/mitchellh/cli v1.1.0 h1:tEElEatulEHDeedTxwckzyYMA5c86fbmNIUL1hBIiTg= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= @@ -512,8 +421,6 @@ github.com/mitchellh/pointerstructure v1.2.1 h1:ZhBBeX8tSlRpu/FFhXH4RC4OJzFlqsQh github.com/mitchellh/pointerstructure v1.2.1/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -521,44 +428,33 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 h1:BQ1HW7hr4IVovMwWg0E0PYcyW8CzqDcVmaew9cujU4s= github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.0-20180130162743-b8a9be070da4/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo/v2 v2.1.3 h1:e/3Cwtogj0HA+25nMP1jCMDIf8RtRYbGwGGuBIFztkc= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/ginkgo/v2 v2.6.0 h1:9t9b9vRUbFq3C4qKFCGkVuq/fIHji802N1nrtkh1mNc= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= -github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E= github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c h1:vwpFWvAO8DeIZfFeqASzZfsxuWPno9ncAebBEP0N3uE= github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c/go.mod h1:otzZQXgoO96RTzDB/Hycg0qZcXZsWJGJRSXbmEIJ+4M= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -570,171 +466,113 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -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.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= 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= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= +github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= 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= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= +github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 h1:Wdi9nwnhFNAlseAOekn6B5G/+GMtks9UKbvRU/CMM/o= github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03/go.mod h1:gRAiPF5C5Nd0eyyRdqIu9qTiFSoZzpTq727b5B8fkkU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rs/zerolog v1.4.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sean-/conswriter v0.0.0-20180208195008-f5ae3917a627/go.mod h1:7zjs06qF79/FKAJpBvFx3P8Ww4UTIMAe+lpNXDHziac= -github.com/sean-/pager v0.0.0-20180208200047-666be9bf53b5/go.mod h1:BeybITEsBEg6qbIiqJ6/Bqeq25bCLbL7YFmpaFfJDuM= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d h1:bVQRCxQvfjNUeRqaY/uT0tFuvuFY0ulgnczuR684Xic= github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d/go.mod h1:Cw4GTlQccdRGSEf6KiMju767x0NEHE0YIVPJSaXjlsw= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.658 h1:q208plt7F8Pj3b1w8D3XDb/vTgHsn/JlEwDCSe+lvyo= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.658/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.658 h1:wF/3PTojsx9/J8CaeiTy0zXxvwrcuu282R4g7fDlgCQ= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.658/go.mod h1:LLex9maWMIQzOFF/mYz5rEsTUwKKcmAbGRehSNRWqgQ= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.480 h1:Dwnfdrk3KXpYRH9Kwrk9sHpZSOmrE7P9LBoNsYUJKR4= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.480/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.480 h1:YEDZmv2ABU8QvwXEVTOQgVEQzDOByhz73vdjL6sERkE= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.480/go.mod h1:zaBIuDDs+rC74X8Aog+LSu91GFtHYRYDC196RGTm2jk= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/vmware/govmomi v0.18.0 h1:f7QxSmP7meCtoAmiKZogvVbLInT+CZx6Px6K5rYsJZo= github.com/vmware/govmomi v0.18.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= -go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= -go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= -go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= -go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= -go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= -go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= -go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -go.uber.org/zap v1.19.0 h1:mZQZefskPPCMIBCSEH0v2/iUqqLrYtaeqwD6FUGUnFE= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= 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.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -745,8 +583,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= -golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o= +golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -759,9 +597,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -770,8 +605,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= @@ -779,9 +612,7 @@ golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -789,13 +620,11 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -813,18 +642,14 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= @@ -834,14 +659,9 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= 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= @@ -860,10 +680,7 @@ golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -872,7 +689,6 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -898,48 +714,34 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= @@ -949,7 +751,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -959,32 +760,25 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1004,7 +798,6 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -1012,15 +805,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM= @@ -1046,13 +832,8 @@ google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0 h1:yfrXXP61wVuLb0vBcG6qaOoIoqYEzOQS8jum51jkv2w= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0 h1:4sAyIHT6ZohtAQDoxws+ez7bROYmUlOVvsUscYCDTqA= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1082,7 +863,6 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= @@ -1092,22 +872,11 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 h1:hrbNEivu7Zn1pxvHk6MBrq9iE22woVILTHqexqBxe6I= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= @@ -1118,17 +887,11 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w= -google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= 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= @@ -1142,6 +905,7 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -1154,16 +918,11 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1178,8 +937,6 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= 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-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1187,37 +944,32 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.22.2 h1:M8ZzAD0V6725Fjg53fKeTJxGsJvRbk4TEm/fexHMtfw= -k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8= -k8s.io/apiextensions-apiserver v0.22.2 h1:zK7qI8Ery7j2CaN23UCFaC1hj7dMiI87n01+nKuewd4= -k8s.io/apiextensions-apiserver v0.22.2/go.mod h1:2E0Ve/isxNl7tWLSUDgi6+cmwHi5fQRdwGVCxbC+KFA= -k8s.io/apimachinery v0.22.2 h1:ejz6y/zNma8clPVfNDLnPbleBo6MpoFy/HBiBqCouVk= -k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= -k8s.io/apiserver v0.22.2/go.mod h1:vrpMmbyjWrgdyOvZTSpsusQq5iigKNWv9o9KlDAbBHI= -k8s.io/client-go v0.22.2 h1:DaSQgs02aCC1QcwUdkKZWOeaVsQjYvWv8ZazcZ6JcHc= -k8s.io/client-go v0.22.2/go.mod h1:sAlhrkVDf50ZHx6z4K0S40wISNTarf1r800F+RlCF6U= -k8s.io/code-generator v0.22.2/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o= -k8s.io/component-base v0.22.2 h1:vNIvE0AIrLhjX8drH0BgCNJcR4QZxMXcJzBsDplDx9M= -k8s.io/component-base v0.22.2/go.mod h1:5Br2QhI9OTe79p+TzPe9JKNQYvEKbq9rTJDWllunGug= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM= -k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM= -k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20220812165043-ad590609e2e5 h1:XmRqFcQlCy/lKRZ39j+RVpokYNroHPqV3mcBRfnhT5o= -k8s.io/utils v0.0.0-20220812165043-ad590609e2e5/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/api v0.26.1 h1:f+SWYiPd/GsiWwVRz+NbFyCgvv75Pk9NK6dlkZgpCRQ= +k8s.io/api v0.26.1/go.mod h1:xd/GBNgR0f707+ATNyPmQ1oyKSgndzXij81FzWGsejg= +k8s.io/apiextensions-apiserver v0.26.1 h1:cB8h1SRk6e/+i3NOrQgSFij1B2S0Y0wDoNl66bn8RMI= +k8s.io/apiextensions-apiserver v0.26.1/go.mod h1:AptjOSXDGuE0JICx/Em15PaoO7buLwTs0dGleIHixSM= +k8s.io/apimachinery v0.26.1 h1:8EZ/eGJL+hY/MYCNwhmDzVqq2lPl3N3Bo8rvweJwXUQ= +k8s.io/apimachinery v0.26.1/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= +k8s.io/client-go v0.26.1 h1:87CXzYJnAMGaa/IDDfRdhTzxk/wzGZ+/HUQpqgVSZXU= +k8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE= +k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4= +k8s.io/component-base v0.26.1/go.mod h1:VHrLR0b58oC035w6YQiBSbtsf0ThuSwXP+p5dD/kAWU= +k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= +k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= +k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y= +k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/controller-runtime v0.10.2 h1:jW8qiY+yMnnPx6O9hu63tgcwaKzd1yLYui+mpvClOOc= -sigs.k8s.io/controller-runtime v0.10.2/go.mod h1:CQp8eyUQZ/Q7PJvnIrB6/hgfTC1kBkGylwsLgOQi1WY= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/controller-runtime v0.14.6 h1:oxstGVvXGNnMvY7TAESYk+lzr6S3V5VFxQ6d92KcwQA= +sigs.k8s.io/controller-runtime v0.14.6/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= +sigs.k8s.io/gateway-api v0.6.2 h1:583XHiX2M2bKEA0SAdkoxL1nY73W1+/M+IAm8LJvbEA= +sigs.k8s.io/gateway-api v0.6.2/go.mod h1:EYJT+jlPWTeNskjV0JTki/03WX1cyAnBhwBJfYHpV/0= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/control-plane/helper/controller/controller.go b/control-plane/helper/controller/controller.go index 87cdde1a6f..db6de05b07 100644 --- a/control-plane/helper/controller/controller.go +++ b/control-plane/helper/controller/controller.go @@ -62,7 +62,7 @@ func (c *Controller) Run(stopCh <-chan struct{}) { // Add an event handler when data is received from the informer. The // event handlers here will block the informer so we just offload them // immediately into a workqueue. - informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ + _, err := informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { // convert the resource object into a key (in this case // we are just doing it in the format of 'namespace/name') @@ -81,6 +81,9 @@ func (c *Controller) Run(stopCh <-chan struct{}) { }, DeleteFunc: c.informerDeleteHandler(queue), }) + if err != nil { + c.Log.Error("error adding informer event handlers", err) + } // If the type is a background syncer, then we startup the background // process. diff --git a/control-plane/subcommand/common/common.go b/control-plane/subcommand/common/common.go index b7927e4490..598ba66ea5 100644 --- a/control-plane/subcommand/common/common.go +++ b/control-plane/subcommand/common/common.go @@ -70,7 +70,7 @@ func ZapLogger(level string, jsonLogging bool) (logr.Logger, error) { level = "debug" } if err := zapLevel.UnmarshalText([]byte(level)); err != nil { - return nil, fmt.Errorf("unknown log level %q: %s", level, err.Error()) + return logr.Logger{}, fmt.Errorf("unknown log level %q: %s", level, err.Error()) } if jsonLogging { return zap.New(zap.UseDevMode(false), zap.Level(zapLevel), zap.JSONEncoder()), nil diff --git a/control-plane/subcommand/connect-init/command.go b/control-plane/subcommand/connect-init/command.go index a5fbe9066c..72090d299b 100644 --- a/control-plane/subcommand/connect-init/command.go +++ b/control-plane/subcommand/connect-init/command.go @@ -316,7 +316,7 @@ func (c *Command) getGatewayRegistration(client *api.Client) backoff.Operation { } for _, gateway := range gatewayList.Services { switch gateway.Kind { - case api.ServiceKindMeshGateway, api.ServiceKindIngressGateway, api.ServiceKindTerminatingGateway: + case api.ServiceKindAPIGateway, api.ServiceKindMeshGateway, api.ServiceKindIngressGateway, api.ServiceKindTerminatingGateway: proxyID = gateway.ID } } diff --git a/control-plane/subcommand/gateway-cleanup/command.go b/control-plane/subcommand/gateway-cleanup/command.go new file mode 100644 index 0000000000..cecb225290 --- /dev/null +++ b/control-plane/subcommand/gateway-cleanup/command.go @@ -0,0 +1,193 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package gatewaycleanup + +import ( + "context" + "errors" + "flag" + "fmt" + "sync" + "time" + + "github.com/cenkalti/backoff" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul-k8s/control-plane/subcommand" + "github.com/hashicorp/consul-k8s/control-plane/subcommand/flags" + "github.com/mitchellh/cli" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +type Command struct { + UI cli.Ui + + flags *flag.FlagSet + k8s *flags.K8SFlags + + flagGatewayClassName string + flagGatewayClassConfigName string + + k8sClient client.Client + + once sync.Once + help string + + ctx context.Context +} + +func (c *Command) init() { + c.flags = flag.NewFlagSet("", flag.ContinueOnError) + + c.flags.StringVar(&c.flagGatewayClassName, "gateway-class-name", "", + "Name of Kubernetes GatewayClass to delete.") + c.flags.StringVar(&c.flagGatewayClassConfigName, "gateway-class-config-name", "", + "Name of Kubernetes GatewayClassConfig to delete.") + + c.k8s = &flags.K8SFlags{} + flags.Merge(c.flags, c.k8s.Flags()) + c.help = flags.Usage(help, c.flags) +} + +func (c *Command) Run(args []string) int { + var err error + c.once.Do(c.init) + if err = c.flags.Parse(args); err != nil { + return 1 + } + // Validate flags + if err := c.validateFlags(); err != nil { + c.UI.Error(err.Error()) + return 1 + } + + if c.ctx == nil { + c.ctx = context.Background() + } + + // Create the Kubernetes clientset + if c.k8sClient == nil { + config, err := subcommand.K8SConfig(c.k8s.KubeConfig()) + if err != nil { + c.UI.Error(fmt.Sprintf("Error retrieving Kubernetes auth: %s", err)) + return 1 + } + + s := runtime.NewScheme() + if err := clientgoscheme.AddToScheme(s); err != nil { + c.UI.Error(fmt.Sprintf("Could not add client-go schema: %s", err)) + return 1 + } + if err := gwv1beta1.Install(s); err != nil { + c.UI.Error(fmt.Sprintf("Could not add api-gateway schema: %s", err)) + return 1 + } + if err := v1alpha1.AddToScheme(s); err != nil { + c.UI.Error(fmt.Sprintf("Could not add consul-k8s schema: %s", err)) + return 1 + } + + c.k8sClient, err = client.New(config, client.Options{Scheme: s}) + if err != nil { + c.UI.Error(fmt.Sprintf("Error initializing Kubernetes client: %s", err)) + return 1 + } + } + + // do the cleanup + + // find the class config and mark it for deletion first so that we + // can do an early return if the gateway class isn't found + config := &v1alpha1.GatewayClassConfig{} + err = c.k8sClient.Get(context.Background(), types.NamespacedName{Name: c.flagGatewayClassConfigName}, config) + if err != nil { + if k8serrors.IsNotFound(err) { + // no gateway class config, just ignore and return + return 0 + } + c.UI.Error(err.Error()) + return 1 + } + + // ignore any returned errors + _ = c.k8sClient.Delete(context.Background(), config) + + // find the gateway class + gatewayClass := &gwv1beta1.GatewayClass{} + err = c.k8sClient.Get(context.Background(), types.NamespacedName{Name: c.flagGatewayClassName}, gatewayClass) + if err != nil { + if k8serrors.IsNotFound(err) { + // no gateway class, just ignore and return + return 0 + } + c.UI.Error(err.Error()) + return 1 + } + + // ignore any returned errors + _ = c.k8sClient.Delete(context.Background(), gatewayClass) + + // make sure they're gone + if err := backoff.Retry(func() error { + err = c.k8sClient.Get(context.Background(), types.NamespacedName{Name: c.flagGatewayClassConfigName}, config) + if err == nil || !k8serrors.IsNotFound(err) { + return errors.New("gateway class config still exists") + } + + err = c.k8sClient.Get(context.Background(), types.NamespacedName{Name: c.flagGatewayClassName}, gatewayClass) + if err == nil || !k8serrors.IsNotFound(err) { + return errors.New("gateway class still exists") + } + + return nil + }, exponentialBackoffWithMaxIntervalAndTime()); err != nil { + c.UI.Error(err.Error()) + // if we failed, return 0 anyway after logging the error + // since we don't want to block someone from uninstallation + } + + return 0 +} + +func (c *Command) validateFlags() error { + if c.flagGatewayClassConfigName == "" { + return errors.New("-gateway-class-config-name must be set") + } + if c.flagGatewayClassName == "" { + return errors.New("-gateway-class-name must be set") + } + + return nil +} + +func (c *Command) Synopsis() string { return synopsis } +func (c *Command) Help() string { + c.once.Do(c.init) + return c.help +} + +const synopsis = "Clean up global gateway resources prior to uninstall." +const help = ` +Usage: consul-k8s-control-plane gateay-cleanup [options] + + Deletes installed gateway class and gateway class config objects + prior to helm uninstallation. This is required due to finalizers + existing on the GatewayClassConfig that will leave around a dangling + object without deleting these prior to their controllers being deleted. + The job is best effort, so if it fails to successfully delete the + objects, it will allow the uninstallation to continue. + +` + +func exponentialBackoffWithMaxIntervalAndTime() *backoff.ExponentialBackOff { + backoff := backoff.NewExponentialBackOff() + backoff.MaxElapsedTime = 10 * time.Second + backoff.MaxInterval = 1 * time.Second + backoff.Reset() + return backoff +} diff --git a/control-plane/subcommand/gateway-cleanup/command_test.go b/control-plane/subcommand/gateway-cleanup/command_test.go new file mode 100644 index 0000000000..56b7651270 --- /dev/null +++ b/control-plane/subcommand/gateway-cleanup/command_test.go @@ -0,0 +1,83 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package gatewaycleanup + +import ( + "testing" + + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/mitchellh/cli" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +func TestRun(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + gatewayClassConfig *v1alpha1.GatewayClassConfig + gatewayClass *gwv1beta1.GatewayClass + }{ + "both exist": { + gatewayClassConfig: &v1alpha1.GatewayClassConfig{}, + gatewayClass: &gwv1beta1.GatewayClass{}, + }, + "gateway class config doesn't exist": { + gatewayClass: &gwv1beta1.GatewayClass{}, + }, + "gateway class doesn't exist": { + gatewayClassConfig: &v1alpha1.GatewayClassConfig{}, + }, + "neither exist": {}, + "finalizers on gatewayclass blocking deletion": { + gatewayClassConfig: &v1alpha1.GatewayClassConfig{}, + gatewayClass: &gwv1beta1.GatewayClass{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"finalizer"}}}, + }, + "finalizers on gatewayclassconfig blocking deletion": { + gatewayClassConfig: &v1alpha1.GatewayClassConfig{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"finalizer"}}}, + gatewayClass: &gwv1beta1.GatewayClass{}, + }, + } { + t.Run(name, func(t *testing.T) { + tt := tt + + t.Parallel() + + s := runtime.NewScheme() + require.NoError(t, gwv1beta1.Install(s)) + require.NoError(t, v1alpha1.AddToScheme(s)) + + objs := []client.Object{} + if tt.gatewayClass != nil { + tt.gatewayClass.Name = "gateway-class" + objs = append(objs, tt.gatewayClass) + } + if tt.gatewayClassConfig != nil { + tt.gatewayClassConfig.Name = "gateway-class-config" + objs = append(objs, tt.gatewayClassConfig) + } + + client := fake.NewClientBuilder().WithScheme(s).WithObjects(objs...).Build() + + ui := cli.NewMockUi() + cmd := Command{ + UI: ui, + k8sClient: client, + flagGatewayClassName: "gateway-class", + flagGatewayClassConfigName: "gateway-class-config", + } + + code := cmd.Run([]string{ + "-gateway-class-config-name", "gateway-class-config", + "-gateway-class-name", "gateway-class", + }) + + require.Equal(t, 0, code) + }) + } +} diff --git a/control-plane/subcommand/inject-connect/command.go b/control-plane/subcommand/inject-connect/command.go index 05937dfe90..c7c04dd481 100644 --- a/control-plane/subcommand/inject-connect/command.go +++ b/control-plane/subcommand/inject-connect/command.go @@ -15,6 +15,8 @@ import ( "sync" "syscall" + apigateway "github.com/hashicorp/consul-k8s/control-plane/api-gateway" + gatewaycontrollers "github.com/hashicorp/consul-k8s/control-plane/api-gateway/controllers" apicommon "github.com/hashicorp/consul-k8s/control-plane/api/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/controllers/endpoints" @@ -38,9 +40,13 @@ import ( "k8s.io/klog/v2" ctrl "sigs.k8s.io/controller-runtime" ctrlRuntimeWebhook "sigs.k8s.io/controller-runtime/pkg/webhook" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) -const WebhookCAFilename = "ca.crt" +const ( + WebhookCAFilename = "ca.crt" +) type Command struct { UI cli.Ui @@ -136,6 +142,8 @@ func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) // We need v1alpha1 here to add the peering api to the scheme utilruntime.Must(v1alpha1.AddToScheme(scheme)) + utilruntime.Must(gwv1beta1.AddToScheme(scheme)) + utilruntime.Must(gwv1alpha2.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme } @@ -447,15 +455,70 @@ func (c *Command) Run(args []string) int { return 1 } - consulMeta := apicommon.ConsulMeta{ - PartitionsEnabled: c.flagEnablePartitions, - Partition: c.consul.Partition, - NamespacesEnabled: c.flagEnableNamespaces, - DestinationNamespace: c.flagConsulDestinationNamespace, - Mirroring: c.flagEnableK8SNSMirroring, - Prefix: c.flagK8SNSMirroringPrefix, + // API Gateway Controllers + if err := gatewaycontrollers.RegisterFieldIndexes(ctx, mgr); err != nil { + setupLog.Error(err, "unable to register field indexes") + return 1 + } + + if err = (&gatewaycontrollers.GatewayClassConfigController{ + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controller").WithName("gateways"), + }).SetupWithManager(ctx, mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", gatewaycontrollers.GatewayClassConfigController{}) + return 1 } + if err := (&gatewaycontrollers.GatewayClassController{ + ControllerName: gatewaycontrollers.GatewayClassControllerName, + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("GatewayClass"), + }).SetupWithManager(ctx, mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "GatewayClass") + return 1 + } + + cache, err := gatewaycontrollers.SetupGatewayControllerWithManager(ctx, mgr, gatewaycontrollers.GatewayControllerConfig{ + HelmConfig: apigateway.HelmConfig{ + ConsulConfig: apigateway.ConsulConfig{ + Address: c.consul.Addresses, + GRPCPort: consulConfig.GRPCPort, + HTTPPort: consulConfig.HTTPPort, + APITimeout: consulConfig.APITimeout, + }, + ImageDataplane: c.flagConsulDataplaneImage, + ImageConsulK8S: c.flagConsulK8sImage, + ConsulDestinationNamespace: c.flagConsulDestinationNamespace, + NamespaceMirroringPrefix: c.flagK8SNSMirroringPrefix, + EnableNamespaces: c.flagEnableNamespaces, + PeeringEnabled: c.flagEnablePeering, + EnableOpenShift: c.flagEnableOpenShift, + EnableNamespaceMirroring: c.flagEnableK8SNSMirroring, + AuthMethod: c.flagACLAuthMethod, + LogLevel: c.flagLogLevel, + LogJSON: c.flagLogJSON, + TLSEnabled: c.consul.UseTLS, + ConsulTLSServerName: c.consul.TLSServerName, + ConsulPartition: c.consul.Partition, + ConsulCACert: string(caCertPem), + }, + ConsulClientConfig: consulConfig, + ConsulServerConnMgr: watcher, + NamespacesEnabled: c.flagEnableNamespaces, + Partition: c.consul.Partition, + }) + if err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Gateway") + return 1 + } + + go cache.Run(ctx) + + // wait for the cache to fill + setupLog.Info("waiting for Consul cache sync") + cache.WaitSynced(ctx) + setupLog.Info("Consul cache synced") + configEntryReconciler := &controllers.ConfigEntryController{ ConsulClientConfig: c.consul.ConsulClientConfig(), ConsulServerConnMgr: watcher, @@ -653,6 +716,15 @@ func (c *Command) Run(args []string) int { LogJSON: c.flagLogJSON, }}) + consulMeta := apicommon.ConsulMeta{ + PartitionsEnabled: c.flagEnablePartitions, + Partition: c.consul.Partition, + NamespacesEnabled: c.flagEnableNamespaces, + DestinationNamespace: c.flagConsulDestinationNamespace, + Mirroring: c.flagEnableK8SNSMirroring, + Prefix: c.flagK8SNSMirroringPrefix, + } + // Note: The path here should be identical to the one on the kubebuilder // annotation in each webhook file. mgr.GetWebhookServer().Register("/mutate-v1alpha1-servicedefaults", diff --git a/hack/copy-crds-to-chart/main.go b/hack/copy-crds-to-chart/main.go index 0f94a77b5d..b5eef3e1aa 100644 --- a/hack/copy-crds-to-chart/main.go +++ b/hack/copy-crds-to-chart/main.go @@ -18,6 +18,12 @@ var ( "consul.hashicorp.com_peeringacceptors.yaml": {}, "consul.hashicorp.com_peeringdialers.yaml": {}, } + // HACK IT (again)! These CRDs need to go in the Helm chart's crds directory which means they + // cannot have any templating in them. They need to be in the CRD directory because we install + // resources that reference them in the main installation sequence. + toCRDDir = map[string]struct{}{ + "consul.hashicorp.com_gatewayclassconfigs.yaml": {}, + } ) func main() { @@ -57,29 +63,36 @@ func realMain(helmPath string) error { if _, ok := requiresPeering[info.Name()]; ok { // Add {{- if and .Values.connectInject.enabled .Values.global.peering.enabled }} {{- end }} wrapper. contents = fmt.Sprintf("{{- if and .Values.connectInject.enabled .Values.global.peering.enabled }}\n%s{{- end }}\n", contents) + } else if _, ok := toCRDDir[info.Name()]; ok { + // No-op (we don't want templating). } else { // Add {{- if .Values.connectInject.enabled }} {{- end }} wrapper. contents = fmt.Sprintf("{{- if .Values.connectInject.enabled }}\n%s{{- end }}\n", contents) } - // Add labels, this is hacky because we're relying on the line number - // but it means we don't need to regex or yaml parse. - splitOnNewlines := strings.Split(contents, "\n") - labelLines := []string{ - ` labels:`, - ` app: {{ template "consul.name" . }}`, - ` chart: {{ template "consul.chart" . }}`, - ` heritage: {{ .Release.Service }}`, - ` release: {{ .Release.Name }}`, - ` component: crd`, + if _, ok := toCRDDir[info.Name()]; !ok { + // Add labels, this is hacky because we're relying on the line number + // but it means we don't need to regex or yaml parse. + splitOnNewlines := strings.Split(contents, "\n") + labelLines := []string{ + ` labels:`, + ` app: {{ template "consul.name" . }}`, + ` chart: {{ template "consul.chart" . }}`, + ` heritage: {{ .Release.Service }}`, + ` release: {{ .Release.Name }}`, + ` component: crd`, + } + withLabels := append(splitOnNewlines[0:9], append(labelLines, splitOnNewlines[9:]...)...) + contents = strings.Join(withLabels, "\n") } - withLabels := append(splitOnNewlines[0:9], append(labelLines, splitOnNewlines[9:]...)...) - contents = strings.Join(withLabels, "\n") // Construct the destination filename. filenameSplit := strings.Split(info.Name(), "_") crdName := filenameSplit[1] destinationPath := filepath.Join(helmPath, "templates", fmt.Sprintf("crd-%s", crdName)) + if _, ok := toCRDDir[info.Name()]; ok { + destinationPath = filepath.Join(helmPath, "crds", formatCRDName(info.Name())) + } // Write it. printf("writing to %s", destinationPath) @@ -90,3 +103,9 @@ func realMain(helmPath string) error { func printf(format string, args ...interface{}) { fmt.Println(fmt.Sprintf(format, args...)) } + +func formatCRDName(name string) string { + name = strings.TrimSuffix(name, ".yaml") + segments := strings.Split(name, "_") + return fmt.Sprintf("%s.%s.yaml", segments[1], segments[0]) +} From e7d528ac2fd550b427db8b37de4c6d9c3c1d1405 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Fri, 26 May 2023 12:19:22 -0400 Subject: [PATCH 169/340] Update consul image on prepare-dev and prepare-release (#2180) Update consul image on prepare-dev and prepare-release --- .github/workflows/pr.yml | 3 +- Makefile | 18 +- charts/consul/Chart.yaml | 4 +- charts/consul/values.yaml | 70 +- .../build-support/functions/10-util.sh | 1689 ++++++++--------- .../build-support/scripts/consul-version.sh | 3 +- 6 files changed, 867 insertions(+), 920 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index b4b431693a..2959554e21 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -5,7 +5,6 @@ on: # these should be the only settings that you will ever need to change env: - CONSUL_IMAGE: hashicorppreview/consul-enterprise:1.16-dev # Consul's enterprise version to use in tests. We use this consul image on release branches too BRANCH: ${{ github.head_ref || github.ref_name }} CONTEXT: "pr" SHA: ${{ github.event.pull_request.head.sha || github.sha }} @@ -22,4 +21,4 @@ jobs: repo: hashicorp/consul-k8s-workflows ref: main token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} - inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ env.SHA }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}", "consul-image":"${{ env.CONSUL_IMAGE }}" }' + inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ env.SHA }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}" }' diff --git a/Makefile b/Makefile index 6fa53d77c3..f687c308c5 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ VERSION = $(shell ./control-plane/build-support/scripts/version.sh control-plane/version/version.go) -CONSUL_VERSION = $(shell ./control-plane/build-support/scripts/consul-version.sh charts/consul/values.yaml) +CONSUL_IMAGE_VERSION = $(shell ./control-plane/build-support/scripts/consul-version.sh charts/consul/values.yaml) # ===========> Helm Targets @@ -162,8 +162,7 @@ version: @echo $(VERSION) consul-version: - @echo $(CONSUL_VERSION) - + @echo $(CONSUL_IMAGE_VERSION) # ===========> Release Targets @@ -174,7 +173,13 @@ endif ifndef RELEASE_DATE $(error RELEASE_DATE is required, use format , (ex. October 4, 2022)) endif - source $(CURDIR)/control-plane/build-support/scripts/functions.sh; prepare_release $(CURDIR) $(RELEASE_VERSION) "$(RELEASE_DATE)" $(LAST_RELEASE_GIT_TAG) $(PRERELEASE_VERSION) +ifndef LAST_RELEASE_GIT_TAG + $(error LAST_RELEASE_GIT_TAG is required) +endif +ifndef CONSUL_VERSION + $(error CONSUL_VERSION is required) +endif + source $(CURDIR)/control-plane/build-support/scripts/functions.sh; prepare_release $(CURDIR) $(RELEASE_VERSION) "$(RELEASE_DATE)" $(LAST_RELEASE_GIT_TAG) $(CONSUL_VERSION) $(PRERELEASE_VERSION) prepare-dev: ifndef RELEASE_VERSION @@ -186,7 +191,10 @@ endif ifndef NEXT_RELEASE_VERSION $(error NEXT_RELEASE_VERSION is required) endif - source $(CURDIR)/control-plane/build-support/scripts/functions.sh; prepare_dev $(CURDIR) $(RELEASE_VERSION) "$(RELEASE_DATE)" $(NEXT_RELEASE_VERSION) +ifndef NEXT_CONSUL_VERSION + $(error NEXT_CONSUL_VERSION is required) +endif + source $(CURDIR)/control-plane/build-support/scripts/functions.sh; prepare_dev $(CURDIR) $(RELEASE_VERSION) "$(RELEASE_DATE)" "" $(NEXT_RELEASE_VERSION) $(NEXT_CONSUL_VERSION) # ===========> Makefile config diff --git a/charts/consul/Chart.yaml b/charts/consul/Chart.yaml index c55c6be6a2..d0216aa36e 100644 --- a/charts/consul/Chart.yaml +++ b/charts/consul/Chart.yaml @@ -4,7 +4,7 @@ apiVersion: v2 name: consul version: 1.2.0-dev -appVersion: 1.16-dev +appVersion: 1.15.1 kubeVersion: ">=1.22.0-0" description: Official HashiCorp Consul Chart home: https://www.consul.io @@ -16,7 +16,7 @@ annotations: artifacthub.io/prerelease: true artifacthub.io/images: | - name: consul - image: docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.16-dev + image: hashicorp/consul:1.15.1 - name: consul-k8s-control-plane image: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.2.0-dev - name: consul-dataplane diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index e71282c520..5bc0dd33bf 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -66,7 +66,7 @@ global: # image: "hashicorp/consul-enterprise:1.10.0-ent" # ``` # @default: hashicorp/consul: - image: docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.16-dev + image: "hashicorp/consul:1.15.1" # Array of objects containing image pull secret names that will be applied to each service account. # This can be used to reference image pull secrets if using a custom consul or consul-k8s-control-plane Docker image. @@ -80,7 +80,7 @@ global: # - name: pull-secret-name-2 # ``` # @type: array - imagePullSecrets: [ ] + imagePullSecrets: [] # The name (and tag) of the consul-k8s-control-plane Docker # image that is used for functionality such as catalog sync. @@ -295,7 +295,7 @@ global: # Refer to [`-recursor`](https://developer.hashicorp.com/consul/docs/agent/config/cli-flags#_recursor) for more details. # If this is an empty array (the default), then Consul DNS will only resolve queries for the Consul top level domain (by default `.consul`). # @type: array - recursors: [ ] + recursors: [] # Enables [TLS](https://developer.hashicorp.com/consul/tutorials/security/tls-encryption-secure) # across the cluster to verify authenticity of the Consul servers and clients. @@ -316,13 +316,13 @@ global: # in the server certificate. This is useful when you need to access the # Consul server(s) externally, for example, if you're using the UI. # @type: array - serverAdditionalDNSSANs: [ ] + serverAdditionalDNSSANs: [] # A list of additional IP addresses to set as Subject Alternative Names (SANs) # in the server certificate. This is useful when you need to access the # Consul server(s) externally, for example, if you're using the UI. # @type: array - serverAdditionalIPSANs: [ ] + serverAdditionalIPSANs: [] # If true, `verify_outgoing`, `verify_server_hostname`, # and `verify_incoming` for internal RPC communication will be set to `true` for Consul servers and clients. @@ -389,7 +389,6 @@ global: # Configure ACLs. acls: - # If true, the Helm chart will automatically manage ACL tokens and policies # for all Consul and consul-k8s-control-plane components. # This requires Consul >= 1.4. @@ -505,7 +504,7 @@ global: # A list of addresses of the primary mesh gateways in the form `:`. # (e.g. ["1.1.1.1:443", "2.3.4.5:443"] # @type: array - primaryGateways: [ ] + primaryGateways: [] # If you are setting `global.federation.enabled` to true and are in a secondary datacenter, # set `k8sAuthMethodHost` to the address of the Kubernetes API server of the secondary datacenter. @@ -655,7 +654,6 @@ global: # be disabled if you plan on connecting to a Consul cluster external to # the Kube cluster. server: - # If true, the chart will install all the resources necessary for a # Consul server cluster. If you're running Consul externally and want agents # within Kubernetes to join that cluster, this should probably be false. @@ -910,7 +908,7 @@ server: # with `-config-dir`. This defaults to false. # # @type: array - extraVolumes: [ ] + extraVolumes: [] # A list of sidecar containers. # Example: @@ -923,7 +921,7 @@ server: # - ... # ``` # @type: array - extraContainers: [ ] + extraContainers: [] # This value defines the [affinity](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) # for server pods. It defaults to allowing only a single server pod on each node, which @@ -1079,7 +1077,7 @@ server: # feature, in case kubernetes cluster is behind egress http proxies. Additionally, # it could be used to configure custom consul parameters. # @type: map - extraEnvironmentVars: { } + extraEnvironmentVars: {} # [Enterprise Only] Values for setting up and running # [snapshot agents](https://developer.hashicorp.com/consul/commands/snapshot/agent) @@ -1135,21 +1133,21 @@ server: # as servers, and other settings to limit exposure too many requests, requests # waiting for too long, and other runtime considerations. limits: - # This object specifies configurations that limit the rate of RPC and gRPC + # This object specifies configurations that limit the rate of RPC and gRPC # requests on the Consul server. Limiting the rate of gRPC and RPC requests - # also limits HTTP requests to the Consul server. + # also limits HTTP requests to the Consul server. # https://developer.hashicorp.com/consul/docs/agent/config/config-files#request_limits - requestLimits: + requestLimits: # Setting for disabling or enabling rate limiting. If not disabled, it # enforces the action that will occur when RequestLimitsReadRate # or RequestLimitsWriteRate is exceeded. The default value of "disabled" will # prevent any rate limiting from occuring. A value of "enforce" will block # the request from processings by returning an error. A value of - # "permissive" will not block the request and will allow the request to + # "permissive" will not block the request and will allow the request to # continue processing. # @type: string mode: "disabled" - + # Setting that controls how frequently RPC, gRPC, and HTTP # queries are allowed to happen. In any large enough time interval, rate # limiter limits the rate to RequestLimitsReadRate tokens per second. @@ -1158,7 +1156,7 @@ server: # buckets. # @type: integer readRate: -1 - + # Setting that controls how frequently RPC, gRPC, and HTTP # writes are allowed to happen. In any large enough time interval, rate # limiter limits the rate to RequestLimitsWriteRate tokens per second. @@ -1187,7 +1185,7 @@ externalServers: # should be the same, however, they may be different if you # wish to use separate hosts for the HTTPS connections. # @type: array - hosts: [ ] + hosts: [] # The HTTPS port of the Consul servers. httpsPort: 8501 @@ -1385,7 +1383,7 @@ client: # with `-config-dir`. This defaults to false. # # @type: array - extraVolumes: [ ] + extraVolumes: [] # A list of sidecar containers. # Example: @@ -1398,7 +1396,7 @@ client: # - ... # ``` # @type: array - extraContainers: [ ] + extraContainers: [] # Toleration Settings for Client pods # This should be a multi-line string matching the Toleration array @@ -1476,7 +1474,7 @@ client: # feature, in case kubernetes cluster is behind egress http proxies. Additionally, # it could be used to configure custom consul parameters. # @type: map - extraEnvironmentVars: { } + extraEnvironmentVars: {} # This value defines the [Pod DNS policy](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy) # for client pods to use. @@ -1570,7 +1568,6 @@ ui: # Set the port value of the UI service. port: - # HTTP port. http: 80 @@ -1581,7 +1578,6 @@ ui: # If not set and using a NodePort service, Kubernetes will automatically assign # a port. nodePort: - # HTTP node port # @type: integer http: null @@ -1634,7 +1630,7 @@ ui: # ``` # # @type: array - hosts: [ ] + hosts: [] # tls is a list of hosts and secret name in an Ingress # which tells the Ingress controller to secure the channel. @@ -1646,7 +1642,7 @@ ui: # secretName: testsecret-tls # ``` # @type: array - tls: [ ] + tls: [] # Annotations to apply to the UI ingress. # @@ -1737,7 +1733,7 @@ syncCatalog: # # Note: `k8sDenyNamespaces` takes precedence over values defined here. # @type: array - k8sAllowNamespaces: [ "*" ] + k8sAllowNamespaces: ["*"] # List of k8s namespaces that should not have their # services synced. This list takes precedence over `k8sAllowNamespaces`. @@ -1747,7 +1743,7 @@ syncCatalog: # `["namespace1", "namespace2"]`, then all k8s namespaces besides `namespace1` # and `namespace2` will be synced. # @type: array - k8sDenyNamespaces: [ "kube-system", "kube-public" ] + k8sDenyNamespaces: ["kube-system", "kube-public"] # [DEPRECATED] Use k8sAllowNamespaces and k8sDenyNamespaces instead. For # backwards compatibility, if both this and the allow/deny lists are set, @@ -2166,7 +2162,6 @@ connectInject: # @type: map meta: null - # Configures metrics for Consul Connect services. All values are overridable # via annotations on a per-pod basis. metrics: @@ -2326,7 +2321,7 @@ connectInject: # `namespaceSelector` takes precedence over both since it is applied first. # `kube-system` and `kube-public` are never injected, even if included here. # @type: array - k8sAllowNamespaces: [ "*" ] + k8sAllowNamespaces: ["*"] # List of k8s namespaces that should not allow Connect # sidecar injection. This list takes precedence over `k8sAllowNamespaces`. @@ -2339,7 +2334,7 @@ connectInject: # Note: `namespaceSelector` takes precedence over this since it is applied first. # `kube-system` and `kube-public` are never injected. # @type: array - k8sDenyNamespaces: [ ] + k8sDenyNamespaces: [] # [Enterprise Only] These settings manage the connect injector's interaction with # Consul namespaces (requires consul-ent v1.7+). @@ -2724,10 +2719,10 @@ ingressGateways: # @default: [{port: 8080, port: 8443}] # @recurse: false ports: - - port: 8080 - nodePort: null - - port: 8443 - nodePort: null + - port: 8080 + nodePort: null + - port: 8443 + nodePort: null # Annotations to apply to the ingress gateway service. Annotations defined # here will be applied to all ingress gateway services in addition to any @@ -2852,7 +2847,7 @@ ingressGateways: # case of annotations where both will be applied. # @type: array gateways: - - name: ingress-gateway + - name: ingress-gateway # Configuration options for terminating gateways. Default values for all # terminating gateways are defined in `terminatingGateways.defaults`. Any of @@ -2887,7 +2882,7 @@ terminatingGateways: # path: path # secret will now mount to /consul/userconfig/my-secret/path # ``` # @type: array - extraVolumes: [ ] + extraVolumes: [] # Resource limits for all terminating gateway pods # @recurse: false @@ -2993,7 +2988,7 @@ terminatingGateways: # case of annotations where both will be applied. # @type: array gateways: - - name: terminating-gateway + - name: terminating-gateway # [DEPRECATED] Use connectInject.apiGateway instead. This stanza will be removed with the release of Consul 1.17 # Configuration settings for the Consul API Gateway integration @@ -3167,7 +3162,6 @@ apiGateway: # Configuration settings for the webhook-cert-manager # `webhook-cert-manager` ensures that cert bundles are up to date for the mutating webhook. webhookCertManager: - # Toleration Settings # This should be a multi-line string matching the Toleration array # in a PodSpec. diff --git a/control-plane/build-support/functions/10-util.sh b/control-plane/build-support/functions/10-util.sh index 26c43cb610..9ba6c26da9 100644 --- a/control-plane/build-support/functions/10-util.sh +++ b/control-plane/build-support/functions/10-util.sh @@ -2,984 +2,931 @@ # SPDX-License-Identifier: MPL-2.0 function err { - if test "${COLORIZE}" -eq 1 - then - tput bold - tput setaf 1 - fi + if test "${COLORIZE}" -eq 1; then + tput bold + tput setaf 1 + fi - echo "$@" 1>&2 + echo "$@" 1>&2 - if test "${COLORIZE}" -eq 1 - then - tput sgr0 - fi + if test "${COLORIZE}" -eq 1; then + tput sgr0 + fi } function status { - if test "${COLORIZE}" -eq 1 - then - tput bold - tput setaf 4 - fi + if test "${COLORIZE}" -eq 1; then + tput bold + tput setaf 4 + fi - echo "$@" + echo "$@" - if test "${COLORIZE}" -eq 1 - then - tput sgr0 - fi + if test "${COLORIZE}" -eq 1; then + tput sgr0 + fi } function status_stage { - if test "${COLORIZE}" -eq 1 - then - tput bold - tput setaf 2 - fi + if test "${COLORIZE}" -eq 1; then + tput bold + tput setaf 2 + fi - echo "$@" + echo "$@" - if test "${COLORIZE}" -eq 1 - then - tput sgr0 - fi + if test "${COLORIZE}" -eq 1; then + tput sgr0 + fi } function debug { - if is_set "${BUILD_DEBUG}" - then - if test "${COLORIZE}" -eq 1 - then - tput setaf 6 - fi - echo "$@" - if test "${COLORIZE}" -eq 1 - then - tput sgr0 - fi - fi + if is_set "${BUILD_DEBUG}"; then + if test "${COLORIZE}" -eq 1; then + tput setaf 6 + fi + echo "$@" + if test "${COLORIZE}" -eq 1; then + tput sgr0 + fi + fi } function sed_i { - if test "$(uname)" == "Darwin" - then - sed -i '' "$@" - return $? - else - sed -i "$@" - return $? - fi + if test "$(uname)" == "Darwin"; then + sed -i '' "$@" + return $? + else + sed -i "$@" + return $? + fi } function is_set { - # Arguments: - # $1 - string value to check its truthiness - # - # Return: - # 0 - is truthy (backwards I know but allows syntax like `if is_set ` to work) - # 1 - is not truthy - - local val=$(tr '[:upper:]' '[:lower:]' <<< "$1") - case $val in - 1 | t | true | y | yes) - return 0 - ;; - *) - return 1 - ;; - esac + # Arguments: + # $1 - string value to check its truthiness + # + # Return: + # 0 - is truthy (backwards I know but allows syntax like `if is_set ` to work) + # 1 - is not truthy + + local val=$(tr '[:upper:]' '[:lower:]' <<<"$1") + case $val in + 1 | t | true | y | yes) + return 0 + ;; + *) + return 1 + ;; + esac } function have_gpg_key { - # Arguments: - # $1 - GPG Key id to check if we have installed - # - # Return: - # 0 - success (we can use this key for signing) - # * - failure (key cannot be used) - - gpg --list-secret-keys $1 > /dev/null 2>&1 - return $? + # Arguments: + # $1 - GPG Key id to check if we have installed + # + # Return: + # 0 - success (we can use this key for signing) + # * - failure (key cannot be used) + + gpg --list-secret-keys $1 >/dev/null 2>&1 + return $? } function parse_version { - # Arguments: - # $1 - Path to the top level Consul source - # $2 - boolean value for whether the release version should be parsed from the source - # $3 - boolean whether to use GIT_DESCRIBE and GIT_COMMIT environment variables - # $4 - boolean whether to omit the version part of the version string. (optional) - # - # Return: - # 0 - success (will write the version to stdout) - # * - error (no version output) - # - # Notes: - # If the GOTAGS environment variable is present then it is used to determine which - # version file to use for parsing. - - local vfile="${1}/version/version.go" - - # ensure the version file exists - if ! test -f "${vfile}" - then - err "Error - File not found: ${vfile}" - return 1 - fi - - local include_release="$2" - local use_git_env="$3" - local omit_version="$4" - - local git_version="" - local git_commit="" - - if test -z "${include_release}" - then - include_release=true - fi - - if test -z "${use_git_env}" - then - use_git_env=true - fi - - if is_set "${use_git_env}" - then - git_version="${GIT_DESCRIBE}" - git_commit="${GIT_COMMIT}" - fi - - # Get the main version out of the source file - version_main=$(awk '$1 == "Version" && $2 == "=" { gsub(/"/, "", $3); print $3 }' < ${vfile}) - release_main=$(awk '$1 == "VersionPrerelease" && $2 == "=" { gsub(/"/, "", $3); print $3 }' < ${vfile}) - - - # try to determine the version if we have build tags - for tag in "$GOTAGS" - do - for vfile in $(find "${1}/version" -name "version_*.go" 2> /dev/null| sort) - do - if grep -q "// +build $tag" "${vfile}" - then - version_main=$(awk '$1 == "Version" && $2 == "=" { gsub(/"/, "", $3); print $3 }' < ${vfile}) - release_main=$(awk '$1 == "VersionPrerelease" && $2 == "=" { gsub(/"/, "", $3); print $3 }' < ${vfile}) - fi - done - done - - local version="${version_main}" - # override the version from source with the value of the GIT_DESCRIBE env var if present - if test -n "${git_version}" - then - version="${git_version}" - fi - - local rel_ver="" - if is_set "${include_release}" - then - # Default to pre-release from the source - rel_ver="${release_main}" - - # When no GIT_DESCRIBE env var is present and no release is in the source then we - # are definitely in dev mode - if test -z "${git_version}" -a -z "${rel_ver}" && is_set "${use_git_env}" - then - rel_ver="dev" - fi - - # Add the release to the version - if test -n "${rel_ver}" -a -n "${git_commit}" - then - rel_ver="${rel_ver} (${git_commit})" - fi - fi - - if test -n "${rel_ver}" - then - if is_set "${omit_version}" - then - echo "${rel_ver}" | tr -d "'" - else - echo "${version}-${rel_ver}" | tr -d "'" - fi - return 0 - elif ! is_set "${omit_version}" - then - echo "${version}" | tr -d "'" - return 0 - else - return 1 - fi + # Arguments: + # $1 - Path to the top level Consul source + # $2 - boolean value for whether the release version should be parsed from the source + # $3 - boolean whether to use GIT_DESCRIBE and GIT_COMMIT environment variables + # $4 - boolean whether to omit the version part of the version string. (optional) + # + # Return: + # 0 - success (will write the version to stdout) + # * - error (no version output) + # + # Notes: + # If the GOTAGS environment variable is present then it is used to determine which + # version file to use for parsing. + + local vfile="${1}/version/version.go" + + # ensure the version file exists + if ! test -f "${vfile}"; then + err "Error - File not found: ${vfile}" + return 1 + fi + + local include_release="$2" + local use_git_env="$3" + local omit_version="$4" + + local git_version="" + local git_commit="" + + if test -z "${include_release}"; then + include_release=true + fi + + if test -z "${use_git_env}"; then + use_git_env=true + fi + + if is_set "${use_git_env}"; then + git_version="${GIT_DESCRIBE}" + git_commit="${GIT_COMMIT}" + fi + + # Get the main version out of the source file + version_main=$(awk '$1 == "Version" && $2 == "=" { gsub(/"/, "", $3); print $3 }' <${vfile}) + release_main=$(awk '$1 == "VersionPrerelease" && $2 == "=" { gsub(/"/, "", $3); print $3 }' <${vfile}) + + # try to determine the version if we have build tags + for tag in "$GOTAGS"; do + for vfile in $(find "${1}/version" -name "version_*.go" 2>/dev/null | sort); do + if grep -q "// +build $tag" "${vfile}"; then + version_main=$(awk '$1 == "Version" && $2 == "=" { gsub(/"/, "", $3); print $3 }' <${vfile}) + release_main=$(awk '$1 == "VersionPrerelease" && $2 == "=" { gsub(/"/, "", $3); print $3 }' <${vfile}) + fi + done + done + + local version="${version_main}" + # override the version from source with the value of the GIT_DESCRIBE env var if present + if test -n "${git_version}"; then + version="${git_version}" + fi + + local rel_ver="" + if is_set "${include_release}"; then + # Default to pre-release from the source + rel_ver="${release_main}" + + # When no GIT_DESCRIBE env var is present and no release is in the source then we + # are definitely in dev mode + if test -z "${git_version}" -a -z "${rel_ver}" && is_set "${use_git_env}"; then + rel_ver="dev" + fi + + # Add the release to the version + if test -n "${rel_ver}" -a -n "${git_commit}"; then + rel_ver="${rel_ver} (${git_commit})" + fi + fi + + if test -n "${rel_ver}"; then + if is_set "${omit_version}"; then + echo "${rel_ver}" | tr -d "'" + else + echo "${version}-${rel_ver}" | tr -d "'" + fi + return 0 + elif ! is_set "${omit_version}"; then + echo "${version}" | tr -d "'" + return 0 + else + return 1 + fi } function get_version { - # Arguments: - # $1 - Path to the top level Consul source - # $2 - Whether the release version should be parsed from source (optional) - # $3 - Whether to use GIT_DESCRIBE and GIT_COMMIT environment variables - # - # Returns: - # 0 - success (the version is also echoed to stdout) - # 1 - error - # - # Notes: - # If a VERSION environment variable is present it will override any parsing of the version from the source - # In addition to processing the main version.go, version_*.go files will be processed if they have - # a Go build tag that matches the one in the GOTAGS environment variable. This tag processing is - # primitive though and will not match complex build tags in the files with negation etc. - - local vers="$VERSION" - if test -z "$vers" - then - # parse the OSS version from version.go - vers="$(parse_version ${1} ${2} ${3})" - fi - - if test -z "$vers" - then - return 1 - else - echo $vers - return 0 - fi + # Arguments: + # $1 - Path to the top level Consul source + # $2 - Whether the release version should be parsed from source (optional) + # $3 - Whether to use GIT_DESCRIBE and GIT_COMMIT environment variables + # + # Returns: + # 0 - success (the version is also echoed to stdout) + # 1 - error + # + # Notes: + # If a VERSION environment variable is present it will override any parsing of the version from the source + # In addition to processing the main version.go, version_*.go files will be processed if they have + # a Go build tag that matches the one in the GOTAGS environment variable. This tag processing is + # primitive though and will not match complex build tags in the files with negation etc. + + local vers="$VERSION" + if test -z "$vers"; then + # parse the OSS version from version.go + vers="$(parse_version ${1} ${2} ${3})" + fi + + if test -z "$vers"; then + return 1 + else + echo $vers + return 0 + fi } function git_branch { - # Arguments: - # $1 - Path to the git repo (optional - assumes pwd is git repo otherwise) - # - # Returns: - # 0 - success - # * - failure - # - # Notes: - # Echos the current branch to stdout when successful - - local gdir="$(pwd)" - if test -d "$1" - then - gdir="$1" - fi - - pushd "${gdir}" > /dev/null - - local ret=0 - local head="$(git status -b --porcelain=v2 | awk '{if ($1 == "#" && $2 =="branch.head") { print $3 }}')" || ret=1 - - popd > /dev/null - - test ${ret} -eq 0 && echo "$head" - return ${ret} + # Arguments: + # $1 - Path to the git repo (optional - assumes pwd is git repo otherwise) + # + # Returns: + # 0 - success + # * - failure + # + # Notes: + # Echos the current branch to stdout when successful + + local gdir="$(pwd)" + if test -d "$1"; then + gdir="$1" + fi + + pushd "${gdir}" >/dev/null + + local ret=0 + local head="$(git status -b --porcelain=v2 | awk '{if ($1 == "#" && $2 =="branch.head") { print $3 }}')" || ret=1 + + popd >/dev/null + + test ${ret} -eq 0 && echo "$head" + return ${ret} } function git_upstream { - # Arguments: - # $1 - Path to the git repo (optional - assumes pwd is git repo otherwise) - # - # Returns: - # 0 - success - # * - failure - # - # Notes: - # Echos the current upstream branch to stdout when successful - - local gdir="$(pwd)" - if test -d "$1" - then - gdir="$1" - fi - - pushd "${gdir}" > /dev/null - - local ret=0 - local head="$(git status -b --porcelain=v2 | awk '{if ($1 == "#" && $2 =="branch.upstream") { print $3 }}')" || ret=1 - - popd > /dev/null - - test ${ret} -eq 0 && echo "$head" - return ${ret} + # Arguments: + # $1 - Path to the git repo (optional - assumes pwd is git repo otherwise) + # + # Returns: + # 0 - success + # * - failure + # + # Notes: + # Echos the current upstream branch to stdout when successful + + local gdir="$(pwd)" + if test -d "$1"; then + gdir="$1" + fi + + pushd "${gdir}" >/dev/null + + local ret=0 + local head="$(git status -b --porcelain=v2 | awk '{if ($1 == "#" && $2 =="branch.upstream") { print $3 }}')" || ret=1 + + popd >/dev/null + + test ${ret} -eq 0 && echo "$head" + return ${ret} } function git_log_summary { - # Arguments: - # $1 - Path to the git repo (optional - assumes pwd is git repo otherwise) - # - # Returns: - # 0 - success - # * - failure - # + # Arguments: + # $1 - Path to the git repo (optional - assumes pwd is git repo otherwise) + # + # Returns: + # 0 - success + # * - failure + # - local gdir="$(pwd)" - if test -d "$1" - then - gdir="$1" - fi + local gdir="$(pwd)" + if test -d "$1"; then + gdir="$1" + fi - pushd "${gdir}" > /dev/null + pushd "${gdir}" >/dev/null - local ret=0 + local ret=0 - local head=$(git_branch) || ret=1 - local upstream=$(git_upstream) || ret=1 - local rev_range="${head}...${upstream}" + local head=$(git_branch) || ret=1 + local upstream=$(git_upstream) || ret=1 + local rev_range="${head}...${upstream}" - if test ${ret} -eq 0 - then - status "Git Changes:" - git log --pretty=oneline ${rev_range} || ret=1 + if test ${ret} -eq 0; then + status "Git Changes:" + git log --pretty=oneline ${rev_range} || ret=1 - fi - return $ret + fi + return $ret } function git_diff { - # Arguments: - # $1 - Path to the git repo (optional - assumes pwd is git repo otherwise) - # $2 .. $N - Optional path specification - # - # Returns: - # 0 - success - # * - failure - # - - local gdir="$(pwd)" - if test -d "$1" - then - gdir="$1" - fi - - shift - - pushd "${gdir}" > /dev/null - - local ret=0 - - local head=$(git_branch) || ret=1 - local upstream=$(git_upstream) || ret=1 - - if test ${ret} -eq 0 - then - status "Git Diff - Paths: $@" - git diff ${HEAD} ${upstream} -- "$@" || ret=1 - fi - return $ret + # Arguments: + # $1 - Path to the git repo (optional - assumes pwd is git repo otherwise) + # $2 .. $N - Optional path specification + # + # Returns: + # 0 - success + # * - failure + # + + local gdir="$(pwd)" + if test -d "$1"; then + gdir="$1" + fi + + shift + + pushd "${gdir}" >/dev/null + + local ret=0 + + local head=$(git_branch) || ret=1 + local upstream=$(git_upstream) || ret=1 + + if test ${ret} -eq 0; then + status "Git Diff - Paths: $@" + git diff ${HEAD} ${upstream} -- "$@" || ret=1 + fi + return $ret } function normalize_git_url { - url="${1#https://}" - url="${url#git@}" - url="${url%.git}" - url="$(sed ${SED_EXT} -e 's/([^\/:]*)[:\/](.*)/\1:\2/' <<< "${url}")" - echo "$url" - return 0 + url="${1#https://}" + url="${url#git@}" + url="${url%.git}" + url="$(sed ${SED_EXT} -e 's/([^\/:]*)[:\/](.*)/\1:\2/' <<<"${url}")" + echo "$url" + return 0 } function git_remote_url { - # Arguments: - # $1 - Path to the top level Consul source - # $2 - Remote name - # - # Returns: - # 0 - success - # * - error - # - # Note: - # The push url for the git remote will be echoed to stdout - - if ! test -d "$1" - then - err "ERROR: '$1' is not a directory. git_remote_url must be called with the path to the top level source as the first argument'" - return 1 - fi - - if test -z "$2" - then - err "ERROR: git_remote_url must be called with a second argument that is the name of the remote" - return 1 - fi - - local ret=0 - - pushd "$1" > /dev/null - - local url=$(git remote get-url --push $2 2>&1) || ret=1 - - popd > /dev/null - - if test "${ret}" -eq 0 - then - echo "${url}" - return 0 - fi + # Arguments: + # $1 - Path to the top level Consul source + # $2 - Remote name + # + # Returns: + # 0 - success + # * - error + # + # Note: + # The push url for the git remote will be echoed to stdout + + if ! test -d "$1"; then + err "ERROR: '$1' is not a directory. git_remote_url must be called with the path to the top level source as the first argument'" + return 1 + fi + + if test -z "$2"; then + err "ERROR: git_remote_url must be called with a second argument that is the name of the remote" + return 1 + fi + + local ret=0 + + pushd "$1" >/dev/null + + local url=$(git remote get-url --push $2 2>&1) || ret=1 + + popd >/dev/null + + if test "${ret}" -eq 0; then + echo "${url}" + return 0 + fi } function find_git_remote { - # Arguments: - # $1 - Path to the top level Consul source - # - # Returns: - # 0 - success - # * - error - # - # Note: - # The remote name to use for publishing will be echoed to stdout upon success - - if ! test -d "$1" - then - err "ERROR: '$1' is not a directory. find_git_remote must be called with the path to the top level source as the first argument'" - return 1 - fi - - need_url=$(normalize_git_url "${PUBLISH_GIT_HOST}:${PUBLISH_GIT_REPO}") - debug "Required normalized remote: ${need_url}" - - pushd "$1" > /dev/null - - local ret=1 - for remote in $(git remote) - do - url=$(git remote get-url --push ${remote}) || continue - url=$(normalize_git_url "${url}") - - debug "Testing Remote: ${remote}: ${url}" - if test "${url}" == "${need_url}" - then - echo "${remote}" - ret=0 - break - fi - done - - popd > /dev/null - return ${ret} + # Arguments: + # $1 - Path to the top level Consul source + # + # Returns: + # 0 - success + # * - error + # + # Note: + # The remote name to use for publishing will be echoed to stdout upon success + + if ! test -d "$1"; then + err "ERROR: '$1' is not a directory. find_git_remote must be called with the path to the top level source as the first argument'" + return 1 + fi + + need_url=$(normalize_git_url "${PUBLISH_GIT_HOST}:${PUBLISH_GIT_REPO}") + debug "Required normalized remote: ${need_url}" + + pushd "$1" >/dev/null + + local ret=1 + for remote in $(git remote); do + url=$(git remote get-url --push ${remote}) || continue + url=$(normalize_git_url "${url}") + + debug "Testing Remote: ${remote}: ${url}" + if test "${url}" == "${need_url}"; then + echo "${remote}" + ret=0 + break + fi + done + + popd >/dev/null + return ${ret} } function git_remote_not_blacklisted { - # Arguments: - # $1 - path to the repo - # $2 - the remote name - # - # Returns: - # 0 - not blacklisted - # * - blacklisted - return 0 + # Arguments: + # $1 - path to the repo + # $2 - the remote name + # + # Returns: + # 0 - not blacklisted + # * - blacklisted + return 0 } function is_git_clean { - # Arguments: - # $1 - Path to git repo - # $2 - boolean whether the git status should be output when not clean - # - # Returns: - # 0 - success - # * - error - # - - if ! test -d "$1" - then - err "ERROR: '$1' is not a directory. is_git_clean must be called with the path to a git repo as the first argument'" - return 1 - fi - - local output_status="$2" - - pushd "${1}" > /dev/null - - local ret=0 - test -z "$(git status --porcelain=v2 2> /dev/null)" || ret=1 - - if is_set "${output_status}" && test "$ret" -ne 0 - then - err "Git repo is not clean" - # --porcelain=v1 is the same as --short except uncolorized - git status --porcelain=v1 - fi - popd > /dev/null - return ${ret} + # Arguments: + # $1 - Path to git repo + # $2 - boolean whether the git status should be output when not clean + # + # Returns: + # 0 - success + # * - error + # + + if ! test -d "$1"; then + err "ERROR: '$1' is not a directory. is_git_clean must be called with the path to a git repo as the first argument'" + return 1 + fi + + local output_status="$2" + + pushd "${1}" >/dev/null + + local ret=0 + test -z "$(git status --porcelain=v2 2>/dev/null)" || ret=1 + + if is_set "${output_status}" && test "$ret" -ne 0; then + err "Git repo is not clean" + # --porcelain=v1 is the same as --short except uncolorized + git status --porcelain=v1 + fi + popd >/dev/null + return ${ret} } function update_git_env { - # Arguments: - # $1 - Path to git repo - # - # Returns: - # 0 - success - # * - error - # - - if ! test -d "$1" - then - err "ERROR: '$1' is not a directory. is_git_clean must be called with the path to a git repo as the first argument'" - return 1 - fi - - export GIT_COMMIT=$(git rev-parse --short HEAD) - export GIT_DIRTY=$(test -n "$(git status --porcelain)" && echo "+CHANGES") - export GIT_DESCRIBE=$(git describe --tags --always) - export GIT_IMPORT=github.com/hashicorp/consul-k8s/version - export GOLDFLAGS="-X ${GIT_IMPORT}.GitCommit=${GIT_COMMIT}${GIT_DIRTY} -X ${GIT_IMPORT}.GitDescribe=${GIT_DESCRIBE}" - return 0 + # Arguments: + # $1 - Path to git repo + # + # Returns: + # 0 - success + # * - error + # + + if ! test -d "$1"; then + err "ERROR: '$1' is not a directory. is_git_clean must be called with the path to a git repo as the first argument'" + return 1 + fi + + export GIT_COMMIT=$(git rev-parse --short HEAD) + export GIT_DIRTY=$(test -n "$(git status --porcelain)" && echo "+CHANGES") + export GIT_DESCRIBE=$(git describe --tags --always) + export GIT_IMPORT=github.com/hashicorp/consul-k8s/version + export GOLDFLAGS="-X ${GIT_IMPORT}.GitCommit=${GIT_COMMIT}${GIT_DIRTY} -X ${GIT_IMPORT}.GitDescribe=${GIT_DESCRIBE}" + return 0 } function git_push_ref { - # Arguments: - # $1 - Path to the top level Consul source - # $2 - Git ref (optional) - # $3 - remote (optional - if not specified we will try to determine it) - # - # Returns: - # 0 - success - # * - error - - if ! test -d "$1" - then - err "ERROR: '$1' is not a directory. push_git_release must be called with the path to the top level source as the first argument'" - return 1 - fi - - local sdir="$1" - local ret=0 - local remote="$3" - - # find the correct remote corresponding to the desired repo (basically prevent pushing enterprise to oss or oss to enterprise) - if test -z "${remote}" - then - local remote=$(find_git_remote "${sdir}") || return 1 - status "Using git remote: ${remote}" - fi - - local ref="" - - pushd "${sdir}" > /dev/null - - if test -z "$2" - then - # If no git ref was provided we lookup the current local branch and its tracking branch - # It must have a tracking upstream and it must be tracking the sanctioned git remote - local head=$(git_branch "${sdir}") || return 1 - local upstream=$(git_upstream "${sdir}") || return 1 - - # upstream branch for this branch does not track the remote we need to push to - # basically this checks that the upstream (could be something like origin/main) references the correct remote - # if it doesn't then the string modification wont apply and the var will reamin unchanged and equal to itself. - if test "${upstream#${remote}/}" == "${upstream}" - then - err "ERROR: Upstream branch '${upstream}' does not track the correct remote '${remote}' - cannot push" - ret=1 - fi - ref="refs/heads/${head}" - else - # A git ref was provided - get the full ref and make sure it isn't ambiguous and also to - # be able to determine whether its a branch or tag we are pushing - ref_out=$(git rev-parse --symbolic-full-name "$2" --) - - # -ne 2 because it should have the ref on one line followed by a line with '--' - if test "$(wc -l <<< "${ref_out}")" -ne 2 - then - err "ERROR: Git ref '$2' is ambiguous" - debug "${ref_out}" - ret=1 - else - ref=$(head -n 1 <<< "${ref_out}") - fi - fi - - if test ${ret} -eq 0 - then - case "${ref}" in - refs/tags/*) - status "Pushing tag ${ref#refs/tags/} to ${remote}" - ;; - refs/heads/*) - status "Pushing local branch ${ref#refs/tags/} to ${remote}" - ;; - *) - err "ERROR: git_push_ref func is refusing to push ref that isn't a branch or tag" - return 1 - esac - - if ! git push "${remote}" "${ref}" - then - err "ERROR: Failed to push ${ref} to remote: ${remote}" - ret=1 - fi - fi - - popd > /dev/null - - return $ret + # Arguments: + # $1 - Path to the top level Consul source + # $2 - Git ref (optional) + # $3 - remote (optional - if not specified we will try to determine it) + # + # Returns: + # 0 - success + # * - error + + if ! test -d "$1"; then + err "ERROR: '$1' is not a directory. push_git_release must be called with the path to the top level source as the first argument'" + return 1 + fi + + local sdir="$1" + local ret=0 + local remote="$3" + + # find the correct remote corresponding to the desired repo (basically prevent pushing enterprise to oss or oss to enterprise) + if test -z "${remote}"; then + local remote=$(find_git_remote "${sdir}") || return 1 + status "Using git remote: ${remote}" + fi + + local ref="" + + pushd "${sdir}" >/dev/null + + if test -z "$2"; then + # If no git ref was provided we lookup the current local branch and its tracking branch + # It must have a tracking upstream and it must be tracking the sanctioned git remote + local head=$(git_branch "${sdir}") || return 1 + local upstream=$(git_upstream "${sdir}") || return 1 + + # upstream branch for this branch does not track the remote we need to push to + # basically this checks that the upstream (could be something like origin/main) references the correct remote + # if it doesn't then the string modification wont apply and the var will reamin unchanged and equal to itself. + if test "${upstream#${remote}/}" == "${upstream}"; then + err "ERROR: Upstream branch '${upstream}' does not track the correct remote '${remote}' - cannot push" + ret=1 + fi + ref="refs/heads/${head}" + else + # A git ref was provided - get the full ref and make sure it isn't ambiguous and also to + # be able to determine whether its a branch or tag we are pushing + ref_out=$(git rev-parse --symbolic-full-name "$2" --) + + # -ne 2 because it should have the ref on one line followed by a line with '--' + if test "$(wc -l <<<"${ref_out}")" -ne 2; then + err "ERROR: Git ref '$2' is ambiguous" + debug "${ref_out}" + ret=1 + else + ref=$(head -n 1 <<<"${ref_out}") + fi + fi + + if test ${ret} -eq 0; then + case "${ref}" in + refs/tags/*) + status "Pushing tag ${ref#refs/tags/} to ${remote}" + ;; + refs/heads/*) + status "Pushing local branch ${ref#refs/tags/} to ${remote}" + ;; + *) + err "ERROR: git_push_ref func is refusing to push ref that isn't a branch or tag" + return 1 + ;; + esac + + if ! git push "${remote}" "${ref}"; then + err "ERROR: Failed to push ${ref} to remote: ${remote}" + ret=1 + fi + fi + + popd >/dev/null + + return $ret } function update_version { - # Arguments: - # $1 - Path to the version file - # $2 - Version string - # $3 - PreRelease version (if unset will become an empty string) - # - # Returns: - # 0 - success - # * - error - - if ! test -f "$1" - then - err "ERROR: '$1' is not a regular file. update_version must be called with the path to a go version file" - return 1 - fi - - if test -z "$2" - then - err "ERROR: The version specified was empty" - return 1 - fi - - local vfile="$1" - local version="$2" - local prerelease="$3" - - sed_i ${SED_EXT} -e "s/(Version[[:space:]]*=[[:space:]]*)\"[^\"]*\"/\1\"${version}\"/g" -e "s/(VersionPrerelease[[:space:]]*=[[:space:]]*)\"[^\"]*\"/\1\"${prerelease}\"/g" "${vfile}" - return $? + # Arguments: + # $1 - Path to the version file + # $2 - Version string + # $3 - PreRelease version (if unset will become an empty string) + # + # Returns: + # 0 - success + # * - error + + if ! test -f "$1"; then + err "ERROR: '$1' is not a regular file. update_version must be called with the path to a go version file" + return 1 + fi + + if test -z "$2"; then + err "ERROR: The version specified was empty" + return 1 + fi + + local vfile="$1" + local version="$2" + local prerelease="$3" + + sed_i ${SED_EXT} -e "s/(Version[[:space:]]*=[[:space:]]*)\"[^\"]*\"/\1\"${version}\"/g" -e "s/(VersionPrerelease[[:space:]]*=[[:space:]]*)\"[^\"]*\"/\1\"${prerelease}\"/g" "${vfile}" + return $? } function update_version_helm { - # Arguments: - # $1 - Path to the directory where the root of the Helm chart is - # $2 - Version string - # $3 - PreRelease version (if unset will become an empty string) - # $4 - Image base path - # - # Returns: - # 0 - success - # * - error - - if ! test -d "$1" - then - err "ERROR: '$1' is not a directory. update_version_helm must be called with the path to the Helm chart" - return 1 - fi - - if test -z "$2" - then - err "ERROR: The version specified was empty" - return 1 - fi - - local vfile="$1/values.yaml" - local cfile="$1/Chart.yaml" - local version="$2" - local prerelease="$3" - local full_version="$2" - if ! test -z "$3" - then - full_version="$2-$3" - fi - - sed_i ${SED_EXT} -e "s/(imageK8S:.*\/consul-k8s-control-plane:)[^\"]*/imageK8S: $4${full_version}/g" "${vfile}" - sed_i ${SED_EXT} -e "s/(version:[[:space:]]*)[^\"]*/\1${full_version}/g" "${cfile}" - sed_i ${SED_EXT} -e "s/(image:.*\/consul-k8s-control-plane:)[^\"]*/image: $4${full_version}/g" "${cfile}" - - if test -z "$3" - then - sed_i ${SED_EXT} -e "s/(artifacthub.io\/prerelease:[[:space:]]*)[^\"]*/\1false/g" "${cfile}" - else - sed_i ${SED_EXT} -e "s/(artifacthub.io\/prerelease:[[:space:]]*)[^\"]*/\1true/g" "${cfile}" - fi - return $? + # Arguments: + # $1 - Path to the directory where the root of the Helm chart is + # $2 - Version string + # $3 - Release version (if unset will become an empty string) + # $4 - Image base path + # $5 - Consul version string + # $6 - Consul image base path + # + # Returns: + # 0 - success + # * - error + + if ! test -d "$1"; then + err "ERROR: '$1' is not a directory. update_version_helm must be called with the path to the Helm chart" + return 1 + fi + + if test -z "$2"; then + err "ERROR: The version specified was empty" + return 1 + fi + + local vfile="$1/values.yaml" + local cfile="$1/Chart.yaml" + local version="$2" + local prerelease="$3" + local full_version="$2" + local full_consul_version="$5" + if ! test -z "$3"; then + full_version="$2-$3" + # strip off the last minor patch version so that the consul image can be set to something like 1.16-dev. The image + # is produced by Consul every night + full_consul_version="${5%.*}-$3" + fi + + sed_i ${SED_EXT} -e "s/(imageK8S:.*\/consul-k8s-control-plane:)[^\"]*/imageK8S: $4${full_version}/g" "${vfile}" + sed_i ${SED_EXT} -e "s/(version:[[:space:]]*)[^\"]*/\1${full_version}/g" "${cfile}" + sed_i ${SED_EXT} -e "s/(appVersion:[[:space:]]*)[^\"]*/\1${full_consul_version}/g" "${cfile}" + sed_i ${SED_EXT} -e "s/(image:.*\/consul-k8s-control-plane:)[^\"]*/image: $4${full_version}/g" "${cfile}" + if ! test -z "$3"; then + sed_i ${SED_EXT} -e "s/(image:.*\/consul:)[^\"]*/image: $6:${full_consul_version}/g" "${cfile}" + sed_i ${SED_EXT} -e "s/(image:.*\/consul:)[^\"]*/image: $6:${full_consul_version}/g" "${vfile}" + else + sed_i ${SED_EXT} -e "s/(image:.*\/consul-enterprise:)[^\"]*/image: $6:${full_consul_version}/g" "${cfile}" + sed_i ${SED_EXT} -e "s/(image:.*\/consul-enterprise:)[^\"]*/image: $6:${full_consul_version}/g" "${vfile}" + fi + + if test -z "$3"; then + sed_i ${SED_EXT} -e "s/(artifacthub.io\/prerelease:[[:space:]]*)[^\"]*/\1false/g" "${cfile}" + else + sed_i ${SED_EXT} -e "s/(artifacthub.io\/prerelease:[[:space:]]*)[^\"]*/\1true/g" "${cfile}" + fi + return $? } function set_version { - # Arguments: - # $1 - Path to top level Consul source - # $2 - The version of the release - # $3 - The release date - # $4 - The pre-release version - # $5 - The helm docker image base path - # - # - # Returns: - # 0 - success - # * - error - - if ! test -d "$1" - then - err "ERROR: '$1' is not a directory. prepare_release must be called with the path to a git repo as the first argument" - return 1 - fi - - if test -z "$2" - then - err "ERROR: The version specified was empty" - return 1 - fi - - local sdir="$1" - local vers="$2" - - status_stage "==> Updating control-plane version/version.go with version info: ${vers} "$4"" - if ! update_version "${sdir}/control-plane/version/version.go" "${vers}" "$4" - then - return 1 - fi - - status_stage "==> Updating cli version/version.go with version info: ${vers} "$4"" - if ! update_version "${sdir}/cli/version/version.go" "${vers}" "$4" - then - return 1 - fi - - status_stage "==> Updating Helm chart versions with version info: ${vers} "$4"" - if ! update_version_helm "${sdir}/charts/consul" "${vers}" "$4" "$5" - then - return 1 - fi - - return 0 + # Arguments: + # $1 - Path to top level Consul source + # $2 - The version of the release + # $3 - The release date + # $4 - The pre-release version + # $5 - The consul-k8s helm docker image base path + # $6 - The consul version + # $7 - The consul helm docker image base path + # + # + # Returns: + # 0 - success + # * - error + + if ! test -d "$1"; then + err "ERROR: '$1' is not a directory. prepare_release must be called with the path to a git repo as the first argument" + return 1 + fi + + if test -z "$2"; then + err "ERROR: The version specified was empty" + return 1 + fi + + local sdir="$1" + local vers="$2" + local consul_vers="$6" + + status_stage "==> Updating control-plane version/version.go with version info: ${vers} "$4"" + if ! update_version "${sdir}/control-plane/version/version.go" "${vers}" "$4"; then + return 1 + fi + + status_stage "==> Updating cli version/version.go with version info: ${vers} "$4"" + if ! update_version "${sdir}/cli/version/version.go" "${vers}" "$4"; then + return 1 + fi + + status_stage "==> Updating Helm chart version, consul-k8s: ${vers} "$4" consul: ${consul_vers} "$4"" + if ! update_version_helm "${sdir}/charts/consul" "${vers}" "$4" "$5" "${consul_vers}" "$7"; then + return 1 + fi + + return 0 } function set_changelog { - # Arguments: - # $1 - Path to top level Consul source - # $2 - Version - # $3 - Release Date - # $4 - The last git release tag - # - # - # Returns: - # 0 - success - # * - error - - # Check if changelog-build is installed - if ! command -v changelog-build &> /dev/null; then - echo "Error: changelog-build is not installed. Please install it and try again." - exit 1 - fi - - local curdir="$1" - local version="$2" - local rel_date="$(date +"%B %d, %Y")" - if test -n "$3" - then - rel_date="$3" - fi - local last_release_date_git_tag=$4 - - if test -z "${version}" - then - err "ERROR: Must specify a version to put into the changelog" - return 1 - fi - - if [ -z "$LAST_RELEASE_GIT_TAG" ]; then - echo "Error: LAST_RELEASE_GIT_TAG not specified." - exit 1 - fi - -cat < tmp && mv tmp "${curdir}"/CHANGELOG.MD + # Arguments: + # $1 - Path to top level Consul source + # $2 - Version + # $3 - Release Date + # $4 - The last git release tag + # + # + # Returns: + # 0 - success + # * - error + + # Check if changelog-build is installed + if ! command -v changelog-build &>/dev/null; then + echo "Error: changelog-build is not installed. Please install it and try again." + exit 1 + fi + + local curdir="$1" + local version="$2" + local rel_date="$(date +"%B %d, %Y")" + if test -n "$3"; then + rel_date="$3" + fi + local last_release_date_git_tag=$4 + + if test -z "${version}"; then + err "ERROR: Must specify a version to put into the changelog" + return 1 + fi + + if [ -z "$LAST_RELEASE_GIT_TAG" ]; then + echo "Error: LAST_RELEASE_GIT_TAG not specified." + exit 1 + fi + + cat <tmp && mv tmp "${curdir}"/CHANGELOG.MD ## ${version} (${rel_date}) $(changelog-build -last-release ${LAST_RELEASE_GIT_TAG} \ - -entries-dir .changelog/ \ - -changelog-template .changelog/changelog.tmpl \ - -note-template .changelog/note.tmpl \ - -this-release $(git rev-parse HEAD)) + -entries-dir .changelog/ \ + -changelog-template .changelog/changelog.tmpl \ + -note-template .changelog/note.tmpl \ + -this-release $(git rev-parse HEAD)) EOT } function prepare_release { - # Arguments: - # $1 - Path to top level Consul source - # $2 - The version of the release - # $3 - The release date - # $4 - The last release git tag for this branch (eg. v1.1.0) - # $5 - The pre-release version - # - # - # Returns: - # 0 - success - # * - error - - echo "release version: " "$1" "$2" "$3" "$4" - set_version "$1" "$2" "$3" "$5" "hashicorp\/consul-k8s-control-plane:" - set_changelog "$1" "$2" "$3" "$4" + # Arguments: + # $1 - Path to top level Consul source + # $2 - The version of the release + # $3 - The release date + # $4 - The last release git tag for this branch (eg. v1.1.0) + # $5 - The pre-release version + # $6 - The consul version + # + # + # Returns: + # 0 - success + # * - error + + echo "prepare_release: dir:$1 consul-k8s:$2 consul:$5 date:"$3" git tag:$4" + set_version "$1" "$2" "$3" "$6" "hashicorp\/consul-k8s-control-plane:" "$5" "hashicorp\/consul" + set_changelog "$1" "$2" "$3" "$4" } function prepare_dev { - # Arguments: - # $1 - Path to top level Consul source - # $2 - The version of the release - # $3 - The release date - # $4 - The version of the next release - # $5 - The last release git tag for this branch (eg. v1.1.0) - # - # Returns: - # 0 - success - # * - error - - echo "dev version: " $1 $4 $3 "dev" - set_version "$1" "$4" "$3" "dev" "docker.mirror.hashicorp.services\/hashicorppreview\/consul-k8s-control-plane:" - - return 0 + # Arguments: + # $1 - Path to top level Consul source + # $2 - The version of the release + # $3 - The release date + # $4 - The last release git tag for this branch (eg. v1.1.0) (Unused) + # $5 - The version of the next release + # $6 - The version of the next consul release + # + # Returns: + # 0 - success + # * - error + + echo "prepare_dev: dir:$1 consul-k8s:$5 consul:$6 date:"$3" mode:dev" + set_version "$1" "$5" "$3" "dev" "docker.mirror.hashicorp.services\/hashicorppreview\/consul-k8s-control-plane:" "$6" "docker.mirror.hashicorp.services\/hashicorppreview\/consul-enterprise" + + return 0 } function git_staging_empty { - # Arguments: - # $1 - Path to git repo - # - # Returns: - # 0 - success (nothing staged) - # * - error (staged files) - - if ! test -d "$1" - then - err "ERROR: '$1' is not a directory. commit_dev_mode must be called with the path to a git repo as the first argument'" - return 1 - fi - - pushd "$1" > /dev/null - - declare -i ret=0 - - for status in $(git status --porcelain=v2 | awk '{print $2}' | cut -b 1) - do - if test "${status}" != "." - then - ret=1 - break - fi - done - - popd > /dev/null - return ${ret} + # Arguments: + # $1 - Path to git repo + # + # Returns: + # 0 - success (nothing staged) + # * - error (staged files) + + if ! test -d "$1"; then + err "ERROR: '$1' is not a directory. commit_dev_mode must be called with the path to a git repo as the first argument'" + return 1 + fi + + pushd "$1" >/dev/null + + declare -i ret=0 + + for status in $(git status --porcelain=v2 | awk '{print $2}' | cut -b 1); do + if test "${status}" != "."; then + ret=1 + break + fi + done + + popd >/dev/null + return ${ret} } function commit_dev_mode { - # Arguments: - # $1 - Path to top level Consul source - # - # Returns: - # 0 - success - # * - error + # Arguments: + # $1 - Path to top level Consul source + # + # Returns: + # 0 - success + # * - error - if ! test -d "$1" - then - err "ERROR: '$1' is not a directory. commit_dev_mode must be called with the path to a git repo as the first argument'" - return 1 - fi + if ! test -d "$1"; then + err "ERROR: '$1' is not a directory. commit_dev_mode must be called with the path to a git repo as the first argument'" + return 1 + fi - status "Checking for previously staged files" - git_staging_empty "$1" || return 1 + status "Checking for previously staged files" + git_staging_empty "$1" || return 1 - declare -i ret=0 + declare -i ret=0 - pushd "$1" > /dev/null + pushd "$1" >/dev/null - status "Staging CHANGELOG.md and version_*.go files" - git add CHANGELOG.md && git add */version/version*.go - ret=$? + status "Staging CHANGELOG.md and version_*.go files" + git add CHANGELOG.md && git add */version/version*.go + ret=$? - if test ${ret} -eq 0 - then - status "Adding Commit" - git commit -m "Putting source back into Dev Mode" - ret=$? - fi + if test ${ret} -eq 0; then + status "Adding Commit" + git commit -m "Putting source back into Dev Mode" + ret=$? + fi - popd >/dev/null - return ${ret} + popd >/dev/null + return ${ret} } function gpg_detach_sign { - # Arguments: - # $1 - File to sign - # $2 - Alternative GPG key to use for signing - # - # Returns: - # 0 - success - # * - failure - - # determine whether the gpg key to use is being overridden - local gpg_key=${HASHICORP_GPG_KEY} - if test -n "$2" - then - gpg_key=$2 - fi - - gpg --default-key "${gpg_key}" --detach-sig --yes -v "$1" - return $? + # Arguments: + # $1 - File to sign + # $2 - Alternative GPG key to use for signing + # + # Returns: + # 0 - success + # * - failure + + # determine whether the gpg key to use is being overridden + local gpg_key=${HASHICORP_GPG_KEY} + if test -n "$2"; then + gpg_key=$2 + fi + + gpg --default-key "${gpg_key}" --detach-sig --yes -v "$1" + return $? } function shasum_directory { - # Arguments: - # $1 - Path to directory containing the files to shasum - # $2 - File to output sha sums to - # - # Returns: - # 0 - success - # * - failure - - if ! test -d "$1" - then - err "ERROR: '$1' is not a directory and shasum_release requires passing a directory as the first argument" - return 1 - fi - - if test -z "$2" - then - err "ERROR: shasum_release requires a second argument to be the filename to output the shasums to but none was given" - return 1 - fi - - pushd $1 > /dev/null - shasum -a256 * > "$2" - ret=$? - popd >/dev/null - - return $ret -} - - function ui_version { - # Arguments: - # $1 - path to index.html - # - # Returns: - # 0 - success - # * -failure - # - # Notes: echoes the version to stdout upon success - if ! test -f "$1" - then - err "ERROR: No such file: '$1'" - return 1 - fi - - local ui_version=$(sed -n ${SED_EXT} -e 's/.*CONSUL_VERSION%22%3A%22([^%]*)%22%2C%22.*/\1/p' < "$1") || return 1 - echo "$ui_version" - return 0 - } - function ui_logo_type { - # Arguments: - # $1 - path to index.html - # - # Returns: - # 0 - success - # * -failure - # - # Notes: echoes the 'logo type' to stdout upon success - # the 'logo' can be one of 'enterprise' or 'oss' - # and doesn't necessarily correspond to the binary type of consul - # the logo is 'enterprise' if the binary type is anything but 'oss' - if ! test -f "$1" - then - err "ERROR: No such file: '$1'" - return 1 - fi - grep -q "data-enterprise-logo" < "$1" - - if test $? -eq 0 - then - echo "enterprise" - else - echo "oss" - fi - return 0 - } + # Arguments: + # $1 - Path to directory containing the files to shasum + # $2 - File to output sha sums to + # + # Returns: + # 0 - success + # * - failure + + if ! test -d "$1"; then + err "ERROR: '$1' is not a directory and shasum_release requires passing a directory as the first argument" + return 1 + fi + + if test -z "$2"; then + err "ERROR: shasum_release requires a second argument to be the filename to output the shasums to but none was given" + return 1 + fi + + pushd $1 >/dev/null + shasum -a256 * >"$2" + ret=$? + popd >/dev/null + + return $ret +} + +function ui_version { + # Arguments: + # $1 - path to index.html + # + # Returns: + # 0 - success + # * -failure + # + # Notes: echoes the version to stdout upon success + if ! test -f "$1"; then + err "ERROR: No such file: '$1'" + return 1 + fi + + local ui_version=$(sed -n ${SED_EXT} -e 's/.*CONSUL_VERSION%22%3A%22([^%]*)%22%2C%22.*/\1/p' <"$1") || return 1 + echo "$ui_version" + return 0 +} +function ui_logo_type { + # Arguments: + # $1 - path to index.html + # + # Returns: + # 0 - success + # * -failure + # + # Notes: echoes the 'logo type' to stdout upon success + # the 'logo' can be one of 'enterprise' or 'oss' + # and doesn't necessarily correspond to the binary type of consul + # the logo is 'enterprise' if the binary type is anything but 'oss' + if ! test -f "$1"; then + err "ERROR: No such file: '$1'" + return 1 + fi + grep -q "data-enterprise-logo" <"$1" + + if test $? -eq 0; then + echo "enterprise" + else + echo "oss" + fi + return 0 +} diff --git a/control-plane/build-support/scripts/consul-version.sh b/control-plane/build-support/scripts/consul-version.sh index 634b5db07d..e245e2a239 100755 --- a/control-plane/build-support/scripts/consul-version.sh +++ b/control-plane/build-support/scripts/consul-version.sh @@ -4,5 +4,4 @@ FILE=$1 VERSION=$(yq .global.image $FILE) -# echo full string "hashicorp/consul:1.15.1" | remove first and last characters | cut everything before ':' -echo "${VERSION}" | sed 's/^.//;s/.$//' | cut -d ':' -f2- +echo "${VERSION}" From f44d888b4690ace2f01d704427943b93b32911fa Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Fri, 26 May 2023 13:09:04 -0400 Subject: [PATCH 170/340] Fix dev mode on main (#2193) --- charts/consul/Chart.yaml | 4 ++-- charts/consul/values.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/charts/consul/Chart.yaml b/charts/consul/Chart.yaml index d0216aa36e..c55c6be6a2 100644 --- a/charts/consul/Chart.yaml +++ b/charts/consul/Chart.yaml @@ -4,7 +4,7 @@ apiVersion: v2 name: consul version: 1.2.0-dev -appVersion: 1.15.1 +appVersion: 1.16-dev kubeVersion: ">=1.22.0-0" description: Official HashiCorp Consul Chart home: https://www.consul.io @@ -16,7 +16,7 @@ annotations: artifacthub.io/prerelease: true artifacthub.io/images: | - name: consul - image: hashicorp/consul:1.15.1 + image: docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.16-dev - name: consul-k8s-control-plane image: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.2.0-dev - name: consul-dataplane diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 5bc0dd33bf..85e82f0fd2 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -66,7 +66,7 @@ global: # image: "hashicorp/consul-enterprise:1.10.0-ent" # ``` # @default: hashicorp/consul: - image: "hashicorp/consul:1.15.1" + image: docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.16-dev # Array of objects containing image pull secret names that will be applied to each service account. # This can be used to reference image pull secrets if using a custom consul or consul-k8s-control-plane Docker image. From 725e78d54c1e4d89193ccb2d476599d326ab8489 Mon Sep 17 00:00:00 2001 From: Thomas Eckert Date: Mon, 29 May 2023 10:33:41 -0400 Subject: [PATCH 171/340] Fix CVEs by updating controller-runtime (#2183) * Bump version of controller runtime * Use SubResourceUpdateOption * Fix test loggr * Fix ProbeHandler * Set runtime to 0.14.6 * Add Changelog * Fix up a few more breaking change issues --- .changelog/2183.txt | 3 + .../api/common/configentry_webhook_test.go | 4 +- .../v1alpha1/exportedservices_webhook_test.go | 4 +- .../api/v1alpha1/mesh_webhook_test.go | 4 +- .../v1alpha1/peeringacceptor_webhook_test.go | 4 +- .../v1alpha1/peeringdialer_webhook_test.go | 4 +- .../v1alpha1/proxydefaults_webhook_test.go | 4 +- .../serviceintentions_webhook_test.go | 8 +- .../consul_client_health_checks_test.go | 4 +- .../endpoints/endpoints_controller_test.go | 20 ++--- .../peering_acceptor_controller_test.go | 14 ++-- .../peering/peering_dialer_controller_test.go | 14 ++-- .../webhook/mesh_webhook_test.go | 36 ++++----- .../webhook/redirect_traffic_test.go | 22 +++--- .../configentry_controller_test.go | 22 +++--- control-plane/go.mod | 34 ++++---- control-plane/go.sum | 79 +++++++++---------- 17 files changed, 139 insertions(+), 141 deletions(-) create mode 100644 .changelog/2183.txt diff --git a/.changelog/2183.txt b/.changelog/2183.txt new file mode 100644 index 0000000000..d54983a8f4 --- /dev/null +++ b/.changelog/2183.txt @@ -0,0 +1,3 @@ +```release-note:security +Fix Prometheus CVEs by bumping controller-runtime. +``` diff --git a/control-plane/api/common/configentry_webhook_test.go b/control-plane/api/common/configentry_webhook_test.go index 7d7139089d..2760ff15ff 100644 --- a/control-plane/api/common/configentry_webhook_test.go +++ b/control-plane/api/common/configentry_webhook_test.go @@ -9,7 +9,7 @@ import ( "errors" "testing" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" capi "github.com/hashicorp/consul/api" "github.com/stretchr/testify/require" "gomodules.xyz/jsonpatch/v2" @@ -115,7 +115,7 @@ func TestValidateConfigEntry(t *testing.T) { }, }, }, - logrtest.NewTestLogger(t), + logrtest.New(t), lister, c.newResource, ConsulMeta{ diff --git a/control-plane/api/v1alpha1/exportedservices_webhook_test.go b/control-plane/api/v1alpha1/exportedservices_webhook_test.go index 19c2b040bb..7cca7ff915 100644 --- a/control-plane/api/v1alpha1/exportedservices_webhook_test.go +++ b/control-plane/api/v1alpha1/exportedservices_webhook_test.go @@ -8,7 +8,7 @@ import ( "encoding/json" "testing" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/hashicorp/consul-k8s/control-plane/api/common" "github.com/stretchr/testify/require" admissionv1 "k8s.io/api/admission/v1" @@ -180,7 +180,7 @@ func TestValidateExportedServices(t *testing.T) { validator := &ExportedServicesWebhook{ Client: client, - Logger: logrtest.NewTestLogger(t), + Logger: logrtest.New(t), decoder: decoder, ConsulMeta: c.consulMeta, } diff --git a/control-plane/api/v1alpha1/mesh_webhook_test.go b/control-plane/api/v1alpha1/mesh_webhook_test.go index bd22b02475..2266a6b77e 100644 --- a/control-plane/api/v1alpha1/mesh_webhook_test.go +++ b/control-plane/api/v1alpha1/mesh_webhook_test.go @@ -8,7 +8,7 @@ import ( "encoding/json" "testing" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/hashicorp/consul-k8s/control-plane/api/common" "github.com/stretchr/testify/require" admissionv1 "k8s.io/api/admission/v1" @@ -97,7 +97,7 @@ func TestValidateMesh(t *testing.T) { validator := &MeshWebhook{ Client: client, - Logger: logrtest.NewTestLogger(t), + Logger: logrtest.New(t), decoder: decoder, } response := validator.Handle(ctx, admission.Request{ diff --git a/control-plane/api/v1alpha1/peeringacceptor_webhook_test.go b/control-plane/api/v1alpha1/peeringacceptor_webhook_test.go index d0cd39d2d0..251ab87c8e 100644 --- a/control-plane/api/v1alpha1/peeringacceptor_webhook_test.go +++ b/control-plane/api/v1alpha1/peeringacceptor_webhook_test.go @@ -8,7 +8,7 @@ import ( "encoding/json" "testing" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/stretchr/testify/require" admissionv1 "k8s.io/api/admission/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -138,7 +138,7 @@ func TestValidatePeeringAcceptor(t *testing.T) { validator := &PeeringAcceptorWebhook{ Client: client, - Logger: logrtest.NewTestLogger(t), + Logger: logrtest.New(t), decoder: decoder, } response := validator.Handle(ctx, admission.Request{ diff --git a/control-plane/api/v1alpha1/peeringdialer_webhook_test.go b/control-plane/api/v1alpha1/peeringdialer_webhook_test.go index a6fcb79f9e..42372451d1 100644 --- a/control-plane/api/v1alpha1/peeringdialer_webhook_test.go +++ b/control-plane/api/v1alpha1/peeringdialer_webhook_test.go @@ -8,7 +8,7 @@ import ( "encoding/json" "testing" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/stretchr/testify/require" admissionv1 "k8s.io/api/admission/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -138,7 +138,7 @@ func TestValidatePeeringDialer(t *testing.T) { validator := &PeeringDialerWebhook{ Client: client, - Logger: logrtest.NewTestLogger(t), + Logger: logrtest.New(t), decoder: decoder, } response := validator.Handle(ctx, admission.Request{ diff --git a/control-plane/api/v1alpha1/proxydefaults_webhook_test.go b/control-plane/api/v1alpha1/proxydefaults_webhook_test.go index 90e59f0bf8..ade806b7e3 100644 --- a/control-plane/api/v1alpha1/proxydefaults_webhook_test.go +++ b/control-plane/api/v1alpha1/proxydefaults_webhook_test.go @@ -8,7 +8,7 @@ import ( "encoding/json" "testing" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/hashicorp/consul-k8s/control-plane/api/common" "github.com/stretchr/testify/require" admissionv1 "k8s.io/api/admission/v1" @@ -122,7 +122,7 @@ func TestValidateProxyDefault(t *testing.T) { validator := &ProxyDefaultsWebhook{ Client: client, - Logger: logrtest.NewTestLogger(t), + Logger: logrtest.New(t), decoder: decoder, } response := validator.Handle(ctx, admission.Request{ diff --git a/control-plane/api/v1alpha1/serviceintentions_webhook_test.go b/control-plane/api/v1alpha1/serviceintentions_webhook_test.go index 507d831e68..238ff7f33e 100644 --- a/control-plane/api/v1alpha1/serviceintentions_webhook_test.go +++ b/control-plane/api/v1alpha1/serviceintentions_webhook_test.go @@ -9,7 +9,7 @@ import ( "fmt" "testing" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/hashicorp/consul-k8s/control-plane/api/common" "github.com/stretchr/testify/require" "gomodules.xyz/jsonpatch/v2" @@ -253,7 +253,7 @@ func TestHandle_ServiceIntentions_Create(t *testing.T) { validator := &ServiceIntentionsWebhook{ Client: client, - Logger: logrtest.NewTestLogger(t), + Logger: logrtest.New(t), decoder: decoder, ConsulMeta: common.ConsulMeta{ NamespacesEnabled: true, @@ -442,7 +442,7 @@ func TestHandle_ServiceIntentions_Update(t *testing.T) { validator := &ServiceIntentionsWebhook{ Client: client, - Logger: logrtest.NewTestLogger(t), + Logger: logrtest.New(t), decoder: decoder, ConsulMeta: common.ConsulMeta{ NamespacesEnabled: true, @@ -602,7 +602,7 @@ func TestHandle_ServiceIntentions_Patches(t *testing.T) { validator := &ServiceIntentionsWebhook{ Client: client, - Logger: logrtest.NewTestLogger(t), + Logger: logrtest.New(t), decoder: decoder, ConsulMeta: common.ConsulMeta{ NamespacesEnabled: namespacesEnabled, diff --git a/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks_test.go b/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks_test.go index 23bd39c1ed..36ad222d68 100644 --- a/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks_test.go +++ b/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks_test.go @@ -6,7 +6,7 @@ package endpoints import ( "testing" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" "github.com/hashicorp/consul-k8s/control-plane/helper/test" "github.com/hashicorp/consul-server-connection-manager/discovery" @@ -241,7 +241,7 @@ func TestUpdateHealthCheckOnConsulClient(t *testing.T) { ctrl := Controller{ ConsulClientConfig: testClient.Cfg, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), } err := ctrl.updateHealthCheckOnConsulClient(testClient.Cfg.APIClientConfig, pod, endpoints, c.updateToStatus) diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go index 871081073d..aad7694b2e 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go @@ -10,7 +10,7 @@ import ( "testing" mapset "github.com/deckarep/golang-set" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" @@ -603,7 +603,7 @@ func TestProcessUpstreams(t *testing.T) { for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { ep := &Controller{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSetWith(), EnableConsulNamespaces: tt.consulNamespacesEnabled, @@ -902,7 +902,7 @@ func TestReconcileCreateEndpoint_MultiportService(t *testing.T) { // Create the endpoints controller ep := &Controller{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -2057,7 +2057,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -3377,7 +3377,7 @@ func TestReconcileUpdateEndpoint(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -3627,7 +3627,7 @@ func TestReconcileUpdateEndpoint_LegacyService(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -4001,7 +4001,7 @@ func TestReconcileDeleteEndpoint(t *testing.T) { // Create the endpoints controller ep := &Controller{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -4146,7 +4146,7 @@ func TestReconcileIgnoresServiceIgnoreLabel(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -4232,7 +4232,7 @@ func TestReconcile_podSpecifiesExplicitService(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -5711,7 +5711,7 @@ func TestCreateServiceRegistrations_withTransparentProxy(t *testing.T) { Client: fakeClient, EnableTransparentProxy: c.tproxyGlobalEnabled, TProxyOverwriteProbes: c.overwriteProbes, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), } serviceRegistration, proxyServiceRegistration, err := epCtrl.createServiceRegistrations(*pod, *endpoints, api.HealthPassing) diff --git a/control-plane/connect-inject/controllers/peering/peering_acceptor_controller_test.go b/control-plane/connect-inject/controllers/peering/peering_acceptor_controller_test.go index af4d3b0f0a..5a0d37135a 100644 --- a/control-plane/connect-inject/controllers/peering/peering_acceptor_controller_test.go +++ b/control-plane/connect-inject/controllers/peering/peering_acceptor_controller_test.go @@ -10,7 +10,7 @@ import ( "testing" "time" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" "github.com/hashicorp/consul-k8s/control-plane/helper/test" @@ -520,7 +520,7 @@ func TestReconcile_CreateUpdatePeeringAcceptor(t *testing.T) { Client: fakeClient, ExposeServersServiceName: "test-expose-servers", ReleaseNamespace: "default", - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, Scheme: s, @@ -638,7 +638,7 @@ func TestReconcile_DeletePeeringAcceptor(t *testing.T) { // Create the peering acceptor controller. controller := &AcceptorController{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, Scheme: s, @@ -782,7 +782,7 @@ func TestReconcile_VersionAnnotation(t *testing.T) { // Create the peering acceptor controller controller := &AcceptorController{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, Scheme: s, @@ -1086,7 +1086,7 @@ func TestAcceptorUpdateStatus(t *testing.T) { // Create the peering acceptor controller. pac := &AcceptorController{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), Scheme: s, } @@ -1198,7 +1198,7 @@ func TestAcceptorUpdateStatusError(t *testing.T) { // Create the peering acceptor controller. controller := &AcceptorController{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), Scheme: s, } @@ -1481,7 +1481,7 @@ func TestAcceptor_RequestsForPeeringTokens(t *testing.T) { fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(tt.secret, &tt.acceptors).Build() controller := AcceptorController{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), } result := controller.requestsForPeeringTokens(tt.secret) diff --git a/control-plane/connect-inject/controllers/peering/peering_dialer_controller_test.go b/control-plane/connect-inject/controllers/peering/peering_dialer_controller_test.go index d1b9cba696..8d999cbf2a 100644 --- a/control-plane/connect-inject/controllers/peering/peering_dialer_controller_test.go +++ b/control-plane/connect-inject/controllers/peering/peering_dialer_controller_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" "github.com/hashicorp/consul-k8s/control-plane/consul" @@ -321,7 +321,7 @@ func TestReconcile_CreateUpdatePeeringDialer(t *testing.T) { // Create the peering dialer controller controller := &PeeringDialerController{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, Scheme: s, @@ -531,7 +531,7 @@ func TestReconcile_VersionAnnotationPeeringDialer(t *testing.T) { // Create the peering dialer controller controller := &PeeringDialerController{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), ConsulClientConfig: consulConfig, ConsulServerConnMgr: watcher, Scheme: s, @@ -755,7 +755,7 @@ func TestReconcileDeletePeeringDialer(t *testing.T) { // Create the peering dialer controller. pdc := &PeeringDialerController{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, Scheme: s, @@ -887,7 +887,7 @@ func TestDialerUpdateStatus(t *testing.T) { // Create the peering dialer controller. controller := &PeeringDialerController{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), Scheme: s, } @@ -999,7 +999,7 @@ func TestDialerUpdateStatusError(t *testing.T) { // Create the peering dialer controller. controller := &PeeringDialerController{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), Scheme: s, } @@ -1282,7 +1282,7 @@ func TestDialer_RequestsForPeeringTokens(t *testing.T) { fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(tt.secret, &tt.dialers).Build() controller := PeeringDialerController{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), } result := controller.requestsForPeeringTokens(tt.secret) diff --git a/control-plane/connect-inject/webhook/mesh_webhook_test.go b/control-plane/connect-inject/webhook/mesh_webhook_test.go index ddc03eaecc..946933f7d7 100644 --- a/control-plane/connect-inject/webhook/mesh_webhook_test.go +++ b/control-plane/connect-inject/webhook/mesh_webhook_test.go @@ -10,7 +10,7 @@ import ( "testing" mapset "github.com/deckarep/golang-set" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/metrics" "github.com/hashicorp/consul-k8s/control-plane/consul" @@ -56,7 +56,7 @@ func TestHandlerHandle(t *testing.T) { { "kube-system namespace", MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -76,7 +76,7 @@ func TestHandlerHandle(t *testing.T) { { "already injected", MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -100,7 +100,7 @@ func TestHandlerHandle(t *testing.T) { { "empty pod basic", MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -142,7 +142,7 @@ func TestHandlerHandle(t *testing.T) { { "pod with upstreams specified", MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -201,7 +201,7 @@ func TestHandlerHandle(t *testing.T) { { "empty pod with injection disabled", MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -227,7 +227,7 @@ func TestHandlerHandle(t *testing.T) { { "empty pod with injection truthy", MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -282,7 +282,7 @@ func TestHandlerHandle(t *testing.T) { { "pod with empty volume mount annotation", MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -336,7 +336,7 @@ func TestHandlerHandle(t *testing.T) { { "pod with volume mount annotation", MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -410,7 +410,7 @@ func TestHandlerHandle(t *testing.T) { { "pod with sidecar volume mount annotation", MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -470,7 +470,7 @@ func TestHandlerHandle(t *testing.T) { { "pod with sidecar invalid volume mount annotation", MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -501,7 +501,7 @@ func TestHandlerHandle(t *testing.T) { { "pod with service annotation", MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -556,7 +556,7 @@ func TestHandlerHandle(t *testing.T) { { "pod with existing label", MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -606,7 +606,7 @@ func TestHandlerHandle(t *testing.T) { { "tproxy with overwriteProbes is enabled", MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableTransparentProxy: true, @@ -696,7 +696,7 @@ func TestHandlerHandle(t *testing.T) { { "multiport pod kube < 1.24 with AuthMethod, serviceaccount has secret ref", MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -755,7 +755,7 @@ func TestHandlerHandle(t *testing.T) { { "multiport pod kube 1.24 with AuthMethod, serviceaccount does not have secret ref", MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -814,7 +814,7 @@ func TestHandlerHandle(t *testing.T) { { "dns redirection enabled", MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableTransparentProxy: true, @@ -888,7 +888,7 @@ func TestHandlerHandle(t *testing.T) { { "dns redirection only enabled if tproxy enabled", MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableTransparentProxy: true, diff --git a/control-plane/connect-inject/webhook/redirect_traffic_test.go b/control-plane/connect-inject/webhook/redirect_traffic_test.go index df6b7a8559..bfa8ad8f2a 100644 --- a/control-plane/connect-inject/webhook/redirect_traffic_test.go +++ b/control-plane/connect-inject/webhook/redirect_traffic_test.go @@ -10,7 +10,7 @@ import ( "testing" mapset "github.com/deckarep/golang-set" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" "github.com/hashicorp/consul-k8s/control-plane/consul" "github.com/hashicorp/consul/sdk/iptables" @@ -48,7 +48,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "basic bare minimum pod", webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -78,7 +78,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "proxy health checks enabled", webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -111,7 +111,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "metrics enabled", webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -145,7 +145,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "metrics enabled with incorrect annotation", webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -180,7 +180,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "overwrite probes, transparent proxy annotation set", webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -221,7 +221,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "exclude inbound ports", webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -254,7 +254,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "exclude outbound ports", webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -287,7 +287,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "exclude outbound CIDRs", webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -320,7 +320,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "exclude UIDs", webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -352,7 +352,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "exclude inbound ports, outbound ports, outbound CIDRs, and UIDs", webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, diff --git a/control-plane/controllers/configentry_controller_test.go b/control-plane/controllers/configentry_controller_test.go index 2404b59365..10af0f7716 100644 --- a/control-plane/controllers/configentry_controller_test.go +++ b/control-plane/controllers/configentry_controller_test.go @@ -10,7 +10,7 @@ import ( "time" "github.com/go-logr/logr" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/hashicorp/consul-k8s/control-plane/api/common" @@ -453,7 +453,7 @@ func TestConfigEntryControllers_createsConfigEntry(t *testing.T) { req.True(written) } - r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.NewTestLogger(t)) + r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.New(t)) namespacedName := types.NamespacedName{ Namespace: kubeNS, Name: c.configEntryResource.KubernetesName(), @@ -953,7 +953,7 @@ func TestConfigEntryControllers_updatesConfigEntry(t *testing.T) { c.updateF(c.configEntryResource) err = fakeClient.Update(ctx, c.configEntryResource) req.NoError(err) - r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.NewTestLogger(t)) + r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.New(t)) resp, err := r.Reconcile(ctx, ctrl.Request{ NamespacedName: namespacedName, }) @@ -1345,7 +1345,7 @@ func TestConfigEntryControllers_deletesConfigEntry(t *testing.T) { Namespace: kubeNS, Name: c.configEntryResourceWithDeletion.KubernetesName(), } - r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.NewTestLogger(t)) + r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.New(t)) resp, err := r.Reconcile(context.Background(), ctrl.Request{ NamespacedName: namespacedName, }) @@ -1392,7 +1392,7 @@ func TestConfigEntryControllers_errorUpdatesSyncStatus(t *testing.T) { reconciler := &ServiceDefaultsController{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), ConfigEntryController: &ConfigEntryController{ ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, @@ -1459,7 +1459,7 @@ func TestConfigEntryControllers_setsSyncedToTrue(t *testing.T) { consulClient := testClient.APIClient reconciler := &ServiceDefaultsController{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), ConfigEntryController: &ConfigEntryController{ ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, @@ -1551,7 +1551,7 @@ func TestConfigEntryControllers_doesNotCreateUnownedConfigEntry(t *testing.T) { // Attempt to create the entry in Kube and run reconcile. reconciler := ServiceDefaultsController{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), ConfigEntryController: &ConfigEntryController{ ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, @@ -1615,7 +1615,7 @@ func TestConfigEntryControllers_doesNotDeleteUnownedConfig(t *testing.T) { consulClient := testClient.APIClient reconciler := &ServiceDefaultsController{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), ConfigEntryController: &ConfigEntryController{ ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, @@ -1695,7 +1695,7 @@ func TestConfigEntryControllers_updatesStatusWhenDeleteFails(t *testing.T) { testClient := test.TestServerWithMockConnMgrWatcher(t, nil) testClient.TestServer.WaitForServiceIntentions(t) - logger := logrtest.NewTestLogger(t) + logger := logrtest.New(t) svcDefaultsReconciler := ServiceDefaultsController{ Client: fakeClient, @@ -1833,7 +1833,7 @@ func TestConfigEntryController_Migration(t *testing.T) { require.True(t, success, "config entry was not created") // Set up the reconciler. - logger := logrtest.NewTestLogger(t) + logger := logrtest.New(t) svcDefaultsReconciler := ServiceDefaultsController{ Client: fakeClient, Log: logger, @@ -2109,7 +2109,7 @@ func TestConfigEntryControllers_assignServiceVirtualIP(t *testing.T) { testClient.TestServer.WaitForLeader(t) consulClient := testClient.APIClient - ctrl := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.NewTestLogger(t)) + ctrl := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.New(t)) namespacedName := types.NamespacedName{ Namespace: kubeNS, Name: c.configEntryResource.KubernetesName(), diff --git a/control-plane/go.mod b/control-plane/go.mod index 9f81da1907..9a65b3dd11 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -29,14 +29,14 @@ require ( github.com/stretchr/testify v1.8.1 go.uber.org/zap v1.24.0 golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 - golang.org/x/text v0.7.0 + golang.org/x/text v0.9.0 golang.org/x/time v0.3.0 - gomodules.xyz/jsonpatch/v2 v2.2.0 + gomodules.xyz/jsonpatch/v2 v2.3.0 k8s.io/api v0.26.1 k8s.io/apimachinery v0.26.1 k8s.io/client-go v0.26.1 - k8s.io/klog/v2 v2.80.1 - k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 + k8s.io/klog/v2 v2.90.1 + k8s.io/utils v0.0.0-20230209194617-a36077c30491 sigs.k8s.io/controller-runtime v0.14.6 sigs.k8s.io/gateway-api v0.6.2 ) @@ -72,17 +72,17 @@ require ( github.com/fatih/color v1.13.0 // indirect github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect github.com/go-logr/zapr v1.2.3 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.20.0 // indirect - github.com/go-openapi/swag v0.19.14 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.1 // indirect + github.com/go-openapi/swag v0.22.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 // indirect github.com/google/gofuzz v1.1.0 // indirect - github.com/google/uuid v1.1.2 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/googleapis/gax-go/v2 v2.0.5 // indirect github.com/gophercloud/gophercloud v0.1.0 // indirect github.com/hashicorp/consul/proto-public v0.1.0 // indirect @@ -108,7 +108,7 @@ require ( github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/linode/linodego v0.7.1 // indirect - github.com/mailru/easyjson v0.7.6 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect @@ -143,13 +143,13 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.6.0 // indirect golang.org/x/crypto v0.1.0 // indirect - golang.org/x/mod v0.7.0 // indirect - golang.org/x/net v0.7.0 // indirect + golang.org/x/mod v0.9.0 // indirect + golang.org/x/net v0.8.0 // indirect golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/term v0.5.0 // indirect - golang.org/x/tools v0.3.0 // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/term v0.6.0 // indirect + golang.org/x/tools v0.7.0 // indirect google.golang.org/api v0.30.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect @@ -162,8 +162,8 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.26.1 // indirect k8s.io/component-base v0.26.1 // indirect - k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect - sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/control-plane/go.sum b/control-plane/go.sum index b915ac004b..1876d81578 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -134,7 +134,6 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= @@ -170,14 +169,12 @@ github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= -github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= +github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= @@ -212,8 +209,9 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -251,8 +249,9 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -374,16 +373,15 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/linode/linodego v0.7.1 h1:4WZmMpSA2NRwlPZcc0+4Gyn7rr99Evk9bnr0B3gXRKE= github.com/linode/linodego v0.7.1/go.mod h1:ga11n3ivecUrPCHN0rANxKmfWBJVkOXfLMZinAbj2sY= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -434,8 +432,6 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 h1:BQ1HW7hr4IVovMwWg0E0PYcyW8CzqDcVmaew9cujU4s= github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= @@ -524,7 +520,6 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= @@ -607,8 +602,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -652,8 +647,8 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= 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= @@ -739,13 +734,13 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -755,8 +750,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -809,14 +804,14 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= 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= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= -gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= +gomodules.xyz/jsonpatch/v2 v2.3.0 h1:8NFhfS6gzxNqjLIYnZxg319wZ5Qjnx4m/CcX+Klzazc= +gomodules.xyz/jsonpatch/v2 v2.3.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -912,8 +907,8 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= @@ -954,12 +949,12 @@ k8s.io/client-go v0.26.1 h1:87CXzYJnAMGaa/IDDfRdhTzxk/wzGZ+/HUQpqgVSZXU= k8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE= k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4= k8s.io/component-base v0.26.1/go.mod h1:VHrLR0b58oC035w6YQiBSbtsf0ThuSwXP+p5dD/kAWU= -k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= -k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= -k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= -k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y= -k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= +k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= +k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= +k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= +k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= @@ -967,8 +962,8 @@ sigs.k8s.io/controller-runtime v0.14.6 h1:oxstGVvXGNnMvY7TAESYk+lzr6S3V5VFxQ6d92 sigs.k8s.io/controller-runtime v0.14.6/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= sigs.k8s.io/gateway-api v0.6.2 h1:583XHiX2M2bKEA0SAdkoxL1nY73W1+/M+IAm8LJvbEA= sigs.k8s.io/gateway-api v0.6.2/go.mod h1:EYJT+jlPWTeNskjV0JTki/03WX1cyAnBhwBJfYHpV/0= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +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/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= From 1734d986739822dbaa32d7d203831027ee054ff7 Mon Sep 17 00:00:00 2001 From: Kaustubh Phatak Date: Mon, 29 May 2023 12:51:09 -0700 Subject: [PATCH 172/340] Adding support for idleTimeout in Service Router spec (#2156) * Adding support for idleTimeout in Service Router spec --- charts/consul/templates/crd-servicerouters.yaml | 4 ++++ control-plane/api/v1alpha1/servicerouter_types.go | 4 ++++ control-plane/api/v1alpha1/servicerouter_types_test.go | 6 +++++- control-plane/api/v1alpha1/zz_generated.deepcopy.go | 1 + .../crd/bases/consul.hashicorp.com_servicerouters.yaml | 4 ++++ 5 files changed, 18 insertions(+), 1 deletion(-) diff --git a/charts/consul/templates/crd-servicerouters.yaml b/charts/consul/templates/crd-servicerouters.yaml index e7a3239e75..0157f646b4 100644 --- a/charts/consul/templates/crd-servicerouters.yaml +++ b/charts/consul/templates/crd-servicerouters.yaml @@ -68,6 +68,10 @@ spec: description: Destination controls how to proxy the matching request(s) to a service. properties: + idleTimeout: + description: IdleTimeout is total amount of time permitted + for the request stream to be idle. + type: string namespace: description: Namespace is the Consul namespace to resolve the service from instead of the current namespace. If diff --git a/control-plane/api/v1alpha1/servicerouter_types.go b/control-plane/api/v1alpha1/servicerouter_types.go index d5baf37368..43e7353bf5 100644 --- a/control-plane/api/v1alpha1/servicerouter_types.go +++ b/control-plane/api/v1alpha1/servicerouter_types.go @@ -140,6 +140,9 @@ type ServiceRouteDestination struct { // This requires that either match.http.pathPrefix or match.http.pathExact // be configured on this route. PrefixRewrite string `json:"prefixRewrite,omitempty"` + // IdleTimeout is total amount of time permitted + // for the request stream to be idle. + IdleTimeout metav1.Duration `json:"idleTimeout,omitempty"` // RequestTimeout is the total amount of time permitted for the entire // downstream request (and retries) to be processed. RequestTimeout metav1.Duration `json:"requestTimeout,omitempty"` @@ -337,6 +340,7 @@ func (in *ServiceRouteDestination) toConsul() *capi.ServiceRouteDestination { Namespace: in.Namespace, Partition: in.Partition, PrefixRewrite: in.PrefixRewrite, + IdleTimeout: in.IdleTimeout.Duration, RequestTimeout: in.RequestTimeout.Duration, NumRetries: in.NumRetries, RetryOnConnectFailure: in.RetryOnConnectFailure, diff --git a/control-plane/api/v1alpha1/servicerouter_types_test.go b/control-plane/api/v1alpha1/servicerouter_types_test.go index d0919871ce..653bdc26c1 100644 --- a/control-plane/api/v1alpha1/servicerouter_types_test.go +++ b/control-plane/api/v1alpha1/servicerouter_types_test.go @@ -82,6 +82,7 @@ func TestServiceRouter_MatchesConsul(t *testing.T) { ServiceSubset: "serviceSubset", Namespace: "namespace", PrefixRewrite: "prefixRewrite", + IdleTimeout: metav1.Duration{Duration: 1 * time.Second}, RequestTimeout: metav1.Duration{Duration: 1 * time.Second}, NumRetries: 1, RetryOnConnectFailure: true, @@ -158,6 +159,7 @@ func TestServiceRouter_MatchesConsul(t *testing.T) { ServiceSubset: "serviceSubset", Namespace: "namespace", PrefixRewrite: "prefixRewrite", + IdleTimeout: 1 * time.Second, RequestTimeout: 1 * time.Second, NumRetries: 1, RetryOnConnectFailure: true, @@ -283,6 +285,7 @@ func TestServiceRouter_ToConsul(t *testing.T) { ServiceSubset: "serviceSubset", Namespace: "namespace", PrefixRewrite: "prefixRewrite", + IdleTimeout: metav1.Duration{Duration: 1 * time.Second}, RequestTimeout: metav1.Duration{Duration: 1 * time.Second}, NumRetries: 1, RetryOnConnectFailure: true, @@ -359,6 +362,7 @@ func TestServiceRouter_ToConsul(t *testing.T) { ServiceSubset: "serviceSubset", Namespace: "namespace", PrefixRewrite: "prefixRewrite", + IdleTimeout: 1 * time.Second, RequestTimeout: 1 * time.Second, NumRetries: 1, RetryOnConnectFailure: true, @@ -717,7 +721,7 @@ func TestServiceRouter_Validate(t *testing.T) { }, namespacesEnabled: false, expectedErrMsgs: []string{ - `servicerouter.consul.hashicorp.com "foo" is invalid: spec.routes[0]: Invalid value: "{\"match\":{\"http\":{}},\"destination\":{\"prefixRewrite\":\"prefixRewrite\",\"requestTimeout\":\"0s\"}}": destination.prefixRewrite requires that either match.http.pathPrefix or match.http.pathExact be configured on this route`, + `servicerouter.consul.hashicorp.com "foo" is invalid: spec.routes[0]: Invalid value: "{\"match\":{\"http\":{}},\"destination\":{\"prefixRewrite\":\"prefixRewrite\",\"idleTimeout\":\"0s\",\"requestTimeout\":\"0s\"}}": destination.prefixRewrite requires that either match.http.pathPrefix or match.http.pathExact be configured on this route`, }, }, "namespaces disabled: single destination namespace specified": { diff --git a/control-plane/api/v1alpha1/zz_generated.deepcopy.go b/control-plane/api/v1alpha1/zz_generated.deepcopy.go index a2e83491ab..f1f5ffb150 100644 --- a/control-plane/api/v1alpha1/zz_generated.deepcopy.go +++ b/control-plane/api/v1alpha1/zz_generated.deepcopy.go @@ -2170,6 +2170,7 @@ func (in *ServiceRoute) DeepCopy() *ServiceRoute { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServiceRouteDestination) DeepCopyInto(out *ServiceRouteDestination) { *out = *in + out.IdleTimeout = in.IdleTimeout out.RequestTimeout = in.RequestTimeout if in.RetryOnStatusCodes != nil { in, out := &in.RetryOnStatusCodes, &out.RetryOnStatusCodes diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml index 25055b0951..5919e23005 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml @@ -64,6 +64,10 @@ spec: description: Destination controls how to proxy the matching request(s) to a service. properties: + idleTimeout: + description: IdleTimeout is total amount of time permitted + for the request stream to be idle. + type: string namespace: description: Namespace is the Consul namespace to resolve the service from instead of the current namespace. If From 370976fce900509712ef3fca865ce3650e969f0e Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Tue, 30 May 2023 10:02:18 -0400 Subject: [PATCH 173/340] Changelog: add support for idleTimeout in Service Router config (#2200) * add changelog --- .changelog/2156.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/2156.txt diff --git a/.changelog/2156.txt b/.changelog/2156.txt new file mode 100644 index 0000000000..cb812b451a --- /dev/null +++ b/.changelog/2156.txt @@ -0,0 +1,3 @@ +```release-note:bug +control-plane: add support for idleTimeout in the Service Router config +``` From fa3e146b3518e509fd731abcbc428508fa53beaa Mon Sep 17 00:00:00 2001 From: Dan Stough Date: Tue, 30 May 2023 14:06:40 -0400 Subject: [PATCH 174/340] build(deps): update controller UBI base to 9.2 (#2204) --- .changelog/2204.txt | 3 +++ .github/workflows/build.yml | 2 +- control-plane/Dockerfile | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 .changelog/2204.txt diff --git a/.changelog/2204.txt b/.changelog/2204.txt new file mode 100644 index 0000000000..6962f485a0 --- /dev/null +++ b/.changelog/2204.txt @@ -0,0 +1,3 @@ +```release-note:security +Bump Dockerfile base image for RedHat UBI `consul-k8s-control-plane` image to `ubi-minimal:9.2`. +``` diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 43194ea7bf..52f94ec71c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -164,7 +164,7 @@ jobs: if: ${{ matrix.goos == 'linux' && matrix.component == 'cli' && matrix.goarch == 'amd64'}} uses: addnab/docker-run-action@v3 # TSCCR: no entry for repository "addnab/docker-run-action" with: - image: registry.access.redhat.com/ubi8/ubi:latest + image: registry.access.redhat.com/ubi9/ubi:latest options: -v ${{ github.workspace }}:/work run: | dnf install -qy openssl diff --git a/control-plane/Dockerfile b/control-plane/Dockerfile index f9c4936b29..a870e20e2b 100644 --- a/control-plane/Dockerfile +++ b/control-plane/Dockerfile @@ -120,7 +120,7 @@ CMD /bin/${BIN_NAME} # We don't rebuild the software because we want the exact checksums and # binary signatures to match the software and our builds aren't fully # reproducible currently. -FROM registry.access.redhat.com/ubi9-minimal:9.1.0 as ubi +FROM registry.access.redhat.com/ubi9-minimal:9.2 as ubi ARG PRODUCT_NAME ARG PRODUCT_VERSION From ea41d4dbb8caaa98031a4e1360242c0fe75addd1 Mon Sep 17 00:00:00 2001 From: Nick Ethier Date: Tue, 30 May 2023 16:00:53 -0400 Subject: [PATCH 175/340] inject envoy_telemetry_bind_socket_dir proxy config when telemetry collector is enabled (#2143) * inject envoy_telemetry_bind_socket_dir proxy config when telemetry collector is enabled * use metrics.enableTelemetryCollector value to gate controller logic * add changelog entry and unit test --- .changelog/2143.txt | 4 ++++ .../templates/connect-inject-deployment.yaml | 1 + .../test/unit/connect-inject-deployment.bats | 13 +++++++++++++ .../endpoints/endpoints_controller.go | 19 ++++++++++++++++--- .../subcommand/inject-connect/command.go | 6 ++++++ 5 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 .changelog/2143.txt diff --git a/.changelog/2143.txt b/.changelog/2143.txt new file mode 100644 index 0000000000..8f58328f3d --- /dev/null +++ b/.changelog/2143.txt @@ -0,0 +1,4 @@ + +```release-note:feature +consul-telemetry-collector: Configure envoy proxy config during registration when consul-telemetry-collector is enabled. +``` diff --git a/charts/consul/templates/connect-inject-deployment.yaml b/charts/consul/templates/connect-inject-deployment.yaml index 2b52c1b81c..479e05b25a 100644 --- a/charts/consul/templates/connect-inject-deployment.yaml +++ b/charts/consul/templates/connect-inject-deployment.yaml @@ -257,6 +257,7 @@ spec: {{- if and .Values.global.tls.enabled .Values.global.tls.enableAutoEncrypt }} -enable-auto-encrypt \ {{- end }} + -enable-telemetry-collector={{ .Values.global.metrics.enableTelemetryCollector}} \ startupProbe: httpGet: path: /readyz/ready diff --git a/charts/consul/test/unit/connect-inject-deployment.bats b/charts/consul/test/unit/connect-inject-deployment.bats index c60ea14f1f..e7d5b3bf48 100755 --- a/charts/consul/test/unit/connect-inject-deployment.bats +++ b/charts/consul/test/unit/connect-inject-deployment.bats @@ -211,6 +211,19 @@ load _helpers [ "${actual}" = "true" ] } +@test "connectInject/Deployment: metrics.enableTelemetryCollector can be configured" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + --set 'connectInject.metrics.enableTelemetryCollector=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-enable-telemetry-collector=true"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} #-------------------------------------------------------------------- # consul and consul-dataplane images diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go index 13f75f1156..584abd48c6 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go @@ -44,9 +44,10 @@ const ( terminatingGateway = "terminating-gateway" ingressGateway = "ingress-gateway" - kubernetesSuccessReasonMsg = "Kubernetes health checks passing" - envoyPrometheusBindAddr = "envoy_prometheus_bind_addr" - defaultNS = "default" + kubernetesSuccessReasonMsg = "Kubernetes health checks passing" + envoyPrometheusBindAddr = "envoy_prometheus_bind_addr" + envoyTelemetryCollectorBindSocketDir = "envoy_telemetry_collector_bind_socket_dir" + defaultNS = "default" // clusterIPTaggedAddressName is the key for the tagged address to store the service's cluster IP and service port // in Consul. Note: This value should not be changed without a corresponding change in Consul. @@ -119,6 +120,10 @@ type Controller struct { // to Consul client agents. EnableAutoEncrypt bool + // EnableTelemetryCollector controls whether the proxy service should be registered + // with config to enable telemetry forwarding. + EnableTelemetryCollector bool + MetricsConfig metrics.Config Log logr.Logger @@ -482,6 +487,10 @@ func (r *Controller) createServiceRegistrations(pod corev1.Pod, serviceEndpoints proxyConfig.Config[envoyPrometheusBindAddr] = prometheusScrapeListener } + if r.EnableTelemetryCollector { + proxyConfig.Config[envoyTelemetryCollectorBindSocketDir] = "/consul/connect-inject" + } + if consulServicePort > 0 { proxyConfig.LocalServiceAddress = "127.0.0.1" proxyConfig.LocalServicePort = consulServicePort @@ -761,6 +770,10 @@ func (r *Controller) createGatewayRegistrations(pod corev1.Pod, serviceEndpoints } } + if r.EnableTelemetryCollector { + service.Proxy.Config[envoyTelemetryCollectorBindSocketDir] = "/consul/service" + } + serviceRegistration := &api.CatalogRegistration{ Node: common.ConsulNodeNameFromK8sNode(pod.Spec.NodeName), Address: pod.Status.HostIP, diff --git a/control-plane/subcommand/inject-connect/command.go b/control-plane/subcommand/inject-connect/command.go index c7c04dd481..671a15f7cd 100644 --- a/control-plane/subcommand/inject-connect/command.go +++ b/control-plane/subcommand/inject-connect/command.go @@ -118,6 +118,9 @@ type Command struct { flagEnableAutoEncrypt bool + // Consul telemetry collector + flagEnableTelemetryCollector bool + // Consul DNS flags. flagEnableConsulDNS bool flagResourcePrefix string @@ -203,6 +206,8 @@ func (c *Command) init() { "Enables updating the CABundle on the webhook within this controller rather than using the web cert manager.") c.flagSet.BoolVar(&c.flagEnableAutoEncrypt, "enable-auto-encrypt", false, "Indicates whether TLS with auto-encrypt should be used when talking to Consul clients.") + c.flagSet.BoolVar(&c.flagEnableTelemetryCollector, "enable-telemetry-collector", false, + "Indicates whether proxies should be registered with configuration to enable forwarding metrics to consul-telemetry-collector") c.flagSet.StringVar(&c.flagLogLevel, "log-level", zapcore.InfoLevel.String(), fmt.Sprintf("Log verbosity level. Supported values (in order of detail) are "+ "%q, %q, %q, and %q.", zapcore.DebugLevel.String(), zapcore.InfoLevel.String(), zapcore.WarnLevel.String(), zapcore.ErrorLevel.String())) @@ -449,6 +454,7 @@ func (c *Command) Run(args []string) int { ReleaseName: c.flagReleaseName, ReleaseNamespace: c.flagReleaseNamespace, EnableAutoEncrypt: c.flagEnableAutoEncrypt, + EnableTelemetryCollector: c.flagEnableTelemetryCollector, Context: ctx, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", endpoints.Controller{}) From cb67e6ac296510377b6a870ad8737497cc7b56e8 Mon Sep 17 00:00:00 2001 From: Nick Ethier Date: Tue, 30 May 2023 20:47:23 -0400 Subject: [PATCH 176/340] update cloud preset to enable telemetry collector (#2205) --- .changelog/2205.txt | 3 +++ charts/consul/values.yaml | 2 +- cli/preset/cloud_preset.go | 13 +++++++++++++ cli/preset/cloud_preset_test.go | 22 ++++++++++++++++++++++ 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 .changelog/2205.txt diff --git a/.changelog/2205.txt b/.changelog/2205.txt new file mode 100644 index 0000000000..6a66970cfc --- /dev/null +++ b/.changelog/2205.txt @@ -0,0 +1,3 @@ +```release-note:improvement +cli: update cloud preset to enable telemetry collector +``` \ No newline at end of file diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 85e82f0fd2..cf6a8e5ab4 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -552,7 +552,7 @@ global: # Configures the Helm chart’s components to forward envoy metrics for the Consul service mesh to the # consul-telemetry-collector. This includes gateway metrics and sidecar metrics. # @type: boolean - enableTelemetryCollector: true + enableTelemetryCollector: false # The name (and tag) of the consul-dataplane Docker image used for the # connect-injected sidecar proxies and mesh, terminating, and ingress gateways. diff --git a/cli/preset/cloud_preset.go b/cli/preset/cloud_preset.go index 5e5c6eccc2..732bad1b14 100644 --- a/cli/preset/cloud_preset.go +++ b/cli/preset/cloud_preset.go @@ -201,6 +201,8 @@ global: bootstrapToken: secretName: %s secretKey: %s + metrics: + enableTelemetryCollector: true cloud: enabled: true resourceId: @@ -215,6 +217,15 @@ global: %s %s %s +telemetryCollector: + enabled: true + cloud: + clientId: + secretName: %s + secretKey: %s + clientSecret: + secretName: %s + secretKey: %s server: replicas: %d affinity: null @@ -231,6 +242,8 @@ controller: secretNameHCPClientID, secretKeyHCPClientID, secretNameHCPClientSecret, secretKeyHCPClientSecret, apiHostCfg, authURLCfg, scadaAddressCfg, + secretNameHCPClientID, secretKeyHCPClientID, + secretNameHCPClientSecret, secretKeyHCPClientSecret, cfg.BootstrapResponse.Cluster.BootstrapExpect, secretNameServerCert) valuesMap := config.ConvertToMap(values) return valuesMap diff --git a/cli/preset/cloud_preset_test.go b/cli/preset/cloud_preset_test.go index 5ade3146ca..770b47ba5c 100644 --- a/cli/preset/cloud_preset_test.go +++ b/cli/preset/cloud_preset_test.go @@ -483,6 +483,8 @@ global: gossipEncryption: secretKey: key secretName: consul-gossip-key + metrics: + enableTelemetryCollector: true tls: caCert: secretKey: tls.crt @@ -494,6 +496,15 @@ server: replicas: 3 serverCert: secretName: consul-server-cert +telemetryCollector: + cloud: + clientId: + secretKey: client-id + secretName: consul-hcp-client-id + clientSecret: + secretKey: client-secret + secretName: consul-hcp-client-secret + enabled: true ` const expectedWithoutOptional = `connectInject: @@ -521,6 +532,8 @@ global: gossipEncryption: secretKey: key secretName: consul-gossip-key + metrics: + enableTelemetryCollector: true tls: caCert: secretKey: tls.crt @@ -532,6 +545,15 @@ server: replicas: 3 serverCert: secretName: consul-server-cert +telemetryCollector: + cloud: + clientId: + secretKey: client-id + secretName: consul-hcp-client-id + clientSecret: + secretKey: client-secret + secretName: consul-hcp-client-secret + enabled: true ` cloudPreset := &CloudPreset{} From f132cdd36a5db6c5ddb51b513397f01e79ecb38f Mon Sep 17 00:00:00 2001 From: chappie <6537530+chapmanc@users.noreply.github.com> Date: Tue, 30 May 2023 18:49:57 -0700 Subject: [PATCH 177/340] Consul Telemetry acceptance test (#2195) --- .changelog/2195.txt | 3 + acceptance/framework/config/config.go | 13 +- acceptance/framework/flags/flags.go | 9 + acceptance/tests/cloud/basic_test.go | 233 ++++++++++++++++++ acceptance/tests/cloud/main_test.go | 18 ++ .../bases/cloud/hcp-mock/deployment.yaml | 29 +++ .../bases/cloud/hcp-mock/kustomization.yaml | 10 + .../bases/cloud/hcp-mock/service.yaml | 17 ++ .../bases/cloud/hcp-mock/serviceaccount.yaml | 7 + .../bases/static-server/deployment.yaml | 4 +- .../consul/templates/server-statefulset.yaml | 22 ++ .../telemetry-collector-deployment.yaml | 33 ++- .../telemetry-collector-service.yaml | 2 +- .../telemetry-collector-serviceaccount.yaml | 2 +- .../consul/test/unit/server-statefulset.bats | 58 +++++ .../unit/telemetry-collector-deployment.bats | 97 ++++++++ .../unit/telemetry-collector-service.bats | 14 ++ .../telemetry-collector-serviceaccount.bats | 15 ++ charts/consul/values.yaml | 24 +- 19 files changed, 597 insertions(+), 13 deletions(-) create mode 100644 .changelog/2195.txt create mode 100644 acceptance/tests/cloud/basic_test.go create mode 100644 acceptance/tests/cloud/main_test.go create mode 100644 acceptance/tests/fixtures/bases/cloud/hcp-mock/deployment.yaml create mode 100644 acceptance/tests/fixtures/bases/cloud/hcp-mock/kustomization.yaml create mode 100644 acceptance/tests/fixtures/bases/cloud/hcp-mock/service.yaml create mode 100644 acceptance/tests/fixtures/bases/cloud/hcp-mock/serviceaccount.yaml diff --git a/.changelog/2195.txt b/.changelog/2195.txt new file mode 100644 index 0000000000..1b450eb40d --- /dev/null +++ b/.changelog/2195.txt @@ -0,0 +1,3 @@ +```release-note:imrpovement +consul-telemetry-collector: add acceptance tests for consul telemetry collector component. +``` \ No newline at end of file diff --git a/acceptance/framework/config/config.go b/acceptance/framework/config/config.go index c771e49653..8a5ba7893e 100644 --- a/acceptance/framework/config/config.go +++ b/acceptance/framework/config/config.go @@ -46,11 +46,14 @@ type TestConfig struct { DisablePeering bool - HelmChartVersion string - ConsulImage string - ConsulK8SImage string - ConsulVersion *version.Version - EnvoyImage string + HelmChartVersion string + ConsulImage string + ConsulK8SImage string + ConsulVersion *version.Version + EnvoyImage string + ConsulCollectorImage string + + HCPResourceID string VaultHelmChartVersion string VaultServerVersion string diff --git a/acceptance/framework/flags/flags.go b/acceptance/framework/flags/flags.go index ea58fda058..3b542c5294 100644 --- a/acceptance/framework/flags/flags.go +++ b/acceptance/framework/flags/flags.go @@ -39,9 +39,12 @@ type TestFlags struct { flagConsulK8sImage string flagConsulVersion string flagEnvoyImage string + flagConsulCollectorImage string flagVaultHelmChartVersion string flagVaultServerVersion string + flagHCPResourceID string + flagNoCleanupOnFailure bool flagDebugDirectory string @@ -74,9 +77,12 @@ func (t *TestFlags) init() { flag.StringVar(&t.flagConsulVersion, "consul-version", "", "The consul version used for all tests.") flag.StringVar(&t.flagHelmChartVersion, "helm-chart-version", config.HelmChartPath, "The helm chart used for all tests.") flag.StringVar(&t.flagEnvoyImage, "envoy-image", "", "The Envoy image to use for all tests.") + flag.StringVar(&t.flagConsulCollectorImage, "consul-collector-image", "", "The consul collector image to use for all tests.") flag.StringVar(&t.flagVaultServerVersion, "vault-server-version", "", "The vault serverversion used for all tests.") flag.StringVar(&t.flagVaultHelmChartVersion, "vault-helm-chart-version", "", "The Vault helm chart used for all tests.") + flag.StringVar(&t.flagHCPResourceID, "hcp-resource-id", "", "The hcp resource id to use for all tests.") + flag.BoolVar(&t.flagEnableMultiCluster, "enable-multi-cluster", false, "If true, the tests that require multiple Kubernetes clusters will be run. "+ "At least one of -secondary-kubeconfig or -secondary-kubecontext is required when this flag is used.") @@ -176,9 +182,12 @@ func (t *TestFlags) TestConfigFromFlags() *config.TestConfig { ConsulK8SImage: t.flagConsulK8sImage, ConsulVersion: consulVersion, EnvoyImage: t.flagEnvoyImage, + ConsulCollectorImage: t.flagConsulCollectorImage, VaultHelmChartVersion: t.flagVaultHelmChartVersion, VaultServerVersion: t.flagVaultServerVersion, + HCPResourceID: t.flagHCPResourceID, + NoCleanupOnFailure: t.flagNoCleanupOnFailure, DebugDirectory: tempDir, UseAKS: t.flagUseAKS, diff --git a/acceptance/tests/cloud/basic_test.go b/acceptance/tests/cloud/basic_test.go new file mode 100644 index 0000000000..8278309ff3 --- /dev/null +++ b/acceptance/tests/cloud/basic_test.go @@ -0,0 +1,233 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package cloud + +import ( + "crypto/tls" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "strings" + "testing" + "time" + + terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" + "github.com/hashicorp/consul-k8s/acceptance/framework/consul" + "github.com/hashicorp/consul-k8s/acceptance/framework/environment" + "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" + "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" + "github.com/hashicorp/consul-k8s/acceptance/framework/logger" + "github.com/hashicorp/serf/testutil/retry" + "github.com/stretchr/testify/require" +) + +type TokenResponse struct { + Token string `json:"token"` +} + +var ( + resourceSecretName = "resource-sec-name" + resourceSecretKey = "resource-sec-key" + resourceSecretKeyValue = "organization/11eb1a35-aac0-f7c7-8fe1-0242ac110008/project/11eb1a35-ab64-d576-8fe1-0242ac110008/hashicorp.consul.global-network-manager.cluster/TEST" + + clientIDSecretName = "clientid-sec-name" + clientIDSecretKey = "clientid-sec-key" + clientIDSecretKeyValue = "clientid" + + clientSecretName = "client-sec-name" + clientSecretKey = "client-sec-key" + clientSecretKeyValue = "client-secret" + + apiHostSecretName = "apihost-sec-name" + apiHostSecretKey = "apihost-sec-key" + apiHostSecretKeyValue = "fake-server:443" + + authUrlSecretName = "authurl-sec-name" + authUrlSecretKey = "authurl-sec-key" + authUrlSecretKeyValue = "https://fake-server:443" + + scadaAddressSecretName = "scadaaddress-sec-name" + scadaAddressSecretKey = "scadaaddress-sec-key" + scadaAddressSecretKeyValue = "fake-server:443" +) + +// The fake-server has a requestToken endpoint to retrieve the token. +func requestToken(endpoint string) (string, error) { + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + + client := &http.Client{Transport: tr} + url := fmt.Sprintf("https://%s/token", endpoint) + req, err := http.NewRequest("GET", url, nil) + if err != nil { + fmt.Println("Error creating request:", err) + return "", errors.New("error creating request") + } + + // Perform the request + resp, err := client.Do(req) + if err != nil { + fmt.Println("Error sending request:", err) + return "", errors.New("error making request") + } + defer resp.Body.Close() + + // Read the response body + body, err := io.ReadAll(resp.Body) + if err != nil { + fmt.Println("Error reading response:", err) + return "", errors.New("error reading body") + } + + var tokenResponse TokenResponse + err = json.Unmarshal(body, &tokenResponse) + if err != nil { + fmt.Println("Error parsing response:", err) + return "", errors.New("error parsing body") + } + + return tokenResponse.Token, nil + +} + +func TestBasicCloud(t *testing.T) { + ctx := suite.Environment().DefaultContext(t) + + kubectlOptions := ctx.KubectlOptions(t) + ns := kubectlOptions.Namespace + k8sClient := environment.KubernetesClientFromOptions(t, kubectlOptions) + + cfg := suite.Config() + + if cfg.HCPResourceID != "" { + resourceSecretKeyValue = cfg.HCPResourceID + } + consul.CreateK8sSecret(t, k8sClient, cfg, ns, resourceSecretName, resourceSecretKey, resourceSecretKeyValue) + consul.CreateK8sSecret(t, k8sClient, cfg, ns, clientIDSecretName, clientIDSecretKey, clientIDSecretKeyValue) + consul.CreateK8sSecret(t, k8sClient, cfg, ns, clientSecretName, clientSecretKey, clientSecretKeyValue) + consul.CreateK8sSecret(t, k8sClient, cfg, ns, apiHostSecretName, apiHostSecretKey, apiHostSecretKeyValue) + consul.CreateK8sSecret(t, k8sClient, cfg, ns, authUrlSecretName, authUrlSecretKey, authUrlSecretKeyValue) + consul.CreateK8sSecret(t, k8sClient, cfg, ns, scadaAddressSecretName, scadaAddressSecretKey, scadaAddressSecretKeyValue) + + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/cloud/hcp-mock") + podName, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "get", "pod", "-l", "app=fake-server", "-o", `jsonpath="{.items[0].metadata.name}"`) + podName = strings.ReplaceAll(podName, "\"", "") + if err != nil { + logger.Log(t, "error finding pod name") + return + } + logger.Log(t, "fake-server pod name:"+podName) + localPort := terratestk8s.GetAvailablePort(t) + tunnel := terratestk8s.NewTunnelWithLogger( + ctx.KubectlOptions(t), + terratestk8s.ResourceTypePod, + podName, + localPort, + 443, + logger.TestLogger{}) + + // Retry creating the port forward since it can fail occasionally. + retry.RunWith(&retry.Counter{Wait: 5 * time.Second, Count: 60}, t, func(r *retry.R) { + // NOTE: It's okay to pass in `t` to ForwardPortE despite being in a retry + // because we're using ForwardPortE (not ForwardPort) so the `t` won't + // get used to fail the test, just for logging. + require.NoError(r, tunnel.ForwardPortE(t)) + }) + + logger.Log(t, "fake-server addr:"+tunnel.Endpoint()) + consulToken, err := requestToken(tunnel.Endpoint()) + if err != nil { + logger.Log(t, "error finding consul token") + return + } + tunnel.Close() + logger.Log(t, "consul test token :"+consulToken) + + releaseName := helpers.RandomName() + + helmValues := map[string]string{ + "global.cloud.enabled": "true", + "global.cloud.resourceId.secretName": resourceSecretName, + "global.cloud.resourceId.secretKey": resourceSecretKey, + + "global.cloud.clientId.secretName": clientIDSecretName, + "global.cloud.clientId.secretKey": clientIDSecretKey, + + "global.cloud.clientSecret.secretName": clientSecretName, + "global.cloud.clientSecret.secretKey": clientSecretKey, + + "global.cloud.apiHost.secretName": apiHostSecretName, + "global.cloud.apiHost.secretKey": apiHostSecretKey, + + "global.cloud.authUrl.secretName": authUrlSecretName, + "global.cloud.authUrl.secretKey": authUrlSecretKey, + + "global.cloud.scadaAddress.secretName": scadaAddressSecretName, + "global.cloud.scadaAddress.secretKey": scadaAddressSecretKey, + "connectInject.default": "true", + + // TODO: Follow up with this bug + "global.acls.manageSystemACLs": "false", + "global.gossipEncryption.autoGenerate": "false", + "global.tls.enabled": "true", + "global.tls.enableAutoEncrypt": "true", + // TODO: Take this out + + "telemetryCollector.enabled": "true", + "telemetryCollector.image": cfg.ConsulCollectorImage, + "telemetryCollector.cloud.clientId.secretName": clientIDSecretName, + "telemetryCollector.cloud.clientId.secretKey": clientIDSecretKey, + + "telemetryCollector.cloud.clientSecret.secretName": clientSecretName, + "telemetryCollector.cloud.clientSecret.secretKey": clientSecretKey, + // Either we set the global.trustedCAs (make sure it's idented exactly) or we + // set TLS to insecure + + "telemetryCollector.extraEnvironmentVars.HCP_API_TLS": "insecure", + "telemetryCollector.extraEnvironmentVars.HCP_AUTH_TLS": "insecure", + "telemetryCollector.extraEnvironmentVars.HCP_SCADA_TLS": "insecure", + "telemetryCollector.extraEnvironmentVars.OTLP_EXPORTER_TLS": "insecure", + + "server.extraEnvironmentVars.HCP_API_TLS": "insecure", + "server.extraEnvironmentVars.HCP_AUTH_TLS": "insecure", + "server.extraEnvironmentVars.HCP_SCADA_TLS": "insecure", + + // This is pregenerated CA used for testing. It can be replaced at any time and isn't + // meant for anything other than testing + // "global.trustedCAs[0]": `-----BEGIN CERTIFICATE----- + // MIICrjCCAZYCCQD5LxMcnMY8rDANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQDDA5m + // YWtlLXNlcnZlci1jYTAeFw0yMzA1MTkxMjIwMzhaFw0zMzA1MTYxMjIwMzhaMBkx + // FzAVBgNVBAMMDmZha2Utc2VydmVyLWNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A + // MIIBCgKCAQEAwhbiII7sMultedFzQVhVZz5Ti+9lWrpZb8y0ZR6NaNvoxDPX151t + // Adh5NegSeH/+351iDBGZHhmKECtBuk8FJgk88O7y8A7Yg+/lyeZd0SJTEeiYUe7d + // sSaBTYSmixyn6s15Y5MVp9gM7t2YXrocRkFxDtdhLMWf0zwzJEwDouFMMiFZw5II + // yDbI6UfwKyB8C8ln10+TcczbheaOMQ1jGn35YWAG/LEdutU6DO2Y/GZYQ41nyLF1 + // klqh34USQPVQSQW7R7GiDxyhh1fGaDF6RAzH4RerzQSNvvTHmBXIGurB/Hnu1n3p + // CwWeatWMU5POy1es73S/EPM0NpWD5RabSwIDAQABMA0GCSqGSIb3DQEBCwUAA4IB + // AQBayoTltSW55PvKVp9cmqGOBMlkIMKPd6Ny4bCb/3UF+3bzQmIblh3O3kEt7WoY + // fA9vp+6cSRGVqgBfR2bi40RrerLNA79yywIZjfBMteNuRoul5VeD+mLyFCo4197r + // Atl2TEx2kl2V8rjCsEBcTqKqetVOMLYEZ2tbCeUt1A/K7OzaJfHgelEYcsVt68Q9 + // /BLoo2UXfOpRrcsx7u7s5HPVbG3bx+1MvGJZ2C3i0B6agnkGDzEpoM4KZGxEefB9 + // DOHIJfie9d9BQD52nZh3SGHz0b3vfJ430XrQmaNZ26fuIEyIYrpvyAhBXckj2iTD + // 1TXpqr/1D7EUbddktyhXTK9e + // -----END CERTIFICATE-----`, + } + if cfg.ConsulImage != "" { + helmValues["global.image"] = cfg.ConsulImage + } + if cfg.ConsulCollectorImage != "" { + helmValues["telemetryCollector.image"] = cfg.ConsulCollectorImage + } + + consulCluster := consul.NewHelmCluster(t, helmValues, suite.Environment().DefaultContext(t), suite.Config(), releaseName) + consulCluster.Create(t) + + logger.Log(t, "creating static-server deployment") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-server") + // time.Sleep(1 * time.Hour) + // TODO: add in test assertions here +} diff --git a/acceptance/tests/cloud/main_test.go b/acceptance/tests/cloud/main_test.go new file mode 100644 index 0000000000..85d1867933 --- /dev/null +++ b/acceptance/tests/cloud/main_test.go @@ -0,0 +1,18 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package cloud + +import ( + "os" + "testing" + + testsuite "github.com/hashicorp/consul-k8s/acceptance/framework/suite" +) + +var suite testsuite.Suite + +func TestMain(m *testing.M) { + suite = testsuite.NewSuite(m) + os.Exit(suite.Run()) +} diff --git a/acceptance/tests/fixtures/bases/cloud/hcp-mock/deployment.yaml b/acceptance/tests/fixtures/bases/cloud/hcp-mock/deployment.yaml new file mode 100644 index 0000000000..7278557cdb --- /dev/null +++ b/acceptance/tests/fixtures/bases/cloud/hcp-mock/deployment.yaml @@ -0,0 +1,29 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: fake-server +spec: + replicas: 1 + selector: + matchLabels: + app: fake-server + template: + metadata: + name: fake-server + labels: + app: fake-server + spec: + containers: + - name: fake-server + # TODO: move this to a hashicorp mirror + image: docker.io/chaapppie/fakeserver:latest + ports: + - containerPort: 443 + name: https + - containerPort: 8080 + name: http + serviceAccountName: fake-server + terminationGracePeriodSeconds: 0 # so deletion is quick diff --git a/acceptance/tests/fixtures/bases/cloud/hcp-mock/kustomization.yaml b/acceptance/tests/fixtures/bases/cloud/hcp-mock/kustomization.yaml new file mode 100644 index 0000000000..dc9c951ab2 --- /dev/null +++ b/acceptance/tests/fixtures/bases/cloud/hcp-mock/kustomization.yaml @@ -0,0 +1,10 @@ + + +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - deployment.yaml + - service.yaml + - serviceaccount.yaml + diff --git a/acceptance/tests/fixtures/bases/cloud/hcp-mock/service.yaml b/acceptance/tests/fixtures/bases/cloud/hcp-mock/service.yaml new file mode 100644 index 0000000000..0cc6f1b9ce --- /dev/null +++ b/acceptance/tests/fixtures/bases/cloud/hcp-mock/service.yaml @@ -0,0 +1,17 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: v1 +kind: Service +metadata: + name: fake-server +spec: + selector: + app: fake-server + ports: + - name: https + port: 443 + targetPort: 443 + - name: http + port: 8080 + targetPort: 8080 diff --git a/acceptance/tests/fixtures/bases/cloud/hcp-mock/serviceaccount.yaml b/acceptance/tests/fixtures/bases/cloud/hcp-mock/serviceaccount.yaml new file mode 100644 index 0000000000..f52d9640cd --- /dev/null +++ b/acceptance/tests/fixtures/bases/cloud/hcp-mock/serviceaccount.yaml @@ -0,0 +1,7 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: v1 +kind: ServiceAccount +metadata: + name: fake-server diff --git a/acceptance/tests/fixtures/bases/static-server/deployment.yaml b/acceptance/tests/fixtures/bases/static-server/deployment.yaml index f61371a880..9f5776c9ca 100644 --- a/acceptance/tests/fixtures/bases/static-server/deployment.yaml +++ b/acceptance/tests/fixtures/bases/static-server/deployment.yaml @@ -18,7 +18,9 @@ spec: spec: containers: - name: static-server - image: docker.mirror.hashicorp.services/hashicorp/http-echo:latest + # Using alpine vs latest as there is a build issue with M1s. Also other tests in multiport-app reference + # alpine so standardizing this. + image: docker.mirror.hashicorp.services/hashicorp/http-echo:alpine args: - -text="hello world" - -listen=:8080 diff --git a/charts/consul/templates/server-statefulset.yaml b/charts/consul/templates/server-statefulset.yaml index aa9198f127..0bde9b881a 100644 --- a/charts/consul/templates/server-statefulset.yaml +++ b/charts/consul/templates/server-statefulset.yaml @@ -198,6 +198,11 @@ spec: medium: "Memory" {{- end }} {{- end }} + {{- if .Values.global.trustedCAs }} + - name: trusted-cas + emptyDir: + medium: "Memory" + {{- end }} {{- range .Values.server.extraVolumes }} - name: userconfig-{{ .name }} {{ .type }}: @@ -354,11 +359,23 @@ spec: key: {{ .Values.global.cloud.scadaAddress.secretKey }} {{- end}} {{- end }} + {{- if .Values.global.trustedCAs }} + - name: SSL_CERT_DIR + value: "/etc/ssl/certs:/trusted-cas" + {{- end }} {{- include "consul.extraEnvironmentVars" .Values.server | nindent 12 }} command: - "/bin/sh" - "-ec" - | + {{- if .Values.global.trustedCAs }} + {{- range $i, $cert := .Values.global.trustedCAs }} + cat < /trusted-cas/custom-ca-{{$i}}.pem + {{- $cert | nindent 14 }} + EOF + {{- end }} + {{- end }} + {{- if and .Values.global.secretsBackend.vault.enabled .Values.global.gossipEncryption.secretName }} GOSSIP_KEY=`cat /vault/secrets/gossip.txt` {{- end }} @@ -426,6 +443,11 @@ spec: mountPath: /consul/vault-ca/ readOnly: true {{- end }} + {{- if .Values.global.trustedCAs }} + - name: trusted-cas + mountPath: /trusted-cas + readOnly: false + {{- end }} ports: {{- if (or (not .Values.global.tls.enabled) (not .Values.global.tls.httpsOnly)) }} - name: http diff --git a/charts/consul/templates/telemetry-collector-deployment.yaml b/charts/consul/templates/telemetry-collector-deployment.yaml index a073128f95..cb0cb67852 100644 --- a/charts/consul/templates/telemetry-collector-deployment.yaml +++ b/charts/consul/templates/telemetry-collector-deployment.yaml @@ -36,6 +36,7 @@ spec: "consul.hashicorp.com/connect-inject-status": "injected" # We aren't using tproxy and we don't have an original pod. This would be simpler if we made a path similar # to gateways + "consul.hashicorp.com/connect-service-port": "metricsserver" "consul.hashicorp.com/transparent-proxy": "false" "consul.hashicorp.com/transparent-proxy-overwrite-probes": "false" "consul.hashicorp.com/connect-k8s-version": {{ $.Chart.Version }} @@ -69,7 +70,9 @@ spec: {{- toYaml .Values.global.extraLabels | nindent 8 }} {{- end }} spec: - serviceAccountName: {{ template "consul.fullname" . }}-telemetry-collector + # This needs to explicitly be consul-telemetry-collector because we look this up from each service consul-dataplane + # to forward metrics to it. + serviceAccountName: consul-telemetry-collector initContainers: # We're manually managing this init container instead of using the connect injector so that we don't run into # any race conditions on the connect-injector deployment or upgrade @@ -202,21 +205,38 @@ spec: name: {{ .Values.global.cloud.scadaAddress.secretName }} key: {{ .Values.global.cloud.scadaAddress.secretKey }} {{- end}} - + {{- if .Values.global.trustedCAs }} + - name: SSL_CERT_DIR + value: "/etc/ssl/certs:/trusted-cas" + {{- end }} + {{- include "consul.extraEnvironmentVars" .Values.telemetryCollector | nindent 12 }} command: - "/bin/sh" - "-ec" - | + {{- if .Values.global.trustedCAs }} + {{- range $i, $cert := .Values.global.trustedCAs }} + cat < /trusted-cas/custom-ca-{{$i}}.pem + {{- $cert | nindent 10 }} + EOF + {{- end }} + {{- end }} + consul-telemetry-collector agent {{- if .Values.telemetryCollector.customExporterConfig }} args: - -config-file-path /consul/config/config.json {{ end }} - {{- if .Values.telemetryCollector.customExporterConfig }} volumeMounts: + {{- if .Values.telemetryCollector.customExporterConfig }} - name: config mountPath: /consul/config - {{- end }} + {{- end }} + {{- if .Values.global.trustedCAs }} + - name: trusted-cas + mountPath: /trusted-cas + readOnly: false + {{- end }} resources: {{- if .Values.telemetryCollector.resources }} {{- toYaml .Values.telemetryCollector.resources | nindent 12 }} @@ -347,6 +367,11 @@ spec: - emptyDir: medium: Memory name: consul-connect-inject-data + {{- if .Values.global.trustedCAs }} + - name: trusted-cas + emptyDir: + medium: "Memory" + {{- end }} {{- if .Values.global.tls.enabled }} {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} - name: consul-ca-cert diff --git a/charts/consul/templates/telemetry-collector-service.yaml b/charts/consul/templates/telemetry-collector-service.yaml index f7fc3f09d9..266c80b4bf 100644 --- a/charts/consul/templates/telemetry-collector-service.yaml +++ b/charts/consul/templates/telemetry-collector-service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: - name: {{ template "consul.fullname" . }}-telemetry-collector + name: consul-telemetry-collector namespace: {{ .Release.Namespace }} labels: app: {{ template "consul.name" . }} diff --git a/charts/consul/templates/telemetry-collector-serviceaccount.yaml b/charts/consul/templates/telemetry-collector-serviceaccount.yaml index f2b6e88171..fca58eede9 100644 --- a/charts/consul/templates/telemetry-collector-serviceaccount.yaml +++ b/charts/consul/templates/telemetry-collector-serviceaccount.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: {{ template "consul.fullname" . }}-telemetry-collector + name: consul-telemetry-collector namespace: {{ .Release.Namespace }} labels: app: {{ template "consul.name" . }} diff --git a/charts/consul/test/unit/server-statefulset.bats b/charts/consul/test/unit/server-statefulset.bats index f0a8b0112b..29621187ab 100755 --- a/charts/consul/test/unit/server-statefulset.bats +++ b/charts/consul/test/unit/server-statefulset.bats @@ -2586,6 +2586,64 @@ MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ } +#-------------------------------------------------------------------- +# global.trustedCAs + +@test "server/StatefulSet: trustedCAs: if trustedCAs is set command is modified correctly" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.trustedCAs[0]=-----BEGIN CERTIFICATE----- +MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].command[2] | contains("cat < /trusted-cas/custom-ca-0.pem")' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/StatefulSet: trustedCAs: if tustedCAs multiple are set" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.trustedCAs[0]=-----BEGIN CERTIFICATE----- +MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ + --set 'global.trustedCAs[1]=-----BEGIN CERTIFICATE----- +MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0]' | tee /dev/stderr) + + + local actual=$(echo $object | jq '.command[2] | contains("cat < /trusted-cas/custom-ca-0.pem")' | tee /dev/stderr) + [ "${actual}" = "true" ] + local actual=$(echo $object | jq '.command[2] | contains("cat < /trusted-cas/custom-ca-1.pem")' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +# global.trustedCAs +@test "server/StatefulSet: trustedCAs: if trustedCAs is set /trusted-cas volumeMount is added" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.trustedCAs[0]=-----BEGIN CERTIFICATE----- +MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ + . | tee /dev/stderr | yq -r '.spec.template.spec' | tee /dev/stderr) + local actual=$(echo $object | jq -r '.volumes[] | select(.name == "trusted-cas") | .name' | tee /dev/stderr) + [ "${actual}" = "trusted-cas" ] +} + +@test "server/StatefulSet: trustedCAs: if trustedCAs is set SSL_CERT_DIR env var is set" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.trustedCAs[0]=-----BEGIN CERTIFICATE----- +MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ + . | tee /dev/stderr | yq -r '.spec.template.spec.containers[0].env[] | select(.name == "SSL_CERT_DIR")' | tee /dev/stderr) + + local actual=$(echo $object | jq -r '.name' | tee /dev/stderr) + [ "${actual}" = "SSL_CERT_DIR" ] + local actual=$(echo $object | jq -r '.value' | tee /dev/stderr) + [ "${actual}" = "/etc/ssl/certs:/trusted-cas" ] +} + #-------------------------------------------------------------------- # snapshotAgent license-autoload diff --git a/charts/consul/test/unit/telemetry-collector-deployment.bats b/charts/consul/test/unit/telemetry-collector-deployment.bats index 8161e90e5d..1e2758e638 100755 --- a/charts/consul/test/unit/telemetry-collector-deployment.bats +++ b/charts/consul/test/unit/telemetry-collector-deployment.bats @@ -73,6 +73,20 @@ load _helpers [ "${actual}" = "testing" ] } +#-------------------------------------------------------------------- +# consul.name + +@test "telemetryCollector/Deployment: name is constant regardless of consul name" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'consul.name=foobar' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].name' | tee /dev/stderr) + [ "${actual}" = "consul-telemetry-collector" ] +} + #-------------------------------------------------------------------- # global.tls.enabled @@ -929,6 +943,67 @@ load _helpers yq '.spec.template.spec.initContainers[1].volumeMounts[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) [ "${actual}" = "" ] } +#-------------------------------------------------------------------- +# trustedCAs + +@test "telemetryCollector/Deployment: trustedCAs: if trustedCAs is set command is modified correctly" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'global.trustedCAs[0]=-----BEGIN CERTIFICATE----- +MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].command[2] | contains("cat < /trusted-cas/custom-ca-0.pem")' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "telemetryCollector/Deployment: trustedCAs: if multiple Trusted cas were set" { + cd `chart_dir` + local object=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'global.trustedCAs[0]=-----BEGIN CERTIFICATE----- +MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ + --set 'global.trustedCAs[1]=-----BEGIN CERTIFICATE----- +MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0]' | tee /dev/stderr) + + + local actual=$(echo $object | jq '.command[2] | contains("cat < /trusted-cas/custom-ca-0.pem")' | tee /dev/stderr) + [ "${actual}" = "true" ] + local actual=$(echo $object | jq '.command[2] | contains("cat < /trusted-cas/custom-ca-1.pem")' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "telemetryCollector/Deployment: trustedCAs: if trustedCAs is set /trusted-cas volumeMount is added" { + cd `chart_dir` + local object=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'global.trustedCAs[0]=-----BEGIN CERTIFICATE----- +MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ + . | tee /dev/stderr | yq -r '.spec.template.spec' | tee /dev/stderr) + local actual=$(echo $object | jq -r '.volumes[] | select(.name == "trusted-cas") | .name' | tee /dev/stderr) + [ "${actual}" = "trusted-cas" ] +} + + +@test "telemetryCollector/Deployment: trustedCAs: if trustedCAs is set SSL_CERT_DIR env var is set" { + cd `chart_dir` + local object=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'global.trustedCAs[0]=-----BEGIN CERTIFICATE----- +MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ + . | tee /dev/stderr | yq -r '.spec.template.spec.containers[0].env[] | select(.name == "SSL_CERT_DIR")' | tee /dev/stderr) + + local actual=$(echo $object | jq -r '.name' | tee /dev/stderr) + [ "${actual}" = "SSL_CERT_DIR" ] + local actual=$(echo $object | jq -r '.value' | tee /dev/stderr) + [ "${actual}" = "/etc/ssl/certs:/trusted-cas" ] +} #-------------------------------------------------------------------- # extraLabels @@ -977,3 +1052,25 @@ load _helpers [ "${actualTemplateFoo}" = "bar" ] [ "${actualTemplateBaz}" = "qux" ] } + +#-------------------------------------------------------------------- +# extraEnvironmentVariables + +@test "telemetryCollector/Deployment: extra environment variables" { + cd `chart_dir` + local object=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.extraEnvironmentVars.HCP_AUTH_TLS=insecure' \ + --set 'telemetryCollector.extraEnvironmentVars.foo=bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r 'map(select(.name == "HCP_AUTH_TLS")) | .[0].value' | tee /dev/stderr) + [ "${actual}" = "insecure" ] + + local actual=$(echo $object | + yq -r 'map(select(.name == "foo")) | .[0].value' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} \ No newline at end of file diff --git a/charts/consul/test/unit/telemetry-collector-service.bats b/charts/consul/test/unit/telemetry-collector-service.bats index 0ee6512c05..c5406b8124 100755 --- a/charts/consul/test/unit/telemetry-collector-service.bats +++ b/charts/consul/test/unit/telemetry-collector-service.bats @@ -31,6 +31,20 @@ load _helpers [ "${actual}" = "true" ] } +#-------------------------------------------------------------------- +# consul.name + +@test "telemetryCollector/Service: name is constant regardless of consul name" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-service.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'consul.name=foobar' \ + . | tee /dev/stderr | + yq -r '.metadata.name' | tee /dev/stderr) + [ "${actual}" = "consul-telemetry-collector" ] +} + #-------------------------------------------------------------------- # annotations diff --git a/charts/consul/test/unit/telemetry-collector-serviceaccount.bats b/charts/consul/test/unit/telemetry-collector-serviceaccount.bats index 211238f72f..589632d5b5 100644 --- a/charts/consul/test/unit/telemetry-collector-serviceaccount.bats +++ b/charts/consul/test/unit/telemetry-collector-serviceaccount.bats @@ -67,3 +67,18 @@ load _helpers yq -r '.metadata.annotations.foo' | tee /dev/stderr) [ "${actual}" = "bar" ] } + + +#-------------------------------------------------------------------- +# consul.name + +@test "telemetryCollector/ServiceAccount: name is constant regardless of consul name" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-serviceaccount.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'consul.name=foobar' \ + . | tee /dev/stderr | + yq -r '.metadata.name' | tee /dev/stderr) + [ "${actual}" = "consul-telemetry-collector" ] +} diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index cf6a8e5ab4..9f004cab30 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -557,7 +557,7 @@ global: # The name (and tag) of the consul-dataplane Docker image used for the # connect-injected sidecar proxies and mesh, terminating, and ingress gateways. # @default: hashicorp/consul-dataplane: - imageConsulDataplane: "hashicorp/consul-dataplane:1.1.0" + imageConsulDataplane: "hashicorppreview/consul-dataplane:1.1-dev" # Configuration for running this Helm chart on the Red Hat OpenShift platform. # This Helm chart currently supports OpenShift v4.x+. @@ -650,6 +650,21 @@ global: # @type: map extraLabels: {} + # Optional PEM-encoded CA certificates that will be added to trusted system CAs. + # + # Example: + # + # ```yaml + # trustedCAs: [ + # | + # -----BEGIN CERTIFICATE----- + # MIIC7jCCApSgAwIBAgIRAIq2zQEVexqxvtxP6J0bXAwwCgYIKoZIzj0EAwIwgbkx + # ... + # ] + # ``` + # @type: array + trustedCAs: [ ] + # Server, when enabled, configures a server cluster to run. This should # be disabled if you plan on connecting to a Consul cluster external to # the Kube cluster. @@ -3275,3 +3290,10 @@ telemetryCollector: # Optional priorityClassName. # @type: string priorityClassName: "" + + # A list of extra environment variables to set within the stateful set. + # These could be used to include proxy settings required for cloud auto-join + # feature, in case kubernetes cluster is behind egress http proxies. Additionally, + # it could be used to configure custom consul parameters. + # @type: map + extraEnvironmentVars: { } From aaee9a71d321d40574f03c0b8aabd9f5acbfe184 Mon Sep 17 00:00:00 2001 From: Eric Haberkorn Date: Wed, 31 May 2023 09:24:22 -0400 Subject: [PATCH 178/340] Fix bug on service intention CRDs causing source partitions and namespaces not to be compared. (#2194) This bug means that swapping partitions and namespaces on sources wouldn't get reflected in Consul. --- .changelog/2194.txt | 3 + .../api/v1alpha1/serviceintentions_types.go | 19 ++++- .../v1alpha1/serviceintentions_types_test.go | 72 +++++++++++++++++++ 3 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 .changelog/2194.txt diff --git a/.changelog/2194.txt b/.changelog/2194.txt new file mode 100644 index 0000000000..997326218b --- /dev/null +++ b/.changelog/2194.txt @@ -0,0 +1,3 @@ +```release-note: +crd: fix bug on service intentions CRD causing some updates to be ignored. +``` diff --git a/control-plane/api/v1alpha1/serviceintentions_types.go b/control-plane/api/v1alpha1/serviceintentions_types.go index fd18ecd3fe..74e02eb617 100644 --- a/control-plane/api/v1alpha1/serviceintentions_types.go +++ b/control-plane/api/v1alpha1/serviceintentions_types.go @@ -234,18 +234,34 @@ func (in *ServiceIntentions) ConsulGlobalResource() bool { return false } +func normalizeEmptyToDefault(value string) string { + if value == "" { + return "default" + } + return value +} + func (in *ServiceIntentions) MatchesConsul(candidate api.ConfigEntry) bool { configEntry, ok := candidate.(*capi.ServiceIntentionsConfigEntry) if !ok { return false } + specialEquality := cmp.Options{ + cmp.FilterPath(func(path cmp.Path) bool { + return path.String() == "Sources.Namespace" + }, cmp.Transformer("NormalizeNamespace", normalizeEmptyToDefault)), + cmp.FilterPath(func(path cmp.Path) bool { + return path.String() == "Sources.Partition" + }, cmp.Transformer("NormalizePartition", normalizeEmptyToDefault)), + } + // No datacenter is passed to ToConsul as we ignore the Meta field when checking for equality. return cmp.Equal( in.ToConsul(""), configEntry, cmpopts.IgnoreFields(capi.ServiceIntentionsConfigEntry{}, "Partition", "Namespace", "Meta", "ModifyIndex", "CreateIndex"), - cmpopts.IgnoreFields(capi.SourceIntention{}, "Partition", "Namespace", "LegacyID", "LegacyMeta", "LegacyCreateTime", "LegacyUpdateTime", "Precedence", "Type"), + cmpopts.IgnoreFields(capi.SourceIntention{}, "LegacyID", "LegacyMeta", "LegacyCreateTime", "LegacyUpdateTime", "Precedence", "Type"), cmpopts.IgnoreUnexported(), cmpopts.EquateEmpty(), // Consul will sort the sources by precedence when returning the resource @@ -255,6 +271,7 @@ func (in *ServiceIntentions) MatchesConsul(candidate api.ConfigEntry) bool { // piggyback on strings.Compare that returns -1 if a < b. return strings.Compare(sourceIntentionSortKey(a), sourceIntentionSortKey(b)) == -1 }), + specialEquality, ) } diff --git a/control-plane/api/v1alpha1/serviceintentions_types_test.go b/control-plane/api/v1alpha1/serviceintentions_types_test.go index df0100e75a..f445dfb246 100644 --- a/control-plane/api/v1alpha1/serviceintentions_types_test.go +++ b/control-plane/api/v1alpha1/serviceintentions_types_test.go @@ -41,6 +41,78 @@ func TestServiceIntentions_MatchesConsul(t *testing.T) { }, Matches: true, }, + "namespaces and partitions equate `default` and empty strings": { + Ours: ServiceIntentions{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + }, + Spec: ServiceIntentionsSpec{ + Destination: IntentionDestination{ + Name: "svc-name", + Namespace: "ns1", + }, + Sources: []*SourceIntention{ + { + Name: "svc1", + Namespace: "", + Partition: "default", + Action: "allow", + }, + }, + }, + }, + Theirs: &capi.ServiceIntentionsConfigEntry{ + Kind: capi.ServiceIntentions, + Name: "svc-name", + Namespace: "ns1", + Sources: []*capi.SourceIntention{ + { + Name: "svc1", + Namespace: "default", + Partition: "", + Action: "allow", + Precedence: 0, + }, + }, + }, + Matches: true, + }, + "source namespaces and partitions are compared": { + Ours: ServiceIntentions{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + }, + Spec: ServiceIntentionsSpec{ + Destination: IntentionDestination{ + Name: "svc-name", + Namespace: "test", + }, + Sources: []*SourceIntention{ + { + Name: "svc1", + Namespace: "test", + Partition: "test", + Action: "allow", + }, + }, + }, + }, + Theirs: &capi.ServiceIntentionsConfigEntry{ + Kind: capi.ServiceIntentions, + Name: "svc-name", + Namespace: "test", + Sources: []*capi.SourceIntention{ + { + Name: "svc1", + Namespace: "not-test", + Partition: "not-test", + Action: "allow", + Precedence: 0, + }, + }, + }, + Matches: false, + }, "all fields set matches": { Ours: ServiceIntentions{ ObjectMeta: metav1.ObjectMeta{ From 3a66856c057ceca6a22fa1c39f6f99c4037b3be8 Mon Sep 17 00:00:00 2001 From: Paul Glass Date: Wed, 31 May 2023 09:15:47 -0500 Subject: [PATCH 179/340] Add CRD for jwt-provider config entry (#2209) * Add CRD for jwt-provider config entry * Pin consul/api to versions containing the jwt-provider config entry * Update Makefile to use v0.10.0 of sigs.k8s.io/controller-tools/cmd/controller-gen --- .changelog/2209.txt | 3 + CONTRIBUTING.md | 3 +- Makefile | 2 +- acceptance/go.mod | 3 +- acceptance/go.sum | 4 + .../config-entries/config_entries_test.go | 16 + .../fixtures/bases/crds-oss/jwtprovider.yaml | 34 + .../templates/connect-inject-clusterrole.yaml | 2 + ...t-inject-mutatingwebhookconfiguration.yaml | 21 + charts/consul/templates/crd-jwtproviders.yaml | 259 +++++++ control-plane/PROJECT | 9 + control-plane/api/common/common.go | 1 + .../api/v1alpha1/jwtprovider_types.go | 663 ++++++++++++++++ .../api/v1alpha1/jwtprovider_types_test.go | 730 ++++++++++++++++++ .../api/v1alpha1/jwtprovider_webhook.go | 61 ++ .../api/v1alpha1/zz_generated.deepcopy.go | 305 ++++++++ .../consul.hashicorp.com_jwtproviders.yaml | 254 ++++++ control-plane/config/rbac/role.yaml | 20 + control-plane/config/webhook/manifests.yaml | 21 + .../configentry_controller_test.go | 126 +++ .../controllers/jwtprovider_controller.go | 43 ++ control-plane/go.mod | 2 +- control-plane/go.sum | 4 +- .../subcommand/inject-connect/command.go | 15 + 24 files changed, 2595 insertions(+), 6 deletions(-) create mode 100644 .changelog/2209.txt create mode 100644 acceptance/tests/fixtures/bases/crds-oss/jwtprovider.yaml create mode 100644 charts/consul/templates/crd-jwtproviders.yaml create mode 100644 control-plane/api/v1alpha1/jwtprovider_types.go create mode 100644 control-plane/api/v1alpha1/jwtprovider_types_test.go create mode 100644 control-plane/api/v1alpha1/jwtprovider_webhook.go create mode 100644 control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml create mode 100644 control-plane/controllers/jwtprovider_controller.go diff --git a/.changelog/2209.txt b/.changelog/2209.txt new file mode 100644 index 0000000000..72a59064e4 --- /dev/null +++ b/.changelog/2209.txt @@ -0,0 +1,3 @@ +```release-note:feature +helm: Add `JWTProvider` CRD for configuring the `jwt-provider` config entry. +``` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f427421294..63ae3b2323 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -214,6 +214,7 @@ rebase the branch on main, fixing any conflicts along the way before the code ca ```go // ServiceRouter is the Schema for the servicerouters API // +kubebuilder:printcolumn:name="Synced",type="string",JSONPath=".status.conditions[?(@.type==\"Synced\")].status",description="The sync status of the resource with Consul" + // +kubebuilder:printcolumn:name="Last Synced",type="date",JSONPath=".status.lastSyncedTime",description="The last successful synced time of the resource with Consul" // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="The age of the resource" type ServiceRouter struct { ``` @@ -232,7 +233,7 @@ rebase the branch on main, fixing any conflicts along the way before the code ca ``` 1. Go to the Consul `api` package for the config entry, e.g. https://github.com/hashicorp/consul/blob/main/api/config_entry_gateways.go 1. Copy the top-level fields over into the `Spec` struct except for - `Kind`, `Name`, `Namespace`, `Meta`, `CreateIndex` and `ModifyIndex`. In this + `Kind`, `Name`, `Namespace`, `Partition`, `Meta`, `CreateIndex` and `ModifyIndex`. In this example, the top-level fields remaining are `TLS` and `Listeners`: ```go diff --git a/Makefile b/Makefile index f687c308c5..02527e4d76 100644 --- a/Makefile +++ b/Makefile @@ -138,7 +138,7 @@ ifeq (, $(shell which controller-gen)) CONTROLLER_GEN_TMP_DIR=$$(mktemp -d) ;\ cd $$CONTROLLER_GEN_TMP_DIR ;\ go mod init tmp ;\ - go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.8.0 ;\ + go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.10.0 ;\ rm -rf $$CONTROLLER_GEN_TMP_DIR ;\ } CONTROLLER_GEN=$(shell go env GOPATH)/bin/controller-gen diff --git a/acceptance/go.mod b/acceptance/go.mod index fb2cb62725..c53caf4952 100644 --- a/acceptance/go.mod +++ b/acceptance/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/gruntwork-io/terratest v0.31.2 github.com/hashicorp/consul-k8s/control-plane v0.0.0-20221117191905-0b1cc2b631e3 - github.com/hashicorp/consul/api v1.20.0 + github.com/hashicorp/consul/api v1.10.1-0.20230530193107-04a0d0133ae4 github.com/hashicorp/consul/sdk v0.13.1 github.com/hashicorp/go-uuid v1.0.3 github.com/hashicorp/go-version v1.6.0 @@ -97,6 +97,7 @@ require ( github.com/urfave/cli v1.22.2 // indirect go.uber.org/atomic v1.7.0 // indirect golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect + golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect golang.org/x/net v0.8.0 // indirect golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect golang.org/x/sys v0.6.0 // indirect diff --git a/acceptance/go.sum b/acceptance/go.sum index 811c11c123..82037c23a0 100644 --- a/acceptance/go.sum +++ b/acceptance/go.sum @@ -362,6 +362,8 @@ github.com/gruntwork-io/terratest v0.31.2 h1:xvYHA80MUq5kx670dM18HInewOrrQrAN+Xb github.com/gruntwork-io/terratest v0.31.2/go.mod h1:EEgJie28gX/4AD71IFqgMj6e99KP5mi81hEtzmDjxTo= github.com/hashicorp/consul-k8s/control-plane v0.0.0-20221117191905-0b1cc2b631e3 h1:4wROIZB8Y4cN/wPILChc2zQ/q00z1VyJitdgyLbITdU= github.com/hashicorp/consul-k8s/control-plane v0.0.0-20221117191905-0b1cc2b631e3/go.mod h1:j9Db/whkzvNC+KP2GftY0HxxleLm9swxXjlu3tYaOAw= +github.com/hashicorp/consul/api v1.10.1-0.20230530193107-04a0d0133ae4 h1:6kUTk+YBgA5X5b3gNAoI18WEK4/t75LcWSimEgmpFdg= +github.com/hashicorp/consul/api v1.10.1-0.20230530193107-04a0d0133ae4/go.mod h1:tXfrC6o0yFTgAW46xd5Ic8STHc9oIBcRVBcwhX5KNCQ= github.com/hashicorp/consul/api v1.20.0 h1:9IHTjNVSZ7MIwjlW3N3a7iGiykCMDpxZu8jsxFJh0yc= github.com/hashicorp/consul/api v1.20.0/go.mod h1:nR64eD44KQ59Of/ECwt2vUmIK2DKsDzAwTmwmLl8Wpo= github.com/hashicorp/consul/sdk v0.13.1 h1:EygWVWWMczTzXGpO93awkHFzfUka6hLYJ0qhETd+6lY= @@ -749,6 +751,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= diff --git a/acceptance/tests/config-entries/config_entries_test.go b/acceptance/tests/config-entries/config_entries_test.go index 4aa7cf11bd..a31f4762fd 100644 --- a/acceptance/tests/config-entries/config_entries_test.go +++ b/acceptance/tests/config-entries/config_entries_test.go @@ -178,6 +178,22 @@ func TestController(t *testing.T) { require.Equal(r, "certFile", terminatingGatewayEntry.Services[0].CertFile) require.Equal(r, "keyFile", terminatingGatewayEntry.Services[0].KeyFile) require.Equal(r, "sni", terminatingGatewayEntry.Services[0].SNI) + + // jwt-provider + entry, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "test-jwt-provider", nil) + require.NoError(r, err) + jwtProviderConfigEntry, ok := entry.(*api.JWTProviderConfigEntry) + require.True(r, ok, "could not cast to JWTProviderConfigEntry") + require.Equal(r, "jwks.txt", jwtProviderConfigEntry.JSONWebKeySet.Local.Filename) + require.Equal(r, "test-issuer", jwtProviderConfigEntry.Issuer) + require.Equal(r, []string{"aud1", "aud2"}, jwtProviderConfigEntry.Issuer) + require.Equal(r, "x-jwt-header", jwtProviderConfigEntry.Locations[0].Header.Name) + require.Equal(r, "x-query-param", jwtProviderConfigEntry.Locations[1].QueryParam.Name) + require.Equal(r, "session-id", jwtProviderConfigEntry.Locations[2].Cookie.Name) + require.Equal(r, "x-forwarded-jwt", jwtProviderConfigEntry.Forwarding.HeaderName) + require.True(r, jwtProviderConfigEntry.Forwarding.PadForwardPayloadHeader) + require.Equal(r, 45, jwtProviderConfigEntry.ClockSkewSeconds) + require.Equal(r, 15, jwtProviderConfigEntry.CacheConfig) }) } diff --git a/acceptance/tests/fixtures/bases/crds-oss/jwtprovider.yaml b/acceptance/tests/fixtures/bases/crds-oss/jwtprovider.yaml new file mode 100644 index 0000000000..d28139dc40 --- /dev/null +++ b/acceptance/tests/fixtures/bases/crds-oss/jwtprovider.yaml @@ -0,0 +1,34 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: JWTProvider +metadata: + name: test-jwt-provider +spec: + jsonWebKeySet: + local: + filename: "jwks.txt" + issuer: "test-issuer" + audiences: + - "aud1" + - "aud2" + locations: + - header: + name: "x-jwt-header" + valuePrefix: "bearer" + forward: true + - queryParam: + name: "x-query-param" + - cookie: + name: "session-id" + forwarding: + headerName: "x-forwarded-jwt" + padForwardPayloadHeader: true + clockSkewSeconds: 45 + cacheConfig: + size: 15 + + + + diff --git a/charts/consul/templates/connect-inject-clusterrole.yaml b/charts/consul/templates/connect-inject-clusterrole.yaml index e152511469..d4b2464dec 100644 --- a/charts/consul/templates/connect-inject-clusterrole.yaml +++ b/charts/consul/templates/connect-inject-clusterrole.yaml @@ -31,6 +31,7 @@ rules: - peeringacceptors - peeringdialers {{- end }} + - jwtproviders verbs: - create - delete @@ -57,6 +58,7 @@ rules: - peeringacceptors/status - peeringdialers/status {{- end }} + - jwtproviders/status verbs: - get - patch diff --git a/charts/consul/templates/connect-inject-mutatingwebhookconfiguration.yaml b/charts/consul/templates/connect-inject-mutatingwebhookconfiguration.yaml index b68efdb9f8..5f26807e0a 100644 --- a/charts/consul/templates/connect-inject-mutatingwebhookconfiguration.yaml +++ b/charts/consul/templates/connect-inject-mutatingwebhookconfiguration.yaml @@ -313,4 +313,25 @@ webhooks: - samenessgroups sideEffects: None {{- end }} +- admissionReviewVersions: + - v1beta1 + - v1 + clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-jwtprovider + failurePolicy: Fail + name: mutate-jwtprovider.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - jwtproviders + sideEffects: None {{- end }} diff --git a/charts/consul/templates/crd-jwtproviders.yaml b/charts/consul/templates/crd-jwtproviders.yaml new file mode 100644 index 0000000000..c7d20883e8 --- /dev/null +++ b/charts/consul/templates/crd-jwtproviders.yaml @@ -0,0 +1,259 @@ +{{- if .Values.connectInject.enabled }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: jwtproviders.consul.hashicorp.com + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd +spec: + group: consul.hashicorp.com + names: + kind: JWTProvider + listKind: JWTProviderList + plural: jwtproviders + singular: jwtprovider + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: JWTProvider is the Schema for the jwtproviders API. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: JWTProviderSpec defines the desired state of JWTProvider + properties: + audiences: + description: Audiences is the set of audiences the JWT is allowed + to access. If specified, all JWTs verified with this provider must + address at least one of these to be considered valid. + items: + type: string + type: array + cacheConfig: + description: CacheConfig defines configuration for caching the validation + result for previously seen JWTs. Caching results can speed up verification + when individual tokens are expected to be handled multiple times. + properties: + size: + description: "Size specifies the maximum number of JWT verification + results to cache. \n Defaults to 0, meaning that JWT caching + is disabled." + type: integer + type: object + clockSkewSeconds: + description: "ClockSkewSeconds specifies the maximum allowable time + difference from clock skew when validating the \"exp\" (Expiration) + and \"nbf\" (Not Before) claims. \n Default value is 30 seconds." + type: integer + forwarding: + description: Forwarding defines rules for forwarding verified JWTs + to the backend. + properties: + headerName: + description: "HeaderName is a header name to use when forwarding + a verified JWT to the backend. The verified JWT could have been + extracted from any location (query param, header, or cookie). + \n The header value will be base64-URL-encoded, and will not + be padded unless PadForwardPayloadHeader is true." + type: string + padForwardPayloadHeader: + description: "PadForwardPayloadHeader determines whether padding + should be added to the base64 encoded token forwarded with ForwardPayloadHeader. + \n Default value is false." + type: boolean + type: object + issuer: + description: Issuer is the entity that must have issued the JWT. This + value must match the "iss" claim of the token. + type: string + jsonWebKeySet: + description: JSONWebKeySet defines a JSON Web Key Set, its location + on disk, or the means with which to fetch a key set from a remote + server. + properties: + local: + description: Local specifies a local source for the key set. + properties: + filename: + description: Filename configures a location on disk where + the JWKS can be found. If specified, the file must be present + on the disk of ALL proxies with intentions referencing this + provider. + type: string + jwks: + description: JWKS contains a base64 encoded JWKS. + type: string + type: object + remote: + description: Remote specifies how to fetch a key set from a remote + server. + properties: + cacheDuration: + description: "CacheDuration is the duration after which cached + keys should be expired. \n Default value is 5 minutes." + format: int64 + type: integer + fetchAsynchronously: + description: "FetchAsynchronously indicates that the JWKS + should be fetched when a client request arrives. Client + requests will be paused until the JWKS is fetched. If false, + the proxy listener will wait for the JWKS to be fetched + before being activated. \n Default value is false." + type: boolean + requestTimeoutMs: + description: RequestTimeoutMs is the number of milliseconds + to time out when making a request for the JWKS. + type: integer + retryPolicy: + description: "RetryPolicy defines a retry policy for fetching + JWKS. \n There is no retry by default." + properties: + numRetries: + description: "NumRetries is the number of times to retry + fetching the JWKS. The retry strategy uses jittered + exponential backoff with a base interval of 1s and max + of 10s. \n Default value is 0." + type: integer + retryPolicyBackOff: + description: "Backoff policy \n Defaults to Envoy's backoff + policy" + properties: + baseInterval: + description: "BaseInterval to be used for the next + back off computation \n The default value from envoy + is 1s" + format: int64 + type: integer + maxInterval: + description: "MaxInternal to be used to specify the + maximum interval between retries. Optional but should + be greater or equal to BaseInterval. \n Defaults + to 10 times BaseInterval" + format: int64 + type: integer + type: object + type: object + uri: + description: URI is the URI of the server to query for the + JWKS. + type: string + type: object + type: object + locations: + description: 'Locations where the JWT will be present in requests. + Envoy will check all of these locations to extract a JWT. If no + locations are specified Envoy will default to: 1. Authorization + header with Bearer schema: "Authorization: Bearer " 2. accessToken + query parameter.' + items: + description: "JWTLocation is a location where the JWT could be present + in requests. \n Only one of Header, QueryParam, or Cookie can + be specified." + properties: + cookie: + description: Cookie defines how to extract a JWT from an HTTP + request cookie. + properties: + name: + description: Name is the name of the cookie containing the + token. + type: string + type: object + header: + description: Header defines how to extract a JWT from an HTTP + request header. + properties: + forward: + description: "Forward defines whether the header with the + JWT should be forwarded after the token has been verified. + If false, the header will not be forwarded to the backend. + \n Default value is false." + type: boolean + name: + description: Name is the name of the header containing the + token. + type: string + valuePrefix: + description: 'ValuePrefix is an optional prefix that precedes + the token in the header value. For example, "Bearer " + is a standard value prefix for a header named "Authorization", + but the prefix is not part of the token itself: "Authorization: + Bearer "' + type: string + type: object + queryParam: + description: QueryParam defines how to extract a JWT from an + HTTP request query parameter. + properties: + name: + description: Name is the name of the query param containing + the token. + type: string + type: object + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/control-plane/PROJECT b/control-plane/PROJECT index eb653ad34a..beec0e6504 100644 --- a/control-plane/PROJECT +++ b/control-plane/PROJECT @@ -90,4 +90,13 @@ resources: kind: SamenessGroup path: github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1beta1 + namespaced: true + controller: true + domain: hashicorp.com + group: consul + kind: JWTProvider + path: github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1 + version: v1alpha1 version: "3" diff --git a/control-plane/api/common/common.go b/control-plane/api/common/common.go index 4faeccada1..779ece9218 100644 --- a/control-plane/api/common/common.go +++ b/control-plane/api/common/common.go @@ -15,6 +15,7 @@ const ( IngressGateway string = "ingressgateway" TerminatingGateway string = "terminatinggateway" SamenessGroup string = "samenessgroup" + JWTProvider string = "jwtprovider" Global string = "global" Mesh string = "mesh" diff --git a/control-plane/api/v1alpha1/jwtprovider_types.go b/control-plane/api/v1alpha1/jwtprovider_types.go new file mode 100644 index 0000000000..fee0ef9a78 --- /dev/null +++ b/control-plane/api/v1alpha1/jwtprovider_types.go @@ -0,0 +1,663 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package v1alpha1 + +import ( + "encoding/base64" + "encoding/json" + "net/url" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/hashicorp/consul-k8s/control-plane/api/common" + "github.com/hashicorp/consul/api" + capi "github.com/hashicorp/consul/api" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" +) + +const ( + JWTProviderKubeKind string = "jwtprovider" +) + +func init() { + SchemeBuilder.Register(&JWTProvider{}, &JWTProviderList{}) +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// JWTProvider is the Schema for the jwtproviders API. +type JWTProvider struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec JWTProviderSpec `json:"spec,omitempty"` + Status `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// JWTProviderList contains a list of JWTProvider. +type JWTProviderList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []JWTProvider `json:"items"` +} + +// JWTProviderSpec defines the desired state of JWTProvider +// +kubebuilder:printcolumn:name="Synced",type="string",JSONPath=".status.conditions[?(@.type==\"Synced\")].status",description="The sync status of the resource with Consul" +// +kubebuilder:printcolumn:name="Last Synced",type="date",JSONPath=".status.lastSyncedTime",description="The last successful synced time of the resource with Consul" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="The age of the resource" +type JWTProviderSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // JSONWebKeySet defines a JSON Web Key Set, its location on disk, or the + // means with which to fetch a key set from a remote server. + JSONWebKeySet *JSONWebKeySet `json:"jsonWebKeySet,omitempty"` + + // Issuer is the entity that must have issued the JWT. + // This value must match the "iss" claim of the token. + Issuer string `json:"issuer,omitempty"` + + // Audiences is the set of audiences the JWT is allowed to access. + // If specified, all JWTs verified with this provider must address + // at least one of these to be considered valid. + Audiences []string `json:"audiences,omitempty"` + + // Locations where the JWT will be present in requests. + // Envoy will check all of these locations to extract a JWT. + // If no locations are specified Envoy will default to: + // 1. Authorization header with Bearer schema: + // "Authorization: Bearer " + // 2. accessToken query parameter. + Locations []*JWTLocation `json:"locations,omitempty"` + + // Forwarding defines rules for forwarding verified JWTs to the backend. + Forwarding *JWTForwardingConfig `json:"forwarding,omitempty"` + + // ClockSkewSeconds specifies the maximum allowable time difference + // from clock skew when validating the "exp" (Expiration) and "nbf" + // (Not Before) claims. + // + // Default value is 30 seconds. + ClockSkewSeconds int `json:"clockSkewSeconds,omitempty"` + + // CacheConfig defines configuration for caching the validation + // result for previously seen JWTs. Caching results can speed up + // verification when individual tokens are expected to be handled + // multiple times. + CacheConfig *JWTCacheConfig `json:"cacheConfig,omitempty"` +} + +type JWTLocations []*JWTLocation + +func (j JWTLocations) toConsul() []*capi.JWTLocation { + if j == nil { + return nil + } + result := make([]*capi.JWTLocation, 0, len(j)) + for _, loc := range j { + result = append(result, loc.toConsul()) + } + return result +} + +func (j JWTLocations) validate(path *field.Path) field.ErrorList { + var errs field.ErrorList + for i, loc := range j { + errs = append(errs, loc.validate(path.Index(i))...) + } + return errs +} + +// JWTLocation is a location where the JWT could be present in requests. +// +// Only one of Header, QueryParam, or Cookie can be specified. +type JWTLocation struct { + // Header defines how to extract a JWT from an HTTP request header. + Header *JWTLocationHeader `json:"header,omitempty"` + + // QueryParam defines how to extract a JWT from an HTTP request + // query parameter. + QueryParam *JWTLocationQueryParam `json:"queryParam,omitempty"` + + // Cookie defines how to extract a JWT from an HTTP request cookie. + Cookie *JWTLocationCookie `json:"cookie,omitempty"` +} + +func (j *JWTLocation) toConsul() *capi.JWTLocation { + if j == nil { + return nil + } + return &capi.JWTLocation{ + Header: j.Header.toConsul(), + QueryParam: j.QueryParam.toConsul(), + Cookie: j.Cookie.toConsul(), + } +} + +func (j *JWTLocation) validate(path *field.Path) field.ErrorList { + var errs field.ErrorList + if j == nil { + return append(errs, field.Invalid(path, j, "location must not be nil")) + } + + if 1 != countTrue( + j.Header != nil, + j.QueryParam != nil, + j.Cookie != nil, + ) { + asJSON, _ := json.Marshal(j) + return append(errs, field.Invalid(path, string(asJSON), "exactly one of 'header', 'queryParam', or 'cookie' is required")) + } + + errs = append(errs, j.Header.validate(path.Child("header"))...) + errs = append(errs, j.QueryParam.validate(path.Child("queryParam"))...) + errs = append(errs, j.Cookie.validate(path.Child("cookie"))...) + return errs +} + +// JWTLocationHeader defines how to extract a JWT from an HTTP +// request header. +type JWTLocationHeader struct { + // Name is the name of the header containing the token. + Name string `json:"name,omitempty"` + + // ValuePrefix is an optional prefix that precedes the token in the + // header value. + // For example, "Bearer " is a standard value prefix for a header named + // "Authorization", but the prefix is not part of the token itself: + // "Authorization: Bearer " + ValuePrefix string `json:"valuePrefix,omitempty"` + + // Forward defines whether the header with the JWT should be + // forwarded after the token has been verified. If false, the + // header will not be forwarded to the backend. + // + // Default value is false. + Forward bool `json:"forward,omitempty"` +} + +func (j *JWTLocationHeader) toConsul() *capi.JWTLocationHeader { + if j == nil { + return nil + } + return &capi.JWTLocationHeader{ + Name: j.Name, + ValuePrefix: j.ValuePrefix, + Forward: j.Forward, + } +} + +func (j *JWTLocationHeader) validate(path *field.Path) field.ErrorList { + var errs field.ErrorList + if j == nil { + return errs + } + + if j.Name == "" { + errs = append(errs, field.Invalid(path.Child("name"), j.Name, "JWT location header name is required")) + } + return errs +} + +// JWTLocationQueryParam defines how to extract a JWT from an HTTP request query parameter. +type JWTLocationQueryParam struct { + // Name is the name of the query param containing the token. + Name string `json:"name,omitempty"` +} + +func (j *JWTLocationQueryParam) toConsul() *capi.JWTLocationQueryParam { + if j == nil { + return nil + } + return &capi.JWTLocationQueryParam{ + Name: j.Name, + } +} + +func (j *JWTLocationQueryParam) validate(path *field.Path) field.ErrorList { + var errs field.ErrorList + if j == nil { + return nil + } + if j.Name == "" { + errs = append(errs, field.Invalid(path.Child("name"), j.Name, "JWT location query parameter name is required")) + } + return errs +} + +// JWTLocationCookie defines how to extract a JWT from an HTTP request cookie. +type JWTLocationCookie struct { + // Name is the name of the cookie containing the token. + Name string `json:"name,omitempty"` +} + +func (j *JWTLocationCookie) toConsul() *capi.JWTLocationCookie { + if j == nil { + return nil + } + return &capi.JWTLocationCookie{ + Name: j.Name, + } +} + +func (j *JWTLocationCookie) validate(path *field.Path) field.ErrorList { + var errs field.ErrorList + if j == nil { + return nil + } + if j.Name == "" { + errs = append(errs, field.Invalid(path.Child("name"), j.Name, "JWT location cookie name is required")) + } + return errs +} + +type JWTForwardingConfig struct { + // HeaderName is a header name to use when forwarding a verified + // JWT to the backend. The verified JWT could have been extracted + // from any location (query param, header, or cookie). + // + // The header value will be base64-URL-encoded, and will not be + // padded unless PadForwardPayloadHeader is true. + HeaderName string `json:"headerName,omitempty"` + + // PadForwardPayloadHeader determines whether padding should be added + // to the base64 encoded token forwarded with ForwardPayloadHeader. + // + // Default value is false. + PadForwardPayloadHeader bool `json:"padForwardPayloadHeader,omitempty"` +} + +func (j *JWTForwardingConfig) toConsul() *capi.JWTForwardingConfig { + if j == nil { + return nil + } + return &capi.JWTForwardingConfig{ + HeaderName: j.HeaderName, + PadForwardPayloadHeader: j.PadForwardPayloadHeader, + } +} + +func (j *JWTForwardingConfig) validate(path *field.Path) field.ErrorList { + var errs field.ErrorList + if j == nil { + return nil + } + + if j.HeaderName == "" { + errs = append(errs, field.Invalid(path.Child("HeaderName"), j.HeaderName, "JWT forwarding header name is required")) + } + return errs +} + +// JSONWebKeySet defines a key set, its location on disk, or the +// means with which to fetch a key set from a remote server. +// +// Exactly one of Local or Remote must be specified. +type JSONWebKeySet struct { + // Local specifies a local source for the key set. + Local *LocalJWKS `json:"local,omitempty"` + + // Remote specifies how to fetch a key set from a remote server. + Remote *RemoteJWKS `json:"remote,omitempty"` +} + +func (j *JSONWebKeySet) toConsul() *capi.JSONWebKeySet { + if j == nil { + return nil + } + + return &capi.JSONWebKeySet{ + Local: j.Local.toConsul(), + Remote: j.Remote.toConsul(), + } +} + +func (j *JSONWebKeySet) validate(path *field.Path) field.ErrorList { + var errs field.ErrorList + if j == nil { + return append(errs, field.Invalid(path, j, "jsonWebKeySet is required")) + } + + if countTrue(j.Local != nil, j.Remote != nil) != 1 { + asJSON, _ := json.Marshal(j) + return append(errs, field.Invalid(path, string(asJSON), "exactly one of 'local' or 'remote' is required")) + } + errs = append(errs, j.Local.validate(path.Child("local"))...) + errs = append(errs, j.Remote.validate(path.Child("remote"))...) + return errs +} + +// LocalJWKS specifies a location for a local JWKS. +// +// Only one of String and Filename can be specified. +type LocalJWKS struct { + // JWKS contains a base64 encoded JWKS. + JWKS string `json:"jwks,omitempty"` + + // Filename configures a location on disk where the JWKS can be + // found. If specified, the file must be present on the disk of ALL + // proxies with intentions referencing this provider. + Filename string `json:"filename,omitempty"` +} + +func (l *LocalJWKS) toConsul() *capi.LocalJWKS { + if l == nil { + return nil + } + return &capi.LocalJWKS{ + JWKS: l.JWKS, + Filename: l.Filename, + } +} + +func (l *LocalJWKS) validate(path *field.Path) field.ErrorList { + var errs field.ErrorList + if l == nil { + return errs + } + + if countTrue(l.JWKS != "", l.Filename != "") != 1 { + asJSON, _ := json.Marshal(l) + return append(errs, field.Invalid(path, string(asJSON), "Exactly one of 'jwks' or 'filename' is required")) + } + if l.JWKS != "" { + if _, err := base64.StdEncoding.DecodeString(l.JWKS); err != nil { + return append(errs, field.Invalid(path.Child("jwks"), l.JWKS, "JWKS must be a valid base64-encoded string")) + } + } + return errs +} + +// RemoteJWKS specifies how to fetch a JWKS from a remote server. +type RemoteJWKS struct { + // URI is the URI of the server to query for the JWKS. + URI string `json:"uri,omitempty"` + + // RequestTimeoutMs is the number of milliseconds to + // time out when making a request for the JWKS. + RequestTimeoutMs int `json:"requestTimeoutMs,omitempty"` + + // CacheDuration is the duration after which cached keys + // should be expired. + // + // Default value is 5 minutes. + CacheDuration time.Duration `json:"cacheDuration,omitempty"` + + // FetchAsynchronously indicates that the JWKS should be fetched + // when a client request arrives. Client requests will be paused + // until the JWKS is fetched. + // If false, the proxy listener will wait for the JWKS to be + // fetched before being activated. + // + // Default value is false. + FetchAsynchronously bool `json:"fetchAsynchronously,omitempty"` + + // RetryPolicy defines a retry policy for fetching JWKS. + // + // There is no retry by default. + RetryPolicy *JWKSRetryPolicy `json:"retryPolicy,omitempty"` +} + +func (r *RemoteJWKS) toConsul() *capi.RemoteJWKS { + if r == nil { + return nil + } + return &capi.RemoteJWKS{ + URI: r.URI, + RequestTimeoutMs: r.RequestTimeoutMs, + CacheDuration: r.CacheDuration, + FetchAsynchronously: r.FetchAsynchronously, + RetryPolicy: r.RetryPolicy.toConsul(), + } +} + +func (r *RemoteJWKS) validate(path *field.Path) field.ErrorList { + var errs field.ErrorList + if r == nil { + return errs + } + + if r.URI == "" { + errs = append(errs, field.Invalid(path.Child("uri"), r.URI, "remote JWKS URI is required")) + } else if _, err := url.ParseRequestURI(r.URI); err != nil { + errs = append(errs, field.Invalid(path.Child("uri"), r.URI, "remote JWKS URI is invalid")) + } + + errs = append(errs, r.RetryPolicy.validate(path.Child("retryPolicy"))...) + return errs +} + +type JWKSRetryPolicy struct { + // NumRetries is the number of times to retry fetching the JWKS. + // The retry strategy uses jittered exponential backoff with + // a base interval of 1s and max of 10s. + // + // Default value is 0. + NumRetries int `json:"numRetries,omitempty"` + + // Backoff policy + // + // Defaults to Envoy's backoff policy + RetryPolicyBackOff *RetryPolicyBackOff `json:"retryPolicyBackOff,omitempty"` +} + +func (j *JWKSRetryPolicy) toConsul() *capi.JWKSRetryPolicy { + if j == nil { + return nil + } + return &capi.JWKSRetryPolicy{ + NumRetries: j.NumRetries, + RetryPolicyBackOff: j.RetryPolicyBackOff.toConsul(), + } +} + +func (j *JWKSRetryPolicy) validate(path *field.Path) field.ErrorList { + var errs field.ErrorList + if j == nil { + return errs + } + + return append(errs, j.RetryPolicyBackOff.validate(path.Child("retryPolicyBackOff"))...) +} + +type RetryPolicyBackOff struct { + // BaseInterval to be used for the next back off computation + // + // The default value from envoy is 1s + BaseInterval time.Duration `json:"baseInterval,omitempty"` + + // MaxInternal to be used to specify the maximum interval between retries. + // Optional but should be greater or equal to BaseInterval. + // + // Defaults to 10 times BaseInterval + MaxInterval time.Duration `json:"maxInterval,omitempty"` +} + +func (r *RetryPolicyBackOff) toConsul() *capi.RetryPolicyBackOff { + if r == nil { + return nil + } + return &capi.RetryPolicyBackOff{ + BaseInterval: r.BaseInterval, + MaxInterval: r.MaxInterval, + } +} + +func (r *RetryPolicyBackOff) validate(path *field.Path) field.ErrorList { + var errs field.ErrorList + if r == nil { + return errs + } + + if (r.MaxInterval != 0) && (r.BaseInterval > r.MaxInterval) { + asJSON, _ := json.Marshal(r) + errs = append(errs, field.Invalid(path, string(asJSON), "maxInterval should be greater or equal to baseInterval")) + } + return errs +} + +type JWTCacheConfig struct { + // Size specifies the maximum number of JWT verification + // results to cache. + // + // Defaults to 0, meaning that JWT caching is disabled. + Size int `json:"size,omitempty"` +} + +func (j *JWTCacheConfig) toConsul() *capi.JWTCacheConfig { + if j == nil { + return nil + } + return &capi.JWTCacheConfig{ + Size: j.Size, + } +} + +func (j *JWTProvider) GetObjectMeta() metav1.ObjectMeta { + return j.ObjectMeta +} + +func (j *JWTProvider) AddFinalizer(name string) { + j.ObjectMeta.Finalizers = append(j.Finalizers(), name) +} + +func (j *JWTProvider) RemoveFinalizer(name string) { + var newFinalizers []string + for _, oldF := range j.Finalizers() { + if oldF != name { + newFinalizers = append(newFinalizers, oldF) + } + } + j.ObjectMeta.Finalizers = newFinalizers +} + +func (j *JWTProvider) Finalizers() []string { + return j.ObjectMeta.Finalizers +} + +func (j *JWTProvider) ConsulKind() string { + return capi.JWTProvider +} + +func (j *JWTProvider) ConsulGlobalResource() bool { + return true +} + +func (j *JWTProvider) ConsulMirroringNS() string { + return common.DefaultConsulNamespace +} + +func (j *JWTProvider) KubeKind() string { + return JWTProviderKubeKind +} + +func (j *JWTProvider) ConsulName() string { + return j.ObjectMeta.Name +} + +func (j *JWTProvider) KubernetesName() string { + return j.ObjectMeta.Name +} + +func (j *JWTProvider) SetSyncedCondition(status corev1.ConditionStatus, reason, message string) { + j.Status.Conditions = Conditions{ + { + Type: ConditionSynced, + Status: status, + LastTransitionTime: metav1.Now(), + Reason: reason, + Message: message, + }, + } +} + +func (j *JWTProvider) SetLastSyncedTime(time *metav1.Time) { + j.Status.LastSyncedTime = time +} + +// SyncedCondition gets the synced condition. +func (j *JWTProvider) SyncedCondition() (status corev1.ConditionStatus, reason, message string) { + cond := j.Status.GetCondition(ConditionSynced) + if cond == nil { + return corev1.ConditionUnknown, "", "" + } + return cond.Status, cond.Reason, cond.Message +} + +// SyncedConditionStatus returns the status of the synced condition. +func (j *JWTProvider) SyncedConditionStatus() corev1.ConditionStatus { + cond := j.Status.GetCondition(ConditionSynced) + if cond == nil { + return corev1.ConditionUnknown + } + return cond.Status +} + +// ToConsul converts the resource to the corresponding Consul API definition. +// Its return type is the generic ConfigEntry but a specific config entry +// type should be constructed e.g. ServiceConfigEntry. +func (j *JWTProvider) ToConsul(datacenter string) api.ConfigEntry { + return &capi.JWTProviderConfigEntry{ + Kind: j.ConsulKind(), + Name: j.ConsulName(), + JSONWebKeySet: j.Spec.JSONWebKeySet.toConsul(), + Issuer: j.Spec.Issuer, + Audiences: j.Spec.Audiences, + Locations: JWTLocations(j.Spec.Locations).toConsul(), + Forwarding: j.Spec.Forwarding.toConsul(), + ClockSkewSeconds: j.Spec.ClockSkewSeconds, + CacheConfig: j.Spec.CacheConfig.toConsul(), + Meta: meta(datacenter), + } +} + +// MatchesConsul returns true if the resource has the same fields as the Consul +// config entry. +func (j *JWTProvider) MatchesConsul(candidate api.ConfigEntry) bool { + configEntry, ok := candidate.(*capi.JWTProviderConfigEntry) + if !ok { + return false + } + // No datacenter is passed to ToConsul as we ignore the Meta field when checking for equality. + return cmp.Equal(j.ToConsul(""), configEntry, cmpopts.IgnoreFields(capi.JWTProviderConfigEntry{}, "Partition", "Namespace", "Meta", "ModifyIndex", "CreateIndex"), cmpopts.IgnoreUnexported(), cmpopts.EquateEmpty()) +} + +// Validate returns an error if the resource is invalid. +func (j *JWTProvider) Validate(consulMeta common.ConsulMeta) error { + var errs field.ErrorList + path := field.NewPath("spec") + + errs = append(errs, j.Spec.JSONWebKeySet.validate(path.Child("jsonWebKeySet"))...) + errs = append(errs, JWTLocations(j.Spec.Locations).validate(path.Child("locations"))...) + errs = append(errs, j.Spec.Forwarding.validate(path.Child("forwarding"))...) + if len(errs) > 0 { + return apierrors.NewInvalid( + schema.GroupKind{Group: ConsulHashicorpGroup, Kind: JWTProviderKubeKind}, + j.KubernetesName(), errs) + } + return nil +} + +// DefaultNamespaceFields sets Consul namespace fields on the config entry +// spec to their default values if namespaces are enabled. +func (j *JWTProvider) DefaultNamespaceFields(_ common.ConsulMeta) {} + +func countTrue(vals ...bool) int { + var result int + for _, v := range vals { + if v { + result++ + } + } + return result +} + +var _ common.ConfigEntryResource = (*JWTProvider)(nil) diff --git a/control-plane/api/v1alpha1/jwtprovider_types_test.go b/control-plane/api/v1alpha1/jwtprovider_types_test.go new file mode 100644 index 0000000000..15a3e7a5d6 --- /dev/null +++ b/control-plane/api/v1alpha1/jwtprovider_types_test.go @@ -0,0 +1,730 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package v1alpha1 + +import ( + "testing" + "time" + + "github.com/hashicorp/consul-k8s/control-plane/api/common" + capi "github.com/hashicorp/consul/api" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Test MatchesConsul for cases that should return true. +func TestJWTProvider_MatchesConsul(t *testing.T) { + cases := map[string]struct { + Ours JWTProvider + Theirs capi.ConfigEntry + Matches bool + }{ + "empty fields matches": { + Ours: JWTProvider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-okta", + }, + Spec: JWTProviderSpec{}, + }, + Theirs: &capi.JWTProviderConfigEntry{ + Kind: capi.JWTProvider, + Name: "test-okta", + Namespace: "default", + CreateIndex: 1, + ModifyIndex: 2, + Meta: map[string]string{ + common.SourceKey: common.SourceValue, + common.DatacenterKey: "datacenter", + }, + }, + Matches: true, + }, + "all fields set matches": { + Ours: JWTProvider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-okta2", + }, + Spec: JWTProviderSpec{ + JSONWebKeySet: &JSONWebKeySet{ + Local: &LocalJWKS{ + JWKS: "jwks-string", + Filename: "jwks-file", + }, + Remote: &RemoteJWKS{ + URI: "https://jwks.example.com", + RequestTimeoutMs: 567, + CacheDuration: 890, + FetchAsynchronously: true, + RetryPolicy: &JWKSRetryPolicy{ + NumRetries: 1, + RetryPolicyBackOff: &RetryPolicyBackOff{ + BaseInterval: 23, + MaxInterval: 456, + }, + }, + }, + }, + Issuer: "test-issuer", + Audiences: []string{"aud1", "aud2"}, + Locations: []*JWTLocation{ + { + Header: &JWTLocationHeader{ + Name: "jwt-header", + ValuePrefix: "my-bearer", + Forward: true, + }, + }, + { + QueryParam: &JWTLocationQueryParam{ + Name: "jwt-query-param", + }, + }, + { + Cookie: &JWTLocationCookie{ + Name: "jwt-cookie", + }, + }, + }, + Forwarding: &JWTForwardingConfig{ + HeaderName: "jwt-forward-header", + PadForwardPayloadHeader: true, + }, + ClockSkewSeconds: 357, + CacheConfig: &JWTCacheConfig{ + Size: 468, + }, + }, + }, + Theirs: &capi.JWTProviderConfigEntry{ + Kind: capi.JWTProvider, + Name: "test-okta2", + Namespace: "default", + JSONWebKeySet: &capi.JSONWebKeySet{ + Local: &capi.LocalJWKS{ + JWKS: "jwks-string", + Filename: "jwks-file", + }, + Remote: &capi.RemoteJWKS{ + URI: "https://jwks.example.com", + RequestTimeoutMs: 567, + CacheDuration: 890, + FetchAsynchronously: true, + RetryPolicy: &capi.JWKSRetryPolicy{ + NumRetries: 1, + RetryPolicyBackOff: &capi.RetryPolicyBackOff{ + BaseInterval: 23, + MaxInterval: 456, + }, + }, + }, + }, + Issuer: "test-issuer", + Audiences: []string{"aud1", "aud2"}, + Locations: []*capi.JWTLocation{ + { + Header: &capi.JWTLocationHeader{ + Name: "jwt-header", + ValuePrefix: "my-bearer", + Forward: true, + }, + }, + { + QueryParam: &capi.JWTLocationQueryParam{ + Name: "jwt-query-param", + }, + }, + { + Cookie: &capi.JWTLocationCookie{ + Name: "jwt-cookie", + }, + }, + }, + Forwarding: &capi.JWTForwardingConfig{ + HeaderName: "jwt-forward-header", + PadForwardPayloadHeader: true, + }, + ClockSkewSeconds: 357, + CacheConfig: &capi.JWTCacheConfig{ + Size: 468, + }, + }, + Matches: true, + }, + "mismatched types does not match": { + Ours: JWTProvider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-okta3", + }, + Spec: JWTProviderSpec{}, + }, + Theirs: &capi.JWTProviderConfigEntry{}, + Matches: false, + }, + } + for name, c := range cases { + c := c + t.Run(name, func(t *testing.T) { + require.Equal(t, c.Matches, c.Ours.MatchesConsul(c.Theirs)) + }) + } +} + +func TestJWTProvider_ToConsul(t *testing.T) { + cases := map[string]struct { + Ours JWTProvider + Exp *capi.JWTProviderConfigEntry + }{ + "empty fields": { + Ours: JWTProvider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-okta1", + }, + Spec: JWTProviderSpec{}, + }, + Exp: &capi.JWTProviderConfigEntry{ + Kind: capi.JWTProvider, + Name: "test-okta1", + Meta: map[string]string{ + common.SourceKey: common.SourceValue, + common.DatacenterKey: "datacenter", + }, + }, + }, + "every field set": { + Ours: JWTProvider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-okta2", + }, + Spec: JWTProviderSpec{ + JSONWebKeySet: &JSONWebKeySet{ + Local: &LocalJWKS{ + JWKS: "jwks-string", + Filename: "jwks-file", + }, + Remote: &RemoteJWKS{ + URI: "https://jwks.example.com", + RequestTimeoutMs: 567, + CacheDuration: 890, + FetchAsynchronously: true, + RetryPolicy: &JWKSRetryPolicy{ + NumRetries: 1, + RetryPolicyBackOff: &RetryPolicyBackOff{ + BaseInterval: 23, + MaxInterval: 456, + }, + }, + }, + }, + Issuer: "test-issuer", + Audiences: []string{"aud1", "aud2"}, + Locations: []*JWTLocation{ + { + Header: &JWTLocationHeader{ + Name: "jwt-header", + ValuePrefix: "my-bearer", + Forward: true, + }, + }, + { + QueryParam: &JWTLocationQueryParam{ + Name: "jwt-query-param", + }, + }, + { + Cookie: &JWTLocationCookie{ + Name: "jwt-cookie", + }, + }, + }, + Forwarding: &JWTForwardingConfig{ + HeaderName: "jwt-forward-header", + PadForwardPayloadHeader: true, + }, + ClockSkewSeconds: 357, + CacheConfig: &JWTCacheConfig{ + Size: 468, + }, + }, + }, + Exp: &capi.JWTProviderConfigEntry{ + Kind: capi.JWTProvider, + Name: "test-okta2", + JSONWebKeySet: &capi.JSONWebKeySet{ + Local: &capi.LocalJWKS{ + JWKS: "jwks-string", + Filename: "jwks-file", + }, + Remote: &capi.RemoteJWKS{ + URI: "https://jwks.example.com", + RequestTimeoutMs: 567, + CacheDuration: 890, + FetchAsynchronously: true, + RetryPolicy: &capi.JWKSRetryPolicy{ + NumRetries: 1, + RetryPolicyBackOff: &capi.RetryPolicyBackOff{ + BaseInterval: 23, + MaxInterval: 456, + }, + }, + }, + }, + Issuer: "test-issuer", + Audiences: []string{"aud1", "aud2"}, + Locations: []*capi.JWTLocation{ + { + Header: &capi.JWTLocationHeader{ + Name: "jwt-header", + ValuePrefix: "my-bearer", + Forward: true, + }, + }, + { + QueryParam: &capi.JWTLocationQueryParam{ + Name: "jwt-query-param", + }, + }, + { + Cookie: &capi.JWTLocationCookie{ + Name: "jwt-cookie", + }, + }, + }, + Forwarding: &capi.JWTForwardingConfig{ + HeaderName: "jwt-forward-header", + PadForwardPayloadHeader: true, + }, + ClockSkewSeconds: 357, + CacheConfig: &capi.JWTCacheConfig{ + Size: 468, + }, + Meta: map[string]string{ + common.SourceKey: common.SourceValue, + common.DatacenterKey: "datacenter", + }, + }, + }, + } + for name, c := range cases { + t.Run(name, func(t *testing.T) { + act := c.Ours.ToConsul("datacenter") + mesh, ok := act.(*capi.JWTProviderConfigEntry) + require.True(t, ok, "could not cast") + require.Equal(t, c.Exp, mesh) + }) + } +} + +func TestJWTProvider_Validate(t *testing.T) { + cases := map[string]struct { + input *JWTProvider + expectedErrMsgs []string + consulMeta common.ConsulMeta + }{ + "valid - local jwks": { + input: &JWTProvider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-okta1", + }, + Spec: JWTProviderSpec{ + JSONWebKeySet: &JSONWebKeySet{ + Local: &LocalJWKS{ + Filename: "jwks.txt", + }, + }, + }, + Status: Status{}, + }, + expectedErrMsgs: nil, + }, + + "valid - remote jwks": { + input: &JWTProvider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-jwt-provider", + }, + Spec: JWTProviderSpec{ + JSONWebKeySet: &JSONWebKeySet{ + Remote: &RemoteJWKS{ + URI: "https://jwks.example.com", + FetchAsynchronously: true, + }, + }, + Locations: []*JWTLocation{ + { + Header: &JWTLocationHeader{ + Name: "Authorization", + }, + }, + }, + Forwarding: &JWTForwardingConfig{ + HeaderName: "jwt-forward-header", + }, + }, + }, + expectedErrMsgs: nil, + }, + + "valid - remote jwks with all fields": { + input: &JWTProvider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-jwt-provider", + }, + Spec: JWTProviderSpec{ + JSONWebKeySet: &JSONWebKeySet{ + Remote: &RemoteJWKS{ + URI: "https://jwks.example.com", + RequestTimeoutMs: 5000, + CacheDuration: 10 * time.Second, + FetchAsynchronously: true, + RetryPolicy: &JWKSRetryPolicy{ + NumRetries: 3, + RetryPolicyBackOff: &RetryPolicyBackOff{ + BaseInterval: 5 * time.Second, + MaxInterval: 20 * time.Second, + }, + }, + }, + }, + Issuer: "test-issuer", + Audiences: []string{"aud1", "aud2"}, + Locations: []*JWTLocation{ + { + Header: &JWTLocationHeader{ + Name: "Authorization", + ValuePrefix: "Bearer", + Forward: true, + }, + }, + { + QueryParam: &JWTLocationQueryParam{ + Name: "access-token", + }, + }, + { + Cookie: &JWTLocationCookie{ + Name: "session-id", + }, + }, + }, + Forwarding: &JWTForwardingConfig{ + HeaderName: "jwt-forward-header", + PadForwardPayloadHeader: true, + }, + ClockSkewSeconds: 20, + CacheConfig: &JWTCacheConfig{ + Size: 30, + }, + }, + }, + expectedErrMsgs: nil, + }, + + "invalid - nil jwks": { + input: &JWTProvider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-no-jwks", + }, + Spec: JWTProviderSpec{ + JSONWebKeySet: nil, + }, + }, + expectedErrMsgs: []string{ + `jwtprovider.consul.hashicorp.com "test-no-jwks" is invalid: spec.jsonWebKeySet: Invalid value: "null": jsonWebKeySet is required`, + }, + }, + + "invalid - empty jwks": { + input: &JWTProvider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-no-jwks", + }, + Spec: JWTProviderSpec{ + JSONWebKeySet: &JSONWebKeySet{}, + }, + }, + expectedErrMsgs: []string{ + `jwtprovider.consul.hashicorp.com "test-no-jwks" is invalid: spec.jsonWebKeySet: Invalid value: "{}": exactly one of 'local' or 'remote' is required`, + }, + }, + + "invalid - local jwks with non-base64 string": { + input: &JWTProvider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-jwks-base64", + }, + Spec: JWTProviderSpec{ + JSONWebKeySet: &JSONWebKeySet{ + Local: &LocalJWKS{ + JWKS: "not base64 encoded", + }, + }, + }, + }, + expectedErrMsgs: []string{ + `jwtprovider.consul.hashicorp.com "test-jwks-base64" is invalid: spec.jsonWebKeySet.local.jwks: Invalid value: "not base64 encoded": JWKS must be a valid base64-encoded string`, + }, + }, + + "invalid - both local and remote jwks set": { + input: &JWTProvider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-jwks-local-and-remote", + }, + Spec: JWTProviderSpec{ + JSONWebKeySet: &JSONWebKeySet{ + Local: &LocalJWKS{Filename: "jwks.txt"}, + Remote: &RemoteJWKS{ + URI: "https://jwks.example.com", + }, + }, + }, + }, + expectedErrMsgs: []string{ + `jwtprovider.consul.hashicorp.com "test-jwks-local-and-remote" is invalid: spec.jsonWebKeySet: Invalid value: "{\"local\":{\"filename\":\"jwks.txt\"},\"remote\":{\"uri\":\"https://jwks.example.com\"}}": exactly one of 'local' or 'remote' is required`, + }, + }, + + "invalid - remote jwks missing uri": { + input: &JWTProvider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-jwks-missing-uri", + }, + Spec: JWTProviderSpec{ + JSONWebKeySet: &JSONWebKeySet{ + Remote: &RemoteJWKS{ + FetchAsynchronously: true, + }, + }, + }, + }, + expectedErrMsgs: []string{ + `jwtprovider.consul.hashicorp.com "test-jwks-missing-uri" is invalid: spec.jsonWebKeySet.remote.uri: Invalid value: "": remote JWKS URI is required`, + }, + }, + + "invalid - remote jwks invalid uri": { + input: &JWTProvider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-jwks-invalid-uri", + }, + Spec: JWTProviderSpec{ + JSONWebKeySet: &JSONWebKeySet{ + Remote: &RemoteJWKS{ + URI: "invalid-uri", + }, + }, + }, + }, + expectedErrMsgs: []string{ + `jwtprovider.consul.hashicorp.com "test-jwks-invalid-uri" is invalid: spec.jsonWebKeySet.remote.uri: Invalid value: "invalid-uri": remote JWKS URI is invalid`, + }, + }, + + "invalid - JWT location with all fields": { + input: &JWTProvider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-jwks-all-locations", + }, + Spec: JWTProviderSpec{ + JSONWebKeySet: &JSONWebKeySet{ + Remote: &RemoteJWKS{ + URI: "https://jwks.example.com", + }, + }, + Locations: []*JWTLocation{ + { + Header: &JWTLocationHeader{ + Name: "jwt-header", + }, + QueryParam: &JWTLocationQueryParam{ + Name: "jwt-query-param", + }, + Cookie: &JWTLocationCookie{ + Name: "jwt-cookie", + }, + }, + }, + }, + }, + expectedErrMsgs: []string{ + `jwtprovider.consul.hashicorp.com "test-jwks-all-locations" is invalid: spec.locations[0]: Invalid value: "{\"header\":{\"name\":\"jwt-header\"},\"queryParam\":{\"name\":\"jwt-query-param\"},\"cookie\":{\"name\":\"jwt-cookie\"}}": exactly one of 'header', 'queryParam', or 'cookie' is required`, + }, + }, + + "invalid - JWT location with two fields": { + input: &JWTProvider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-jwks-two-locations", + }, + Spec: JWTProviderSpec{ + JSONWebKeySet: &JSONWebKeySet{ + Remote: &RemoteJWKS{ + URI: "https://jwks.example.com", + }, + }, + Locations: []*JWTLocation{ + { + Header: &JWTLocationHeader{ + Name: "jwt-header", + }, + Cookie: &JWTLocationCookie{ + Name: "jwt-cookie", + }, + }, + }, + }, + }, + expectedErrMsgs: []string{ + `jwtprovider.consul.hashicorp.com "test-jwks-two-locations" is invalid: spec.locations[0]: Invalid value: "{\"header\":{\"name\":\"jwt-header\"},\"cookie\":{\"name\":\"jwt-cookie\"}}": exactly one of 'header', 'queryParam', or 'cookie' is required`, + }, + }, + + "invalid - remote jwks retry policy maxInterval < baseInterval": { + input: &JWTProvider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-jwks-retry-intervals", + }, + Spec: JWTProviderSpec{ + JSONWebKeySet: &JSONWebKeySet{ + Remote: &RemoteJWKS{ + URI: "https://jwks.example.com", + RetryPolicy: &JWKSRetryPolicy{ + NumRetries: 0, + RetryPolicyBackOff: &RetryPolicyBackOff{ + BaseInterval: 100 * time.Second, + MaxInterval: 10 * time.Second, + }, + }, + }, + }, + }, + }, + expectedErrMsgs: []string{ + `jwtprovider.consul.hashicorp.com "test-jwks-retry-intervals" is invalid: spec.jsonWebKeySet.remote.retryPolicy.retryPolicyBackOff: Invalid value: "{\"baseInterval\":100000000000,\"maxInterval\":10000000000}": maxInterval should be greater or equal to baseInterval`, + }, + }, + } + + for name, testCase := range cases { + t.Run(name, func(t *testing.T) { + err := testCase.input.Validate(testCase.consulMeta) + if len(testCase.expectedErrMsgs) != 0 { + require.Error(t, err) + for _, s := range testCase.expectedErrMsgs { + require.Contains(t, err.Error(), s) + } + } else { + require.NoError(t, err) + } + }) + } + +} + +func TestJWTProvider_AddFinalizer(t *testing.T) { + jwt := &JWTProvider{} + jwt.AddFinalizer("finalizer") + require.Equal(t, []string{"finalizer"}, jwt.ObjectMeta.Finalizers) +} + +func TestJWTProvider_RemoveFinalizer(t *testing.T) { + jwt := &JWTProvider{ + ObjectMeta: metav1.ObjectMeta{ + Finalizers: []string{"f1", "f2"}, + }, + } + jwt.RemoveFinalizer("f1") + require.Equal(t, []string{"f2"}, jwt.ObjectMeta.Finalizers) +} + +func TestJWTProvider_SetSyncedCondition(t *testing.T) { + jwt := &JWTProvider{} + jwt.SetSyncedCondition(corev1.ConditionTrue, "reason", "message") + + require.Equal(t, corev1.ConditionTrue, jwt.Status.Conditions[0].Status) + require.Equal(t, "reason", jwt.Status.Conditions[0].Reason) + require.Equal(t, "message", jwt.Status.Conditions[0].Message) + now := metav1.Now() + require.True(t, jwt.Status.Conditions[0].LastTransitionTime.Before(&now)) +} + +func TestJWTProvider_SetLastSyncedTime(t *testing.T) { + jwt := &JWTProvider{} + syncedTime := metav1.NewTime(time.Now()) + jwt.SetLastSyncedTime(&syncedTime) + require.Equal(t, &syncedTime, jwt.Status.LastSyncedTime) +} + +func TestJWTProvider_GetSyncedConditionStatus(t *testing.T) { + cases := []corev1.ConditionStatus{ + corev1.ConditionUnknown, + corev1.ConditionFalse, + corev1.ConditionTrue, + } + for _, status := range cases { + t.Run(string(status), func(t *testing.T) { + jwt := &JWTProvider{ + Status: Status{ + Conditions: []Condition{{ + Type: ConditionSynced, + Status: status, + }}, + }, + } + + require.Equal(t, status, jwt.SyncedConditionStatus()) + }) + } +} + +func TestJWTProvider_GetConditionWhenStatusNil(t *testing.T) { + require.Nil(t, (&JWTProvider{}).GetCondition(ConditionSynced)) +} + +func TestJWTProvider_SyncedConditionStatusWhenStatusNil(t *testing.T) { + require.Equal(t, corev1.ConditionUnknown, (&JWTProvider{}).SyncedConditionStatus()) +} + +func TestJWTProvider_SyncedConditionWhenStatusNil(t *testing.T) { + status, reason, message := (&JWTProvider{}).SyncedCondition() + require.Equal(t, corev1.ConditionUnknown, status) + require.Equal(t, "", reason) + require.Equal(t, "", message) +} + +func TestJWTProvider_ConsulKind(t *testing.T) { + require.Equal(t, capi.JWTProvider, (&JWTProvider{}).ConsulKind()) +} + +func TestJWTProvider_KubeKind(t *testing.T) { + require.Equal(t, "jwtprovider", (&JWTProvider{}).KubeKind()) +} + +func TestJWTProvider_ConsulName(t *testing.T) { + require.Equal(t, "foo", (&JWTProvider{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}).ConsulName()) +} + +func TestJWTProvider_KubernetesName(t *testing.T) { + require.Equal(t, "foo", (&JWTProvider{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}).KubernetesName()) +} + +func TestJWTProvider_ConsulNamespace(t *testing.T) { + require.Equal(t, common.DefaultConsulNamespace, (&JWTProvider{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}}).ConsulMirroringNS()) +} + +func TestJWTProvider_ConsulGlobalResource(t *testing.T) { + require.True(t, (&JWTProvider{}).ConsulGlobalResource()) +} + +func TestJWTProvider_ObjectMeta(t *testing.T) { + meta := metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + } + jwt := &JWTProvider{ + ObjectMeta: meta, + } + require.Equal(t, meta, jwt.GetObjectMeta()) +} diff --git a/control-plane/api/v1alpha1/jwtprovider_webhook.go b/control-plane/api/v1alpha1/jwtprovider_webhook.go new file mode 100644 index 0000000000..c434c83c01 --- /dev/null +++ b/control-plane/api/v1alpha1/jwtprovider_webhook.go @@ -0,0 +1,61 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package v1alpha1 + +import ( + "context" + "net/http" + + "github.com/go-logr/logr" + "github.com/hashicorp/consul-k8s/control-plane/api/common" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// +kubebuilder:object:generate=false + +type JWTProviderWebhook struct { + Logger logr.Logger + + // ConsulMeta contains metadata specific to the Consul installation. + ConsulMeta common.ConsulMeta + + decoder *admission.Decoder + client.Client +} + +// NOTE: The path value in the below line is the path to the webhook. +// If it is updated, run code-gen, update subcommand/controller/command.go +// and the consul-helm value for the path to the webhook. +// +// NOTE: The below line cannot be combined with any other comment. If it is it will break the code generation. +// +// +kubebuilder:webhook:verbs=create;update,path=/mutate-v1alpha1-jwtprovider,mutating=true,failurePolicy=fail,groups=consul.hashicorp.com,resources=jwtproviders,versions=v1alpha1,name=mutate-jwtprovider.consul.hashicorp.com,sideEffects=None,admissionReviewVersions=v1beta1;v1 + +func (v *JWTProviderWebhook) Handle(ctx context.Context, req admission.Request) admission.Response { + var resource JWTProvider + err := v.decoder.Decode(req, &resource) + if err != nil { + return admission.Errored(http.StatusBadRequest, err) + } + + return common.ValidateConfigEntry(ctx, req, v.Logger, v, &resource, v.ConsulMeta) +} + +func (v *JWTProviderWebhook) List(ctx context.Context) ([]common.ConfigEntryResource, error) { + var resourceList JWTProviderList + if err := v.Client.List(ctx, &resourceList); err != nil { + return nil, err + } + var entries []common.ConfigEntryResource + for _, item := range resourceList.Items { + entries = append(entries, common.ConfigEntryResource(&item)) + } + return entries, nil +} + +func (v *JWTProviderWebhook) InjectDecoder(d *admission.Decoder) error { + v.decoder = d + return nil +} diff --git a/control-plane/api/v1alpha1/zz_generated.deepcopy.go b/control-plane/api/v1alpha1/zz_generated.deepcopy.go index f1f5ffb150..e8bff89986 100644 --- a/control-plane/api/v1alpha1/zz_generated.deepcopy.go +++ b/control-plane/api/v1alpha1/zz_generated.deepcopy.go @@ -834,6 +834,261 @@ func (in IntentionPermissions) DeepCopy() IntentionPermissions { return *out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JSONWebKeySet) DeepCopyInto(out *JSONWebKeySet) { + *out = *in + if in.Local != nil { + in, out := &in.Local, &out.Local + *out = new(LocalJWKS) + **out = **in + } + if in.Remote != nil { + in, out := &in.Remote, &out.Remote + *out = new(RemoteJWKS) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JSONWebKeySet. +func (in *JSONWebKeySet) DeepCopy() *JSONWebKeySet { + if in == nil { + return nil + } + out := new(JSONWebKeySet) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JWKSRetryPolicy) DeepCopyInto(out *JWKSRetryPolicy) { + *out = *in + if in.RetryPolicyBackOff != nil { + in, out := &in.RetryPolicyBackOff, &out.RetryPolicyBackOff + *out = new(RetryPolicyBackOff) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWKSRetryPolicy. +func (in *JWKSRetryPolicy) DeepCopy() *JWKSRetryPolicy { + if in == nil { + return nil + } + out := new(JWKSRetryPolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JWTCacheConfig) DeepCopyInto(out *JWTCacheConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWTCacheConfig. +func (in *JWTCacheConfig) DeepCopy() *JWTCacheConfig { + if in == nil { + return nil + } + out := new(JWTCacheConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JWTForwardingConfig) DeepCopyInto(out *JWTForwardingConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWTForwardingConfig. +func (in *JWTForwardingConfig) DeepCopy() *JWTForwardingConfig { + if in == nil { + return nil + } + out := new(JWTForwardingConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JWTLocation) DeepCopyInto(out *JWTLocation) { + *out = *in + if in.Header != nil { + in, out := &in.Header, &out.Header + *out = new(JWTLocationHeader) + **out = **in + } + if in.QueryParam != nil { + in, out := &in.QueryParam, &out.QueryParam + *out = new(JWTLocationQueryParam) + **out = **in + } + if in.Cookie != nil { + in, out := &in.Cookie, &out.Cookie + *out = new(JWTLocationCookie) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWTLocation. +func (in *JWTLocation) DeepCopy() *JWTLocation { + if in == nil { + return nil + } + out := new(JWTLocation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JWTLocationCookie) DeepCopyInto(out *JWTLocationCookie) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWTLocationCookie. +func (in *JWTLocationCookie) DeepCopy() *JWTLocationCookie { + if in == nil { + return nil + } + out := new(JWTLocationCookie) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JWTLocationHeader) DeepCopyInto(out *JWTLocationHeader) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWTLocationHeader. +func (in *JWTLocationHeader) DeepCopy() *JWTLocationHeader { + if in == nil { + return nil + } + out := new(JWTLocationHeader) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JWTLocationQueryParam) DeepCopyInto(out *JWTLocationQueryParam) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWTLocationQueryParam. +func (in *JWTLocationQueryParam) DeepCopy() *JWTLocationQueryParam { + if in == nil { + return nil + } + out := new(JWTLocationQueryParam) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JWTProvider) DeepCopyInto(out *JWTProvider) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWTProvider. +func (in *JWTProvider) DeepCopy() *JWTProvider { + if in == nil { + return nil + } + out := new(JWTProvider) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *JWTProvider) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JWTProviderList) DeepCopyInto(out *JWTProviderList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]JWTProvider, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWTProviderList. +func (in *JWTProviderList) DeepCopy() *JWTProviderList { + if in == nil { + return nil + } + out := new(JWTProviderList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *JWTProviderList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JWTProviderSpec) DeepCopyInto(out *JWTProviderSpec) { + *out = *in + if in.JSONWebKeySet != nil { + in, out := &in.JSONWebKeySet, &out.JSONWebKeySet + *out = new(JSONWebKeySet) + (*in).DeepCopyInto(*out) + } + if in.Audiences != nil { + in, out := &in.Audiences, &out.Audiences + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Locations != nil { + in, out := &in.Locations, &out.Locations + *out = make([]*JWTLocation, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(JWTLocation) + (*in).DeepCopyInto(*out) + } + } + } + if in.Forwarding != nil { + in, out := &in.Forwarding, &out.Forwarding + *out = new(JWTForwardingConfig) + **out = **in + } + if in.CacheConfig != nil { + in, out := &in.CacheConfig, &out.CacheConfig + *out = new(JWTCacheConfig) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWTProviderSpec. +func (in *JWTProviderSpec) DeepCopy() *JWTProviderSpec { + if in == nil { + return nil + } + out := new(JWTProviderSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LeastRequestConfig) DeepCopyInto(out *LeastRequestConfig) { *out = *in @@ -896,6 +1151,21 @@ func (in *LoadBalancer) DeepCopy() *LoadBalancer { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LocalJWKS) DeepCopyInto(out *LocalJWKS) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalJWKS. +func (in *LocalJWKS) DeepCopy() *LocalJWKS { + if in == nil { + return nil + } + out := new(LocalJWKS) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Mesh) DeepCopyInto(out *Mesh) { *out = *in @@ -1543,6 +1813,41 @@ func (in *ProxyDefaultsSpec) DeepCopy() *ProxyDefaultsSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RemoteJWKS) DeepCopyInto(out *RemoteJWKS) { + *out = *in + if in.RetryPolicy != nil { + in, out := &in.RetryPolicy, &out.RetryPolicy + *out = new(JWKSRetryPolicy) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemoteJWKS. +func (in *RemoteJWKS) DeepCopy() *RemoteJWKS { + if in == nil { + return nil + } + out := new(RemoteJWKS) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RetryPolicyBackOff) DeepCopyInto(out *RetryPolicyBackOff) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RetryPolicyBackOff. +func (in *RetryPolicyBackOff) DeepCopy() *RetryPolicyBackOff { + if in == nil { + return nil + } + out := new(RetryPolicyBackOff) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RingHashConfig) DeepCopyInto(out *RingHashConfig) { *out = *in diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml new file mode 100644 index 0000000000..8ca1ec0748 --- /dev/null +++ b/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml @@ -0,0 +1,254 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: jwtproviders.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: JWTProvider + listKind: JWTProviderList + plural: jwtproviders + singular: jwtprovider + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: JWTProvider is the Schema for the jwtproviders API. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: JWTProviderSpec defines the desired state of JWTProvider + properties: + audiences: + description: Audiences is the set of audiences the JWT is allowed + to access. If specified, all JWTs verified with this provider must + address at least one of these to be considered valid. + items: + type: string + type: array + cacheConfig: + description: CacheConfig defines configuration for caching the validation + result for previously seen JWTs. Caching results can speed up verification + when individual tokens are expected to be handled multiple times. + properties: + size: + description: "Size specifies the maximum number of JWT verification + results to cache. \n Defaults to 0, meaning that JWT caching + is disabled." + type: integer + type: object + clockSkewSeconds: + description: "ClockSkewSeconds specifies the maximum allowable time + difference from clock skew when validating the \"exp\" (Expiration) + and \"nbf\" (Not Before) claims. \n Default value is 30 seconds." + type: integer + forwarding: + description: Forwarding defines rules for forwarding verified JWTs + to the backend. + properties: + headerName: + description: "HeaderName is a header name to use when forwarding + a verified JWT to the backend. The verified JWT could have been + extracted from any location (query param, header, or cookie). + \n The header value will be base64-URL-encoded, and will not + be padded unless PadForwardPayloadHeader is true." + type: string + padForwardPayloadHeader: + description: "PadForwardPayloadHeader determines whether padding + should be added to the base64 encoded token forwarded with ForwardPayloadHeader. + \n Default value is false." + type: boolean + type: object + issuer: + description: Issuer is the entity that must have issued the JWT. This + value must match the "iss" claim of the token. + type: string + jsonWebKeySet: + description: JSONWebKeySet defines a JSON Web Key Set, its location + on disk, or the means with which to fetch a key set from a remote + server. + properties: + local: + description: Local specifies a local source for the key set. + properties: + filename: + description: Filename configures a location on disk where + the JWKS can be found. If specified, the file must be present + on the disk of ALL proxies with intentions referencing this + provider. + type: string + jwks: + description: JWKS contains a base64 encoded JWKS. + type: string + type: object + remote: + description: Remote specifies how to fetch a key set from a remote + server. + properties: + cacheDuration: + description: "CacheDuration is the duration after which cached + keys should be expired. \n Default value is 5 minutes." + format: int64 + type: integer + fetchAsynchronously: + description: "FetchAsynchronously indicates that the JWKS + should be fetched when a client request arrives. Client + requests will be paused until the JWKS is fetched. If false, + the proxy listener will wait for the JWKS to be fetched + before being activated. \n Default value is false." + type: boolean + requestTimeoutMs: + description: RequestTimeoutMs is the number of milliseconds + to time out when making a request for the JWKS. + type: integer + retryPolicy: + description: "RetryPolicy defines a retry policy for fetching + JWKS. \n There is no retry by default." + properties: + numRetries: + description: "NumRetries is the number of times to retry + fetching the JWKS. The retry strategy uses jittered + exponential backoff with a base interval of 1s and max + of 10s. \n Default value is 0." + type: integer + retryPolicyBackOff: + description: "Backoff policy \n Defaults to Envoy's backoff + policy" + properties: + baseInterval: + description: "BaseInterval to be used for the next + back off computation \n The default value from envoy + is 1s" + format: int64 + type: integer + maxInterval: + description: "MaxInternal to be used to specify the + maximum interval between retries. Optional but should + be greater or equal to BaseInterval. \n Defaults + to 10 times BaseInterval" + format: int64 + type: integer + type: object + type: object + uri: + description: URI is the URI of the server to query for the + JWKS. + type: string + type: object + type: object + locations: + description: 'Locations where the JWT will be present in requests. + Envoy will check all of these locations to extract a JWT. If no + locations are specified Envoy will default to: 1. Authorization + header with Bearer schema: "Authorization: Bearer " 2. accessToken + query parameter.' + items: + description: "JWTLocation is a location where the JWT could be present + in requests. \n Only one of Header, QueryParam, or Cookie can + be specified." + properties: + cookie: + description: Cookie defines how to extract a JWT from an HTTP + request cookie. + properties: + name: + description: Name is the name of the cookie containing the + token. + type: string + type: object + header: + description: Header defines how to extract a JWT from an HTTP + request header. + properties: + forward: + description: "Forward defines whether the header with the + JWT should be forwarded after the token has been verified. + If false, the header will not be forwarded to the backend. + \n Default value is false." + type: boolean + name: + description: Name is the name of the header containing the + token. + type: string + valuePrefix: + description: 'ValuePrefix is an optional prefix that precedes + the token in the header value. For example, "Bearer " + is a standard value prefix for a header named "Authorization", + but the prefix is not part of the token itself: "Authorization: + Bearer "' + type: string + type: object + queryParam: + description: QueryParam defines how to extract a JWT from an + HTTP request query parameter. + properties: + name: + description: Name is the name of the query param containing + the token. + type: string + type: object + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/control-plane/config/rbac/role.yaml b/control-plane/config/rbac/role.yaml index bd2ce10a25..28ddd913b5 100644 --- a/control-plane/config/rbac/role.yaml +++ b/control-plane/config/rbac/role.yaml @@ -66,6 +66,26 @@ rules: - get - patch - update +- apiGroups: + - consul.hashicorp.com + resources: + - jwtproviders + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - consul.hashicorp.com + resources: + - jwtproviders/status + verbs: + - get + - patch + - update - apiGroups: - consul.hashicorp.com resources: diff --git a/control-plane/config/webhook/manifests.yaml b/control-plane/config/webhook/manifests.yaml index 305e3bbe67..3a65e10d1b 100644 --- a/control-plane/config/webhook/manifests.yaml +++ b/control-plane/config/webhook/manifests.yaml @@ -50,6 +50,27 @@ webhooks: resources: - ingressgateways sideEffects: None +- admissionReviewVersions: + - v1beta1 + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-v1alpha1-jwtprovider + failurePolicy: Fail + name: mutate-jwtprovider.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - jwtproviders + sideEffects: None - admissionReviewVersions: - v1beta1 - v1 diff --git a/control-plane/controllers/configentry_controller_test.go b/control-plane/controllers/configentry_controller_test.go index 10af0f7716..38a02732d0 100644 --- a/control-plane/controllers/configentry_controller_test.go +++ b/control-plane/controllers/configentry_controller_test.go @@ -432,6 +432,50 @@ func TestConfigEntryControllers_createsConfigEntry(t *testing.T) { require.Equal(t, "sni", resource.Services[0].SNI) }, }, + { + kubeKind: "JWTProvider", + consulKind: capi.JWTProvider, + configEntryResource: &v1alpha1.JWTProvider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-jwt-provider", + Namespace: kubeNS, + }, + Spec: v1alpha1.JWTProviderSpec{ + JSONWebKeySet: &v1alpha1.JSONWebKeySet{ + Local: &v1alpha1.LocalJWKS{ + Filename: "jwks.txt", + }, + }, + Issuer: "test-issuer", + }, + }, + reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) testReconciler { + return &JWTProviderController{ + Client: client, + Log: logger, + ConfigEntryController: &ConfigEntryController{ + ConsulClientConfig: cfg, + ConsulServerConnMgr: watcher, + DatacenterName: datacenterName, + }, + } + }, + compare: func(t *testing.T, consulEntry capi.ConfigEntry) { + jwt, ok := consulEntry.(*capi.JWTProviderConfigEntry) + require.True(t, ok, "cast error") + require.Equal(t, capi.JWTProvider, jwt.Kind) + require.Equal(t, "test-jwt-provider", jwt.Name) + require.Equal(t, + &capi.JSONWebKeySet{ + Local: &capi.LocalJWKS{ + Filename: "jwks.txt", + }, + }, + jwt.JSONWebKeySet, + ) + require.Equal(t, "test-issuer", jwt.Issuer) + }, + }, } for _, c := range cases { @@ -909,6 +953,57 @@ func TestConfigEntryControllers_updatesConfigEntry(t *testing.T) { require.Equal(t, "new-sni", resource.Services[0].SNI) }, }, + + { + kubeKind: "JWTProvider", + consulKind: capi.JWTProvider, + configEntryResource: &v1alpha1.JWTProvider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-jwt-provider", + Namespace: kubeNS, + }, + Spec: v1alpha1.JWTProviderSpec{ + JSONWebKeySet: &v1alpha1.JSONWebKeySet{ + Local: &v1alpha1.LocalJWKS{ + Filename: "jwks.txt", + }, + }, + Issuer: "test-issuer", + }, + }, + reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) testReconciler { + return &JWTProviderController{ + Client: client, + Log: logger, + ConfigEntryController: &ConfigEntryController{ + ConsulClientConfig: cfg, + ConsulServerConnMgr: watcher, + DatacenterName: datacenterName, + }, + } + }, + updateF: func(resource common.ConfigEntryResource) { + jwt := resource.(*v1alpha1.JWTProvider) + jwt.Spec.Issuer = "test-updated-issuer" + jwt.Spec.Audiences = []string{"aud1"} + }, + compare: func(t *testing.T, consulEntry capi.ConfigEntry) { + jwt, ok := consulEntry.(*capi.JWTProviderConfigEntry) + require.True(t, ok, "cast error") + require.Equal(t, capi.JWTProvider, jwt.Kind) + require.Equal(t, "test-jwt-provider", jwt.Name) + require.Equal(t, + &capi.JSONWebKeySet{ + Local: &capi.LocalJWKS{ + Filename: "jwks.txt", + }, + }, + jwt.JSONWebKeySet, + ) + require.Equal(t, "test-updated-issuer", jwt.Issuer) + require.Equal(t, []string{"aud1"}, jwt.Audiences) + }, + }, } for _, c := range cases { @@ -1310,6 +1405,37 @@ func TestConfigEntryControllers_deletesConfigEntry(t *testing.T) { } }, }, + { + kubeKind: "JWTProvider", + consulKind: capi.JWTProvider, + configEntryResourceWithDeletion: &v1alpha1.JWTProvider{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.Global, + Namespace: kubeNS, + DeletionTimestamp: &metav1.Time{Time: time.Now()}, + Finalizers: []string{FinalizerName}, + }, + Spec: v1alpha1.JWTProviderSpec{ + JSONWebKeySet: &v1alpha1.JSONWebKeySet{ + Local: &v1alpha1.LocalJWKS{ + Filename: "jwks.txt", + }, + }, + Issuer: "test-issuer", + }, + }, + reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) testReconciler { + return &JWTProviderController{ + Client: client, + Log: logger, + ConfigEntryController: &ConfigEntryController{ + ConsulClientConfig: cfg, + ConsulServerConnMgr: watcher, + DatacenterName: datacenterName, + }, + } + }, + }, } for _, c := range cases { diff --git a/control-plane/controllers/jwtprovider_controller.go b/control-plane/controllers/jwtprovider_controller.go new file mode 100644 index 0000000000..157f4bc855 --- /dev/null +++ b/control-plane/controllers/jwtprovider_controller.go @@ -0,0 +1,43 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package controllers + +import ( + "context" + + "github.com/go-logr/logr" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + consulv1alpha1 "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" +) + +// JWTProviderController reconciles a JWTProvider object. +type JWTProviderController struct { + client.Client + Log logr.Logger + Scheme *runtime.Scheme + ConfigEntryController *ConfigEntryController +} + +//+kubebuilder:rbac:groups=consul.hashicorp.com,resources=jwtproviders,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=consul.hashicorp.com,resources=jwtproviders/status,verbs=get;update;patch + +func (r *JWTProviderController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + return r.ConfigEntryController.ReconcileEntry(ctx, r, req, &consulv1alpha1.JWTProvider{}) +} + +func (r *JWTProviderController) Logger(name types.NamespacedName) logr.Logger { + return r.Log.WithValues("request", name) +} + +func (r *JWTProviderController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { + return r.Status().Update(ctx, obj, opts...) +} + +func (r *JWTProviderController) SetupWithManager(mgr ctrl.Manager) error { + return setupWithManager(mgr, &consulv1alpha1.JWTProvider{}, r) +} diff --git a/control-plane/go.mod b/control-plane/go.mod index 9a65b3dd11..96f1d91fed 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -10,7 +10,7 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83383d github.com/hashicorp/consul-server-connection-manager v0.1.2 - github.com/hashicorp/consul/api v1.10.1-0.20230512003852-bd0eb07ed3ca + github.com/hashicorp/consul/api v1.10.1-0.20230530193107-04a0d0133ae4 github.com/hashicorp/consul/sdk v0.13.1 github.com/hashicorp/go-bexpr v0.1.11 github.com/hashicorp/go-discover v0.0.0-20230519164032-214571b6a530 diff --git a/control-plane/go.sum b/control-plane/go.sum index 1876d81578..3248aba347 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -262,8 +262,8 @@ github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83 github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83383d/go.mod h1:IHIHMzkoMwlv6rLsgwcoFBVYupR7/1pKEOHBMjD4L0k= github.com/hashicorp/consul-server-connection-manager v0.1.2 h1:tNVQHUPuMbd+cMdD8kd+qkZUYpmLmrHMAV/49f4L53I= github.com/hashicorp/consul-server-connection-manager v0.1.2/go.mod h1:NzQoVi1KcxGI2SangsDue8+ZPuXZWs+6BKAKrDNyg+w= -github.com/hashicorp/consul/api v1.10.1-0.20230512003852-bd0eb07ed3ca h1:5UPVYOlJg/HBEJ2q82rkkQ3ZLzeMnF5MOpGcw2kh+XU= -github.com/hashicorp/consul/api v1.10.1-0.20230512003852-bd0eb07ed3ca/go.mod h1:tXfrC6o0yFTgAW46xd5Ic8STHc9oIBcRVBcwhX5KNCQ= +github.com/hashicorp/consul/api v1.10.1-0.20230530193107-04a0d0133ae4 h1:6kUTk+YBgA5X5b3gNAoI18WEK4/t75LcWSimEgmpFdg= +github.com/hashicorp/consul/api v1.10.1-0.20230530193107-04a0d0133ae4/go.mod h1:tXfrC6o0yFTgAW46xd5Ic8STHc9oIBcRVBcwhX5KNCQ= github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3usQMXZF6ziK3SzXU= github.com/hashicorp/consul/proto-public v0.1.0/go.mod h1:vs2KkuWwtjkIgA5ezp4YKPzQp4GitV+q/+PvksrA92k= github.com/hashicorp/consul/sdk v0.13.1 h1:EygWVWWMczTzXGpO93awkHFzfUka6hLYJ0qhETd+6lY= diff --git a/control-plane/subcommand/inject-connect/command.go b/control-plane/subcommand/inject-connect/command.go index 671a15f7cd..676681baea 100644 --- a/control-plane/subcommand/inject-connect/command.go +++ b/control-plane/subcommand/inject-connect/command.go @@ -634,6 +634,15 @@ func (c *Command) Run(args []string) int { setupLog.Error(err, "unable to create controller", "controller", apicommon.SamenessGroup) return 1 } + if err = (&controllers.JWTProviderController{ + ConfigEntryController: configEntryReconciler, + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controller").WithName(apicommon.JWTProvider), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", apicommon.JWTProvider) + return 1 + } if err = mgr.AddReadyzCheck("ready", webhook.ReadinessCheck{CertDir: c.flagCertDir}.Ready); err != nil { setupLog.Error(err, "unable to create readiness check", "controller", endpoints.Controller{}) @@ -799,6 +808,12 @@ func (c *Command) Run(args []string) int { Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.SamenessGroup), ConsulMeta: consulMeta, }}) + mgr.GetWebhookServer().Register("/mutate-v1alpha1-jwtprovider", + &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.JWTProviderWebhook{ + Client: mgr.GetClient(), + Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.JWTProvider), + ConsulMeta: consulMeta, + }}) if c.flagEnableWebhookCAUpdate { err = c.updateWebhookCABundle(ctx) From 5f3f26d8d633739426247c71ac8fe51eb6a7fe97 Mon Sep 17 00:00:00 2001 From: Andrew Stucki Date: Wed, 31 May 2023 11:07:49 -0400 Subject: [PATCH 180/340] API Gateway tenancy tests + fixes (#2201) * Initial scaffolding * Fix up some infinite reconciliation issues and initial other bugs * overhaul * get basic e2e working again * Add resource ref validation * Fix up namespace/reference grants * fix binding * clean up logging * cleanup * Get some binder unit tests working again * log guard * Fix unit test * Fix up more binder tests * get more binder tests working * finish binder tests * fix setter test * light touches and un-bak passing tests * Remove controller test as the wiring of deployments is predominantly tests via acceptance tests * Update reference grant tests * fix linter issues * fix acceptance test linters * Fix validation tests * Fix up consul cache tests * fixing up a few more tests * Finish up translation test work * Fix last bit of tests --- .../api-gateway/api_gateway_tenancy_test.go | 427 ++ .../tests/api-gateway/api_gateway_test.go | 8 +- .../bases/api-gateway/apigateway.yaml | 17 +- .../bases/api-gateway/certificate.yaml | 11 + .../api-gateways/certificate/certificate.yaml | 11 + .../certificate/kustomization.yaml | 5 + .../cases/api-gateways/gateway/gateway.yaml | 20 + .../api-gateways/gateway/kustomization.yaml | 5 + .../api-gateways/httproute/kustomization.yaml | 5 + .../cases/api-gateways/httproute/route.yaml | 8 + .../templates/connect-inject-clusterrole.yaml | 1 + .../templates/gateway-gatewayclass.yaml | 2 +- .../api-gateway/binding/annotations.go | 10 +- .../api-gateway/binding/annotations_test.go | 15 +- control-plane/api-gateway/binding/binder.go | 290 +- .../api-gateway/binding/binder_test.go | 4130 ++++++++--------- .../reference_grant.go} | 94 +- .../reference_grant_test.go} | 113 +- .../api-gateway/binding/references.go | 135 - control-plane/api-gateway/binding/result.go | 52 +- .../api-gateway/binding/route_binding.go | 651 ++- control-plane/api-gateway/binding/setter.go | 112 +- .../api-gateway/binding/setter_test.go | 8 +- control-plane/api-gateway/binding/snapshot.go | 22 +- control-plane/api-gateway/binding/utils.go | 105 - .../api-gateway/binding/validation.go | 139 +- .../api-gateway/binding/validation_test.go | 342 +- control-plane/api-gateway/cache/consul.go | 570 +-- .../api-gateway/cache/consul_test.go | 265 +- control-plane/api-gateway/cache/gateway.go | 136 + control-plane/api-gateway/cache/kubernetes.go | 32 + .../api-gateway/cache/subscription.go | 30 + control-plane/api-gateway/common/constants.go | 8 + control-plane/api-gateway/common/diff.go | 263 ++ .../api-gateway/common/finalizers.go | 60 + .../api-gateway/{ => common}/helm_config.go | 2 +- control-plane/api-gateway/common/helpers.go | 218 + .../utils_test.go => common/helpers_test.go} | 106 +- .../api-gateway/{ => common}/labels.go | 2 +- control-plane/api-gateway/common/reference.go | 184 + control-plane/api-gateway/common/resources.go | 574 +++ control-plane/api-gateway/common/secrets.go | 68 + .../api-gateway/common/translation.go | 363 ++ .../translation_test.go} | 948 ++-- .../controllers/gateway_controller.go | 930 ++-- .../controllers/gateway_controller_test.go | 464 -- .../api-gateway/controllers/index.go | 27 +- .../api-gateway/gatekeeper/dataplane.go | 6 +- .../api-gateway/gatekeeper/deployment.go | 14 +- .../api-gateway/gatekeeper/gatekeeper.go | 6 +- .../api-gateway/gatekeeper/gatekeeper_test.go | 128 +- control-plane/api-gateway/gatekeeper/init.go | 4 +- control-plane/api-gateway/gatekeeper/role.go | 6 +- .../api-gateway/gatekeeper/service.go | 13 +- .../api-gateway/gatekeeper/serviceaccount.go | 6 +- .../translation/config_entry_translation.go | 516 -- .../translation/k8s_cache_translation.go | 134 - .../translation/k8s_cache_translation_test.go | 460 -- .../connect-inject/constants/constants.go | 3 + .../subcommand/inject-connect/command.go | 17 +- 60 files changed, 6448 insertions(+), 6853 deletions(-) create mode 100644 acceptance/tests/api-gateway/api_gateway_tenancy_test.go create mode 100644 acceptance/tests/fixtures/bases/api-gateway/certificate.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/certificate/certificate.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/certificate/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/gateway/gateway.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/gateway/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/httproute/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/httproute/route.yaml rename control-plane/api-gateway/{controllers/reference_validator.go => binding/reference_grant.go} (58%) rename control-plane/api-gateway/{controllers/reference_validator_test.go => binding/reference_grant_test.go} (79%) delete mode 100644 control-plane/api-gateway/binding/references.go delete mode 100644 control-plane/api-gateway/binding/utils.go create mode 100644 control-plane/api-gateway/cache/gateway.go create mode 100644 control-plane/api-gateway/cache/kubernetes.go create mode 100644 control-plane/api-gateway/cache/subscription.go create mode 100644 control-plane/api-gateway/common/constants.go create mode 100644 control-plane/api-gateway/common/diff.go create mode 100644 control-plane/api-gateway/common/finalizers.go rename control-plane/api-gateway/{ => common}/helm_config.go (98%) create mode 100644 control-plane/api-gateway/common/helpers.go rename control-plane/api-gateway/{binding/utils_test.go => common/helpers_test.go} (56%) rename control-plane/api-gateway/{ => common}/labels.go (97%) create mode 100644 control-plane/api-gateway/common/reference.go create mode 100644 control-plane/api-gateway/common/resources.go create mode 100644 control-plane/api-gateway/common/secrets.go create mode 100644 control-plane/api-gateway/common/translation.go rename control-plane/api-gateway/{translation/config_entry_translation_test.go => common/translation_test.go} (51%) delete mode 100644 control-plane/api-gateway/controllers/gateway_controller_test.go delete mode 100644 control-plane/api-gateway/translation/config_entry_translation.go delete mode 100644 control-plane/api-gateway/translation/k8s_cache_translation.go delete mode 100644 control-plane/api-gateway/translation/k8s_cache_translation_test.go diff --git a/acceptance/tests/api-gateway/api_gateway_tenancy_test.go b/acceptance/tests/api-gateway/api_gateway_tenancy_test.go new file mode 100644 index 0000000000..3455b69f58 --- /dev/null +++ b/acceptance/tests/api-gateway/api_gateway_tenancy_test.go @@ -0,0 +1,427 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package apigateway + +import ( + "context" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/base64" + "encoding/pem" + "fmt" + "math/big" + "path" + "strconv" + "testing" + "time" + + terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" + "github.com/hashicorp/consul-k8s/acceptance/framework/config" + "github.com/hashicorp/consul-k8s/acceptance/framework/consul" + "github.com/hashicorp/consul-k8s/acceptance/framework/environment" + "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" + "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" + "github.com/hashicorp/consul-k8s/acceptance/framework/logger" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/sdk/testutil/retry" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +var ( + gatewayGroup = gwv1beta1.Group(gwv1beta1.GroupVersion.Group) + consulGroup = gwv1beta1.Group(v1alpha1.GroupVersion.Group) + gatewayKind = gwv1beta1.Kind("Gateway") + serviceKind = gwv1beta1.Kind("Service") + secretKind = gwv1beta1.Kind("Secret") + meshServiceKind = gwv1beta1.Kind("MeshService") + httpRouteKind = gwv1beta1.Kind("HTTPRoute") + tcpRouteKind = gwv1beta1.Kind("TCPRoute") +) + +func TestAPIGateway_Tenancy(t *testing.T) { + cases := []struct { + secure bool + namespaceMirroring bool + }{ + { + secure: false, + namespaceMirroring: false, + }, + { + secure: true, + namespaceMirroring: false, + }, + { + secure: false, + namespaceMirroring: true, + }, + { + secure: true, + namespaceMirroring: true, + }, + } + for _, c := range cases { + name := fmt.Sprintf("secure: %t, namespaces: %t", c.secure, c.namespaceMirroring) + t.Run(name, func(t *testing.T) { + cfg := suite.Config() + + if !cfg.EnableEnterprise && c.namespaceMirroring { + t.Skipf("skipping this test because -enable-enterprise is not set") + } + + ctx := suite.Environment().DefaultContext(t) + + helmValues := map[string]string{ + "global.enableConsulNamespaces": strconv.FormatBool(c.namespaceMirroring), + "global.acls.manageSystemACLs": strconv.FormatBool(c.secure), + "global.tls.enabled": strconv.FormatBool(c.secure), + "global.logLevel": "trace", + "connectInject.enabled": "true", + "connectInject.consulNamespaces.mirroringK8S": strconv.FormatBool(c.namespaceMirroring), + } + + releaseName := helpers.RandomName() + consulCluster := consul.NewHelmCluster(t, helmValues, ctx, cfg, releaseName) + + consulCluster.Create(t) + + serviceNamespace, serviceK8SOptions := createNamespace(t, ctx, cfg) + certificateNamespace, certificateK8SOptions := createNamespace(t, ctx, cfg) + gatewayNamespace, gatewayK8SOptions := createNamespace(t, ctx, cfg) + routeNamespace, routeK8SOptions := createNamespace(t, ctx, cfg) + + logger.Logf(t, "creating target server in %s namespace", serviceNamespace) + k8s.DeployKustomize(t, serviceK8SOptions, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + + logger.Logf(t, "creating certificate resources in %s namespace", certificateNamespace) + applyFixture(t, cfg, certificateK8SOptions, "cases/api-gateways/certificate") + + logger.Logf(t, "creating gateway in %s namespace", gatewayNamespace) + applyFixture(t, cfg, gatewayK8SOptions, "cases/api-gateways/gateway") + + logger.Logf(t, "creating route resources in %s namespace", routeNamespace) + applyFixture(t, cfg, routeK8SOptions, "cases/api-gateways/httproute") + + // patch certificate with data + logger.Log(t, "patching certificate with generated data") + certificate := generateCertificate(t, nil, "gateway.test.local") + k8s.RunKubectl(t, certificateK8SOptions, "patch", "secret", "certificate", "-p", fmt.Sprintf(`{"data":{"tls.crt":"%s","tls.key":"%s"}}`, base64.StdEncoding.EncodeToString(certificate.CertPEM), base64.StdEncoding.EncodeToString(certificate.PrivateKeyPEM)), "--type=merge") + + // patch the resources to reference each other + logger.Log(t, "patching gateway to certificate") + k8s.RunKubectl(t, gatewayK8SOptions, "patch", "gateway", "gateway", "-p", fmt.Sprintf(`{"spec":{"listeners":[{"protocol":"HTTPS","port":8082,"name":"https","tls":{"certificateRefs":[{"name":"certificate","namespace":"%s"}]},"allowedRoutes":{"namespaces":{"from":"All"}}}]}}`, certificateNamespace), "--type=merge") + + logger.Log(t, "patching route to target server") + k8s.RunKubectl(t, routeK8SOptions, "patch", "httproute", "route", "-p", fmt.Sprintf(`{"spec":{"rules":[{"backendRefs":[{"name":"static-server","namespace":"%s","port":80}]}]}}`, serviceNamespace), "--type=merge") + + logger.Log(t, "patching route to gateway") + k8s.RunKubectl(t, routeK8SOptions, "patch", "httproute", "route", "-p", fmt.Sprintf(`{"spec":{"parentRefs":[{"name":"gateway","namespace":"%s"}]}}`, gatewayNamespace), "--type=merge") + + // Grab a kubernetes and consul client so that we can verify binding + // behavior prior to issuing requests through the gateway. + k8sClient := ctx.ControllerRuntimeClient(t) + consulClient, _ := consulCluster.SetupConsulClient(t, c.secure) + + retryCheck(t, 60, func(r *retry.R) { + var gateway gwv1beta1.Gateway + err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway", Namespace: gatewayNamespace}, &gateway) + require.NoError(r, err) + + // check our statuses + checkStatusCondition(r, gateway.Status.Conditions, trueCondition("Accepted", "Accepted")) + checkStatusCondition(r, gateway.Status.Conditions, falseCondition("Programmed", "Pending")) + // we expect a sync error here since dropping the listener means the gateway is now invalid + checkStatusCondition(r, gateway.Status.Conditions, falseCondition("Synced", "SyncError")) + + require.Len(r, gateway.Status.Listeners, 1) + require.EqualValues(r, 0, gateway.Status.Listeners[0].AttachedRoutes) + checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, trueCondition("Accepted", "Accepted")) + checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, falseCondition("Conflicted", "NoConflicts")) + checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, falseCondition("ResolvedRefs", "RefNotPermitted")) + }) + + retryCheck(t, 10, func(r *retry.R) { + // since the sync operation should fail above, check that we don't have the entry in Consul. + _, _, err := consulClient.ConfigEntries().Get(api.APIGateway, "gateway", &api.QueryOptions{ + Namespace: namespaceForConsul(c.namespaceMirroring, gatewayNamespace), + }) + require.Error(r, err) + require.EqualError(r, err, `Unexpected response code: 404 (Config entry not found for "api-gateway" / "gateway")`) + }) + + // route failure + retryCheck(t, 10, func(r *retry.R) { + var httproute gwv1beta1.HTTPRoute + err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "route", Namespace: routeNamespace}, &httproute) + require.NoError(r, err) + + require.Len(r, httproute.Status.Parents, 1) + require.EqualValues(r, gatewayClassControllerName, httproute.Status.Parents[0].ControllerName) + require.EqualValues(r, "gateway", httproute.Status.Parents[0].ParentRef.Name) + require.NotNil(r, httproute.Status.Parents[0].ParentRef.Namespace) + require.EqualValues(r, gatewayNamespace, *httproute.Status.Parents[0].ParentRef.Namespace) + checkStatusCondition(r, httproute.Status.Parents[0].Conditions, falseCondition("Accepted", "RefNotPermitted")) + checkStatusCondition(r, httproute.Status.Parents[0].Conditions, falseCondition("ResolvedRefs", "RefNotPermitted")) + // the route itself actually gets synced to Consul + checkStatusCondition(r, httproute.Status.Parents[0].Conditions, trueCondition("Synced", "Synced")) + }) + + retryCheck(t, 10, func(r *retry.R) { + // since we're not bound, check to make sure that the route doesn't target the gateway in Consul. + entry, _, err := consulClient.ConfigEntries().Get(api.HTTPRoute, "route", &api.QueryOptions{ + Namespace: namespaceForConsul(c.namespaceMirroring, routeNamespace), + }) + require.NoError(r, err) + route := entry.(*api.HTTPRouteConfigEntry) + + require.EqualValues(r, "route", route.Meta["k8s-name"]) + require.EqualValues(r, routeNamespace, route.Meta["k8s-namespace"]) + require.Len(r, route.Parents, 0) + }) + + retryCheck(t, 10, func(r *retry.R) { + // we only sync validly referenced certificates over, so check to make sure it is not created. + _, _, err := consulClient.ConfigEntries().Get(api.InlineCertificate, "certificate", &api.QueryOptions{ + Namespace: namespaceForConsul(c.namespaceMirroring, certificateNamespace), + }) + require.Error(r, err) + require.EqualError(r, err, `Unexpected response code: 404 (Config entry not found for "inline-certificate" / "certificate")`) + }) + + // now create reference grants + createReferenceGrant(t, k8sClient, "gateway-certificate", gatewayNamespace, certificateNamespace) + createReferenceGrant(t, k8sClient, "route-gateway", routeNamespace, gatewayNamespace) + createReferenceGrant(t, k8sClient, "route-service", routeNamespace, serviceNamespace) + + // gateway updated with references allowed + retryCheck(t, 10, func(r *retry.R) { + var gateway gwv1beta1.Gateway + err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway", Namespace: gatewayNamespace}, &gateway) + require.NoError(r, err) + + // check our statuses + checkStatusCondition(r, gateway.Status.Conditions, trueCondition("Accepted", "Accepted")) + checkStatusCondition(r, gateway.Status.Conditions, trueCondition("Programmed", "Programmed")) + checkStatusCondition(r, gateway.Status.Conditions, trueCondition("Synced", "Synced")) + require.Len(r, gateway.Status.Listeners, 1) + require.EqualValues(r, 1, gateway.Status.Listeners[0].AttachedRoutes) + checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, trueCondition("Accepted", "Accepted")) + checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, falseCondition("Conflicted", "NoConflicts")) + checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) + }) + + // check the Consul gateway is updated, with the listener. + retryCheck(t, 10, func(r *retry.R) { + entry, _, err := consulClient.ConfigEntries().Get(api.APIGateway, "gateway", &api.QueryOptions{ + Namespace: namespaceForConsul(c.namespaceMirroring, gatewayNamespace), + }) + require.NoError(r, err) + gateway := entry.(*api.APIGatewayConfigEntry) + + require.EqualValues(r, "gateway", gateway.Meta["k8s-name"]) + require.EqualValues(r, gatewayNamespace, gateway.Meta["k8s-namespace"]) + require.Len(r, gateway.Listeners, 1) + checkConsulStatusCondition(t, gateway.Status.Conditions, trueConsulCondition("Accepted", "Accepted")) + checkConsulStatusCondition(t, gateway.Status.Conditions, trueConsulCondition("ResolvedRefs", "ResolvedRefs")) + }) + + // route updated with gateway and services allowed + retryCheck(t, 10, func(r *retry.R) { + var httproute gwv1beta1.HTTPRoute + err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "route", Namespace: routeNamespace}, &httproute) + require.NoError(r, err) + + require.Len(r, httproute.Status.Parents, 1) + require.EqualValues(r, gatewayClassControllerName, httproute.Status.Parents[0].ControllerName) + require.EqualValues(r, "gateway", httproute.Status.Parents[0].ParentRef.Name) + require.NotNil(r, httproute.Status.Parents[0].ParentRef.Namespace) + require.EqualValues(r, gatewayNamespace, *httproute.Status.Parents[0].ParentRef.Namespace) + checkStatusCondition(r, httproute.Status.Parents[0].Conditions, trueCondition("Accepted", "Accepted")) + checkStatusCondition(r, httproute.Status.Parents[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) + }) + + // now check to make sure that the route is updated and valid + retryCheck(t, 10, func(r *retry.R) { + // since we're not bound, check to make sure that the route doesn't target the gateway in Consul. + entry, _, err := consulClient.ConfigEntries().Get(api.HTTPRoute, "route", &api.QueryOptions{ + Namespace: namespaceForConsul(c.namespaceMirroring, routeNamespace), + }) + require.NoError(r, err) + route := entry.(*api.HTTPRouteConfigEntry) + + require.EqualValues(r, "route", route.Meta["k8s-name"]) + require.EqualValues(r, routeNamespace, route.Meta["k8s-namespace"]) + require.Len(r, route.Parents, 1) + }) + + // and check to make sure that the certificate exists + retryCheck(t, 10, func(r *retry.R) { + entry, _, err := consulClient.ConfigEntries().Get(api.InlineCertificate, "certificate", &api.QueryOptions{ + Namespace: namespaceForConsul(c.namespaceMirroring, certificateNamespace), + }) + require.NoError(r, err) + certificate := entry.(*api.InlineCertificateConfigEntry) + + require.EqualValues(r, "certificate", certificate.Meta["k8s-name"]) + require.EqualValues(r, certificateNamespace, certificate.Meta["k8s-namespace"]) + }) + }) + } +} + +func applyFixture(t *testing.T, cfg *config.TestConfig, k8sOptions *terratestk8s.KubectlOptions, fixture string) { + t.Helper() + + out, err := k8s.RunKubectlAndGetOutputE(t, k8sOptions, "apply", "-k", path.Join("../fixtures", fixture)) + require.NoError(t, err, out) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.RunKubectlAndGetOutputE(t, k8sOptions, "delete", "-k", path.Join("../fixtures", fixture)) + }) +} + +func createNamespace(t *testing.T, ctx environment.TestContext, cfg *config.TestConfig) (string, *terratestk8s.KubectlOptions) { + t.Helper() + + namespace := helpers.RandomName() + + logger.Logf(t, "creating Kubernetes namespace %s", namespace) + k8s.RunKubectl(t, ctx.KubectlOptions(t), "create", "ns", namespace) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "ns", namespace) + }) + + return namespace, &terratestk8s.KubectlOptions{ + ContextName: ctx.KubectlOptions(t).ContextName, + ConfigPath: ctx.KubectlOptions(t).ConfigPath, + Namespace: namespace, + } +} + +type certificateInfo struct { + Cert *x509.Certificate + PrivateKey *rsa.PrivateKey + CertPEM []byte + PrivateKeyPEM []byte +} + +func generateCertificate(t *testing.T, ca *certificateInfo, commonName string) *certificateInfo { + t.Helper() + + bits := 1024 + privateKey, err := rsa.GenerateKey(rand.Reader, bits) + require.NoError(t, err) + + usage := x509.KeyUsageDigitalSignature + if ca == nil { + usage = x509.KeyUsageCertSign + } + + expiration := time.Now().AddDate(10, 0, 0) + cert := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + Organization: []string{"Testing, INC."}, + Country: []string{"US"}, + Province: []string{""}, + Locality: []string{"San Francisco"}, + StreetAddress: []string{"Fake Street"}, + PostalCode: []string{"11111"}, + CommonName: commonName, + }, + IsCA: ca == nil, + NotBefore: time.Now().Add(-10 * time.Minute), + NotAfter: expiration, + SubjectKeyId: []byte{1, 2, 3, 4, 6}, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: usage, + BasicConstraintsValid: true, + } + caCert := cert + if ca != nil { + caCert = ca.Cert + } + caPrivateKey := privateKey + if ca != nil { + caPrivateKey = ca.PrivateKey + } + data, err := x509.CreateCertificate(rand.Reader, cert, caCert, &privateKey.PublicKey, caPrivateKey) + require.NoError(t, err) + + certBytes := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: data, + }) + + privateKeyBytes := pem.EncodeToMemory(&pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(privateKey), + }) + + return &certificateInfo{ + Cert: cert, + CertPEM: certBytes, + PrivateKey: privateKey, + PrivateKeyPEM: privateKeyBytes, + } +} + +func retryCheck(t *testing.T, count int, fn func(r *retry.R)) { + t.Helper() + + counter := &retry.Counter{Count: count, Wait: 2 * time.Second} + retry.RunWith(counter, t, fn) +} + +func createReferenceGrant(t *testing.T, client client.Client, name, from, to string) { + t.Helper() + + // we just create a reference grant for all combinations in the given namespaces + + require.NoError(t, client.Create(context.Background(), &gwv1beta1.ReferenceGrant{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: to, + }, + Spec: gwv1beta1.ReferenceGrantSpec{ + From: []gwv1beta1.ReferenceGrantFrom{{ + Group: gatewayGroup, + Kind: gatewayKind, + Namespace: gwv1beta1.Namespace(from), + }, { + Group: gatewayGroup, + Kind: httpRouteKind, + Namespace: gwv1beta1.Namespace(from), + }, { + Group: gatewayGroup, + Kind: tcpRouteKind, + Namespace: gwv1beta1.Namespace(from), + }}, + To: []gwv1beta1.ReferenceGrantTo{{ + Group: gatewayGroup, + Kind: gatewayKind, + }, { + Kind: serviceKind, + }, { + Group: consulGroup, + Kind: meshServiceKind, + }, { + Kind: secretKind, + }}, + }, + })) +} + +func namespaceForConsul(namespaceMirroringEnabled bool, namespace string) string { + if namespaceMirroringEnabled { + return namespace + } + return "" +} diff --git a/acceptance/tests/api-gateway/api_gateway_test.go b/acceptance/tests/api-gateway/api_gateway_test.go index 251d966803..006f1456d3 100644 --- a/acceptance/tests/api-gateway/api_gateway_test.go +++ b/acceptance/tests/api-gateway/api_gateway_test.go @@ -31,7 +31,7 @@ const ( ) // Test that api gateway basic functionality works in a default installation and a secure installation. -func TestAPIGateway(t *testing.T) { +func TestAPIGateway_Basic(t *testing.T) { cases := []struct { secure bool }{ @@ -110,7 +110,7 @@ func TestAPIGateway(t *testing.T) { // check our statuses checkStatusCondition(r, gateway.Status.Conditions, trueCondition("Accepted", "Accepted")) - require.Len(r, gateway.Status.Listeners, 2) + require.Len(r, gateway.Status.Listeners, 3) require.EqualValues(r, 1, gateway.Status.Listeners[0].AttachedRoutes) checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, trueCondition("Accepted", "Accepted")) checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, falseCondition("Conflicted", "NoConflicts")) @@ -119,6 +119,10 @@ func TestAPIGateway(t *testing.T) { checkStatusCondition(r, gateway.Status.Listeners[1].Conditions, trueCondition("Accepted", "Accepted")) checkStatusCondition(r, gateway.Status.Listeners[1].Conditions, falseCondition("Conflicted", "NoConflicts")) checkStatusCondition(r, gateway.Status.Listeners[1].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) + require.EqualValues(r, 1, gateway.Status.Listeners[2].AttachedRoutes) + checkStatusCondition(r, gateway.Status.Listeners[2].Conditions, trueCondition("Accepted", "Accepted")) + checkStatusCondition(r, gateway.Status.Listeners[2].Conditions, falseCondition("Conflicted", "NoConflicts")) + checkStatusCondition(r, gateway.Status.Listeners[2].Conditions, falseCondition("ResolvedRefs", "InvalidCertificateRef")) // check that we have an address to use require.Len(r, gateway.Status.Addresses, 1) diff --git a/acceptance/tests/fixtures/bases/api-gateway/apigateway.yaml b/acceptance/tests/fixtures/bases/api-gateway/apigateway.yaml index de7ac7b5de..f0e4eddc03 100644 --- a/acceptance/tests/fixtures/bases/api-gateway/apigateway.yaml +++ b/acceptance/tests/fixtures/bases/api-gateway/apigateway.yaml @@ -11,6 +11,21 @@ spec: - protocol: HTTP port: 8080 name: http + allowedRoutes: + namespaces: + from: "All" - protocol: TCP port: 8081 - name: tcp \ No newline at end of file + name: tcp + allowedRoutes: + namespaces: + from: "All" + - protocol: HTTPS + port: 8082 + name: https + tls: + certificateRefs: + - name: "certificate" + allowedRoutes: + namespaces: + from: "All" diff --git a/acceptance/tests/fixtures/bases/api-gateway/certificate.yaml b/acceptance/tests/fixtures/bases/api-gateway/certificate.yaml new file mode 100644 index 0000000000..d35dc559e2 --- /dev/null +++ b/acceptance/tests/fixtures/bases/api-gateway/certificate.yaml @@ -0,0 +1,11 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: v1 +kind: Secret +metadata: + name: certificate +type: kubernetes.io/tls +data: + tls.crt: "" + tls.key: "" \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/api-gateways/certificate/certificate.yaml b/acceptance/tests/fixtures/cases/api-gateways/certificate/certificate.yaml new file mode 100644 index 0000000000..d35dc559e2 --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/certificate/certificate.yaml @@ -0,0 +1,11 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: v1 +kind: Secret +metadata: + name: certificate +type: kubernetes.io/tls +data: + tls.crt: "" + tls.key: "" \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/api-gateways/certificate/kustomization.yaml b/acceptance/tests/fixtures/cases/api-gateways/certificate/kustomization.yaml new file mode 100644 index 0000000000..42b7526335 --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/certificate/kustomization.yaml @@ -0,0 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - certificate.yaml diff --git a/acceptance/tests/fixtures/cases/api-gateways/gateway/gateway.yaml b/acceptance/tests/fixtures/cases/api-gateways/gateway/gateway.yaml new file mode 100644 index 0000000000..14c39978b7 --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/gateway/gateway.yaml @@ -0,0 +1,20 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: gateway +spec: + gatewayClassName: consul-api-gateway + listeners: + - protocol: HTTPS + port: 8080 + name: https + tls: + certificateRefs: + - name: "certificate" + namespace: "default" + allowedRoutes: + namespaces: + from: "All" \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/api-gateways/gateway/kustomization.yaml b/acceptance/tests/fixtures/cases/api-gateways/gateway/kustomization.yaml new file mode 100644 index 0000000000..8efac31693 --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/gateway/kustomization.yaml @@ -0,0 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - gateway.yaml diff --git a/acceptance/tests/fixtures/cases/api-gateways/httproute/kustomization.yaml b/acceptance/tests/fixtures/cases/api-gateways/httproute/kustomization.yaml new file mode 100644 index 0000000000..7a6713835c --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/httproute/kustomization.yaml @@ -0,0 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - route.yaml diff --git a/acceptance/tests/fixtures/cases/api-gateways/httproute/route.yaml b/acceptance/tests/fixtures/cases/api-gateways/httproute/route.yaml new file mode 100644 index 0000000000..9f7f66b433 --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/httproute/route.yaml @@ -0,0 +1,8 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: route +spec: {} \ No newline at end of file diff --git a/charts/consul/templates/connect-inject-clusterrole.yaml b/charts/consul/templates/connect-inject-clusterrole.yaml index d4b2464dec..e476949997 100644 --- a/charts/consul/templates/connect-inject-clusterrole.yaml +++ b/charts/consul/templates/connect-inject-clusterrole.yaml @@ -71,6 +71,7 @@ rules: - list - watch - delete + - update - apiGroups: [ "rbac.authorization.k8s.io" ] resources: [ "roles" ] verbs: diff --git a/charts/consul/templates/gateway-gatewayclass.yaml b/charts/consul/templates/gateway-gatewayclass.yaml index 612627fbda..a6856620d2 100644 --- a/charts/consul/templates/gateway-gatewayclass.yaml +++ b/charts/consul/templates/gateway-gatewayclass.yaml @@ -10,7 +10,7 @@ metadata: release: {{ .Release.Name }} component: api-gateway-controller spec: - controllerName: consul.hashicorp.com/consul-api-gateway-controller + controllerName: "hashicorp.com/consul-api-gateway-controller" parametersRef: group: consul.hashicorp.com kind: GatewayClassConfig diff --git a/control-plane/api-gateway/binding/annotations.go b/control-plane/api-gateway/binding/annotations.go index 2cb8f41792..2bd4d0db15 100644 --- a/control-plane/api-gateway/binding/annotations.go +++ b/control-plane/api-gateway/binding/annotations.go @@ -8,14 +8,10 @@ import ( gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" ) -const ( - group = "api-gateway.consul.hashicorp.com" - annotationConfigKey = "api-gateway.consul.hashicorp.com/config" -) - func serializeGatewayClassConfig(gw *gwv1beta1.Gateway, gwcc *v1alpha1.GatewayClassConfig) (*v1alpha1.GatewayClassConfig, bool) { if gwcc == nil { return nil, false @@ -25,7 +21,7 @@ func serializeGatewayClassConfig(gw *gwv1beta1.Gateway, gwcc *v1alpha1.GatewayCl gw.Annotations = make(map[string]string) } - if annotatedConfig, ok := gw.Annotations[annotationConfigKey]; ok { + if annotatedConfig, ok := gw.Annotations[common.AnnotationGatewayClassConfig]; ok { var config v1alpha1.GatewayClassConfig if err := json.Unmarshal([]byte(annotatedConfig), &config.Spec); err == nil { // if we can unmarshal the gateway, return it @@ -36,6 +32,6 @@ func serializeGatewayClassConfig(gw *gwv1beta1.Gateway, gwcc *v1alpha1.GatewayCl // otherwise if we failed to unmarshal or there was no annotation, marshal it onto // the gateway marshaled, _ := json.Marshal(gwcc.Spec) - gw.Annotations[annotationConfigKey] = string(marshaled) + gw.Annotations[common.AnnotationGatewayClassConfig] = string(marshaled) return gwcc, true } diff --git a/control-plane/api-gateway/binding/annotations_test.go b/control-plane/api-gateway/binding/annotations_test.go index 1886ba80f5..edb44ccfb4 100644 --- a/control-plane/api-gateway/binding/annotations_test.go +++ b/control-plane/api-gateway/binding/annotations_test.go @@ -14,6 +14,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" ) @@ -45,7 +46,7 @@ func TestSerializeGatewayClassConfig_HappyPath(t *testing.T) { Name: "the config", }, Spec: v1alpha1.GatewayClassConfigSpec{ - ServiceType: pointerTo(corev1.ServiceType("serviceType")), + ServiceType: common.PointerTo(corev1.ServiceType("serviceType")), NodeSelector: map[string]string{ "selector": "of node", }, @@ -83,7 +84,7 @@ func TestSerializeGatewayClassConfig_HappyPath(t *testing.T) { Name: "the config", }, Spec: v1alpha1.GatewayClassConfigSpec{ - ServiceType: pointerTo(corev1.ServiceType("serviceType")), + ServiceType: common.PointerTo(corev1.ServiceType("serviceType")), NodeSelector: map[string]string{ "selector": "of node", }, @@ -111,7 +112,7 @@ func TestSerializeGatewayClassConfig_HappyPath(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "my-gw", Annotations: map[string]string{ - annotationConfigKey: `{"serviceType":"serviceType","nodeSelector":{"selector":"of node"},"tolerations":[{"key":"key","operator":"op","value":"120","effect":"to the moon","tolerationSeconds":0}],"copyAnnotations":{"service":["service"]}}`, + common.AnnotationGatewayClassConfig: `{"serviceType":"serviceType","nodeSelector":{"selector":"of node"},"tolerations":[{"key":"key","operator":"op","value":"120","effect":"to the moon","tolerationSeconds":0}],"copyAnnotations":{"service":["service"]}}`, }, }, Spec: gwv1beta1.GatewaySpec{}, @@ -123,7 +124,7 @@ func TestSerializeGatewayClassConfig_HappyPath(t *testing.T) { Name: "the config", }, Spec: v1alpha1.GatewayClassConfigSpec{ - ServiceType: pointerTo(corev1.ServiceType("serviceType")), + ServiceType: common.PointerTo(corev1.ServiceType("serviceType")), NodeSelector: map[string]string{ "selector": "of node", }, @@ -152,7 +153,7 @@ func TestSerializeGatewayClassConfig_HappyPath(t *testing.T) { Name: "my-gw", Annotations: map[string]string{ // we remove the opening brace to make unmarshalling fail - annotationConfigKey: `"serviceType":"serviceType","nodeSelector":{"selector":"of node"},"tolerations":[{"key":"key","operator":"op","value":"120","effect":"to the moon","tolerationSeconds":0}],"copyAnnotations":{"service":["service"]}}`, + common.AnnotationGatewayClassConfig: `"serviceType":"serviceType","nodeSelector":{"selector":"of node"},"tolerations":[{"key":"key","operator":"op","value":"120","effect":"to the moon","tolerationSeconds":0}],"copyAnnotations":{"service":["service"]}}`, }, }, Spec: gwv1beta1.GatewaySpec{}, @@ -164,7 +165,7 @@ func TestSerializeGatewayClassConfig_HappyPath(t *testing.T) { Name: "the config", }, Spec: v1alpha1.GatewayClassConfigSpec{ - ServiceType: pointerTo(corev1.ServiceType("serviceType")), + ServiceType: common.PointerTo(corev1.ServiceType("serviceType")), NodeSelector: map[string]string{ "selector": "of node", }, @@ -195,7 +196,7 @@ func TestSerializeGatewayClassConfig_HappyPath(t *testing.T) { } var config v1alpha1.GatewayClassConfig - err := json.Unmarshal([]byte(tt.args.gw.Annotations[annotationConfigKey]), &config.Spec) + err := json.Unmarshal([]byte(tt.args.gw.Annotations[common.AnnotationGatewayClassConfig]), &config.Spec) require.NoError(t, err) if diff := cmp.Diff(config.Spec, tt.args.gwcc.Spec); diff != "" { diff --git a/control-plane/api-gateway/binding/binder.go b/control-plane/api-gateway/binding/binder.go index 00e928db0a..391c083724 100644 --- a/control-plane/api-gateway/binding/binder.go +++ b/control-plane/api-gateway/binding/binder.go @@ -4,8 +4,9 @@ package binding import ( - "github.com/google/go-cmp/cmp" - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/translation" + mapset "github.com/deckarep/golang-set" + "github.com/go-logr/logr" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "github.com/hashicorp/consul/api" corev1 "k8s.io/api/core/v1" @@ -15,47 +16,21 @@ import ( gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) -const ( - // gatewayFinalizer is the finalizer we add to any gateway object. - gatewayFinalizer = "gateway-finalizer.consul.hashicorp.com" - - // namespaceNameLabel represents that label added automatically to namespaces in newer Kubernetes clusters. - namespaceNameLabel = "kubernetes.io/metadata.name" -) - -var ( - // constants extracted for ease of use. - kindGateway = "Gateway" - kindSecret = "Secret" - betaGroup = gwv1beta1.GroupVersion.Group - - // the list of kinds we can support by listener protocol. - supportedKindsForProtocol = map[gwv1beta1.ProtocolType][]gwv1beta1.RouteGroupKind{ - gwv1beta1.HTTPProtocolType: {{ - Group: (*gwv1beta1.Group)(&gwv1beta1.GroupVersion.Group), - Kind: "HTTPRoute", - }}, - gwv1beta1.HTTPSProtocolType: {{ - Group: (*gwv1beta1.Group)(&gwv1beta1.GroupVersion.Group), - Kind: "HTTPRoute", - }}, - gwv1beta1.TCPProtocolType: {{ - Group: (*gwv1alpha2.Group)(&gwv1alpha2.GroupVersion.Group), - Kind: "TCPRoute", - }}, - } -) - // BinderConfig configures a binder instance with all of the information // that it needs to know to generate a snapshot of bound state. type BinderConfig struct { + // Logger for any internal logs + Logger logr.Logger // Translator instance initialized with proper name/namespace translation // configuration from helm. - Translator translation.K8sToConsulTranslator + Translator common.ResourceTranslator // ControllerName is the name of the controller used in determining which // gateways we control, also leveraged for setting route statuses. ControllerName string + // Namespaces is a map of all namespaces in Kubernetes indexed by their names for looking up labels + // for AllowedRoutes matching purposes. + Namespaces map[string]corev1.Namespace // GatewayClassConfig is the configuration corresponding to the given // GatewayClass -- if it is nil we should treat the gateway as deleted // since the gateway is now pointing to an invalid gateway class @@ -73,13 +48,9 @@ type BinderConfig struct { HTTPRoutes []gwv1beta1.HTTPRoute // TCPRoutes is a list of TCPRoute objects that ought to be bound to the Gateway. TCPRoutes []gwv1alpha2.TCPRoute - // Secrets is a list of Secret objects that a Gateway references. - Secrets []corev1.Secret - // Pods are any pods that are part of the Gateway deployment + // Pods are any pods that are part of the Gateway deployment. Pods []corev1.Pod - // MeshServices are all the MeshService objects that can be used for service lookup - MeshServices []v1alpha1.MeshService - // Service is the service associated with a Gateway deployment + // Service is the deployed service associated with the Gateway deployment. Service *corev1.Service // TODO: Do we need to pass in Routes that have references to a Gateway in their statuses @@ -95,37 +66,35 @@ type BinderConfig struct { ConsulTCPRoutes []api.TCPRouteConfigEntry // ConsulInlineCertificates is a list of certificates that have been created in Consul. ConsulInlineCertificates []api.InlineCertificateConfigEntry - // ConnectInjectedServices is a list of all services that have been injected by our connect-injector - // and that we can, therefore reference on the mesh. - ConnectInjectedServices []api.CatalogService - // GatewayServices are the consul services for a given gateway - GatewayServices []api.CatalogService + // GatewayServices are the services associated with the Gateway + ConsulGatewayServices []api.CatalogService - // Namespaces is a map of all namespaces in Kubernetes indexed by their names for looking up labels - // for AllowedRoutes matching purposes. - Namespaces map[string]corev1.Namespace - // ControlledGateways is a map of all Gateway objects that we currently should be interested in. This - // is used to determine whether we should garbage collect Certificate or Route objects when they become - // disassociated with a particular Gateway. - ControlledGateways map[types.NamespacedName]gwv1beta1.Gateway + // Resources is a map containing all service targets to verify + // against the routing backends. + Resources *common.ResourceMap } // Binder is used for generating a Snapshot of all operations that should occur both // in Kubernetes and Consul as a result of binding routes to a Gateway. type Binder struct { - statusSetter *setter - config BinderConfig + statusSetter *setter + key types.NamespacedName + nonNormalizedConsulKey api.ResourceReference + normalizedConsulKey api.ResourceReference + config BinderConfig } // NewBinder creates a Binder object with the given configuration. func NewBinder(config BinderConfig) *Binder { - return &Binder{config: config, statusSetter: newSetter(config.ControllerName)} -} - -// gatewayRef returns a Consul-based reference for the given Kubernetes gateway to -// be used for marking a deletion that is needed in Consul. -func (b *Binder) gatewayRef() api.ResourceReference { - return b.config.Translator.ReferenceForGateway(&b.config.Gateway) + id := client.ObjectKeyFromObject(&config.Gateway) + + return &Binder{ + config: config, + statusSetter: newSetter(config.ControllerName), + key: id, + nonNormalizedConsulKey: config.Translator.NonNormalizedConfigEntryReference(api.APIGateway, id), + normalizedConsulKey: config.Translator.ConfigEntryReference(api.APIGateway, id), + } } // isGatewayDeleted returns whether we should treat the given gateway as a deleted object. @@ -139,32 +108,43 @@ func (b *Binder) isGatewayDeleted() bool { // Snapshot generates a snapshot of operations that need to occur in Kubernetes and Consul // in order for a Gateway to be reconciled. -func (b *Binder) Snapshot() Snapshot { +func (b *Binder) Snapshot() *Snapshot { // at this point we assume all tcp routes and http routes // actually reference this gateway - tracker := b.references() - meshServiceMap := meshServiceMap(b.config.MeshServices) - serviceMap := serviceMap(b.config.ConnectInjectedServices) - seenRoutes := map[api.ResourceReference]struct{}{} - snapshot := Snapshot{} - gwcc := b.config.GatewayClassConfig + snapshot := NewSnapshot() + + registrationPods := []corev1.Pod{} + // filter out any pod that is being deleted + for _, pod := range b.config.Pods { + if !isDeleted(&pod) { + registrationPods = append(registrationPods, pod) + } + } + + gatewayClassConfig := b.config.GatewayClassConfig isGatewayDeleted := b.isGatewayDeleted() + + var gatewayValidation gatewayValidationResult + var listenerValidation listenerValidationResults + if !isGatewayDeleted { var updated bool - gwcc, updated = serializeGatewayClassConfig(&b.config.Gateway, gwcc) + + gatewayClassConfig, updated = serializeGatewayClassConfig(&b.config.Gateway, gatewayClassConfig) // we don't have a deletion but if we add a finalizer for the gateway, then just add it and return // otherwise try and resolve as much as possible - if ensureFinalizer(&b.config.Gateway) || updated { + if common.EnsureFinalizer(&b.config.Gateway) || updated { // if we've added the finalizer or serialized the class config, then update - snapshot.Kubernetes.Updates = append(snapshot.Kubernetes.Updates, &b.config.Gateway) + snapshot.Kubernetes.Updates.Add(&b.config.Gateway) return snapshot } - } - httpRouteBinder := b.newHTTPRouteBinder(tracker, serviceMap, meshServiceMap) - tcpRouteBinder := b.newTCPRouteBinder(tracker, serviceMap, meshServiceMap) + // calculate the status for the gateway + gatewayValidation = validateGateway(b.config.Gateway, registrationPods, b.config.ConsulGateway) + listenerValidation = validateListeners(b.config.Gateway, b.config.Gateway.Spec.Listeners, b.config.Resources) + } // used for tracking how many routes have successfully bound to which listeners // on a gateway for reporting the number of bound routes in a gateway listener's @@ -174,76 +154,54 @@ func (b *Binder) Snapshot() Snapshot { // attempt to bind all routes for _, r := range b.config.HTTPRoutes { - snapshot = httpRouteBinder.bind(pointerTo(r), boundCounts, seenRoutes, snapshot) + b.bindRoute(common.PointerTo(r), boundCounts, snapshot) } for _, r := range b.config.TCPRoutes { - snapshot = tcpRouteBinder.bind(pointerTo(r), boundCounts, seenRoutes, snapshot) + b.bindRoute(common.PointerTo(r), boundCounts, snapshot) } - // now cleanup any routes that we haven't already processed - - for _, r := range b.config.ConsulHTTPRoutes { - snapshot = b.cleanHTTPRoute(pointerTo(r), seenRoutes, snapshot) - } - - for _, r := range b.config.ConsulTCPRoutes { - snapshot = b.cleanTCPRoute(pointerTo(r), seenRoutes, snapshot) - } - - // process certificates - - seenCerts := make(map[types.NamespacedName]api.ResourceReference) - for _, secret := range b.config.Secrets { - if isGatewayDeleted { - // we bypass the secret creation since we want to be able to GC if necessary - continue + // process secrets + gatewaySecrets := secretsForGateway(b.config.Gateway, b.config.Resources) + certs := mapset.NewSet() + if !isGatewayDeleted { + // we only do this if the gateway isn't going to be deleted so that the + // resources can get GC'd + for secret := range gatewaySecrets.Iter() { + // ignore the error if the certificate cannot be processed and just don't add it into the final + // sync set + if err := b.config.Resources.TranslateInlineCertificate(secret.(types.NamespacedName)); err != nil { + b.config.Logger.Error(err, "error parsing referenced secret, ignoring") + continue + } + certs.Add(secret) } + } - certificate := b.config.Translator.SecretToInlineCertificate(secret) - certificateRef := translation.EntryToReference(&certificate) + // now cleanup any routes or certificates that we haven't already processed - // mark the certificate as processed - seenCerts[objectToMeta(&secret)] = certificateRef - // add the certificate to the set of upsert operations needed in Consul - snapshot.Consul.Updates = append(snapshot.Consul.Updates, &certificate) - } + snapshot.Consul.Deletions = b.config.Resources.ResourcesToGC(b.key) + snapshot.Consul.Updates = b.config.Resources.Mutations() - // clean up any inline certs that are now stale and can be GC'd - for _, cert := range b.config.ConsulInlineCertificates { - certRef := translation.EntryToNamespacedName(&cert) - if _, ok := seenCerts[certRef]; !ok { - // check to see if nothing is now referencing the certificate - if tracker.canGCSecret(certRef) { - ref := translation.EntryToReference(&cert) - // we can GC this now since it's not referenced by any Gateway - snapshot.Consul.Deletions = append(snapshot.Consul.Deletions, ref) - } - } - } + // finally, handle the gateway itself // we only want to upsert the gateway into Consul or update its status // if the gateway hasn't been marked for deletion if !isGatewayDeleted { - snapshot.GatewayClassConfig = gwcc + snapshot.GatewayClassConfig = gatewayClassConfig snapshot.UpsertGatewayDeployment = true - entry := b.config.Translator.GatewayToAPIGateway(b.config.Gateway, seenCerts) - snapshot.Consul.Updates = append(snapshot.Consul.Updates, &entry) - - registrationPods := []corev1.Pod{} - // filter out any pod that is being deleted - for _, pod := range b.config.Pods { - if !isDeleted(&pod) { - registrationPods = append(registrationPods, pod) - } - } + entry := b.config.Translator.ToAPIGateway(b.config.Gateway, b.config.Resources) + snapshot.Consul.Updates = append(snapshot.Consul.Updates, &common.ConsulUpdateOperation{ + Entry: entry, + OnUpdate: b.handleGatewaySyncStatus(snapshot, &b.config.Gateway), + }) registrations := registrationsForPods(entry.Namespace, b.config.Gateway, registrationPods) snapshot.Consul.Registrations = registrations // deregister any not explicitly registered service - for _, service := range b.config.GatewayServices { + for _, service := range b.config.ConsulGatewayServices { found := false for _, registration := range registrations { if service.ServiceID == registration.Service.ID { @@ -262,8 +220,6 @@ func (b *Binder) Snapshot() Snapshot { // calculate the status for the gateway var status gwv1beta1.GatewayStatus - gatewayValidation := validateGateway(b.config.Gateway, registrationPods, b.config.ConsulGateway) - listenerValidation := validateListeners(b.config.Gateway.Namespace, b.config.Gateway.Spec.Listeners, b.config.Secrets) for i, listener := range b.config.Gateway.Spec.Listeners { status.Listeners = append(status.Listeners, gwv1beta1.ListenerStatus{ Name: listener.Name, @@ -272,23 +228,25 @@ func (b *Binder) Snapshot() Snapshot { Conditions: listenerValidation.Conditions(b.config.Gateway.Generation, i), }) } - status.Conditions = gatewayValidation.Conditions(b.config.Gateway.Generation, listenerValidation.Invalid()) + status.Conditions = b.config.Gateway.Status.Conditions + + // we do this loop to not accidentally override any additional statuses that + // have been set anywhere outside of validation. + for _, condition := range gatewayValidation.Conditions(b.config.Gateway.Generation, listenerValidation.Invalid()) { + status.Conditions, _ = setCondition(status.Conditions, condition) + } status.Addresses = addressesForGateway(b.config.Service, registrationPods) // only mark the gateway as needing a status update if there's a diff with its old // status, this keeps the controller from infinitely reconciling - if !cmp.Equal(status, b.config.Gateway.Status, cmp.FilterPath(func(p cmp.Path) bool { - path := p.String() - return path == "Listeners.Conditions.LastTransitionTime" || path == "Conditions.LastTransitionTime" - }, cmp.Ignore())) { + if !common.GatewayStatusesEqual(status, b.config.Gateway.Status) { b.config.Gateway.Status = status - snapshot.Kubernetes.StatusUpdates = append(snapshot.Kubernetes.StatusUpdates, &b.config.Gateway) + snapshot.Kubernetes.StatusUpdates.Add(&b.config.Gateway) } } else { // if the gateway has been deleted, unset whatever we've set on it - ref := b.gatewayRef() - snapshot.Consul.Deletions = append(snapshot.Consul.Deletions, ref) - for _, service := range b.config.GatewayServices { + snapshot.Consul.Deletions = append(snapshot.Consul.Deletions, b.nonNormalizedConsulKey) + for _, service := range b.config.ConsulGatewayServices { // deregister all gateways snapshot.Consul.Deregistrations = append(snapshot.Consul.Deregistrations, api.CatalogDeregistration{ Node: service.Node, @@ -296,44 +254,33 @@ func (b *Binder) Snapshot() Snapshot { Namespace: service.Namespace, }) } - if removeFinalizer(&b.config.Gateway) { - snapshot.Kubernetes.Updates = append(snapshot.Kubernetes.Updates, &b.config.Gateway) + if common.RemoveFinalizer(&b.config.Gateway) { + snapshot.Kubernetes.Updates.Add(&b.config.Gateway) } } return snapshot } -// serviceMap constructs a map of services indexed by their Kubernetes namespace and name -// from the annotations that are set on the service. -func serviceMap(services []api.CatalogService) map[types.NamespacedName]api.CatalogService { - smap := make(map[types.NamespacedName]api.CatalogService) - for _, service := range services { - smap[serviceToNamespacedName(&service)] = service - } - return smap -} +func secretsForGateway(gateway gwv1beta1.Gateway, resources *common.ResourceMap) mapset.Set { + set := mapset.NewSet() -// meshServiceMap constructs a map of services indexed by their Kubernetes namespace and name. -func meshServiceMap(services []v1alpha1.MeshService) map[types.NamespacedName]v1alpha1.MeshService { - smap := make(map[types.NamespacedName]v1alpha1.MeshService) - for _, service := range services { - smap[client.ObjectKeyFromObject(&service)] = service - } - return smap -} + for _, listener := range gateway.Spec.Listeners { + if listener.TLS == nil { + continue + } -// serviceToNamespacedName returns the Kubernetes namespace and name of a Consul catalog service -// based on the Metadata annotations written on the service. -func serviceToNamespacedName(s *api.CatalogService) types.NamespacedName { - var ( - metaKeyKubeNS = "k8s-namespace" - metaKeyKubeServiceName = "k8s-service-name" - ) - return types.NamespacedName{ - Namespace: s.ServiceMeta[metaKeyKubeNS], - Name: s.ServiceMeta[metaKeyKubeServiceName], + for _, cert := range listener.TLS.CertificateRefs { + if resources.GatewayCanReferenceSecret(gateway, cert) { + if common.NilOrEqual(cert.Group, "") && common.NilOrEqual(cert.Kind, common.KindSecret) { + key := common.IndexedNamespacedNameWithDefault(cert.Name, cert.Namespace, gateway.Namespace) + set.Add(key) + } + } + } } + + return set } func addressesForGateway(service *corev1.Service, pods []corev1.Pod) []gwv1beta1.GatewayAddress { @@ -366,13 +313,13 @@ func addressesFromLoadBalancer(service *corev1.Service) []gwv1beta1.GatewayAddre for _, ingress := range service.Status.LoadBalancer.Ingress { if ingress.IP != "" { addresses = append(addresses, gwv1beta1.GatewayAddress{ - Type: pointerTo(gwv1beta1.IPAddressType), + Type: common.PointerTo(gwv1beta1.IPAddressType), Value: ingress.IP, }) } if ingress.Hostname != "" { addresses = append(addresses, gwv1beta1.GatewayAddress{ - Type: pointerTo(gwv1beta1.HostnameAddressType), + Type: common.PointerTo(gwv1beta1.HostnameAddressType), Value: ingress.Hostname, }) } @@ -386,7 +333,7 @@ func addressesFromClusterIP(service *corev1.Service) []gwv1beta1.GatewayAddress if service.Spec.ClusterIP != "" { addresses = append(addresses, gwv1beta1.GatewayAddress{ - Type: pointerTo(gwv1beta1.IPAddressType), + Type: common.PointerTo(gwv1beta1.IPAddressType), Value: service.Spec.ClusterIP, }) } @@ -402,7 +349,7 @@ func addressesFromPods(pods []corev1.Pod) []gwv1beta1.GatewayAddress { if pod.Status.PodIP != "" { if _, found := seenIPs[pod.Status.PodIP]; !found { addresses = append(addresses, gwv1beta1.GatewayAddress{ - Type: pointerTo(gwv1beta1.IPAddressType), + Type: common.PointerTo(gwv1beta1.IPAddressType), Value: pod.Status.PodIP, }) seenIPs[pod.Status.PodIP] = struct{}{} @@ -421,7 +368,7 @@ func addressesFromPodHosts(pods []corev1.Pod) []gwv1beta1.GatewayAddress { if pod.Status.HostIP != "" { if _, found := seenIPs[pod.Status.HostIP]; !found { addresses = append(addresses, gwv1beta1.GatewayAddress{ - Type: pointerTo(gwv1beta1.IPAddressType), + Type: common.PointerTo(gwv1beta1.IPAddressType), Value: pod.Status.HostIP, }) seenIPs[pod.Status.HostIP] = struct{}{} @@ -431,3 +378,8 @@ func addressesFromPodHosts(pods []corev1.Pod) []gwv1beta1.GatewayAddress { return addresses } + +// isDeleted checks if the deletion timestamp is set for an object. +func isDeleted(object client.Object) bool { + return !object.GetDeletionTimestamp().IsZero() +} diff --git a/control-plane/api-gateway/binding/binder_test.go b/control-plane/api-gateway/binding/binder_test.go index 6078944004..484b6d1a60 100644 --- a/control-plane/api-gateway/binding/binder_test.go +++ b/control-plane/api-gateway/binding/binder_test.go @@ -4,10 +4,18 @@ package binding import ( - "strings" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" "testing" + "time" + logrtest "github.com/go-logr/logr/testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "github.com/hashicorp/consul/api" "github.com/stretchr/testify/require" @@ -19,335 +27,309 @@ import ( gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) -func TestBinder_Lifecycle(t *testing.T) { - t.Parallel() +func init() { + timeFunc = func() metav1.Time { + return metav1.Time{} + } +} - className := "gateway-class" - gatewayClassName := gwv1beta1.ObjectName(className) - controllerName := "test-controller" - deletionTimestamp := pointerTo(metav1.Now()) - gatewayClass := &gwv1beta1.GatewayClass{ +const ( + testGatewayClassName = "gateway-class" + testControllerName = "test-controller" + routeListenerReferenceGrantErrorMessage = `http-listener-allowed-selector: reference not permitted due to lack of ReferenceGrant; http-listener-default-same: reference not permitted due to lack of ReferenceGrant; http-listener-explicit-all-allowed: reference not permitted due to lack of ReferenceGrant; http-listener-explicit-allowed-same: reference not permitted due to lack of ReferenceGrant; http-listener-hostname: reference not permitted due to lack of ReferenceGrant; http-listener-mismatched-kind-allowed: reference not permitted due to lack of ReferenceGrant; http-listener-tls: reference not permitted due to lack of ReferenceGrant; tcp-listener-allowed-selector: reference not permitted due to lack of ReferenceGrant; tcp-listener-default-same: reference not permitted due to lack of ReferenceGrant; tcp-listener-explicit-all-allowed: reference not permitted due to lack of ReferenceGrant; tcp-listener-explicit-allowed-same: reference not permitted due to lack of ReferenceGrant; tcp-listener-mismatched-kind-allowed: reference not permitted due to lack of ReferenceGrant; tcp-listener-tls: reference not permitted due to lack of ReferenceGrant` +) + +var ( + testGatewayClassObjectName = gwv1beta1.ObjectName(testGatewayClassName) + deletionTimestamp = common.PointerTo(metav1.Now()) + + testGatewayClass = &gwv1beta1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ - Name: className, + Name: testGatewayClassName, }, Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: gwv1beta1.GatewayController(controllerName), + ControllerName: gwv1beta1.GatewayController(testControllerName), }, } +) + +type resourceMapResources struct { + grants []gwv1beta1.ReferenceGrant + secrets []corev1.Secret + gateways []gwv1beta1.Gateway + httpRoutes []gwv1beta1.HTTPRoute + tcpRoutes []gwv1alpha2.TCPRoute + meshServices []v1alpha1.MeshService + services []types.NamespacedName + consulHTTPRoutes []api.HTTPRouteConfigEntry + consulTCPRoutes []api.TCPRouteConfigEntry +} + +func newTestResourceMap(t *testing.T, resources resourceMapResources) *common.ResourceMap { + resourceMap := common.NewResourceMap(common.ResourceTranslator{}, NewReferenceValidator(resources.grants), logrtest.NewTestLogger(t)) + + for _, s := range resources.services { + resourceMap.AddService(s, s.Name) + } + for _, s := range resources.meshServices { + resourceMap.AddMeshService(s) + } + for _, s := range resources.secrets { + resourceMap.ReferenceCountCertificate(s) + } + for _, g := range resources.gateways { + resourceMap.ReferenceCountGateway(g) + } + for _, r := range resources.httpRoutes { + resourceMap.ReferenceCountHTTPRoute(r) + } + for _, r := range resources.tcpRoutes { + resourceMap.ReferenceCountTCPRoute(r) + } + for _, r := range resources.consulHTTPRoutes { + resourceMap.ReferenceCountConsulHTTPRoute(r) + } + for _, r := range resources.consulTCPRoutes { + resourceMap.ReferenceCountConsulTCPRoute(r) + } + return resourceMap +} + +func TestBinder_Lifecycle(t *testing.T) { + t.Parallel() + + certificateOne, secretOne := generateTestCertificate(t, "default", "secret-one") + certificateTwo, secretTwo := generateTestCertificate(t, "default", "secret-two") for name, tt := range map[string]struct { - config BinderConfig - expected Snapshot + resources resourceMapResources + config BinderConfig + expectedStatusUpdates []client.Object + expectedUpdates []client.Object + expectedConsulDeletions []api.ResourceReference + expectedConsulUpdates []api.ConfigEntry }{ "no gateway class and empty routes": { config: BinderConfig{ Gateway: gwv1beta1.Gateway{}, }, - expected: Snapshot{ - Consul: ConsulSnapshot{ - Deletions: []api.ResourceReference{{ - Kind: api.APIGateway, - }}, - }, - }, + expectedConsulDeletions: []api.ResourceReference{{ + Kind: api.APIGateway, + }}, }, "no gateway class and empty routes remove finalizer": { config: BinderConfig{ Gateway: gwv1beta1.Gateway{ ObjectMeta: metav1.ObjectMeta{ - Finalizers: []string{gatewayFinalizer}, + Finalizers: []string{common.GatewayFinalizer}, }, }, }, - expected: Snapshot{ - Kubernetes: KubernetesSnapshot{ - Updates: []client.Object{ - &gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Finalizers: []string{}, - }, - }, - }, - }, - Consul: ConsulSnapshot{ - Deletions: []api.ResourceReference{{ - Kind: api.APIGateway, - }}, - }, + expectedUpdates: []client.Object{ + addClassConfig(gwv1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{}}}), + }, + expectedConsulDeletions: []api.ResourceReference{ + {Kind: api.APIGateway}, }, }, "deleting gateway empty routes": { config: BinderConfig{ - ControllerName: controllerName, - GatewayClass: gatewayClass, + ControllerName: testControllerName, + GatewayClass: testGatewayClass, Gateway: gwv1beta1.Gateway{ ObjectMeta: metav1.ObjectMeta{ DeletionTimestamp: deletionTimestamp, - Finalizers: []string{gatewayFinalizer}, + Finalizers: []string{common.GatewayFinalizer}, }, Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gatewayClassName, + GatewayClassName: testGatewayClassObjectName, }, }, }, - expected: Snapshot{ - Kubernetes: KubernetesSnapshot{ - Updates: []client.Object{ - &gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - DeletionTimestamp: deletionTimestamp, - Finalizers: []string{}, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gatewayClassName, - }, - }, + expectedUpdates: []client.Object{ + addClassConfig(gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{DeletionTimestamp: deletionTimestamp, Finalizers: []string{}}, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: testGatewayClassObjectName, }, - }, - Consul: ConsulSnapshot{ - Deletions: []api.ResourceReference{{ - Kind: api.APIGateway, - }}, - }, + }), + }, + expectedConsulDeletions: []api.ResourceReference{ + {Kind: api.APIGateway}, }, }, "basic gateway no finalizer": { config: BinderConfig{ - ControllerName: controllerName, - GatewayClass: gatewayClass, + ControllerName: testControllerName, + GatewayClass: testGatewayClass, Gateway: gwv1beta1.Gateway{ Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gatewayClassName, + GatewayClassName: testGatewayClassObjectName, }, }, }, - expected: Snapshot{ - Kubernetes: KubernetesSnapshot{ - Updates: []client.Object{ - &gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gatewayClassName, - }, - }, + expectedUpdates: []client.Object{ + addClassConfig(gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{Finalizers: []string{common.GatewayFinalizer}}, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: testGatewayClassObjectName, }, - }, - Consul: ConsulSnapshot{}, + }), }, }, "basic gateway": { - config: BinderConfig{ - ControllerName: controllerName, - GatewayClass: gatewayClass, - Gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gatewayClassName, + config: controlledBinder(BinderConfig{ + Gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{ + Listeners: []gwv1beta1.Listener{{ + Protocol: gwv1beta1.HTTPSProtocolType, + TLS: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{ + {Name: "secret-one"}, + }, + Mode: common.PointerTo(gwv1beta1.TLSModeTerminate), + }, + }}, + }), + }), + resources: resourceMapResources{ + secrets: []corev1.Secret{ + secretOne, + }, + }, + expectedStatusUpdates: []client.Object{ + addClassConfig(gatewayWithFinalizerStatus( + gwv1beta1.GatewaySpec{ Listeners: []gwv1beta1.Listener{{ + Protocol: gwv1beta1.HTTPSProtocolType, TLS: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{{ - Name: "secret-one", - }}, + Mode: common.PointerTo(gwv1beta1.TLSModeTerminate), + CertificateRefs: []gwv1beta1.SecretObjectReference{ + {Name: "secret-one"}, + }, }, }}, }, - }, - Secrets: []corev1.Secret{{ - ObjectMeta: metav1.ObjectMeta{ - Name: "secret-one", - }, - }}, - }, - expected: Snapshot{ - Kubernetes: KubernetesSnapshot{ - StatusUpdates: []client.Object{ - &gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gatewayClassName, - Listeners: []gwv1beta1.Listener{{ - TLS: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{{ - Name: "secret-one", - }}, - }, - }}, + gwv1beta1.GatewayStatus{ + Addresses: []gwv1beta1.GatewayAddress{}, + Conditions: []metav1.Condition{ + { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "gateway accepted", + }, { + Type: "Programmed", + Status: metav1.ConditionFalse, + Reason: "Pending", + Message: "gateway pods are still being scheduled", }, - Status: gwv1beta1.GatewayStatus{ - Addresses: []gwv1beta1.GatewayAddress{}, - Conditions: []metav1.Condition{{ + }, + Listeners: []gwv1beta1.ListenerStatus{{ + SupportedKinds: supportedKindsForProtocol[gwv1beta1.HTTPSProtocolType], + Conditions: []metav1.Condition{ + { Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "ListenersNotValid", - Message: "one or more listeners are invalid", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "listener accepted", }, { - Type: "Programmed", + Type: "Conflicted", Status: metav1.ConditionFalse, - Reason: "Pending", - Message: "gateway pods are still being scheduled", - }}, - Listeners: []gwv1beta1.ListenerStatus{{ - Conditions: []metav1.Condition{{ - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "UnsupportedProtocol", - Message: "listener protocol is unsupported", - }, { - Type: "Conflicted", - Status: metav1.ConditionFalse, - Reason: "NoConflicts", - Message: "listener has no conflicts", - }, { - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved certificate references", - }}, - }}, - }, - }, - }, - }, - Consul: ConsulSnapshot{ - Updates: []api.ConfigEntry{ - &api.InlineCertificateConfigEntry{ - Kind: api.InlineCertificate, - Name: "secret-one", - Meta: map[string]string{ - "k8s-name": "secret-one", - "k8s-namespace": "", - "k8s-service-name": "secret-one", - "managed-by": "consul-k8s-gateway-controller", - }, - }, - &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Meta: map[string]string{ - "k8s-name": "", - "k8s-namespace": "", - "k8s-service-name": "", - "managed-by": "consul-k8s-gateway-controller", - }, - Listeners: []api.APIGatewayListener{{ - TLS: api.APIGatewayTLSConfiguration{ - Certificates: []api.ResourceReference{{ - Kind: api.InlineCertificate, - Name: "secret-one", - }}, + Reason: "NoConflicts", + Message: "listener has no conflicts", + }, { + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved certificate references", }, + }, + }}, + }), + ), + }, + expectedConsulUpdates: []api.ConfigEntry{ + certificateOne, + &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "gateway", + Meta: map[string]string{ + "k8s-name": "gateway", + "k8s-namespace": "default", + }, + Listeners: []api.APIGatewayListener{{ + Protocol: "http", + TLS: api.APIGatewayTLSConfiguration{ + Certificates: []api.ResourceReference{{ + Kind: api.InlineCertificate, + Name: "secret-one", }}, }, - }, - Registrations: []api.CatalogRegistration{}, + }}, }, }, }, "gateway http route no finalizer": { - config: BinderConfig{ - ControllerName: controllerName, - GatewayClass: gatewayClass, - Gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gatewayClassName, - }, - }, - HTTPRoutes: []gwv1beta1.HTTPRoute{{ - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, + config: controlledBinder(BinderConfig{ + Gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), + HTTPRoutes: []gwv1beta1.HTTPRoute{ + { + TypeMeta: metav1.TypeMeta{ + Kind: "HTTPRoute", + APIVersion: "gateway.networking.k8s.io/v1beta1", }, - }, - }}, - }, - expected: Snapshot{ - Kubernetes: KubernetesSnapshot{ - Updates: []client.Object{ - &gwv1beta1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, - }, - }, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", }, - }, - StatusUpdates: []client.Object{ - &gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gatewayClassName, - }, - Status: gwv1beta1.GatewayStatus{ - Addresses: []gwv1beta1.GatewayAddress{}, - Conditions: []metav1.Condition{{ - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "gateway accepted", - }, { - Type: "Programmed", - Status: metav1.ConditionFalse, - Reason: "Pending", - Message: "gateway pods are still being scheduled", + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", }}, }, }, }, }, - Consul: ConsulSnapshot{ - Updates: []api.ConfigEntry{ - &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "gateway", - Meta: map[string]string{ - "k8s-name": "gateway", - "k8s-namespace": "", - "k8s-service-name": "gateway", - "managed-by": "consul-k8s-gateway-controller", - }, - Listeners: []api.APIGatewayListener{}, - }, + }), + expectedUpdates: []client.Object{ + common.PointerTo(testHTTPRoute("route", []string{"gateway"}, nil)), + }, + expectedStatusUpdates: []client.Object{ + addClassConfig(gatewayWithFinalizerStatus(gwv1beta1.GatewaySpec{}, gwv1beta1.GatewayStatus{ + Addresses: []gwv1beta1.GatewayAddress{}, + Conditions: []metav1.Condition{{ + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "gateway accepted", + }, { + Type: "Programmed", + Status: metav1.ConditionFalse, + Reason: "Pending", + Message: "gateway pods are still being scheduled", + }}, + })), + }, + expectedConsulUpdates: []api.ConfigEntry{ + &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "gateway", + Meta: map[string]string{ + "k8s-name": "gateway", + "k8s-namespace": "default", }, - Registrations: []api.CatalogRegistration{}, + Listeners: []api.APIGatewayListener{}, }, }, }, "gateway http route deleting": { - config: BinderConfig{ - ControllerName: controllerName, - GatewayClass: gatewayClass, - Gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gatewayClassName, - }, - }, + config: controlledBinder(BinderConfig{ + Gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), HTTPRoutes: []gwv1beta1.HTTPRoute{{ ObjectMeta: metav1.ObjectMeta{ + Name: "route", DeletionTimestamp: deletionTimestamp, - Finalizers: []string{gatewayFinalizer}, + Finalizers: []string{common.GatewayFinalizer}, }, Spec: gwv1beta1.HTTPRouteSpec{ CommonRouteSpec: gwv1beta1.CommonRouteSpec{ @@ -357,171 +339,122 @@ func TestBinder_Lifecycle(t *testing.T) { }, }, }}, - }, - expected: Snapshot{ - Kubernetes: KubernetesSnapshot{ - Updates: []client.Object{ - &gwv1beta1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{ - DeletionTimestamp: deletionTimestamp, - Finalizers: []string{}, - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, - }, - }, - }, - }, - StatusUpdates: []client.Object{ - &gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gatewayClassName, - }, - Status: gwv1beta1.GatewayStatus{ - Addresses: []gwv1beta1.GatewayAddress{}, - Conditions: []metav1.Condition{{ - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "gateway accepted", - }, { - Type: "Programmed", - Status: metav1.ConditionFalse, - Reason: "Pending", - Message: "gateway pods are still being scheduled", - }}, - }, - }, - }, - }, - Consul: ConsulSnapshot{ - Updates: []api.ConfigEntry{ - &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "gateway", - Meta: map[string]string{ - "k8s-name": "gateway", - "k8s-namespace": "", - "k8s-service-name": "gateway", - "managed-by": "consul-k8s-gateway-controller", - }, - Listeners: []api.APIGatewayListener{}, - }, + ConsulHTTPRoutes: []api.HTTPRouteConfigEntry{{ + Kind: api.HTTPRoute, + Name: "route", + Parents: []api.ResourceReference{ + {Kind: api.APIGateway, Name: "gateway"}, }, - Deletions: []api.ResourceReference{{ - Kind: api.HTTPRoute, - }}, - Registrations: []api.CatalogRegistration{}, - }, - }, - }, - "gateway tcp route no finalizer": { - config: BinderConfig{ - ControllerName: controllerName, - GatewayClass: gatewayClass, - Gateway: gwv1beta1.Gateway{ + }}, + }), + expectedUpdates: []client.Object{ + &gwv1beta1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gatewayClassName, + Name: "route", + DeletionTimestamp: deletionTimestamp, + Finalizers: []string{}, }, - }, - TCPRoutes: []gwv1alpha2.TCPRoute{{ - Spec: gwv1alpha2.TCPRouteSpec{ + Spec: gwv1beta1.HTTPRouteSpec{ CommonRouteSpec: gwv1beta1.CommonRouteSpec{ ParentRefs: []gwv1beta1.ParentReference{{ Name: "gateway", }}, }, }, - }}, + }, }, - expected: Snapshot{ - Kubernetes: KubernetesSnapshot{ - Updates: []client.Object{ - &gwv1alpha2.TCPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, - }, - }, - }, + expectedStatusUpdates: []client.Object{ + addClassConfig(gatewayWithFinalizerStatus(gwv1beta1.GatewaySpec{}, gwv1beta1.GatewayStatus{ + Addresses: []gwv1beta1.GatewayAddress{}, + Conditions: []metav1.Condition{{ + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "gateway accepted", + }, { + Type: "Programmed", + Status: metav1.ConditionFalse, + Reason: "Pending", + Message: "gateway pods are still being scheduled", + }}, + })), + }, + expectedConsulUpdates: []api.ConfigEntry{ + &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "gateway", + Meta: map[string]string{ + "k8s-name": "gateway", + "k8s-namespace": "default", }, - StatusUpdates: []client.Object{ - &gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gatewayClassName, - }, - Status: gwv1beta1.GatewayStatus{ - Addresses: []gwv1beta1.GatewayAddress{}, - Conditions: []metav1.Condition{{ - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "gateway accepted", - }, { - Type: "Programmed", - Status: metav1.ConditionFalse, - Reason: "Pending", - Message: "gateway pods are still being scheduled", + Listeners: []api.APIGatewayListener{}, + }, + }, + expectedConsulDeletions: []api.ResourceReference{ + {Kind: api.HTTPRoute, Name: "route"}, + }, + }, + "gateway tcp route no finalizer": { + config: controlledBinder(BinderConfig{ + Gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), + TCPRoutes: []gwv1alpha2.TCPRoute{ + { + TypeMeta: metav1.TypeMeta{ + Kind: "TCPRoute", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", }}, }, }, }, }, - Consul: ConsulSnapshot{ - Updates: []api.ConfigEntry{ - &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "gateway", - Meta: map[string]string{ - "k8s-name": "gateway", - "k8s-namespace": "", - "k8s-service-name": "gateway", - "managed-by": "consul-k8s-gateway-controller", - }, - Listeners: []api.APIGatewayListener{}, - }, + }), + expectedUpdates: []client.Object{ + common.PointerTo(testTCPRoute("route", []string{"gateway"}, nil)), + }, + expectedStatusUpdates: []client.Object{ + addClassConfig(gatewayWithFinalizerStatus(gwv1beta1.GatewaySpec{}, gwv1beta1.GatewayStatus{ + Addresses: []gwv1beta1.GatewayAddress{}, + Conditions: []metav1.Condition{{ + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "gateway accepted", + }, { + Type: "Programmed", + Status: metav1.ConditionFalse, + Reason: "Pending", + Message: "gateway pods are still being scheduled", + }}, + })), + }, + expectedConsulUpdates: []api.ConfigEntry{ + &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "gateway", + Meta: map[string]string{ + "k8s-name": "gateway", + "k8s-namespace": "default", }, - Registrations: []api.CatalogRegistration{}, + Listeners: []api.APIGatewayListener{}, }, }, }, "gateway tcp route deleting": { - config: BinderConfig{ - ControllerName: controllerName, - GatewayClass: gatewayClass, - Gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gatewayClassName, - }, - }, + config: controlledBinder(BinderConfig{ + Gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), TCPRoutes: []gwv1alpha2.TCPRoute{{ ObjectMeta: metav1.ObjectMeta{ + Name: "route", DeletionTimestamp: deletionTimestamp, - Finalizers: []string{gatewayFinalizer}, + Finalizers: []string{common.GatewayFinalizer}, }, Spec: gwv1alpha2.TCPRouteSpec{ CommonRouteSpec: gwv1beta1.CommonRouteSpec{ @@ -531,460 +464,317 @@ func TestBinder_Lifecycle(t *testing.T) { }, }, }}, - }, - expected: Snapshot{ - Kubernetes: KubernetesSnapshot{ - Updates: []client.Object{ - &gwv1alpha2.TCPRoute{ - ObjectMeta: metav1.ObjectMeta{ - DeletionTimestamp: deletionTimestamp, - Finalizers: []string{}, - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, - }, - }, - }, + ConsulTCPRoutes: []api.TCPRouteConfigEntry{{ + Kind: api.TCPRoute, + Name: "route", + Parents: []api.ResourceReference{ + {Kind: api.APIGateway, Name: "gateway"}, }, - StatusUpdates: []client.Object{ - &gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gatewayClassName, - }, - Status: gwv1beta1.GatewayStatus{ - Addresses: []gwv1beta1.GatewayAddress{}, - Conditions: []metav1.Condition{{ - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "gateway accepted", - }, { - Type: "Programmed", - Status: metav1.ConditionFalse, - Reason: "Pending", - Message: "gateway pods are still being scheduled", - }}, - }, - }, + }}, + }), + expectedUpdates: []client.Object{ + &gwv1alpha2.TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route", + DeletionTimestamp: deletionTimestamp, + Finalizers: []string{}, }, - }, - Consul: ConsulSnapshot{ - Updates: []api.ConfigEntry{ - &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "gateway", - Meta: map[string]string{ - "k8s-name": "gateway", - "k8s-namespace": "", - "k8s-service-name": "gateway", - "managed-by": "consul-k8s-gateway-controller", - }, - Listeners: []api.APIGatewayListener{}, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{{ + Name: "gateway", + }}, }, }, - Deletions: []api.ResourceReference{{ - Kind: api.TCPRoute, + }, + }, + expectedStatusUpdates: []client.Object{ + addClassConfig(gatewayWithFinalizerStatus(gwv1beta1.GatewaySpec{}, gwv1beta1.GatewayStatus{ + Addresses: []gwv1beta1.GatewayAddress{}, + Conditions: []metav1.Condition{{ + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "gateway accepted", + }, { + Type: "Programmed", + Status: metav1.ConditionFalse, + Reason: "Pending", + Message: "gateway pods are still being scheduled", }}, - Registrations: []api.CatalogRegistration{}, + })), + }, + expectedConsulUpdates: []api.ConfigEntry{ + &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "gateway", + Meta: map[string]string{ + "k8s-name": "gateway", + "k8s-namespace": "default", + }, + Listeners: []api.APIGatewayListener{}, }, }, + expectedConsulDeletions: []api.ResourceReference{ + {Kind: api.TCPRoute, Name: "route"}, + }, }, "gateway deletion routes and secrets": { - config: BinderConfig{ - ControllerName: controllerName, - GatewayClass: gatewayClass, + config: controlledBinder(BinderConfig{ Gateway: gwv1beta1.Gateway{ ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", + Name: "gateway-deleted", DeletionTimestamp: deletionTimestamp, - Finalizers: []string{gatewayFinalizer}, + Finalizers: []string{common.GatewayFinalizer}, }, Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gatewayClassName, + GatewayClassName: testGatewayClassName, Listeners: []gwv1beta1.Listener{{ TLS: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{{ - Name: "secret-one", - }, { - Name: "secret-two", - }}, + CertificateRefs: []gwv1beta1.SecretObjectReference{ + {Name: "secret-one"}, + {Name: "secret-two"}, + }, }, }}, }, }, - ControlledGateways: map[types.NamespacedName]gwv1beta1.Gateway{ - {Name: "gateway"}: { - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - DeletionTimestamp: deletionTimestamp, - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gatewayClassName, - Listeners: []gwv1beta1.Listener{{ - TLS: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{{ - Name: "secret-one", - }, { - Name: "secret-two", - }}, - }, - }}, + HTTPRoutes: []gwv1beta1.HTTPRoute{ + testHTTPRoute("http-route-one", []string{"gateway-deleted"}, nil), + testHTTPRouteStatus("http-route-two", nil, []gwv1alpha2.RouteParentStatus{ + {ParentRef: gwv1beta1.ParentReference{Name: "gateway-deleted"}, ControllerName: testControllerName, Conditions: []metav1.Condition{ + { + Type: "Accepted", + Status: metav1.ConditionTrue, + }, + }}, + {ParentRef: gwv1beta1.ParentReference{Name: "gateway"}, ControllerName: testControllerName, Conditions: []metav1.Condition{ + { + Type: "Accepted", + Status: metav1.ConditionTrue, + }, + }}, + }), + }, + TCPRoutes: []gwv1alpha2.TCPRoute{ + testTCPRoute("tcp-route-one", []string{"gateway-deleted"}, nil), + testTCPRouteStatus("tcp-route-two", nil, []gwv1alpha2.RouteParentStatus{ + {ParentRef: gwv1beta1.ParentReference{Name: "gateway-deleted"}, ControllerName: testControllerName, Conditions: []metav1.Condition{ + { + Type: "Accepted", + Status: metav1.ConditionTrue, + }, + }}, + {ParentRef: gwv1beta1.ParentReference{Name: "gateway"}, ControllerName: testControllerName, Conditions: []metav1.Condition{ + { + Type: "Accepted", + Status: metav1.ConditionTrue, + }, + }}, + }), + }, + ConsulHTTPRoutes: []api.HTTPRouteConfigEntry{ + { + Kind: api.HTTPRoute, Name: "http-route-two", Meta: map[string]string{ + "k8s-name": "http-route-two", + "k8s-namespace": "", + }, + Parents: []api.ResourceReference{ + {Kind: api.APIGateway, Name: "gateway-deleted"}, + {Kind: api.APIGateway, Name: "gateway"}, }, }, - {Name: "gateway-two"}: { - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway-two", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gatewayClassName, - Listeners: []gwv1beta1.Listener{{ - TLS: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{{ - Name: "secret-one", - }, { - Name: "secret-three", - }}, - }, - }}, + { + Kind: api.HTTPRoute, Name: "http-route-one", Meta: map[string]string{ + "k8s-name": "http-route-one", + "k8s-namespace": "", + }, + Parents: []api.ResourceReference{ + {Kind: api.APIGateway, Name: "gateway-deleted"}, }, }, }, - Secrets: []corev1.Secret{{ - ObjectMeta: metav1.ObjectMeta{ - Name: "secret-one", - }, - }, { - ObjectMeta: metav1.ObjectMeta{ - Name: "secret-two", + ConsulTCPRoutes: []api.TCPRouteConfigEntry{ + { + Kind: api.TCPRoute, Name: "tcp-route-two", + Meta: map[string]string{ + "k8s-name": "tcp-route-two", + "k8s-namespace": "", + }, + Parents: []api.ResourceReference{ + {Kind: api.APIGateway, Name: "gateway-deleted"}, + {Kind: api.APIGateway, Name: "gateway"}, + }, }, - }}, - HTTPRoutes: []gwv1beta1.HTTPRoute{{ - ObjectMeta: metav1.ObjectMeta{ - Name: "http-route-one", - Finalizers: []string{gatewayFinalizer}, + { + Kind: api.TCPRoute, Name: "tcp-route-one", + Meta: map[string]string{ + "k8s-name": "tcp-route-one", + "k8s-namespace": "", + }, + Parents: []api.ResourceReference{ + {Kind: api.APIGateway, Name: "gateway-deleted"}, + }, }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, + }, + ConsulInlineCertificates: []api.InlineCertificateConfigEntry{ + *certificateOne, + *certificateTwo, + }, + }), + resources: resourceMapResources{ + secrets: []corev1.Secret{ + secretOne, + secretTwo, + }, + gateways: []gwv1beta1.Gateway{ + gatewayWithFinalizer(gwv1beta1.GatewaySpec{ + Listeners: []gwv1beta1.Listener{{ + TLS: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{ + {Name: "secret-one"}, + {Name: "secret-three"}, + }, + }, + }}, + }), + }, + }, + expectedStatusUpdates: []client.Object{ + common.PointerTo(testHTTPRouteStatus("http-route-two", nil, []gwv1beta1.RouteParentStatus{ + {ParentRef: gwv1beta1.ParentReference{Name: "gateway"}, ControllerName: testControllerName, Conditions: []metav1.Condition{ + { + Type: "Accepted", + Status: metav1.ConditionTrue, + }, + }}, + }, "gateway-deleted")), + common.PointerTo(testTCPRouteStatus("tcp-route-two", nil, []gwv1beta1.RouteParentStatus{ + {ParentRef: gwv1beta1.ParentReference{Name: "gateway"}, ControllerName: testControllerName, Conditions: []metav1.Condition{ + { + Type: "Accepted", + Status: metav1.ConditionTrue, }, + }}, + }, "gateway-deleted")), + }, + expectedUpdates: []client.Object{ + &gwv1beta1.HTTPRoute{ + TypeMeta: metav1.TypeMeta{ + Kind: "HTTPRoute", + APIVersion: "gateway.networking.k8s.io/v1beta1", }, - }, { ObjectMeta: metav1.ObjectMeta{ - Name: "http-route-two", - Finalizers: []string{gatewayFinalizer}, + Name: "http-route-one", + // removing a finalizer + Finalizers: []string{}, }, Spec: gwv1beta1.HTTPRouteSpec{ CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }, { - Name: "gateway-two", - }}, + ParentRefs: []gwv1beta1.ParentReference{ + {Name: "gateway-deleted"}, + }, }, }, - Status: gwv1beta1.HTTPRouteStatus{ - RouteStatus: gwv1beta1.RouteStatus{ - Parents: []gwv1beta1.RouteParentStatus{{ - ControllerName: gwv1beta1.GatewayController(controllerName), - ParentRef: gwv1beta1.ParentReference{Name: "gateway"}, - Conditions: []metav1.Condition{{ - Type: "Accepted", - Status: metav1.ConditionTrue, - }}, - }, { - ControllerName: gwv1beta1.GatewayController(controllerName), - ParentRef: gwv1beta1.ParentReference{Name: "gateway-two"}, - Conditions: []metav1.Condition{{ - Type: "Accepted", - Status: metav1.ConditionTrue, - }}, - }}, - }, + Status: gwv1beta1.HTTPRouteStatus{RouteStatus: gwv1beta1.RouteStatus{Parents: []gwv1alpha2.RouteParentStatus{}}}, + }, + &gwv1alpha2.TCPRoute{ + TypeMeta: metav1.TypeMeta{ + Kind: "TCPRoute", + APIVersion: "gateway.networking.k8s.io/v1beta1", }, - }}, - TCPRoutes: []gwv1alpha2.TCPRoute{{ ObjectMeta: metav1.ObjectMeta{ Name: "tcp-route-one", - Finalizers: []string{gatewayFinalizer}, + Finalizers: []string{}, }, Spec: gwv1alpha2.TCPRouteSpec{ CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, + ParentRefs: []gwv1beta1.ParentReference{ + {Name: "gateway-deleted"}, + }, }, }, - }, { + Status: gwv1alpha2.TCPRouteStatus{RouteStatus: gwv1beta1.RouteStatus{Parents: []gwv1alpha2.RouteParentStatus{}}}, + }, + addClassConfig(gwv1beta1.Gateway{ ObjectMeta: metav1.ObjectMeta{ - Name: "tcp-route-two", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }, { - Name: "gateway-two", - }}, - }, + Name: "gateway-deleted", + DeletionTimestamp: deletionTimestamp, + Finalizers: []string{}, }, - Status: gwv1alpha2.TCPRouteStatus{ - RouteStatus: gwv1beta1.RouteStatus{ - Parents: []gwv1beta1.RouteParentStatus{{ - ControllerName: gwv1beta1.GatewayController(controllerName), - ParentRef: gwv1beta1.ParentReference{Name: "gateway"}, - Conditions: []metav1.Condition{{ - Type: "Accepted", - Status: metav1.ConditionTrue, - }}, - }, { - ControllerName: gwv1beta1.GatewayController(controllerName), - ParentRef: gwv1beta1.ParentReference{Name: "gateway-two"}, - Conditions: []metav1.Condition{{ - Type: "Accepted", - Status: metav1.ConditionTrue, - }}, - }}, - }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: testGatewayClassName, + Listeners: []gwv1beta1.Listener{{ + TLS: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{ + {Name: "secret-one"}, + {Name: "secret-two"}, + }, + }, + }}, }, - }}, - ConsulHTTPRoutes: []api.HTTPRouteConfigEntry{{ + }), + }, + expectedConsulUpdates: []api.ConfigEntry{ + &api.HTTPRouteConfigEntry{ Kind: api.HTTPRoute, Name: "http-route-two", Meta: map[string]string{ - "k8s-name": "http-route-two", - "k8s-namespace": "", - "k8s-service-name": "http-route-two", - "managed-by": "consul-k8s-gateway-controller", + "k8s-name": "http-route-two", + "k8s-namespace": "", }, + // dropped ref to gateway Parents: []api.ResourceReference{{ Kind: api.APIGateway, Name: "gateway", - }, { - Kind: api.APIGateway, - Name: "gateway-two", }}, - }}, - ConsulTCPRoutes: []api.TCPRouteConfigEntry{{ + }, + &api.TCPRouteConfigEntry{ Kind: api.TCPRoute, Name: "tcp-route-two", Meta: map[string]string{ - "k8s-name": "tcp-route-two", - "k8s-namespace": "", - "k8s-service-name": "tcp-route-two", - "managed-by": "consul-k8s-gateway-controller", + "k8s-name": "tcp-route-two", + "k8s-namespace": "", }, + // dropped ref to gateway Parents: []api.ResourceReference{{ Kind: api.APIGateway, Name: "gateway", - }, { - Kind: api.APIGateway, - Name: "gateway-two", - }}, - }}, - ConsulInlineCertificates: []api.InlineCertificateConfigEntry{{ - Kind: api.InlineCertificate, - Name: "secret-one", - Meta: map[string]string{ - "k8s-name": "secret-one", - "k8s-namespace": "", - "k8s-service-name": "secret-one", - "managed-by": "consul-k8s-gateway-controller", - }, - }, { - Kind: api.InlineCertificate, - Name: "secret-two", - Meta: map[string]string{ - "k8s-name": "secret-two", - "k8s-namespace": "", - "k8s-service-name": "secret-two", - "managed-by": "consul-k8s-gateway-controller", - }, - }}, - }, - expected: Snapshot{ - Kubernetes: KubernetesSnapshot{ - StatusUpdates: []client.Object{ - &gwv1beta1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Name: "http-route-two", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }, { - Name: "gateway-two", - }}, - }, - }, - Status: gwv1beta1.HTTPRouteStatus{ - RouteStatus: gwv1beta1.RouteStatus{ - // removed gateway status - Parents: []gwv1beta1.RouteParentStatus{{ - ControllerName: gwv1beta1.GatewayController(controllerName), - ParentRef: gwv1beta1.ParentReference{Name: "gateway-two"}, - Conditions: []metav1.Condition{{ - Type: "Accepted", - Status: metav1.ConditionTrue, - }}, - }}, - }, - }, - }, - &gwv1alpha2.TCPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Name: "tcp-route-two", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }, { - Name: "gateway-two", - }}, - }, - }, - // removed gateway status - Status: gwv1alpha2.TCPRouteStatus{ - RouteStatus: gwv1beta1.RouteStatus{ - Parents: []gwv1beta1.RouteParentStatus{{ - ControllerName: gwv1beta1.GatewayController(controllerName), - ParentRef: gwv1beta1.ParentReference{Name: "gateway-two"}, - Conditions: []metav1.Condition{{ - Type: "Accepted", - Status: metav1.ConditionTrue, - }}, - }}, - }, - }, - }, - }, - Updates: []client.Object{ - &gwv1beta1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Name: "http-route-one", - Finalizers: []string{}, - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, - }, - }, - }, - &gwv1alpha2.TCPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Name: "tcp-route-one", - Finalizers: []string{}, - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, - }, - }, - }, - &gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - DeletionTimestamp: deletionTimestamp, - Finalizers: []string{}, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gatewayClassName, - Listeners: []gwv1beta1.Listener{{ - TLS: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{{ - Name: "secret-one", - }, { - Name: "secret-two", - }}, - }, - }}, - }, - }, - }, - }, - Consul: ConsulSnapshot{ - Updates: []api.ConfigEntry{ - &api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "http-route-two", - Meta: map[string]string{ - "k8s-name": "http-route-two", - "k8s-namespace": "", - "k8s-service-name": "http-route-two", - "managed-by": "consul-k8s-gateway-controller", - }, - // dropped ref to gateway - Parents: []api.ResourceReference{{ - Kind: api.APIGateway, - Name: "gateway-two", - }}, - }, - &api.TCPRouteConfigEntry{ - Kind: api.TCPRoute, - Name: "tcp-route-two", - Meta: map[string]string{ - "k8s-name": "tcp-route-two", - "k8s-namespace": "", - "k8s-service-name": "tcp-route-two", - "managed-by": "consul-k8s-gateway-controller", - }, - // dropped ref to gateway - Parents: []api.ResourceReference{{ - Kind: api.APIGateway, - Name: "gateway-two", - }}, - }, - }, - Deletions: []api.ResourceReference{{ - Kind: api.HTTPRoute, - Name: "http-route-one", - }, { - Kind: api.TCPRoute, - Name: "tcp-route-one", - }, { - Kind: api.InlineCertificate, - Name: "secret-two", - }, { - Kind: api.APIGateway, - Name: "gateway", }}, }, }, + expectedConsulDeletions: []api.ResourceReference{ + {Kind: api.HTTPRoute, Name: "http-route-one"}, + {Kind: api.TCPRoute, Name: "tcp-route-one"}, + {Kind: api.InlineCertificate, Name: "secret-two"}, + {Kind: api.APIGateway, Name: "gateway-deleted"}, + }, }, } { t.Run(name, func(t *testing.T) { - tt.config.ControllerName = controllerName + tt.resources.gateways = append(tt.resources.gateways, tt.config.Gateway) + tt.resources.httpRoutes = append(tt.resources.httpRoutes, tt.config.HTTPRoutes...) + tt.resources.tcpRoutes = append(tt.resources.tcpRoutes, tt.config.TCPRoutes...) + tt.resources.consulHTTPRoutes = append(tt.resources.consulHTTPRoutes, tt.config.ConsulHTTPRoutes...) + tt.resources.consulTCPRoutes = append(tt.resources.consulTCPRoutes, tt.config.ConsulTCPRoutes...) + + tt.config.Resources = newTestResourceMap(t, tt.resources) + tt.config.ControllerName = testControllerName + tt.config.Logger = logrtest.NewTestLogger(t) tt.config.GatewayClassConfig = &v1alpha1.GatewayClassConfig{} serializeGatewayClassConfig(&tt.config.Gateway, tt.config.GatewayClassConfig) binder := NewBinder(tt.config) actual := binder.Snapshot() - diff := cmp.Diff(tt.expected, actual, cmp.FilterPath(func(p cmp.Path) bool { - return p.String() == "GatewayClassConfig" || strings.HasSuffix(p.String(), "LastTransitionTime") || strings.HasSuffix(p.String(), "Annotations") || strings.HasSuffix(p.String(), "UpsertGatewayDeployment") - }, cmp.Ignore())) - if diff != "" { - t.Error("undexpected diff", diff) - } + actualConsulUpdates := common.ConvertSliceFunc(actual.Consul.Updates, func(op *common.ConsulUpdateOperation) api.ConfigEntry { + return op.Entry + }) + + require.ElementsMatch(t, tt.expectedConsulUpdates, actualConsulUpdates, "consul updates differ", cmp.Diff(tt.expectedConsulUpdates, actualConsulUpdates)) + require.ElementsMatch(t, tt.expectedConsulDeletions, actual.Consul.Deletions, "consul deletions differ") + require.ElementsMatch(t, tt.expectedStatusUpdates, actual.Kubernetes.StatusUpdates.Operations(), "kubernetes statuses differ", cmp.Diff(tt.expectedStatusUpdates, actual.Kubernetes.StatusUpdates.Operations())) + require.ElementsMatch(t, tt.expectedUpdates, actual.Kubernetes.Updates.Operations(), "kubernetes updates differ", cmp.Diff(tt.expectedUpdates, actual.Kubernetes.Updates.Operations())) }) } } @@ -992,45 +782,26 @@ func TestBinder_Lifecycle(t *testing.T) { func TestBinder_Registrations(t *testing.T) { t.Parallel() - className := "gateway-class" - gatewayClassName := gwv1beta1.ObjectName(className) - controllerName := "test-controller" - deletionTimestamp := pointerTo(metav1.Now()) - gatewayClass := &gwv1beta1.GatewayClass{ - ObjectMeta: metav1.ObjectMeta{ - Name: className, - }, - Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: gwv1beta1.GatewayController(controllerName), - }, + setDeleted := func(gateway gwv1beta1.Gateway) gwv1beta1.Gateway { + gateway.DeletionTimestamp = deletionTimestamp + return gateway } - gatewayName := "gateway" for name, tt := range map[string]struct { config BinderConfig + resources resourceMapResources expectedRegistrations []string expectedDeregistrations []api.CatalogDeregistration }{ "deleting gateway with consul services": { - config: BinderConfig{ - ControllerName: controllerName, - GatewayClass: gatewayClass, - Gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: gatewayName, - Finalizers: []string{gatewayFinalizer}, - DeletionTimestamp: deletionTimestamp, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gatewayClassName, - }, - }, - GatewayServices: []api.CatalogService{ + config: controlledBinder(BinderConfig{ + Gateway: setDeleted(gatewayWithFinalizer(gwv1beta1.GatewaySpec{})), + ConsulGatewayServices: []api.CatalogService{ {Node: "test", ServiceID: "pod1", Namespace: "namespace1"}, {Node: "test", ServiceID: "pod2", Namespace: "namespace1"}, {Node: "test", ServiceID: "pod3", Namespace: "namespace1"}, }, - }, + }), expectedDeregistrations: []api.CatalogDeregistration{ {Node: "test", ServiceID: "pod1", Namespace: "namespace1"}, {Node: "test", ServiceID: "pod2", Namespace: "namespace1"}, @@ -1038,18 +809,8 @@ func TestBinder_Registrations(t *testing.T) { }, }, "gateway with consul services and mixed pods": { - config: BinderConfig{ - ControllerName: controllerName, - GatewayClass: gatewayClass, - Gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: gatewayName, - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gatewayClassName, - }, - }, + config: controlledBinder(BinderConfig{ + Gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), Pods: []corev1.Pod{ { ObjectMeta: metav1.ObjectMeta{Name: "pod1", Namespace: "namespace1"}, @@ -1072,12 +833,12 @@ func TestBinder_Registrations(t *testing.T) { }, }, }, - GatewayServices: []api.CatalogService{ + ConsulGatewayServices: []api.CatalogService{ {Node: "test", ServiceID: "pod1", Namespace: "namespace1"}, {Node: "test", ServiceID: "pod2", Namespace: "namespace1"}, {Node: "test", ServiceID: "pod3", Namespace: "namespace1"}, }, - }, + }), expectedRegistrations: []string{"pod1", "pod3", "pod4"}, expectedDeregistrations: []api.CatalogDeregistration{ {Node: "test", ServiceID: "pod2", Namespace: "namespace1"}, @@ -1085,7 +846,15 @@ func TestBinder_Registrations(t *testing.T) { }, } { t.Run(name, func(t *testing.T) { - tt.config.ControllerName = controllerName + tt.resources.gateways = append(tt.resources.gateways, tt.config.Gateway) + tt.resources.httpRoutes = append(tt.resources.httpRoutes, tt.config.HTTPRoutes...) + tt.resources.tcpRoutes = append(tt.resources.tcpRoutes, tt.config.TCPRoutes...) + tt.resources.consulHTTPRoutes = append(tt.resources.consulHTTPRoutes, tt.config.ConsulHTTPRoutes...) + tt.resources.consulTCPRoutes = append(tt.resources.consulTCPRoutes, tt.config.ConsulTCPRoutes...) + + tt.config.Resources = newTestResourceMap(t, tt.resources) + tt.config.ControllerName = testControllerName + tt.config.Logger = logrtest.NewTestLogger(t) tt.config.GatewayClassConfig = &v1alpha1.GatewayClassConfig{} serializeGatewayClassConfig(&tt.config.Gateway, tt.config.GatewayClassConfig) @@ -1098,7 +867,7 @@ func TestBinder_Registrations(t *testing.T) { expected := tt.expectedRegistrations[i] require.EqualValues(t, expected, registration.Service.ID) - require.EqualValues(t, gatewayName, registration.Service.Service) + require.EqualValues(t, "gateway", registration.Service.Service) } require.EqualValues(t, tt.expectedDeregistrations, actual.Consul.Deregistrations) @@ -1109,133 +878,114 @@ func TestBinder_Registrations(t *testing.T) { func TestBinder_BindingRulesKitchenSink(t *testing.T) { t.Parallel() - className := "gateway-class" - gatewayClassName := gwv1beta1.ObjectName(className) - controllerName := "test-controller" - gatewayClass := &gwv1beta1.GatewayClass{ - ObjectMeta: metav1.ObjectMeta{ - Name: className, - }, - Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: gwv1beta1.GatewayController(controllerName), - }, - } - - gateway := gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gatewayClassName, - Listeners: []gwv1beta1.Listener{{ - Name: "http-listener-default-same", - Protocol: gwv1beta1.HTTPProtocolType, - }, { - Name: "http-listener-hostname", - Protocol: gwv1beta1.HTTPProtocolType, - Hostname: pointerTo[gwv1beta1.Hostname]("host.name"), - }, { - Name: "http-listener-mismatched-kind-allowed", - Protocol: gwv1beta1.HTTPProtocolType, - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Kinds: []gwv1beta1.RouteGroupKind{{ - Kind: "Foo", - }}, - }, - }, { - Name: "http-listener-explicit-all-allowed", - Protocol: gwv1beta1.HTTPProtocolType, - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: pointerTo(gwv1beta1.NamespacesFromAll), - }, + gateway := gatewayWithFinalizer(gwv1beta1.GatewaySpec{ + Listeners: []gwv1beta1.Listener{{ + Name: "http-listener-default-same", + Protocol: gwv1beta1.HTTPProtocolType, + }, { + Name: "http-listener-hostname", + Protocol: gwv1beta1.HTTPProtocolType, + Hostname: common.PointerTo[gwv1beta1.Hostname]("host.name"), + }, { + Name: "http-listener-mismatched-kind-allowed", + Protocol: gwv1beta1.HTTPProtocolType, + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Kinds: []gwv1beta1.RouteGroupKind{{ + Kind: "Foo", + }}, + }, + }, { + Name: "http-listener-explicit-all-allowed", + Protocol: gwv1beta1.HTTPProtocolType, + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: common.PointerTo(gwv1beta1.NamespacesFromAll), }, - }, { - Name: "http-listener-explicit-allowed-same", - Protocol: gwv1beta1.HTTPProtocolType, - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: pointerTo(gwv1beta1.NamespacesFromSame), - }, + }, + }, { + Name: "http-listener-explicit-allowed-same", + Protocol: gwv1beta1.HTTPProtocolType, + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: common.PointerTo(gwv1beta1.NamespacesFromSame), }, - }, { - Name: "http-listener-allowed-selector", - Protocol: gwv1beta1.HTTPProtocolType, - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: pointerTo(gwv1beta1.NamespacesFromSelector), - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "test": "foo", - }, + }, + }, { + Name: "http-listener-allowed-selector", + Protocol: gwv1beta1.HTTPProtocolType, + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: common.PointerTo(gwv1beta1.NamespacesFromSelector), + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "test": "foo", }, }, }, - }, { - Name: "http-listener-tls", - Protocol: gwv1beta1.HTTPSProtocolType, - TLS: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{{ - Name: "secret-one", - }}, - }, - }, { - Name: "tcp-listener-default-same", - Protocol: gwv1beta1.TCPProtocolType, - }, { - Name: "tcp-listener-mismatched-kind-allowed", - Protocol: gwv1beta1.TCPProtocolType, - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Kinds: []gwv1beta1.RouteGroupKind{{ - Kind: "Foo", - }}, - }, - }, { - Name: "tcp-listener-explicit-all-allowed", - Protocol: gwv1beta1.TCPProtocolType, - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: pointerTo(gwv1beta1.NamespacesFromAll), - }, + }, + }, { + Name: "http-listener-tls", + Protocol: gwv1beta1.HTTPSProtocolType, + TLS: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{{ + Name: "secret-one", + }}, + }, + }, { + Name: "tcp-listener-default-same", + Protocol: gwv1beta1.TCPProtocolType, + }, { + Name: "tcp-listener-mismatched-kind-allowed", + Protocol: gwv1beta1.TCPProtocolType, + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Kinds: []gwv1beta1.RouteGroupKind{{ + Kind: "Foo", + }}, + }, + }, { + Name: "tcp-listener-explicit-all-allowed", + Protocol: gwv1beta1.TCPProtocolType, + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: common.PointerTo(gwv1beta1.NamespacesFromAll), }, - }, { - Name: "tcp-listener-explicit-allowed-same", - Protocol: gwv1beta1.TCPProtocolType, - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: pointerTo(gwv1beta1.NamespacesFromSame), - }, + }, + }, { + Name: "tcp-listener-explicit-allowed-same", + Protocol: gwv1beta1.TCPProtocolType, + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: common.PointerTo(gwv1beta1.NamespacesFromSame), }, - }, { - Name: "tcp-listener-allowed-selector", - Protocol: gwv1beta1.TCPProtocolType, - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: pointerTo(gwv1beta1.NamespacesFromSelector), - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "test": "foo", - }, + }, + }, { + Name: "tcp-listener-allowed-selector", + Protocol: gwv1beta1.TCPProtocolType, + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: common.PointerTo(gwv1beta1.NamespacesFromSelector), + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "test": "foo", }, }, }, - }, { - Name: "tcp-listener-tls", - Protocol: gwv1beta1.TCPProtocolType, - TLS: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{{ - Name: "secret-one", - }}, - }, - }}, - }, - } + }, + }, { + Name: "tcp-listener-tls", + Protocol: gwv1beta1.TCPProtocolType, + TLS: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{{ + Name: "secret-one", + }}, + }, + }}, + }) namespaces := map[string]corev1.Namespace{ - "": { + "default": { ObjectMeta: metav1.ObjectMeta{ - Name: "", + Name: "default", }, }, "test": { @@ -1248,1436 +998,1358 @@ func TestBinder_BindingRulesKitchenSink(t *testing.T) { }, } - defaultNamespacePointer := pointerTo[gwv1beta1.Namespace]("") + _, secretOne := generateTestCertificate(t, "", "secret-one") - httpTypeMeta := metav1.TypeMeta{} - httpTypeMeta.SetGroupVersionKind(gwv1beta1.SchemeGroupVersion.WithKind("HTTPRoute")) - tcpTypeMeta := metav1.TypeMeta{} - tcpTypeMeta.SetGroupVersionKind(gwv1beta1.SchemeGroupVersion.WithKind("TCPRoute")) + gateway.Namespace = "default" + defaultNamespacePointer := common.PointerTo[gwv1beta1.Namespace]("default") for name, tt := range map[string]struct { - httpRoute *gwv1beta1.HTTPRoute - expectedHTTPRouteUpdate *gwv1beta1.HTTPRoute - expectedHTTPRouteUpdateStatus *gwv1beta1.HTTPRoute - expectedHTTPConsulRouteUpdate *api.HTTPRouteConfigEntry - expectedHTTPConsulRouteDelete *api.ResourceReference - - tcpRoute *gwv1alpha2.TCPRoute - expectedTCPRouteUpdate *gwv1alpha2.TCPRoute - expectedTCPRouteUpdateStatus *gwv1alpha2.TCPRoute - expectedTCPConsulRouteUpdate *api.TCPRouteConfigEntry - expectedTCPConsulRouteDelete *api.ResourceReference + httpRoute *gwv1beta1.HTTPRoute + tcpRoute *gwv1alpha2.TCPRoute + referenceGrants []gwv1beta1.ReferenceGrant + expectedStatusUpdates []client.Object }{ "untargeted http route same namespace": { - httpRoute: &gwv1beta1.HTTPRoute{ - TypeMeta: httpTypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, - }, - }, - }, - expectedHTTPRouteUpdateStatus: &gwv1beta1.HTTPRoute{ - TypeMeta: httpTypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, - }, - }, - Status: gwv1beta1.HTTPRouteStatus{ - RouteStatus: gwv1beta1.RouteStatus{ - Parents: []gwv1beta1.RouteParentStatus{{ - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }}, - }, - }, + httpRoute: testHTTPRouteBackends("route", "default", nil, []gwv1beta1.ParentReference{ + {Name: "gateway"}, + }), + expectedStatusUpdates: []client.Object{ + testHTTPRouteStatusBackends("route", "default", nil, []gwv1beta1.RouteParentStatus{ + {ControllerName: testControllerName, ParentRef: gwv1beta1.ParentReference{Name: "gateway"}, Conditions: []metav1.Condition{ + { + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, + { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }, + }}, + }), }, }, "untargeted http route same namespace missing backend": { - httpRoute: &gwv1beta1.HTTPRoute{ - TypeMeta: httpTypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, - }, - Rules: []gwv1beta1.HTTPRouteRule{{ - BackendRefs: []gwv1beta1.HTTPBackendRef{{ - BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: gwv1beta1.ObjectName("backend"), - }, - }, - }}, + httpRoute: testHTTPRouteBackends("route", "default", []gwv1beta1.BackendObjectReference{ + {Name: gwv1beta1.ObjectName("backend")}, + }, []gwv1beta1.ParentReference{ + {Name: "gateway"}, + }), + expectedStatusUpdates: []client.Object{ + testHTTPRouteStatusBackends("route", "default", []gwv1beta1.BackendObjectReference{ + {Name: gwv1beta1.ObjectName("backend")}, + }, []gwv1beta1.RouteParentStatus{ + {ControllerName: testControllerName, ParentRef: gwv1beta1.ParentReference{Name: "gateway"}, Conditions: []metav1.Condition{ + { + Type: "ResolvedRefs", + Status: metav1.ConditionFalse, + Reason: "BackendNotFound", + Message: "default/backend: backend not found", + }, + { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }, }}, - }, - }, - expectedHTTPRouteUpdateStatus: &gwv1beta1.HTTPRoute{ - TypeMeta: httpTypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, - }, - Rules: []gwv1beta1.HTTPRouteRule{{ - BackendRefs: []gwv1beta1.HTTPBackendRef{{ - BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: gwv1beta1.ObjectName("backend"), - }, - }, - }}, - }}, - }, - Status: gwv1beta1.HTTPRouteStatus{ - RouteStatus: gwv1beta1.RouteStatus{ - Parents: []gwv1beta1.RouteParentStatus{{ - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionFalse, - Reason: "BackendNotFound", - Message: "/backend: backend not found", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }}, - }, - }, + }), }, }, "untargeted http route same namespace invalid backend type": { - httpRoute: &gwv1beta1.HTTPRoute{ - TypeMeta: httpTypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, - }, - Rules: []gwv1beta1.HTTPRouteRule{{ - BackendRefs: []gwv1beta1.HTTPBackendRef{{ - BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: gwv1beta1.ObjectName("backend"), - Group: pointerTo[gwv1beta1.Group]("invalid.foo.com"), - }, - }, - }}, - }}, - }, - }, - expectedHTTPRouteUpdateStatus: &gwv1beta1.HTTPRoute{ - TypeMeta: httpTypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, - }, - Rules: []gwv1beta1.HTTPRouteRule{{ - BackendRefs: []gwv1beta1.HTTPBackendRef{{ - BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: gwv1beta1.ObjectName("backend"), - Group: pointerTo[gwv1beta1.Group]("invalid.foo.com"), - }, - }, - }}, + httpRoute: testHTTPRouteBackends("route", "default", []gwv1beta1.BackendObjectReference{ + { + Name: gwv1beta1.ObjectName("backend"), + Group: common.PointerTo[gwv1beta1.Group]("invalid.foo.com"), + }, + }, []gwv1beta1.ParentReference{ + {Name: "gateway"}, + }), + expectedStatusUpdates: []client.Object{ + testHTTPRouteStatusBackends("route", "default", []gwv1beta1.BackendObjectReference{ + { + Name: gwv1beta1.ObjectName("backend"), + Group: common.PointerTo[gwv1beta1.Group]("invalid.foo.com"), + }, + }, []gwv1beta1.RouteParentStatus{ + {ControllerName: testControllerName, ParentRef: gwv1beta1.ParentReference{Name: "gateway"}, Conditions: []metav1.Condition{ + { + Type: "ResolvedRefs", + Status: metav1.ConditionFalse, + Reason: "InvalidKind", + Message: "default/backend [Service.invalid.foo.com]: invalid backend kind", + }, + { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }, }}, - }, - Status: gwv1beta1.HTTPRouteStatus{ - RouteStatus: gwv1beta1.RouteStatus{ - Parents: []gwv1beta1.RouteParentStatus{{ - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionFalse, - Reason: "InvalidKind", - Message: "/backend [Service.invalid.foo.com]: invalid backend kind", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }}, - }, - }, + }), }, }, "untargeted http route different namespace": { - httpRoute: &gwv1beta1.HTTPRoute{ - TypeMeta: httpTypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Namespace: "test", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - Namespace: defaultNamespacePointer, - }}, - }, - }, + httpRoute: testHTTPRouteBackends("route", "other", nil, []gwv1beta1.ParentReference{ + { + Name: "gateway", + Namespace: defaultNamespacePointer, + }, + }), + expectedStatusUpdates: []client.Object{ + testHTTPRouteStatusBackends("route", "other", nil, []gwv1beta1.RouteParentStatus{ + {ControllerName: testControllerName, ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + Namespace: defaultNamespacePointer, + }, Conditions: []metav1.Condition{ + { + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: routeListenerReferenceGrantErrorMessage, + }, + }}, + }), }, - expectedHTTPRouteUpdateStatus: &gwv1beta1.HTTPRoute{ - TypeMeta: httpTypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Namespace: "test", - Finalizers: []string{gatewayFinalizer}, + }, + "untargeted http route different namespace and reference grants": { + httpRoute: testHTTPRouteBackends("route", "other", nil, []gwv1beta1.ParentReference{ + { + Name: "gateway", + Namespace: defaultNamespacePointer, }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - Namespace: defaultNamespacePointer, - }}, + }), + referenceGrants: []gwv1beta1.ReferenceGrant{ + {ObjectMeta: metav1.ObjectMeta{Namespace: "default", Name: "grant"}, Spec: gwv1beta1.ReferenceGrantSpec{ + From: []gwv1beta1.ReferenceGrantFrom{ + {Group: gwv1beta1.GroupName, Kind: "HTTPRoute", Namespace: gwv1beta1.Namespace("other")}, }, - }, - Status: gwv1beta1.HTTPRouteStatus{ - RouteStatus: gwv1beta1.RouteStatus{ - Parents: []gwv1beta1.RouteParentStatus{{ - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }}, + To: []gwv1beta1.ReferenceGrantTo{ + {Group: gwv1beta1.GroupName, Kind: "Gateway"}, }, - }, + }}, + }, + expectedStatusUpdates: []client.Object{ + testHTTPRouteStatusBackends("route", "other", nil, []gwv1beta1.RouteParentStatus{ + {ControllerName: testControllerName, ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + Namespace: defaultNamespacePointer, + }, Conditions: []metav1.Condition{ + { + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }, + }}, + }), }, }, "targeted http route same namespace": { - httpRoute: &gwv1beta1.HTTPRoute{ - TypeMeta: httpTypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Finalizers: []string{gatewayFinalizer}, + httpRoute: testHTTPRouteBackends("route", "default", nil, []gwv1beta1.ParentReference{ + { + Name: "gateway", + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-default-same"), + }, { + Name: "gateway", + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-hostname"), + }, { + Name: "gateway", + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), + }, { + Name: "gateway", + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), + }, { + Name: "gateway", + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), + }, { + Name: "gateway", + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), + }, { + Name: "gateway", + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-tls"), + }, { + Name: "gateway", + SectionName: common.PointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-default-same"), - }, { - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-hostname"), - }, { - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), - }, { - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), - }, { - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), - }, { - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), - }, { - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-tls"), - }, { + }), + expectedStatusUpdates: []client.Object{ + testHTTPRouteStatusBackends("route", "default", nil, []gwv1beta1.RouteParentStatus{ + { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-default-same"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", }}, - }, - }, - }, - expectedHTTPRouteUpdateStatus: &gwv1beta1.HTTPRoute{ - TypeMeta: httpTypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-default-same"), - }, { - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-hostname"), - }, { + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), - }, { + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-hostname"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), - }, { + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-mismatched-kind-allowed: listener does not support route protocol", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), - }, { + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), - }, { + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-tls"), - }, { + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-allowed-selector: listener does not allow binding routes from the given namespace", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-tls"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", }}, - }, - }, - Status: gwv1beta1.HTTPRouteStatus{ - RouteStatus: gwv1beta1.RouteStatus{ - Parents: []gwv1beta1.RouteParentStatus{{ - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-default-same"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-hostname"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-mismatched-kind-allowed: listener does not support route protocol", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-allowed-selector: listener does not allow binding routes from the given namespace", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-tls"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "tcp-listener-explicit-all-allowed: listener does not support route protocol", - }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + SectionName: common.PointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "tcp-listener-explicit-all-allowed: listener does not support route protocol", }}, }, - }, + }), }, }, "targeted http route different namespace": { - httpRoute: &gwv1beta1.HTTPRoute{ - TypeMeta: httpTypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Namespace: "test", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-default-same"), - }, { - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-hostname"), - }, { - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), - }, { - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), - }, { - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), - }, { - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), - }, { - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-tls"), - }, { - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), - }}, + referenceGrants: []gwv1beta1.ReferenceGrant{ + {ObjectMeta: metav1.ObjectMeta{Namespace: "default", Name: "grant"}, Spec: gwv1beta1.ReferenceGrantSpec{ + From: []gwv1beta1.ReferenceGrantFrom{ + {Group: gwv1beta1.GroupName, Kind: "HTTPRoute", Namespace: gwv1beta1.Namespace("test")}, }, - }, + To: []gwv1beta1.ReferenceGrantTo{ + {Group: gwv1beta1.GroupName, Kind: "Gateway"}, + }, + }}, }, - expectedHTTPRouteUpdateStatus: &gwv1beta1.HTTPRoute{ - TypeMeta: httpTypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Namespace: "test", - Finalizers: []string{gatewayFinalizer}, + httpRoute: testHTTPRouteBackends("route", "test", nil, []gwv1beta1.ParentReference{ + { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-default-same"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-hostname"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-tls"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: common.PointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ + }), + expectedStatusUpdates: []client.Object{ + testHTTPRouteStatusBackends("route", "test", nil, []gwv1beta1.RouteParentStatus{ + { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-default-same"), - }, { + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-default-same"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-default-same: listener does not allow binding routes from the given namespace", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-hostname"), - }, { + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-hostname"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-hostname: listener does not allow binding routes from the given namespace", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), - }, { + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-mismatched-kind-allowed: listener does not support route protocol", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), - }, { + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), - }, { + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-explicit-allowed-same: listener does not allow binding routes from the given namespace", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), - }, { + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-tls"), - }, { + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-tls"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-tls: listener does not allow binding routes from the given namespace", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), + SectionName: common.PointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "tcp-listener-explicit-all-allowed: listener does not support route protocol", }}, }, - }, - Status: gwv1beta1.HTTPRouteStatus{ - RouteStatus: gwv1beta1.RouteStatus{ - Parents: []gwv1beta1.RouteParentStatus{{ - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-default-same"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-default-same: listener does not allow binding routes from the given namespace", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-hostname"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-hostname: listener does not allow binding routes from the given namespace", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-mismatched-kind-allowed: listener does not support route protocol", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-explicit-allowed-same: listener does not allow binding routes from the given namespace", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-tls"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-tls: listener does not allow binding routes from the given namespace", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "tcp-listener-explicit-all-allowed: listener does not support route protocol", - }}, - }}, - }, - }, + }), }, }, "untargeted tcp route same namespace": { - tcpRoute: &gwv1alpha2.TCPRoute{ - TypeMeta: tcpTypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, - }, - }, - }, - expectedTCPRouteUpdateStatus: &gwv1alpha2.TCPRoute{ - TypeMeta: tcpTypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, - }, - }, - Status: gwv1alpha2.TCPRouteStatus{ - RouteStatus: gwv1beta1.RouteStatus{ - Parents: []gwv1beta1.RouteParentStatus{{ - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }}, - }, - }, + tcpRoute: testTCPRouteBackends("route", "default", nil, []gwv1beta1.ParentReference{ + {Name: "gateway"}, + }), + expectedStatusUpdates: []client.Object{ + testTCPRouteStatusBackends("route", "default", nil, []gwv1beta1.RouteParentStatus{ + {ControllerName: testControllerName, ParentRef: gwv1beta1.ParentReference{Name: "gateway"}, Conditions: []metav1.Condition{ + { + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, + { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }, + }}, + }), }, }, "untargeted tcp route same namespace missing backend": { - tcpRoute: &gwv1alpha2.TCPRoute{ - TypeMeta: tcpTypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, - }, - Rules: []gwv1alpha2.TCPRouteRule{{ - BackendRefs: []gwv1beta1.BackendRef{{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: gwv1beta1.ObjectName("backend"), - }, - }}, + tcpRoute: testTCPRouteBackends("route", "default", []gwv1beta1.BackendObjectReference{ + {Name: gwv1beta1.ObjectName("backend")}, + }, []gwv1beta1.ParentReference{ + {Name: "gateway"}, + }), + expectedStatusUpdates: []client.Object{ + testTCPRouteStatusBackends("route", "default", []gwv1beta1.BackendObjectReference{ + {Name: gwv1beta1.ObjectName("backend")}, + }, []gwv1beta1.RouteParentStatus{ + {ControllerName: testControllerName, ParentRef: gwv1beta1.ParentReference{Name: "gateway"}, Conditions: []metav1.Condition{ + { + Type: "ResolvedRefs", + Status: metav1.ConditionFalse, + Reason: "BackendNotFound", + Message: "default/backend: backend not found", + }, + { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }, }}, - }, - }, - expectedTCPRouteUpdateStatus: &gwv1alpha2.TCPRoute{ - TypeMeta: tcpTypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, - }, - Rules: []gwv1alpha2.TCPRouteRule{{ - BackendRefs: []gwv1beta1.BackendRef{{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: gwv1beta1.ObjectName("backend"), - }, - }}, - }}, - }, - Status: gwv1alpha2.TCPRouteStatus{ - RouteStatus: gwv1beta1.RouteStatus{ - Parents: []gwv1beta1.RouteParentStatus{{ - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionFalse, - Reason: "BackendNotFound", - Message: "/backend: backend not found", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }}, - }, - }, + }), }, }, "untargeted tcp route same namespace invalid backend type": { - tcpRoute: &gwv1alpha2.TCPRoute{ - TypeMeta: tcpTypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, - }, - Rules: []gwv1alpha2.TCPRouteRule{{ - BackendRefs: []gwv1beta1.BackendRef{{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: gwv1beta1.ObjectName("backend"), - Group: pointerTo[gwv1beta1.Group]("invalid.foo.com"), - }, - }}, - }}, - }, - }, - expectedTCPRouteUpdateStatus: &gwv1alpha2.TCPRoute{ - TypeMeta: tcpTypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, - }, - Rules: []gwv1alpha2.TCPRouteRule{{ - BackendRefs: []gwv1beta1.BackendRef{{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: gwv1beta1.ObjectName("backend"), - Group: pointerTo[gwv1beta1.Group]("invalid.foo.com"), - }, - }}, + tcpRoute: testTCPRouteBackends("route", "default", []gwv1beta1.BackendObjectReference{ + { + Name: gwv1beta1.ObjectName("backend"), + Group: common.PointerTo[gwv1beta1.Group]("invalid.foo.com"), + }, + }, []gwv1beta1.ParentReference{ + {Name: "gateway"}, + }), + expectedStatusUpdates: []client.Object{ + testTCPRouteStatusBackends("route", "default", []gwv1beta1.BackendObjectReference{ + { + Name: gwv1beta1.ObjectName("backend"), + Group: common.PointerTo[gwv1beta1.Group]("invalid.foo.com"), + }, + }, []gwv1beta1.RouteParentStatus{ + {ControllerName: testControllerName, ParentRef: gwv1beta1.ParentReference{Name: "gateway"}, Conditions: []metav1.Condition{ + { + Type: "ResolvedRefs", + Status: metav1.ConditionFalse, + Reason: "InvalidKind", + Message: "default/backend [Service.invalid.foo.com]: invalid backend kind", + }, + { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }, }}, - }, - Status: gwv1alpha2.TCPRouteStatus{ - RouteStatus: gwv1beta1.RouteStatus{ - Parents: []gwv1beta1.RouteParentStatus{{ - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionFalse, - Reason: "InvalidKind", - Message: "/backend [Service.invalid.foo.com]: invalid backend kind", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }}, - }, - }, + }), }, }, "untargeted tcp route different namespace": { - tcpRoute: &gwv1alpha2.TCPRoute{ - TypeMeta: tcpTypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Namespace: "test", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - Namespace: defaultNamespacePointer, - }}, - }, - }, + tcpRoute: testTCPRouteBackends("route", "other", nil, []gwv1beta1.ParentReference{ + { + Name: "gateway", + Namespace: defaultNamespacePointer, + }, + }), + expectedStatusUpdates: []client.Object{ + testTCPRouteStatusBackends("route", "other", nil, []gwv1beta1.RouteParentStatus{ + {ControllerName: testControllerName, ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + Namespace: defaultNamespacePointer, + }, Conditions: []metav1.Condition{ + { + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: routeListenerReferenceGrantErrorMessage, + }, + }}, + }), }, - expectedTCPRouteUpdateStatus: &gwv1alpha2.TCPRoute{ - TypeMeta: tcpTypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Namespace: "test", - Finalizers: []string{gatewayFinalizer}, + }, + "untargeted tcp route different namespace and reference grants": { + tcpRoute: testTCPRouteBackends("route", "other", nil, []gwv1beta1.ParentReference{ + { + Name: "gateway", + Namespace: defaultNamespacePointer, }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - Namespace: defaultNamespacePointer, - }}, + }), + referenceGrants: []gwv1beta1.ReferenceGrant{ + {ObjectMeta: metav1.ObjectMeta{Namespace: "default", Name: "grant"}, Spec: gwv1beta1.ReferenceGrantSpec{ + From: []gwv1beta1.ReferenceGrantFrom{ + {Group: gwv1beta1.GroupName, Kind: "TCPRoute", Namespace: gwv1beta1.Namespace("other")}, }, - }, - Status: gwv1alpha2.TCPRouteStatus{ - RouteStatus: gwv1beta1.RouteStatus{ - Parents: []gwv1beta1.RouteParentStatus{{ - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }}, + To: []gwv1beta1.ReferenceGrantTo{ + {Group: gwv1beta1.GroupName, Kind: "Gateway"}, }, - }, + }}, + }, + expectedStatusUpdates: []client.Object{ + testTCPRouteStatusBackends("route", "other", nil, []gwv1beta1.RouteParentStatus{ + {ControllerName: testControllerName, ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + Namespace: defaultNamespacePointer, + }, Conditions: []metav1.Condition{ + { + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", + }, + }}, + }), }, }, "targeted tcp route same namespace": { - tcpRoute: &gwv1alpha2.TCPRoute{ - TypeMeta: tcpTypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-default-same"), - }, { - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-mismatched-kind-allowed"), - }, { - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), - }, { - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-allowed-same"), - }, { - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-allowed-selector"), - }, { - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-tls"), - }, { - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), - }}, - }, - }, - }, - expectedTCPRouteUpdateStatus: &gwv1alpha2.TCPRoute{ - TypeMeta: tcpTypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Finalizers: []string{gatewayFinalizer}, + tcpRoute: testTCPRouteBackends("route", "default", nil, []gwv1beta1.ParentReference{ + { + Name: "gateway", + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-default-same"), + }, { + Name: "gateway", + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-hostname"), + }, { + Name: "gateway", + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), + }, { + Name: "gateway", + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), + }, { + Name: "gateway", + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), + }, { + Name: "gateway", + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), + }, { + Name: "gateway", + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-tls"), + }, { + Name: "gateway", + SectionName: common.PointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-default-same"), - }, { - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-mismatched-kind-allowed"), - }, { - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), - }, { - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-allowed-same"), - }, { - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-allowed-selector"), - }, { - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-tls"), - }, { + }), + expectedStatusUpdates: []client.Object{ + testTCPRouteStatusBackends("route", "default", nil, []gwv1beta1.RouteParentStatus{ + { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), - }}, - }, - }, - Status: gwv1alpha2.TCPRouteStatus{ - RouteStatus: gwv1beta1.RouteStatus{ - Parents: []gwv1beta1.RouteParentStatus{{ - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-default-same"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-mismatched-kind-allowed"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "tcp-listener-mismatched-kind-allowed: listener does not support route protocol", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-allowed-same"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-allowed-selector"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "tcp-listener-allowed-selector: listener does not allow binding routes from the given namespace", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-tls"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-explicit-all-allowed: listener does not support route protocol", - }}, - }}, - }, - }, - }, - }, - "targeted tcp route different namespace": { - tcpRoute: &gwv1alpha2.TCPRoute{ - TypeMeta: tcpTypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Namespace: "test", - Finalizers: []string{gatewayFinalizer}, - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-default-same"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-default-same: listener does not support route protocol", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-default-same"), - }, { + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-hostname"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-hostname: listener does not support route protocol", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-mismatched-kind-allowed"), - }, { + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-mismatched-kind-allowed: listener does not support route protocol", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), - }, { + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-explicit-all-allowed: listener does not support route protocol", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-allowed-same"), - }, { + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-explicit-allowed-same: listener does not support route protocol", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-allowed-selector"), - }, { + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-allowed-selector: listener does not support route protocol", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-tls"), - }, { + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-tls"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-tls: listener does not support route protocol", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), + SectionName: common.PointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", }}, }, - }, + }), + }, + }, + "targeted tcp route different namespace": { + referenceGrants: []gwv1beta1.ReferenceGrant{ + {ObjectMeta: metav1.ObjectMeta{Namespace: "default", Name: "grant"}, Spec: gwv1beta1.ReferenceGrantSpec{ + From: []gwv1beta1.ReferenceGrantFrom{ + {Group: gwv1beta1.GroupName, Kind: "TCPRoute", Namespace: gwv1beta1.Namespace("test")}, + }, + To: []gwv1beta1.ReferenceGrantTo{ + {Group: gwv1beta1.GroupName, Kind: "Gateway"}, + }, + }}, }, - expectedTCPRouteUpdateStatus: &gwv1alpha2.TCPRoute{ - TypeMeta: tcpTypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - Namespace: "test", - Finalizers: []string{gatewayFinalizer}, + tcpRoute: testTCPRouteBackends("route", "test", nil, []gwv1beta1.ParentReference{ + { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-default-same"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-hostname"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-tls"), + }, { + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: common.PointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ + }), + expectedStatusUpdates: []client.Object{ + testTCPRouteStatusBackends("route", "test", nil, []gwv1beta1.RouteParentStatus{ + { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-default-same"), - }, { + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-default-same"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-default-same: listener does not support route protocol", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-mismatched-kind-allowed"), - }, { + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-hostname"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-hostname: listener does not support route protocol", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), - }, { + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-mismatched-kind-allowed: listener does not support route protocol", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-allowed-same"), - }, { + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-explicit-all-allowed: listener does not support route protocol", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-allowed-selector"), - }, { + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-explicit-allowed-same: listener does not support route protocol", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-tls"), - }, { + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-allowed-selector: listener does not support route protocol", + }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ Name: "gateway", Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), + SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-tls"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionFalse, + Reason: "NotAllowedByListeners", + Message: "http-listener-tls: listener does not support route protocol", }}, - }, - }, - Status: gwv1alpha2.TCPRouteStatus{ - RouteStatus: gwv1beta1.RouteStatus{ - Parents: []gwv1beta1.RouteParentStatus{{ - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-default-same"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "tcp-listener-default-same: listener does not allow binding routes from the given namespace", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-mismatched-kind-allowed"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "tcp-listener-mismatched-kind-allowed: listener does not support route protocol", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-allowed-same"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "tcp-listener-explicit-allowed-same: listener does not allow binding routes from the given namespace", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-allowed-selector"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("tcp-listener-tls"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "tcp-listener-tls: listener does not allow binding routes from the given namespace", - }}, - }, { - ControllerName: gatewayClass.Spec.ControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: pointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-explicit-all-allowed: listener does not support route protocol", - }}, + }, { + ControllerName: testControllerName, + ParentRef: gwv1beta1.ParentReference{ + Name: "gateway", + Namespace: defaultNamespacePointer, + SectionName: common.PointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), + }, + Conditions: []metav1.Condition{{ + Type: "ResolvedRefs", + Status: metav1.ConditionTrue, + Reason: "ResolvedRefs", + Message: "resolved backend references", + }, { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", }}, }, - }, + }), }, }, } { t.Run(name, func(t *testing.T) { - config := BinderConfig{ - ControllerName: controllerName, - GatewayClassConfig: &v1alpha1.GatewayClassConfig{}, - GatewayClass: gatewayClass, - Gateway: gateway, - Namespaces: namespaces, - ControlledGateways: map[types.NamespacedName]gwv1beta1.Gateway{ - {Name: "gateway"}: gateway, + g := *addClassConfig(gateway) + + resources := resourceMapResources{ + gateways: []gwv1beta1.Gateway{g}, + secrets: []corev1.Secret{ + secretOne, }, - Secrets: []corev1.Secret{{ - ObjectMeta: metav1.ObjectMeta{ - Name: "secret-one", - }, - }}, + grants: tt.referenceGrants, } - serializeGatewayClassConfig(&config.Gateway, config.GatewayClassConfig) if tt.httpRoute != nil { - config.HTTPRoutes = append(config.HTTPRoutes, *tt.httpRoute) + resources.httpRoutes = append(resources.httpRoutes, *tt.httpRoute) } if tt.tcpRoute != nil { - config.TCPRoutes = append(config.TCPRoutes, *tt.tcpRoute) + resources.tcpRoutes = append(resources.tcpRoutes, *tt.tcpRoute) } + config := controlledBinder(BinderConfig{ + Gateway: g, + GatewayClassConfig: &v1alpha1.GatewayClassConfig{}, + Namespaces: namespaces, + Resources: newTestResourceMap(t, resources), + HTTPRoutes: resources.httpRoutes, + TCPRoutes: resources.tcpRoutes, + }) + binder := NewBinder(config) actual := binder.Snapshot() - compareUpdates(t, tt.expectedHTTPRouteUpdate, actual.Kubernetes.Updates) - compareUpdates(t, tt.expectedTCPRouteUpdate, actual.Kubernetes.Updates) - compareUpdates(t, tt.expectedHTTPRouteUpdateStatus, actual.Kubernetes.StatusUpdates) - compareUpdates(t, tt.expectedTCPRouteUpdateStatus, actual.Kubernetes.StatusUpdates) + compareUpdates(t, tt.expectedStatusUpdates, actual.Kubernetes.StatusUpdates.Operations()) }) } } -func compareUpdates[T client.Object](t *testing.T, expected T, updates []client.Object) { +func compareUpdates(t *testing.T, expected []client.Object, actual []client.Object) { t.Helper() - if isNil(expected) { - for _, update := range updates { - if u, ok := update.(T); ok { - t.Error("found unexpected update", u) - } - } - } else { - found := false - for _, update := range updates { - if u, ok := update.(T); ok { - diff := cmp.Diff(expected, u, cmp.FilterPath(func(p cmp.Path) bool { - return p.String() == "Status.RouteStatus.Parents.Conditions.LastTransitionTime" - }, cmp.Ignore())) - if diff != "" { - t.Error("diff between actual and expected", diff) - } - found = true - } + filtered := common.Filter(actual, func(o client.Object) bool { + if _, ok := o.(*gwv1beta1.HTTPRoute); ok { + return false } - if !found { - t.Error("expected route update not found in", updates) + if _, ok := o.(*gwv1alpha2.TCPRoute); ok { + return false } + return true + }) + + require.ElementsMatch(t, expected, filtered, "statuses don't match", cmp.Diff(expected, filtered)) +} + +func addClassConfig(g gwv1beta1.Gateway) *gwv1beta1.Gateway { + serializeGatewayClassConfig(&g, &v1alpha1.GatewayClassConfig{}) + return &g +} + +func gatewayWithFinalizer(spec gwv1beta1.GatewaySpec) gwv1beta1.Gateway { + spec.GatewayClassName = testGatewayClassObjectName + + typeMeta := metav1.TypeMeta{} + typeMeta.SetGroupVersionKind(gwv1beta1.SchemeGroupVersion.WithKind("Gateway")) + + return gwv1beta1.Gateway{ + TypeMeta: typeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway", + Namespace: "default", + Finalizers: []string{common.GatewayFinalizer}, + }, + Spec: spec, + } +} + +func gatewayWithFinalizerStatus(spec gwv1beta1.GatewaySpec, status gwv1beta1.GatewayStatus) gwv1beta1.Gateway { + g := gatewayWithFinalizer(spec) + g.Status = status + return g +} + +func testHTTPRoute(name string, parents []string, services []string) gwv1beta1.HTTPRoute { + var parentRefs []gwv1beta1.ParentReference + var rules []gwv1beta1.HTTPRouteRule + + for _, parent := range parents { + parentRefs = append(parentRefs, gwv1beta1.ParentReference{Name: gwv1beta1.ObjectName(parent)}) + } + + for _, service := range services { + rules = append(rules, gwv1beta1.HTTPRouteRule{ + BackendRefs: []gwv1beta1.HTTPBackendRef{ + { + BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: gwv1beta1.ObjectName(service), + }, + }, + }, + }, + }) + } + + httpTypeMeta := metav1.TypeMeta{} + httpTypeMeta.SetGroupVersionKind(gwv1beta1.SchemeGroupVersion.WithKind("HTTPRoute")) + + return gwv1beta1.HTTPRoute{ + TypeMeta: httpTypeMeta, + ObjectMeta: metav1.ObjectMeta{Name: name, Finalizers: []string{common.GatewayFinalizer}}, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: parentRefs, + }, + Rules: rules, + }, + } +} + +func testHTTPRouteBackends(name, namespace string, services []gwv1beta1.BackendObjectReference, parents []gwv1beta1.ParentReference) *gwv1beta1.HTTPRoute { + var rules []gwv1beta1.HTTPRouteRule + for _, service := range services { + rules = append(rules, gwv1beta1.HTTPRouteRule{ + BackendRefs: []gwv1beta1.HTTPBackendRef{ + { + BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: service, + }, + }, + }, + }) + } + + httpTypeMeta := metav1.TypeMeta{} + httpTypeMeta.SetGroupVersionKind(gwv1beta1.SchemeGroupVersion.WithKind("HTTPRoute")) + + return &gwv1beta1.HTTPRoute{ + TypeMeta: httpTypeMeta, + ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace, Finalizers: []string{common.GatewayFinalizer}}, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: parents, + }, + Rules: rules, + }, + } +} + +func testHTTPRouteStatusBackends(name, namespace string, services []gwv1beta1.BackendObjectReference, parentStatuses []gwv1beta1.RouteParentStatus) *gwv1beta1.HTTPRoute { + var parentRefs []gwv1beta1.ParentReference + + for _, parent := range parentStatuses { + parentRefs = append(parentRefs, parent.ParentRef) + } + + route := testHTTPRouteBackends(name, namespace, services, parentRefs) + route.Status.RouteStatus.Parents = parentStatuses + return route +} + +func testHTTPRouteStatus(name string, services []string, parentStatuses []gwv1beta1.RouteParentStatus, extraParents ...string) gwv1beta1.HTTPRoute { + parentRefs := extraParents + + for _, parent := range parentStatuses { + parentRefs = append(parentRefs, string(parent.ParentRef.Name)) + } + + route := testHTTPRoute(name, parentRefs, services) + route.Status.RouteStatus.Parents = parentStatuses + + return route +} + +func testTCPRoute(name string, parents []string, services []string) gwv1alpha2.TCPRoute { + var parentRefs []gwv1beta1.ParentReference + var rules []gwv1alpha2.TCPRouteRule + + for _, parent := range parents { + parentRefs = append(parentRefs, gwv1beta1.ParentReference{Name: gwv1beta1.ObjectName(parent)}) + } + + for _, service := range services { + rules = append(rules, gwv1alpha2.TCPRouteRule{ + BackendRefs: []gwv1beta1.BackendRef{ + { + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: gwv1beta1.ObjectName(service), + }, + }, + }, + }) + } + + tcpTypeMeta := metav1.TypeMeta{} + tcpTypeMeta.SetGroupVersionKind(gwv1beta1.SchemeGroupVersion.WithKind("TCPRoute")) + + return gwv1alpha2.TCPRoute{ + TypeMeta: tcpTypeMeta, + ObjectMeta: metav1.ObjectMeta{Name: name, Finalizers: []string{common.GatewayFinalizer}}, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: parentRefs, + }, + Rules: rules, + }, + } +} + +func testTCPRouteBackends(name, namespace string, services []gwv1beta1.BackendObjectReference, parents []gwv1beta1.ParentReference) *gwv1alpha2.TCPRoute { + var rules []gwv1alpha2.TCPRouteRule + for _, service := range services { + rules = append(rules, gwv1alpha2.TCPRouteRule{ + BackendRefs: []gwv1beta1.BackendRef{ + {BackendObjectReference: service}, + }, + }) + } + + tcpTypeMeta := metav1.TypeMeta{} + tcpTypeMeta.SetGroupVersionKind(gwv1beta1.SchemeGroupVersion.WithKind("TCPRoute")) + + return &gwv1alpha2.TCPRoute{ + TypeMeta: tcpTypeMeta, + ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace, Finalizers: []string{common.GatewayFinalizer}}, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: parents, + }, + Rules: rules, + }, + } +} + +func testTCPRouteStatusBackends(name, namespace string, services []gwv1beta1.BackendObjectReference, parentStatuses []gwv1beta1.RouteParentStatus) *gwv1alpha2.TCPRoute { + var parentRefs []gwv1beta1.ParentReference + + for _, parent := range parentStatuses { + parentRefs = append(parentRefs, parent.ParentRef) + } + + route := testTCPRouteBackends(name, namespace, services, parentRefs) + route.Status.RouteStatus.Parents = parentStatuses + return route +} + +func testTCPRouteStatus(name string, services []string, parentStatuses []gwv1beta1.RouteParentStatus, extraParents ...string) gwv1alpha2.TCPRoute { + parentRefs := extraParents + + for _, parent := range parentStatuses { + parentRefs = append(parentRefs, string(parent.ParentRef.Name)) + } + + route := testTCPRoute(name, parentRefs, services) + route.Status.RouteStatus.Parents = parentStatuses + + return route +} + +func controlledBinder(config BinderConfig) BinderConfig { + config.ControllerName = testControllerName + config.GatewayClass = testGatewayClass + return config +} + +func generateTestCertificate(t *testing.T, namespace, name string) (*api.InlineCertificateConfigEntry, corev1.Secret) { + privateKey, err := rsa.GenerateKey(rand.Reader, 1024) + require.NoError(t, err) + + usage := x509.KeyUsageCertSign + expiration := time.Now().AddDate(10, 0, 0) + + cert := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: "consul.test", + }, + IsCA: true, + NotBefore: time.Now().Add(-10 * time.Minute), + NotAfter: expiration, + SubjectKeyId: []byte{1, 2, 3, 4, 6}, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: usage, + BasicConstraintsValid: true, + } + caCert := cert + caPrivateKey := privateKey + + data, err := x509.CreateCertificate(rand.Reader, cert, caCert, &privateKey.PublicKey, caPrivateKey) + require.NoError(t, err) + + certBytes := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: data, + }) + + privateKeyBytes := pem.EncodeToMemory(&pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(privateKey), + }) + + secret := corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + Data: map[string][]byte{ + corev1.TLSCertKey: certBytes, + corev1.TLSPrivateKeyKey: privateKeyBytes, + }, } + + certificate, err := (common.ResourceTranslator{}).ToInlineCertificate(secret) + require.NoError(t, err) + + return certificate, secret } diff --git a/control-plane/api-gateway/controllers/reference_validator.go b/control-plane/api-gateway/binding/reference_grant.go similarity index 58% rename from control-plane/api-gateway/controllers/reference_validator.go rename to control-plane/api-gateway/binding/reference_grant.go index 91b4c0ea51..12c0f3b048 100644 --- a/control-plane/api-gateway/controllers/reference_validator.go +++ b/control-plane/api-gateway/binding/reference_grant.go @@ -1,27 +1,37 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package controllers +package binding import ( - "context" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) -type ReferenceValidator struct { - client.Client +type referenceValidator struct { + grants map[string]map[types.NamespacedName]gwv1beta1.ReferenceGrant } -func NewReferenceValidator(client client.Client) *ReferenceValidator { - return &ReferenceValidator{ - client, +func NewReferenceValidator(grants []gwv1beta1.ReferenceGrant) common.ReferenceValidator { + byNamespace := make(map[string]map[types.NamespacedName]gwv1beta1.ReferenceGrant) + for _, grant := range grants { + grantsForNamespace, ok := byNamespace[grant.Namespace] + if !ok { + grantsForNamespace = make(map[types.NamespacedName]gwv1beta1.ReferenceGrant) + } + grantsForNamespace[client.ObjectKeyFromObject(&grant)] = grant + byNamespace[grant.Namespace] = grantsForNamespace + } + return &referenceValidator{ + grants: byNamespace, } } -func (rv *ReferenceValidator) GatewayCanReferenceSecret(ctx context.Context, gateway gwv1beta1.Gateway, secretRef gwv1beta1.SecretObjectReference) (bool, error) { +func (rv *referenceValidator) GatewayCanReferenceSecret(gateway gwv1beta1.Gateway, secretRef gwv1beta1.SecretObjectReference) bool { fromNS := gateway.GetNamespace() fromGK := metav1.GroupKind{ Group: gateway.GroupVersionKind().Group, @@ -30,12 +40,12 @@ func (rv *ReferenceValidator) GatewayCanReferenceSecret(ctx context.Context, gat // Kind should default to Secret if not set // https://github.com/kubernetes-sigs/gateway-api/blob/v0.6.2/apis/v1beta1/object_reference_types.go#LL59C21-L59C21 - toNS, toGK := createValuesFromRef(secretRef.Namespace, secretRef.Group, secretRef.Kind, "Secret") + toNS, toGK := createValuesFromRef(secretRef.Namespace, secretRef.Group, secretRef.Kind, "", common.KindSecret) - return referenceAllowed(ctx, fromGK, fromNS, toGK, toNS, string(secretRef.Name), rv.Client) + return rv.referenceAllowed(fromGK, fromNS, toGK, toNS, string(secretRef.Name)) } -func (rv *ReferenceValidator) HTTPRouteCanReferenceGateway(ctx context.Context, httproute gwv1beta1.HTTPRoute, parentRef gwv1beta1.ParentReference) (bool, error) { +func (rv *referenceValidator) HTTPRouteCanReferenceGateway(httproute gwv1beta1.HTTPRoute, parentRef gwv1beta1.ParentReference) bool { fromNS := httproute.GetNamespace() fromGK := metav1.GroupKind{ Group: httproute.GroupVersionKind().Group, @@ -44,12 +54,12 @@ func (rv *ReferenceValidator) HTTPRouteCanReferenceGateway(ctx context.Context, // Kind should default to Gateway if not set // https://github.com/kubernetes-sigs/gateway-api/blob/v0.6.2/apis/v1beta1/shared_types.go#L48 - toNS, toGK := createValuesFromRef(parentRef.Namespace, parentRef.Group, parentRef.Kind, "Gateway") + toNS, toGK := createValuesFromRef(parentRef.Namespace, parentRef.Group, parentRef.Kind, common.BetaGroup, common.KindGateway) - return referenceAllowed(ctx, fromGK, fromNS, toGK, toNS, string(parentRef.Name), rv.Client) + return rv.referenceAllowed(fromGK, fromNS, toGK, toNS, string(parentRef.Name)) } -func (rv *ReferenceValidator) HTTPRouteCanReferenceBackend(ctx context.Context, httproute gwv1beta1.HTTPRoute, backendRef gwv1beta1.BackendRef) (bool, error) { +func (rv *referenceValidator) HTTPRouteCanReferenceBackend(httproute gwv1beta1.HTTPRoute, backendRef gwv1beta1.BackendRef) bool { fromNS := httproute.GetNamespace() fromGK := metav1.GroupKind{ Group: httproute.GroupVersionKind().Group, @@ -58,13 +68,12 @@ func (rv *ReferenceValidator) HTTPRouteCanReferenceBackend(ctx context.Context, // Kind should default to Service if not set // https://github.com/kubernetes-sigs/gateway-api/blob/v0.6.2/apis/v1beta1/object_reference_types.go#L106 - toNS, toGK := createValuesFromRef(backendRef.Namespace, backendRef.Group, backendRef.Kind, "Service") - - return referenceAllowed(ctx, fromGK, fromNS, toGK, toNS, string(backendRef.Name), rv.Client) + toNS, toGK := createValuesFromRef(backendRef.Namespace, backendRef.Group, backendRef.Kind, "", common.KindService) + return rv.referenceAllowed(fromGK, fromNS, toGK, toNS, string(backendRef.Name)) } -func (rv *ReferenceValidator) TCPRouteCanReferenceGateway(ctx context.Context, tcpRoute gwv1alpha2.TCPRoute, parentRef gwv1beta1.ParentReference) (bool, error) { +func (rv *referenceValidator) TCPRouteCanReferenceGateway(tcpRoute gwv1alpha2.TCPRoute, parentRef gwv1beta1.ParentReference) bool { fromNS := tcpRoute.GetNamespace() fromGK := metav1.GroupKind{ Group: tcpRoute.GroupVersionKind().Group, @@ -73,12 +82,12 @@ func (rv *ReferenceValidator) TCPRouteCanReferenceGateway(ctx context.Context, t // Kind should default to Gateway if not set // https://github.com/kubernetes-sigs/gateway-api/blob/v0.6.2/apis/v1beta1/shared_types.go#L48 - toNS, toGK := createValuesFromRef(parentRef.Namespace, parentRef.Group, parentRef.Kind, "Gateway") + toNS, toGK := createValuesFromRef(parentRef.Namespace, parentRef.Group, parentRef.Kind, common.BetaGroup, common.KindGateway) - return referenceAllowed(ctx, fromGK, fromNS, toGK, toNS, string(parentRef.Name), rv.Client) + return rv.referenceAllowed(fromGK, fromNS, toGK, toNS, string(parentRef.Name)) } -func (rv *ReferenceValidator) TCPRouteCanReferenceBackend(ctx context.Context, tcpRoute gwv1alpha2.TCPRoute, backendRef gwv1beta1.BackendRef) (bool, error) { +func (rv *referenceValidator) TCPRouteCanReferenceBackend(tcpRoute gwv1alpha2.TCPRoute, backendRef gwv1beta1.BackendRef) bool { fromNS := tcpRoute.GetNamespace() fromGK := metav1.GroupKind{ Group: tcpRoute.GroupVersionKind().Group, @@ -87,20 +96,20 @@ func (rv *ReferenceValidator) TCPRouteCanReferenceBackend(ctx context.Context, t // Kind should default to Service if not set // https://github.com/kubernetes-sigs/gateway-api/blob/v0.6.2/apis/v1beta1/object_reference_types.go#L106 - toNS, toGK := createValuesFromRef(backendRef.Namespace, backendRef.Group, backendRef.Kind, "Service") - - return referenceAllowed(ctx, fromGK, fromNS, toGK, toNS, string(backendRef.Name), rv.Client) + toNS, toGK := createValuesFromRef(backendRef.Namespace, backendRef.Group, backendRef.Kind, common.BetaGroup, common.KindService) + return rv.referenceAllowed(fromGK, fromNS, toGK, toNS, string(backendRef.Name)) } -func createValuesFromRef(ns *gwv1beta1.Namespace, group *gwv1beta1.Group, kind *gwv1beta1.Kind, defaultKind string) (string, metav1.GroupKind) { +func createValuesFromRef(ns *gwv1beta1.Namespace, group *gwv1beta1.Group, kind *gwv1beta1.Kind, defaultGroup, defaultKind string) (string, metav1.GroupKind) { toNS := "" if ns != nil { toNS = string(*ns) } gk := metav1.GroupKind{ - Kind: defaultKind, + Kind: defaultKind, + Group: defaultGroup, } if group != nil { gk.Group = string(*group) @@ -119,22 +128,22 @@ func createValuesFromRef(ns *gwv1beta1.Namespace, group *gwv1beta1.Group, kind * // // For example, a Gateway in namespace "foo" may only reference a Secret in namespace "bar" // if a ReferenceGrant in namespace "bar" allows references from namespace "foo". -func referenceAllowed(ctx context.Context, fromGK metav1.GroupKind, fromNamespace string, toGK metav1.GroupKind, toNamespace, toName string, c client.Client) (bool, error) { +func (rv *referenceValidator) referenceAllowed(fromGK metav1.GroupKind, fromNamespace string, toGK metav1.GroupKind, toNamespace, toName string) bool { // Reference does not cross namespaces if toNamespace == "" || toNamespace == fromNamespace { - return true, nil + return true } // Fetch all ReferenceGrants in the referenced namespace - refGrants, err := getReferenceGrantsInNamespace(ctx, toNamespace, c) - if err != nil || len(refGrants) == 0 { - return false, err + grants, ok := rv.grants[toNamespace] + if !ok { + return false } - for _, refGrant := range refGrants { + for _, grant := range grants { // Check for a From that applies fromMatch := false - for _, from := range refGrant.Spec.From { + for _, from := range grant.Spec.From { if fromGK.Group == string(from.Group) && fromGK.Kind == string(from.Kind) && fromNamespace == string(from.Namespace) { fromMatch = true break @@ -146,32 +155,21 @@ func referenceAllowed(ctx context.Context, fromGK metav1.GroupKind, fromNamespac } // Check for a To that applies - for _, to := range refGrant.Spec.To { + for _, to := range grant.Spec.To { if toGK.Group == string(to.Group) && toGK.Kind == string(to.Kind) { if to.Name == nil || *to.Name == "" { // No name specified is treated as a wildcard within the namespace - return true, nil + return true } if gwv1beta1.ObjectName(toName) == *to.Name { // The ReferenceGrant specifically targets this object - return true, nil + return true } } } } // No ReferenceGrant was found which allows this cross-namespace reference - return false, nil -} - -// This function will get all reference grants in the given namespace. -func getReferenceGrantsInNamespace(ctx context.Context, namespace string, c client.Client) ([]gwv1beta1.ReferenceGrant, error) { - refGrantList := &gwv1beta1.ReferenceGrantList{} - if err := c.List(ctx, refGrantList, client.InNamespace(namespace)); err != nil { - return nil, err - } - refGrants := refGrantList.Items - - return refGrants, nil + return false } diff --git a/control-plane/api-gateway/controllers/reference_validator_test.go b/control-plane/api-gateway/binding/reference_grant_test.go similarity index 79% rename from control-plane/api-gateway/controllers/reference_validator_test.go rename to control-plane/api-gateway/binding/reference_grant_test.go index e507568ae1..a325a2e927 100644 --- a/control-plane/api-gateway/controllers/reference_validator_test.go +++ b/control-plane/api-gateway/binding/reference_grant_test.go @@ -1,18 +1,17 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package controllers +package binding import ( "context" - "k8s.io/apimachinery/pkg/runtime" + "testing" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - "testing" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client/fake" ) const ( @@ -34,7 +33,7 @@ func TestGatewayCanReferenceSecret(t *testing.T) { objName := gwv1beta1.ObjectName("mysecret") - basicValidReferenceGrant := &gwv1beta1.ReferenceGrant{ + basicValidReferenceGrant := gwv1beta1.ReferenceGrant{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ Namespace: ToNamespace, @@ -67,7 +66,7 @@ func TestGatewayCanReferenceSecret(t *testing.T) { ctx context.Context gateway gwv1beta1.Gateway secret gwv1beta1.SecretObjectReference - k8sReferenceGrants []runtime.Object + k8sReferenceGrants []gwv1beta1.ReferenceGrant }{ "gateway allowed to secret": { canReference: true, @@ -90,7 +89,7 @@ func TestGatewayCanReferenceSecret(t *testing.T) { Namespace: &secretRefNamespace, Name: objName, }, - k8sReferenceGrants: []runtime.Object{ + k8sReferenceGrants: []gwv1beta1.ReferenceGrant{ basicValidReferenceGrant, }, }, @@ -98,15 +97,9 @@ func TestGatewayCanReferenceSecret(t *testing.T) { for name, tc := range cases { t.Run(name, func(t *testing.T) { - s := runtime.NewScheme() - require.NoError(t, gwv1beta1.Install(s)) - require.NoError(t, gwv1beta1.AddToScheme(s)) + rv := NewReferenceValidator(tc.k8sReferenceGrants) + canReference := rv.GatewayCanReferenceSecret(tc.gateway, tc.secret) - client := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(tc.k8sReferenceGrants...).Build() - rv := NewReferenceValidator(client) - canReference, err := rv.GatewayCanReferenceSecret(tc.ctx, tc.gateway, tc.secret) - - require.Equal(t, tc.err, err) require.Equal(t, tc.canReference, canReference) }) } @@ -117,7 +110,7 @@ func TestHTTPRouteCanReferenceGateway(t *testing.T) { objName := gwv1beta1.ObjectName("mygateway") - basicValidReferenceGrant := &gwv1beta1.ReferenceGrant{ + basicValidReferenceGrant := gwv1beta1.ReferenceGrant{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ Namespace: ToNamespace, @@ -150,7 +143,7 @@ func TestHTTPRouteCanReferenceGateway(t *testing.T) { ctx context.Context httpRoute gwv1beta1.HTTPRoute gatewayRef gwv1beta1.ParentReference - k8sReferenceGrants []runtime.Object + k8sReferenceGrants []gwv1beta1.ReferenceGrant }{ "httproute allowed to gateway": { canReference: true, @@ -175,7 +168,7 @@ func TestHTTPRouteCanReferenceGateway(t *testing.T) { SectionName: nil, Port: nil, }, - k8sReferenceGrants: []runtime.Object{ + k8sReferenceGrants: []gwv1beta1.ReferenceGrant{ basicValidReferenceGrant, }, }, @@ -183,15 +176,9 @@ func TestHTTPRouteCanReferenceGateway(t *testing.T) { for name, tc := range cases { t.Run(name, func(t *testing.T) { - s := runtime.NewScheme() - require.NoError(t, gwv1beta1.Install(s)) - require.NoError(t, gwv1beta1.AddToScheme(s)) - - client := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(tc.k8sReferenceGrants...).Build() - rv := NewReferenceValidator(client) - canReference, err := rv.HTTPRouteCanReferenceGateway(tc.ctx, tc.httpRoute, tc.gatewayRef) + rv := NewReferenceValidator(tc.k8sReferenceGrants) + canReference := rv.HTTPRouteCanReferenceGateway(tc.httpRoute, tc.gatewayRef) - require.Equal(t, tc.err, err) require.Equal(t, tc.canReference, canReference) }) } @@ -202,7 +189,7 @@ func TestHTTPRouteCanReferenceBackend(t *testing.T) { objName := gwv1beta1.ObjectName("myBackendRef") - basicValidReferenceGrant := &gwv1beta1.ReferenceGrant{ + basicValidReferenceGrant := gwv1beta1.ReferenceGrant{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ Namespace: ToNamespace, @@ -235,7 +222,7 @@ func TestHTTPRouteCanReferenceBackend(t *testing.T) { ctx context.Context httpRoute gwv1beta1.HTTPRoute backendRef gwv1beta1.BackendRef - k8sReferenceGrants []runtime.Object + k8sReferenceGrants []gwv1beta1.ReferenceGrant }{ "httproute allowed to gateway": { canReference: true, @@ -262,7 +249,7 @@ func TestHTTPRouteCanReferenceBackend(t *testing.T) { }, Weight: nil, }, - k8sReferenceGrants: []runtime.Object{ + k8sReferenceGrants: []gwv1beta1.ReferenceGrant{ basicValidReferenceGrant, }, }, @@ -270,15 +257,9 @@ func TestHTTPRouteCanReferenceBackend(t *testing.T) { for name, tc := range cases { t.Run(name, func(t *testing.T) { - s := runtime.NewScheme() - require.NoError(t, gwv1beta1.Install(s)) - require.NoError(t, gwv1beta1.AddToScheme(s)) - - client := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(tc.k8sReferenceGrants...).Build() - rv := NewReferenceValidator(client) - canReference, err := rv.HTTPRouteCanReferenceBackend(tc.ctx, tc.httpRoute, tc.backendRef) + rv := NewReferenceValidator(tc.k8sReferenceGrants) + canReference := rv.HTTPRouteCanReferenceBackend(tc.httpRoute, tc.backendRef) - require.Equal(t, tc.err, err) require.Equal(t, tc.canReference, canReference) }) } @@ -289,7 +270,7 @@ func TestTCPRouteCanReferenceGateway(t *testing.T) { objName := gwv1beta1.ObjectName("mygateway") - basicValidReferenceGrant := &gwv1beta1.ReferenceGrant{ + basicValidReferenceGrant := gwv1beta1.ReferenceGrant{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ Namespace: ToNamespace, @@ -322,7 +303,7 @@ func TestTCPRouteCanReferenceGateway(t *testing.T) { ctx context.Context tcpRoute gwv1alpha2.TCPRoute gatewayRef gwv1beta1.ParentReference - k8sReferenceGrants []runtime.Object + k8sReferenceGrants []gwv1beta1.ReferenceGrant }{ "tcpRoute allowed to gateway": { canReference: true, @@ -347,7 +328,7 @@ func TestTCPRouteCanReferenceGateway(t *testing.T) { SectionName: nil, Port: nil, }, - k8sReferenceGrants: []runtime.Object{ + k8sReferenceGrants: []gwv1beta1.ReferenceGrant{ basicValidReferenceGrant, }, }, @@ -355,15 +336,9 @@ func TestTCPRouteCanReferenceGateway(t *testing.T) { for name, tc := range cases { t.Run(name, func(t *testing.T) { - s := runtime.NewScheme() - require.NoError(t, gwv1beta1.Install(s)) - require.NoError(t, gwv1beta1.AddToScheme(s)) + rv := NewReferenceValidator(tc.k8sReferenceGrants) + canReference := rv.TCPRouteCanReferenceGateway(tc.tcpRoute, tc.gatewayRef) - client := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(tc.k8sReferenceGrants...).Build() - rv := NewReferenceValidator(client) - canReference, err := rv.TCPRouteCanReferenceGateway(tc.ctx, tc.tcpRoute, tc.gatewayRef) - - require.Equal(t, tc.err, err) require.Equal(t, tc.canReference, canReference) }) } @@ -374,7 +349,7 @@ func TestTCPRouteCanReferenceBackend(t *testing.T) { objName := gwv1beta1.ObjectName("myBackendRef") - basicValidReferenceGrant := &gwv1beta1.ReferenceGrant{ + basicValidReferenceGrant := gwv1beta1.ReferenceGrant{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ Namespace: ToNamespace, @@ -407,7 +382,7 @@ func TestTCPRouteCanReferenceBackend(t *testing.T) { ctx context.Context tcpRoute gwv1alpha2.TCPRoute backendRef gwv1beta1.BackendRef - k8sReferenceGrants []runtime.Object + k8sReferenceGrants []gwv1beta1.ReferenceGrant }{ "tcpRoute allowed to gateway": { canReference: true, @@ -434,7 +409,7 @@ func TestTCPRouteCanReferenceBackend(t *testing.T) { }, Weight: nil, }, - k8sReferenceGrants: []runtime.Object{ + k8sReferenceGrants: []gwv1beta1.ReferenceGrant{ basicValidReferenceGrant, }, }, @@ -442,15 +417,9 @@ func TestTCPRouteCanReferenceBackend(t *testing.T) { for name, tc := range cases { t.Run(name, func(t *testing.T) { - s := runtime.NewScheme() - require.NoError(t, gwv1beta1.Install(s)) - require.NoError(t, gwv1beta1.AddToScheme(s)) - - client := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(tc.k8sReferenceGrants...).Build() - rv := NewReferenceValidator(client) - canReference, err := rv.TCPRouteCanReferenceBackend(tc.ctx, tc.tcpRoute, tc.backendRef) + rv := NewReferenceValidator(tc.k8sReferenceGrants) + canReference := rv.TCPRouteCanReferenceBackend(tc.tcpRoute, tc.backendRef) - require.Equal(t, tc.err, err) require.Equal(t, tc.canReference, canReference) }) } @@ -461,7 +430,7 @@ func TestReferenceAllowed(t *testing.T) { objName := gwv1beta1.ObjectName("myObject") - basicValidReferenceGrant := &gwv1beta1.ReferenceGrant{ + basicValidReferenceGrant := gwv1beta1.ReferenceGrant{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ Namespace: ToNamespace, @@ -493,7 +462,7 @@ func TestReferenceAllowed(t *testing.T) { toGK metav1.GroupKind toNamespace string toName string - k8sReferenceGrants []runtime.Object + k8sReferenceGrants []gwv1beta1.ReferenceGrant }{ "same namespace": { refAllowed: true, @@ -510,8 +479,8 @@ func TestReferenceAllowed(t *testing.T) { }, toNamespace: FromNamespace, toName: string(objName), - k8sReferenceGrants: []runtime.Object{ - &gwv1beta1.ReferenceGrant{ + k8sReferenceGrants: []gwv1beta1.ReferenceGrant{ + { TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ Namespace: FromNamespace, @@ -550,7 +519,7 @@ func TestReferenceAllowed(t *testing.T) { }, toNamespace: ToNamespace, toName: string(objName), - k8sReferenceGrants: []runtime.Object{ + k8sReferenceGrants: []gwv1beta1.ReferenceGrant{ basicValidReferenceGrant, }, }, @@ -569,7 +538,7 @@ func TestReferenceAllowed(t *testing.T) { }, toNamespace: ToNamespace, toName: string(objName), - k8sReferenceGrants: []runtime.Object{ + k8sReferenceGrants: []gwv1beta1.ReferenceGrant{ basicValidReferenceGrant, }, }, @@ -605,8 +574,8 @@ func TestReferenceAllowed(t *testing.T) { }, toNamespace: ToNamespace, toName: string(objName), - k8sReferenceGrants: []runtime.Object{ - &gwv1beta1.ReferenceGrant{ + k8sReferenceGrants: []gwv1beta1.ReferenceGrant{ + { TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ Namespace: ToNamespace, @@ -634,15 +603,9 @@ func TestReferenceAllowed(t *testing.T) { for name, tc := range cases { t.Run(name, func(t *testing.T) { - s := runtime.NewScheme() - require.NoError(t, gwv1beta1.Install(s)) - require.NoError(t, gwv1beta1.AddToScheme(s)) - - client := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(tc.k8sReferenceGrants...).Build() - - refAllowed, err := referenceAllowed(tc.ctx, tc.fromGK, tc.fromNamespace, tc.toGK, tc.toNamespace, tc.toName, client) + rv := NewReferenceValidator(tc.k8sReferenceGrants).(*referenceValidator) + refAllowed := rv.referenceAllowed(tc.fromGK, tc.fromNamespace, tc.toGK, tc.toNamespace, tc.toName) - require.Equal(t, tc.err, err) require.Equal(t, tc.refAllowed, refAllowed) }) } diff --git a/control-plane/api-gateway/binding/references.go b/control-plane/api-gateway/binding/references.go deleted file mode 100644 index ec598c6364..0000000000 --- a/control-plane/api-gateway/binding/references.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package binding - -import ( - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -// referenceTracker acts as a reference counting object for: -// 1. the number of controlled gateways that are referenced by an HTTPRoute -// 2. the number of controlled gateways that are referenced by a TCPRoute -// 3. the number of gateways that reference a certificate Secret -// -// These are used for determining when dissasociating from a gateway -// should cause us to cleanup a route or certificate both in Consul and -// whatever state we have set on the object in Kubernetes. -type referenceTracker struct { - httpRouteReferencesGateways map[types.NamespacedName]int - tcpRouteReferencesGateways map[types.NamespacedName]int - certificatesReferencedByGateways map[types.NamespacedName]int -} - -// isLastReference checks if the given gateway is the last controlled gateway -// that a route references. If it is and the gateway has been deleted, we -// should clean up all state created for the route. -func (r referenceTracker) isLastReference(object client.Object) bool { - key := types.NamespacedName{ - Namespace: object.GetNamespace(), - Name: object.GetName(), - } - - switch object.(type) { - case *gwv1alpha2.TCPRoute: - return r.tcpRouteReferencesGateways[key] == 1 - case *gwv1beta1.HTTPRoute: - return r.httpRouteReferencesGateways[key] == 1 - default: - return false - } -} - -// canGCSecret checks if we can garbage collect a secret that has -// not been upserted. -func (r referenceTracker) canGCSecret(key types.NamespacedName) bool { - // should this be 1 or 0? - return r.certificatesReferencedByGateways[key] == 1 -} - -// references initializes a referenceTracker based on the HTTPRoutes, TCPRoutes, -// and ControlledGateways associated with this Binder. -func (b *Binder) references() referenceTracker { - tracker := referenceTracker{ - httpRouteReferencesGateways: make(map[types.NamespacedName]int), - tcpRouteReferencesGateways: make(map[types.NamespacedName]int), - certificatesReferencedByGateways: make(map[types.NamespacedName]int), - } - - for _, route := range b.config.HTTPRoutes { - references := map[types.NamespacedName]struct{}{} - for _, ref := range route.Spec.ParentRefs { - for _, gateway := range b.config.ControlledGateways { - parentName := string(ref.Name) - parentNamespace := valueOr(ref.Namespace, route.Namespace) - if nilOrEqual(ref.Group, betaGroup) && - nilOrEqual(ref.Kind, kindGateway) && - gateway.Namespace == parentNamespace && - gateway.Name == parentName { - // the route references a gateway we control, store the ref to this gateway - references[types.NamespacedName{ - Namespace: parentNamespace, - Name: parentName, - }] = struct{}{} - } - } - } - tracker.httpRouteReferencesGateways[types.NamespacedName{ - Namespace: route.Namespace, - Name: route.Name, - }] = len(references) - } - - for _, route := range b.config.TCPRoutes { - references := map[types.NamespacedName]struct{}{} - for _, ref := range route.Spec.ParentRefs { - for _, gateway := range b.config.ControlledGateways { - parentName := string(ref.Name) - parentNamespace := valueOr(ref.Namespace, route.Namespace) - if nilOrEqual(ref.Group, betaGroup) && - nilOrEqual(ref.Kind, kindGateway) && - gateway.Namespace == parentNamespace && - gateway.Name == parentName { - // the route references a gateway we control, store the ref to this gateway - references[types.NamespacedName{ - Namespace: parentNamespace, - Name: parentName, - }] = struct{}{} - } - } - } - tracker.tcpRouteReferencesGateways[types.NamespacedName{ - Namespace: route.Namespace, - Name: route.Name, - }] = len(references) - } - - for _, gateway := range b.config.ControlledGateways { - references := map[types.NamespacedName]struct{}{} - for _, listener := range gateway.Spec.Listeners { - if listener.TLS == nil { - continue - } - for _, ref := range listener.TLS.CertificateRefs { - if nilOrEqual(ref.Group, "") && - nilOrEqual(ref.Kind, kindSecret) { - // the gateway references a secret, store it - references[types.NamespacedName{ - Namespace: valueOr(ref.Namespace, gateway.Namespace), - Name: string(ref.Name), - }] = struct{}{} - } - } - } - - for ref := range references { - count := tracker.certificatesReferencedByGateways[ref] - tracker.certificatesReferencedByGateways[ref] = count + 1 - } - } - - return tracker -} diff --git a/control-plane/api-gateway/binding/result.go b/control-plane/api-gateway/binding/result.go index 0fd291b0ef..65198eeaf4 100644 --- a/control-plane/api-gateway/binding/result.go +++ b/control-plane/api-gateway/binding/result.go @@ -9,11 +9,23 @@ import ( "sort" "strings" + mapset "github.com/deckarep/golang-set" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) +var ( + // override function for tests. + timeFunc = metav1.Now +) + +var ( + // This is used for any error related to a lack of proper reference grant creation. + errRefNotPermitted = errors.New("reference not permitted due to lack of ReferenceGrant") +) + var ( // Each of the below are specified in the Gateway spec under RouteConditionReason // the general usage is that each error is specified as errRoute* where * corresponds @@ -25,7 +37,6 @@ var ( errRouteNoMatchingListenerHostname = errors.New("listener cannot bind route with a non-aligned hostname") errRouteInvalidKind = errors.New("invalid backend kind") errRouteBackendNotFound = errors.New("backend not found") - errRouteRefNotPermitted = errors.New("reference not permitted due to lack of ReferenceGrant") ) // routeValidationResult holds the result of validating a route globally, in other @@ -44,8 +55,8 @@ type routeValidationResult struct { // a validation error. func (v routeValidationResult) Type() string { return (&metav1.GroupKind{ - Group: valueOr(v.backend.Group, ""), - Kind: valueOr(v.backend.Kind, "Service"), + Group: common.ValueOr(v.backend.Group, ""), + Kind: common.ValueOr(v.backend.Kind, common.KindService), }).String() } @@ -81,7 +92,7 @@ func (e routeValidationResults) Condition() metav1.Condition { Reason: "BackendNotFound", Message: fmt.Sprintf("%s: %s", v.String(), err.Error()), } - case errRouteRefNotPermitted: + case errRefNotPermitted: return metav1.Condition{ Type: "ResolvedRefs", Status: metav1.ConditionFalse, @@ -167,6 +178,10 @@ func (b bindResults) Condition() metav1.Condition { if result.err == errRouteNoMatchingListenerHostname { reason = "NoMatchingListenerHostname" } + // or if we have a ref not permitted, then use that + if result.err == errRefNotPermitted { + reason = "RefNotPermitted" + } } } @@ -188,6 +203,18 @@ type parentBindResult struct { // attempted to bind to a gateway using its parent references. type parentBindResults []parentBindResult +func (p parentBindResults) boundSections() mapset.Set { + set := mapset.NewSet() + for _, result := range p { + for _, r := range result.results { + if r.err == nil { + set.Add(string(r.section)) + } + } + } + return set +} + var ( // Each of the below are specified in the Gateway spec under ListenerConditionReason // the general usage is that each error is specified as errListener* where * corresponds @@ -200,6 +227,7 @@ var ( errListenerProtocolConflict = errors.New("listener protocol conflicts with another listener") errListenerInvalidCertificateRef_NotFound = errors.New("certificate not found") errListenerInvalidCertificateRef_NotSupported = errors.New("certificate type is not supported") + errListenerInvalidCertificateRef_InvalidData = errors.New("certificate is invalid or does not contain a supported server name") // Below is where any custom generic listener validation errors should go. // We map anything under here to a custom ListenerConditionReason of Invalid on @@ -223,7 +251,7 @@ type listenerValidationResult struct { // acceptedCondition constructs the condition for the Accepted status type. func (l listenerValidationResult) acceptedCondition(generation int64) metav1.Condition { - now := metav1.Now() + now := timeFunc() switch l.acceptedErr { case errListenerPortUnavailable: return metav1.Condition{ @@ -267,7 +295,7 @@ func (l listenerValidationResult) acceptedCondition(generation int64) metav1.Con // conflictedCondition constructs the condition for the Conflicted status type. func (l listenerValidationResult) conflictedCondition(generation int64) metav1.Condition { - now := metav1.Now() + now := timeFunc() switch l.conflictedErr { case errListenerProtocolConflict: @@ -302,10 +330,10 @@ func (l listenerValidationResult) conflictedCondition(generation int64) metav1.C // acceptedCondition constructs the condition for the ResolvedRefs status type. func (l listenerValidationResult) resolvedRefsCondition(generation int64) metav1.Condition { - now := metav1.Now() + now := timeFunc() switch l.refErr { - case errListenerInvalidCertificateRef_NotFound: + case errListenerInvalidCertificateRef_NotFound, errListenerInvalidCertificateRef_NotSupported, errListenerInvalidCertificateRef_InvalidData: return metav1.Condition{ Type: "ResolvedRefs", Status: metav1.ConditionFalse, @@ -314,11 +342,11 @@ func (l listenerValidationResult) resolvedRefsCondition(generation int64) metav1 Message: l.refErr.Error(), LastTransitionTime: now, } - case errListenerInvalidCertificateRef_NotSupported: + case errRefNotPermitted: return metav1.Condition{ Type: "ResolvedRefs", Status: metav1.ConditionFalse, - Reason: "InvalidCertificateRef", + Reason: "RefNotPermitted", ObservedGeneration: generation, Message: l.refErr.Error(), LastTransitionTime: now, @@ -387,7 +415,7 @@ type gatewayValidationResult struct { // programmedCondition returns a condition for the Programmed status type. func (l gatewayValidationResult) programmedCondition(generation int64) metav1.Condition { - now := metav1.Now() + now := timeFunc() switch l.programmedErr { case errGatewayPending_Pods, errGatewayPending_Consul: @@ -415,7 +443,7 @@ func (l gatewayValidationResult) programmedCondition(generation int64) metav1.Co // for whether or not any of the gateway's listeners are invalid, if they are, it overrides whatever // Reason is set as an error on the result and instead uses the ListenersNotValid reason. func (l gatewayValidationResult) acceptedCondition(generation int64, listenersInvalid bool) metav1.Condition { - now := metav1.Now() + now := timeFunc() if l.acceptedErr == nil { if listenersInvalid { diff --git a/control-plane/api-gateway/binding/route_binding.go b/control-plane/api-gateway/binding/route_binding.go index 5175c8f647..187d579852 100644 --- a/control-plane/api-gateway/binding/route_binding.go +++ b/control-plane/api-gateway/binding/route_binding.go @@ -4,10 +4,9 @@ package binding import ( - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/translation" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + mapset "github.com/deckarep/golang-set" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul/api" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" @@ -15,237 +14,91 @@ import ( gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) -// consulHTTPRouteFor returns the Consul HTTPRouteConfigEntry for the given reference. -func (b *Binder) consulHTTPRouteFor(ref api.ResourceReference) *api.HTTPRouteConfigEntry { - for _, route := range b.config.ConsulHTTPRoutes { - if route.Namespace == ref.Namespace && route.Partition == ref.Partition && route.Name == ref.Name { - return &route - } - } - return nil -} - -// consulTCPRouteFor returns the Consul TCPRouteConfigEntry for the given reference. -func (b *Binder) consulTCPRouteFor(ref api.ResourceReference) *api.TCPRouteConfigEntry { - for _, route := range b.config.ConsulTCPRoutes { - if route.Namespace == ref.Namespace && route.Partition == ref.Partition && route.Name == ref.Name { - return &route - } - } - return nil -} - -// routeBinder encapsulates the binding logic for binding a route to the given Gateway. -// The logic for route binding is almost identical between different route types, but -// due to the strong typing in the Spec and Go's inability to deal with fields via generics -// we have to pull in a bunch of accessors (which ideally should be in the upstream spec) -// for each route type. -// -// From the generic signature -- T: the type of Kubernetes route, U: the type of Consul config entry -// -// TODO: consider moving the function closures to something like an interface that we can -// implement the accessors on for each route type. -type routeBinder[T client.Object, U api.ConfigEntry] struct { - // isGatewayDeleted is used to determine whether we should just ignore - // attempting to bind the route (since we no longer know whether we - // should manage the route we only want to remove any state we've - // set on it). - isGatewayDeleted bool - // gateway is the gateway that we want to use for binding - gateway *gwv1beta1.Gateway - // gatewayRef is a Consul reference used to prune no-longer bound - // parents from a Consul resource we've created. - gatewayRef api.ResourceReference - // tracker is the referenceTracker used to determine when we want to cleanup - // routes based on a deleted gateway. - tracker referenceTracker - // namespaces is the set of namespaces in Consul that use for determining - // whether a route in a given namespace can bind to a gateway with AllowedRoutes set - namespaces map[string]corev1.Namespace - // services is a catalog of all connect-injected services to check a route against - // for resolving its backend refs - services map[types.NamespacedName]api.CatalogService - // meshServices is a catalog of all mesh service objects to check a route against - // for resolving its backend refs - meshServices map[types.NamespacedName]v1alpha1.MeshService - - // translationReferenceFunc is a function used to translate a Kubernetes object into - // a Consul object reference - translationReferenceFunc func(route T) api.ResourceReference - // lookupFunc is a function used for finding an existing Consul object based on - // its object reference - lookupFunc func(api.ResourceReference) U - // getParentsFunc is a function used for getting the parent references of a Consul route object - getParentsFunc func(U) []api.ResourceReference - // setParentsFunc is a function used for setting the parent references of a route object - setParentsFunc func(U, []api.ResourceReference) - // removeStatusRefsFunc is a function used for removing the statuses for the given parent - // references from a route - removeStatusRefsFunc func(T, []gwv1beta1.ParentReference) bool - // getHostnamesFunc is a function used for getting the hostnames associated with a route - getHostnamesFunc func(T) []gwv1beta1.Hostname - // getParentRefsFunc is used for getting the parent references of a Kubernetes route object - getParentRefsFunc func(T) []gwv1beta1.ParentReference - // translationFunc is used for translating a Kubernetes route into the corresponding Consul config entry - translationFunc func(T, map[types.NamespacedName]api.ResourceReference, map[types.NamespacedName]api.CatalogService, map[types.NamespacedName]v1alpha1.MeshService) U - // setRouteConditionFunc is used for adding or overwriting a condition on a route at the given - // parent - setRouteConditionFunc func(T, *gwv1beta1.ParentReference, metav1.Condition) bool - // getBackendRefsFunc returns a list of all backend references that we need to validate against the - // list of known connect-injected services - getBackendRefsFunc func(T) []gwv1beta1.BackendRef - // removeControllerStatusFunc is used to remove all of the statuses set by our controller when GC'ing - // a route - removeControllerStatusFunc func(T) bool -} - -// newRouteBinder creates a new route binder for the given Kubernetes and Consul route types -// generally this is lightly wrapped by other constructors that pass in the various closures -// needed for accessing fields on the objects. -func newRouteBinder[T client.Object, U api.ConfigEntry]( - isGatewayDeleted bool, - gateway *gwv1beta1.Gateway, - gatewayRef api.ResourceReference, - namespaces map[string]corev1.Namespace, - services map[types.NamespacedName]api.CatalogService, - meshServices map[types.NamespacedName]v1alpha1.MeshService, - tracker referenceTracker, - translationReferenceFunc func(route T) api.ResourceReference, - lookupFunc func(api.ResourceReference) U, - getParentsFunc func(U) []api.ResourceReference, - setParentsFunc func(U, []api.ResourceReference), - removeStatusRefsFunc func(T, []gwv1beta1.ParentReference) bool, - getHostnamesFunc func(T) []gwv1beta1.Hostname, - getParentRefsFunc func(T) []gwv1beta1.ParentReference, - translationFunc func(T, map[types.NamespacedName]api.ResourceReference, map[types.NamespacedName]api.CatalogService, map[types.NamespacedName]v1alpha1.MeshService) U, - setRouteConditionFunc func(T, *gwv1beta1.ParentReference, metav1.Condition) bool, - getBackendRefsFunc func(T) []gwv1beta1.BackendRef, - removeControllerStatusFunc func(T) bool, -) *routeBinder[T, U] { - return &routeBinder[T, U]{ - isGatewayDeleted: isGatewayDeleted, - gateway: gateway, - gatewayRef: gatewayRef, - namespaces: namespaces, - services: services, - meshServices: meshServices, - tracker: tracker, - translationReferenceFunc: translationReferenceFunc, - lookupFunc: lookupFunc, - getParentsFunc: getParentsFunc, - setParentsFunc: setParentsFunc, - removeStatusRefsFunc: removeStatusRefsFunc, - getHostnamesFunc: getHostnamesFunc, - getParentRefsFunc: getParentRefsFunc, - translationFunc: translationFunc, - setRouteConditionFunc: setRouteConditionFunc, - getBackendRefsFunc: getBackendRefsFunc, - removeControllerStatusFunc: removeControllerStatusFunc, - } -} - -// bind contains the main logic for binding a route to a given gateway. -func (r *routeBinder[T, U]) bind(route T, boundCount map[gwv1beta1.SectionName]int, seenRoutes map[api.ResourceReference]struct{}, snapshot Snapshot) (updatedSnapshot Snapshot) { - routeRef := r.translationReferenceFunc(route) - existing := r.lookupFunc(routeRef) - gatewayRefs := filterParentRefs(objectToMeta(r.gateway), route.GetNamespace(), r.getParentRefsFunc(route)) - - // mark this route as having been processed - seenRoutes[routeRef] = struct{}{} +// bindRoute contains the main logic for binding a route to a given gateway. +func (r *Binder) bindRoute(route client.Object, boundCount map[gwv1beta1.SectionName]int, snapshot *Snapshot) { + // use the non-normalized key since we can't write back enterprise metadata + // on non-enterprise installations + routeConsulKey := r.config.Translator.NonNormalizedConfigEntryReference(entryKind(route), client.ObjectKeyFromObject(route)) + filteredParents := filterParentRefs(r.key, route.GetNamespace(), getRouteParents(route)) // flags to mark that some operation needs to occur - consulNeedsDelete := false kubernetesNeedsUpdate := false kubernetesNeedsStatusUpdate := false - // Since the update can either be for an existing resource (in the case - // of a deleted gateway) or for a resource generated by translating a - // bound gateway we just set the resource that we want to push out an - // update for. If this is not nil, we push it into the snapshot. - var consulUpdate U // we do this in a closure at the end to make sure we don't accidentally // add something multiple times into the list of update/delete operations // instead we just set a flag indicating that an update is needed and then // append to the snapshot right before returning defer func() { - if !isNil(consulUpdate) { - snapshot.Consul.Updates = append(snapshot.Consul.Updates, consulUpdate) - } - if consulNeedsDelete { - snapshot.Consul.Deletions = append(snapshot.Consul.Deletions, routeRef) - } if kubernetesNeedsUpdate { - snapshot.Kubernetes.Updates = append(snapshot.Kubernetes.Updates, route) + snapshot.Kubernetes.Updates.Add(route) } if kubernetesNeedsStatusUpdate { - snapshot.Kubernetes.StatusUpdates = append(snapshot.Kubernetes.StatusUpdates, route) + snapshot.Kubernetes.StatusUpdates.Add(route) } - - updatedSnapshot = snapshot }() if isDeleted(route) { // mark the route as needing to get cleaned up if we detect that it's being deleted - consulNeedsDelete = true - if removeFinalizer(route) { + if common.RemoveFinalizer(route) { kubernetesNeedsUpdate = true } return } - if r.isGatewayDeleted { - // first check if this is our only ref for the route - if r.tracker.isLastReference(route) { - // if it is, then mark everything for deletion - consulNeedsDelete = true - if r.removeControllerStatusFunc(route) { - kubernetesNeedsStatusUpdate = true - } - if removeFinalizer(route) { - kubernetesNeedsUpdate = true - } - return + if r.isGatewayDeleted() { + if canGCOnUnbind(routeConsulKey, r.config.Resources) && common.RemoveFinalizer(route) { + kubernetesNeedsUpdate = true + } else { + // Remove the condition since we no longer know if we should + // control the route and drop any references for the Consul route. + // This only gets run if we can't GC the route at the end of this + // loop. + r.dropConsulRouteParent(snapshot, route, r.nonNormalizedConsulKey, r.config.Resources) } - // otherwise remove the condition since we no longer know if we should - // control the route and drop any references for the Consul route - if !isNil(existing) { - // this drops all the parent refs - r.setParentsFunc(existing, parentsForRoute(r.gatewayRef, r.getParentsFunc(existing), nil)) - // and then we mark the route as needing updated - consulUpdate = existing - // drop the status conditions - if r.removeStatusRefsFunc(route, gatewayRefs) { - kubernetesNeedsStatusUpdate = true - } + // drop the status conditions + if r.statusSetter.removeRouteReferences(route, filteredParents) { + kubernetesNeedsStatusUpdate = true } return } - if ensureFinalizer(route) { + if common.EnsureFinalizer(route) { kubernetesNeedsUpdate = true return } // TODO: scrub route refs from statuses that no longer exist - validation := validateRefs(route.GetNamespace(), r.getBackendRefsFunc(route), r.services, r.meshServices) + validation := validateRefs(route, getRouteBackends(route), r.config.Resources) // the spec is dumb and makes you set a parent for any status, even when the // status is not with respect to a parent, as is the case of resolved refs // so we need to set the status on all parents - for _, ref := range gatewayRefs { - if r.setRouteConditionFunc(route, &ref, validation.Condition()) { + for _, parent := range filteredParents { + if r.statusSetter.setRouteCondition(route, &parent, validation.Condition()) { kubernetesNeedsStatusUpdate = true } } - results := make(parentBindResults, 0) - namespace := r.namespaces[route.GetNamespace()] - gk := route.GetObjectKind().GroupVersionKind().GroupKind() - for _, ref := range gatewayRefs { - result := make(bindResults, 0) - for _, listener := range listenersFor(r.gateway, ref.SectionName) { - if !routeKindIsAllowedForListener(supportedKindsForProtocol[listener.Protocol], gk) { + namespace := r.config.Namespaces[route.GetNamespace()] + groupKind := route.GetObjectKind().GroupVersionKind().GroupKind() + + var results parentBindResults + + for _, ref := range filteredParents { + var result bindResults + + for _, listener := range listenersFor(&r.config.Gateway, ref.SectionName) { + if !canReferenceGateway(route, ref, r.config.Resources) { + result = append(result, bindResult{ + section: listener.Name, + err: errRefNotPermitted, + }) + continue + } + + if !routeKindIsAllowedForListener(supportedKindsForProtocol[listener.Protocol], groupKind) { result = append(result, bindResult{ section: listener.Name, err: errRouteNotAllowedByListeners_Protocol, @@ -253,7 +106,7 @@ func (r *routeBinder[T, U]) bind(route T, boundCount map[gwv1beta1.SectionName]i continue } - if !routeKindIsAllowedForListenerExplicit(listener.AllowedRoutes, gk) { + if !routeKindIsAllowedForListenerExplicit(listener.AllowedRoutes, groupKind) { result = append(result, bindResult{ section: listener.Name, err: errRouteNotAllowedByListeners_Protocol, @@ -261,7 +114,7 @@ func (r *routeBinder[T, U]) bind(route T, boundCount map[gwv1beta1.SectionName]i continue } - if !routeAllowedForListenerNamespaces(r.gateway.Namespace, listener.AllowedRoutes, namespace) { + if !routeAllowedForListenerNamespaces(r.config.Gateway.Namespace, listener.AllowedRoutes, namespace) { result = append(result, bindResult{ section: listener.Name, err: errRouteNotAllowedByListeners_Namespace, @@ -269,7 +122,7 @@ func (r *routeBinder[T, U]) bind(route T, boundCount map[gwv1beta1.SectionName]i continue } - if !routeAllowedForListenerHostname(listener.Hostname, r.getHostnamesFunc(route)) { + if !routeAllowedForListenerHostname(listener.Hostname, getRouteHostnames(route)) { result = append(result, bindResult{ section: listener.Name, err: errRouteNoMatchingListenerHostname, @@ -281,7 +134,7 @@ func (r *routeBinder[T, U]) bind(route T, boundCount map[gwv1beta1.SectionName]i section: listener.Name, }) - boundCount[listener.Name] = boundCount[listener.Name] + 1 + boundCount[listener.Name]++ } results = append(results, parentBindResult{ @@ -292,7 +145,7 @@ func (r *routeBinder[T, U]) bind(route T, boundCount map[gwv1beta1.SectionName]i updated := false for _, result := range results { - if r.setRouteConditionFunc(route, &result.parent, result.results.Condition()) { + if r.statusSetter.setRouteCondition(route, &result.parent, result.results.Condition()) { updated = true } } @@ -301,189 +154,285 @@ func (r *routeBinder[T, U]) bind(route T, boundCount map[gwv1beta1.SectionName]i kubernetesNeedsStatusUpdate = true } - entry := r.translationFunc(route, nil, r.services, r.meshServices) - // make all parent refs explicit based on what actually bound - if isNil(existing) { - r.setParentsFunc(entry, parentsForRoute(r.gatewayRef, nil, results)) - } else { - r.setParentsFunc(entry, parentsForRoute(r.gatewayRef, r.getParentsFunc(existing), results)) + r.mutateRouteWithBindingResults(snapshot, route, r.nonNormalizedConsulKey, r.config.Resources, results) +} + +// filterParentRefs returns the subset of parent references on a route that point to the given gateway. +func filterParentRefs(gateway types.NamespacedName, namespace string, refs []gwv1beta1.ParentReference) []gwv1beta1.ParentReference { + references := []gwv1beta1.ParentReference{} + for _, ref := range refs { + if common.NilOrEqual(ref.Group, common.BetaGroup) && + common.NilOrEqual(ref.Kind, common.KindGateway) && + gateway.Namespace == common.ValueOr(ref.Namespace, namespace) && + gateway.Name == string(ref.Name) { + references = append(references, ref) + } } - consulUpdate = entry - return + return references } -// newTCPRouteBinder wraps newRouteBinder with the proper closures needed for accessing TCPRoutes and their config entries. -func (b *Binder) newTCPRouteBinder(tracker referenceTracker, services map[types.NamespacedName]api.CatalogService, meshServices map[types.NamespacedName]v1alpha1.MeshService) *routeBinder[*gwv1alpha2.TCPRoute, *api.TCPRouteConfigEntry] { - return newRouteBinder( - b.isGatewayDeleted(), - &b.config.Gateway, - b.gatewayRef(), - b.config.Namespaces, - services, - meshServices, - tracker, - b.config.Translator.ReferenceForTCPRoute, - b.consulTCPRouteFor, - func(t *api.TCPRouteConfigEntry) []api.ResourceReference { return t.Parents }, - func(t *api.TCPRouteConfigEntry, parents []api.ResourceReference) { t.Parents = parents }, - b.statusSetter.removeTCPRouteReferences, - func(t *gwv1alpha2.TCPRoute) []gwv1beta1.Hostname { return nil }, - func(t *gwv1alpha2.TCPRoute) []gwv1beta1.ParentReference { return t.Spec.ParentRefs }, - b.config.Translator.TCPRouteToTCPRoute, - b.statusSetter.setTCPRouteCondition, - func(t *gwv1alpha2.TCPRoute) []gwv1beta1.BackendRef { - refs := []gwv1beta1.BackendRef{} - for _, rule := range t.Spec.Rules { - refs = append(refs, rule.BackendRefs...) - } - return refs - }, - b.statusSetter.removeTCPStatuses, - ) +// listenersFor returns the listeners corresponding the given section name. If the section +// name is actually specified, the returned set should just have one listener, if it is +// unspecified, the all gatweway listeners should be returned. +func listenersFor(gateway *gwv1beta1.Gateway, name *gwv1beta1.SectionName) []gwv1beta1.Listener { + listeners := []gwv1beta1.Listener{} + for _, listener := range gateway.Spec.Listeners { + if name == nil { + listeners = append(listeners, listener) + continue + } + if listener.Name == *name { + listeners = append(listeners, listener) + } + } + return listeners } -// newHTTPRouteBinder wraps newRouteBinder with the proper closures needed for accessing HTTPRoutes and their config entries. -func (b *Binder) newHTTPRouteBinder(tracker referenceTracker, services map[types.NamespacedName]api.CatalogService, meshServices map[types.NamespacedName]v1alpha1.MeshService) *routeBinder[*gwv1beta1.HTTPRoute, *api.HTTPRouteConfigEntry] { - return newRouteBinder( - b.isGatewayDeleted(), - &b.config.Gateway, - b.gatewayRef(), - b.config.Namespaces, - services, - meshServices, - tracker, - b.config.Translator.ReferenceForHTTPRoute, - b.consulHTTPRouteFor, - func(t *api.HTTPRouteConfigEntry) []api.ResourceReference { return t.Parents }, - func(t *api.HTTPRouteConfigEntry, parents []api.ResourceReference) { t.Parents = parents }, - b.statusSetter.removeHTTPRouteReferences, - func(t *gwv1beta1.HTTPRoute) []gwv1beta1.Hostname { return t.Spec.Hostnames }, - func(t *gwv1beta1.HTTPRoute) []gwv1beta1.ParentReference { return t.Spec.ParentRefs }, - b.config.Translator.HTTPRouteToHTTPRoute, - b.statusSetter.setHTTPRouteCondition, - func(t *gwv1beta1.HTTPRoute) []gwv1beta1.BackendRef { - refs := []gwv1beta1.BackendRef{} - for _, rule := range t.Spec.Rules { - for _, ref := range rule.BackendRefs { - refs = append(refs, ref.BackendRef) +func consulParentMatches(namespace string, gatewayKey api.ResourceReference, parent api.ResourceReference) bool { + gatewayKey = common.NormalizeMeta(gatewayKey) + + if parent.Namespace == "" { + parent.Namespace = namespace + } + if parent.Kind == "" { + parent.Kind = api.APIGateway + } + + parent = common.NormalizeMeta(parent) + + return parent.Kind == api.APIGateway && + parent.Name == gatewayKey.Name && + parent.Namespace == gatewayKey.Namespace && + parent.Partition == gatewayKey.Partition +} + +func (r *Binder) dropConsulRouteParent(snapshot *Snapshot, object client.Object, gateway api.ResourceReference, resources *common.ResourceMap) { + switch object.(type) { + case *gwv1beta1.HTTPRoute: + resources.MutateHTTPRoute(client.ObjectKeyFromObject(object), r.handleRouteSyncStatus(snapshot, object), func(entry api.HTTPRouteConfigEntry) api.HTTPRouteConfigEntry { + entry.Parents = common.Filter(entry.Parents, func(parent api.ResourceReference) bool { + return consulParentMatches(entry.Namespace, gateway, parent) + }) + return entry + }) + case *gwv1alpha2.TCPRoute: + resources.MutateTCPRoute(client.ObjectKeyFromObject(object), r.handleRouteSyncStatus(snapshot, object), func(entry api.TCPRouteConfigEntry) api.TCPRouteConfigEntry { + entry.Parents = common.Filter(entry.Parents, func(parent api.ResourceReference) bool { + return consulParentMatches(entry.Namespace, gateway, parent) + }) + return entry + }) + } +} + +func (r *Binder) mutateRouteWithBindingResults(snapshot *Snapshot, object client.Object, gatewayConsulKey api.ResourceReference, resources *common.ResourceMap, results parentBindResults) { + key := client.ObjectKeyFromObject(object) + + parents := mapset.NewSet() + // the normalized set keeps us from accidentally adding the same thing + // twice due to the Consul server normalizing our refs. + normalized := make(map[api.ResourceReference]api.ResourceReference) + for section := range results.boundSections().Iter() { + ref := api.ResourceReference{ + Kind: api.APIGateway, + Name: gatewayConsulKey.Name, + SectionName: section.(string), + Namespace: gatewayConsulKey.Namespace, + Partition: gatewayConsulKey.Partition, + } + parents.Add(ref) + normalized[common.NormalizeMeta(ref)] = ref + } + + switch object.(type) { + case *gwv1beta1.HTTPRoute: + resources.TranslateAndMutateHTTPRoute(key, r.handleRouteSyncStatus(snapshot, object), func(old *api.HTTPRouteConfigEntry, new api.HTTPRouteConfigEntry) api.HTTPRouteConfigEntry { + if old != nil { + for _, parent := range old.Parents { + // drop any references that already exist + if parents.Contains(parent) { + parents.Remove(parent) + } + if id, ok := normalized[parent]; ok { + parents.Remove(id) + } + } + + // set the old parent states + new.Parents = old.Parents + } + // and now add what is left + for parent := range parents.Iter() { + new.Parents = append(new.Parents, parent.(api.ResourceReference)) + } + return new + }) + case *gwv1alpha2.TCPRoute: + resources.TranslateAndMutateTCPRoute(key, r.handleRouteSyncStatus(snapshot, object), func(old *api.TCPRouteConfigEntry, new api.TCPRouteConfigEntry) api.TCPRouteConfigEntry { + if old != nil { + for _, parent := range old.Parents { + // drop any references that already exist + if parents.Contains(parent) { + parents.Remove(parent) + } } + + // set the old parent states + new.Parents = old.Parents } - return refs - }, - b.statusSetter.removeHTTPStatuses, - ) + // and now add what is left + for parent := range parents.Iter() { + new.Parents = append(new.Parents, parent.(api.ResourceReference)) + } + return new + }) + } } -// cleanRoute removes a gateway reference from the given route config entry -// and marks adds it to the snapshot if its mutated the entry at all. -func cleanRoute[T api.ConfigEntry]( - route T, - seenRoutes map[api.ResourceReference]struct{}, - snapshot Snapshot, - gatewayRef api.ResourceReference, - getParentsFunc func(T) []api.ResourceReference, - setParentsFunc func(T, []api.ResourceReference), -) Snapshot { - routeRef := translation.EntryToReference(route) - if _, ok := seenRoutes[routeRef]; !ok { - existingParents := getParentsFunc(route) - parents := parentsForRoute(gatewayRef, existingParents, nil) - if len(parents) == 0 { - // we can GC this now since we've dropped all refs from it - snapshot.Consul.Deletions = append(snapshot.Consul.Deletions, routeRef) - } else if len(existingParents) != len(parents) { - // we've mutated the length, which means this route needs an update - setParentsFunc(route, parents) - snapshot.Consul.Updates = append(snapshot.Consul.Updates, route) - } +func entryKind(object client.Object) string { + switch object.(type) { + case *gwv1beta1.HTTPRoute: + return api.HTTPRoute + case *gwv1alpha2.TCPRoute: + return api.TCPRoute } - return snapshot + return "" } -// cleanHTTPRoute wraps cleanRoute with the proper closures for HTTPRoute config entries. -func (b *Binder) cleanHTTPRoute(route *api.HTTPRouteConfigEntry, seenRoutes map[api.ResourceReference]struct{}, snapshot Snapshot) Snapshot { - return cleanRoute(route, seenRoutes, snapshot, b.gatewayRef(), - func(route *api.HTTPRouteConfigEntry) []api.ResourceReference { return route.Parents }, - func(route *api.HTTPRouteConfigEntry, parents []api.ResourceReference) { route.Parents = parents }, - ) +func canGCOnUnbind(id api.ResourceReference, resources *common.ResourceMap) bool { + switch id.Kind { + case api.HTTPRoute: + return resources.CanGCHTTPRouteOnUnbind(id) + case api.TCPRoute: + return resources.CanGCTCPRouteOnUnbind(id) + } + return true } -// cleanTCPRoute wraps cleanRoute with the proper closures for TCPRoute config entries. -func (b *Binder) cleanTCPRoute(route *api.TCPRouteConfigEntry, seenRoutes map[api.ResourceReference]struct{}, snapshot Snapshot) Snapshot { - return cleanRoute(route, seenRoutes, snapshot, b.gatewayRef(), - func(route *api.TCPRouteConfigEntry) []api.ResourceReference { return route.Parents }, - func(route *api.TCPRouteConfigEntry, parents []api.ResourceReference) { route.Parents = parents }, - ) +func getRouteHostnames(object client.Object) []gwv1beta1.Hostname { + switch v := object.(type) { + case *gwv1beta1.HTTPRoute: + return v.Spec.Hostnames + } + return nil } -// parentsForRoute constructs a list of Consul route parent references based on what parents actually bound -// on a given route. This is necessary due to the fact that some additional validation in Kubernetes might -// require a route not to actually be accepted by a gateway, whereas we may have laxer logic inside of Consul -// itself. In these cases we want to just drop the parent reference in the Consul config entry we are going -// to write in order for it not to succeed in binding where Kubernetes failed to bind. -func parentsForRoute(ref api.ResourceReference, existing []api.ResourceReference, results parentBindResults) []api.ResourceReference { - // store all section names that bound - parentSet := map[string]struct{}{} - for _, result := range results { - for _, r := range result.results { - if r.err == nil { - parentSet[string(r.section)] = struct{}{} - } - } +func getRouteParents(object client.Object) []gwv1beta1.ParentReference { + switch v := object.(type) { + case *gwv1beta1.HTTPRoute: + return v.Spec.ParentRefs + case *gwv1alpha2.TCPRoute: + return v.Spec.ParentRefs } + return nil +} - // first, filter out all of the parent refs that don't correspond to this gateway - parents := []api.ResourceReference{} - for _, parent := range existing { - if parent.Kind == api.APIGateway && - parent.Name == ref.Name && - parent.Namespace == ref.Namespace { - continue - } - parents = append(parents, parent) +func getRouteParentsStatus(object client.Object) []gwv1beta1.RouteParentStatus { + switch v := object.(type) { + case *gwv1beta1.HTTPRoute: + return v.Status.RouteStatus.Parents + case *gwv1alpha2.TCPRoute: + return v.Status.RouteStatus.Parents } + return nil +} - // now construct the bound set - for parent := range parentSet { - parents = append(parents, api.ResourceReference{ - Kind: api.APIGateway, - Name: ref.Name, - Namespace: ref.Namespace, - SectionName: parent, - }) +func setRouteParentsStatus(object client.Object, parents []gwv1beta1.RouteParentStatus) { + switch v := object.(type) { + case *gwv1beta1.HTTPRoute: + v.Status.RouteStatus.Parents = parents + case *gwv1alpha2.TCPRoute: + v.Status.RouteStatus.Parents = parents } - return parents } -// filterParentRefs returns the subset of parent references on a route that point to the given gateway. -func filterParentRefs(gateway types.NamespacedName, namespace string, refs []gwv1beta1.ParentReference) []gwv1beta1.ParentReference { - references := []gwv1beta1.ParentReference{} - for _, ref := range refs { - if nilOrEqual(ref.Group, betaGroup) && - nilOrEqual(ref.Kind, kindGateway) && - gateway.Namespace == valueOr(ref.Namespace, namespace) && - gateway.Name == string(ref.Name) { - references = append(references, ref) - } +func getRouteBackends(object client.Object) []gwv1beta1.BackendRef { + switch v := object.(type) { + case *gwv1beta1.HTTPRoute: + return common.Flatten(common.ConvertSliceFunc(v.Spec.Rules, func(rule gwv1beta1.HTTPRouteRule) []gwv1beta1.BackendRef { + return common.ConvertSliceFunc(rule.BackendRefs, func(rule gwv1beta1.HTTPBackendRef) gwv1beta1.BackendRef { + return rule.BackendRef + }) + })) + case *gwv1alpha2.TCPRoute: + return common.Flatten(common.ConvertSliceFunc(v.Spec.Rules, func(rule gwv1alpha2.TCPRouteRule) []gwv1beta1.BackendRef { + return rule.BackendRefs + })) } + return nil +} - return references +func canReferenceGateway(object client.Object, ref gwv1beta1.ParentReference, resources *common.ResourceMap) bool { + switch v := object.(type) { + case *gwv1beta1.HTTPRoute: + return resources.HTTPRouteCanReferenceGateway(*v, ref) + case *gwv1alpha2.TCPRoute: + return resources.TCPRouteCanReferenceGateway(*v, ref) + } + return false } -// listenersFor returns the listeners corresponding the given section name. If the section -// name is actually specified, the returned set should just have one listener, if it is -// unspecified, the all gatweway listeners should be returned. -func listenersFor(gateway *gwv1beta1.Gateway, name *gwv1beta1.SectionName) []gwv1beta1.Listener { - listeners := []gwv1beta1.Listener{} - for _, listener := range gateway.Spec.Listeners { - if name == nil { - listeners = append(listeners, listener) - continue +func canReferenceBackend(object client.Object, ref gwv1beta1.BackendRef, resources *common.ResourceMap) bool { + switch v := object.(type) { + case *gwv1beta1.HTTPRoute: + return resources.HTTPRouteCanReferenceBackend(*v, ref) + case *gwv1alpha2.TCPRoute: + return resources.TCPRouteCanReferenceBackend(*v, ref) + } + return false +} + +func (r *Binder) handleRouteSyncStatus(snapshot *Snapshot, object client.Object) func(error) { + return func(err error) { + condition := metav1.Condition{ + Type: "Synced", + Status: metav1.ConditionTrue, + ObservedGeneration: object.GetGeneration(), + LastTransitionTime: timeFunc(), + Reason: "Synced", + Message: "route synced to Consul", } - if listener.Name == *name { - listeners = append(listeners, listener) + if err != nil { + condition = metav1.Condition{ + Type: "Synced", + Status: metav1.ConditionFalse, + ObservedGeneration: object.GetGeneration(), + LastTransitionTime: timeFunc(), + Reason: "SyncError", + Message: err.Error(), + } + } + if r.statusSetter.setRouteConditionOnAllRefs(object, condition) { + snapshot.Kubernetes.StatusUpdates.Add(object) + } + } +} + +func (r *Binder) handleGatewaySyncStatus(snapshot *Snapshot, gateway *gwv1beta1.Gateway) func(error) { + return func(err error) { + condition := metav1.Condition{ + Type: "Synced", + Status: metav1.ConditionTrue, + ObservedGeneration: gateway.Generation, + LastTransitionTime: timeFunc(), + Reason: "Synced", + Message: "gateway synced to Consul", + } + if err != nil { + condition = metav1.Condition{ + Type: "Synced", + Status: metav1.ConditionFalse, + ObservedGeneration: gateway.Generation, + LastTransitionTime: timeFunc(), + Reason: "SyncError", + Message: err.Error(), + } + } + + if conditions, updated := setCondition(gateway.Status.Conditions, condition); updated { + gateway.Status.Conditions = conditions + snapshot.Kubernetes.StatusUpdates.Add(gateway) } } - return listeners } diff --git a/control-plane/api-gateway/binding/setter.go b/control-plane/api-gateway/binding/setter.go index 93727b37b2..5b3a9096d6 100644 --- a/control-plane/api-gateway/binding/setter.go +++ b/control-plane/api-gateway/binding/setter.go @@ -4,8 +4,9 @@ package binding import ( + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + "sigs.k8s.io/controller-runtime/pkg/client" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) @@ -19,26 +20,12 @@ func newSetter(controllerName string) *setter { return &setter{controllerName: controllerName} } -// setHTTPRouteCondition sets an HTTPRoute condition on its status with the given parent. -func (s *setter) setHTTPRouteCondition(route *gwv1beta1.HTTPRoute, parent *gwv1beta1.ParentReference, condition metav1.Condition) bool { - condition.LastTransitionTime = metav1.Now() - condition.ObservedGeneration = route.Generation - - status := s.getParentStatus(route.Status.Parents, parent) - conditions, modified := setCondition(status.Conditions, condition) - if modified { - status.Conditions = conditions - route.Status.Parents = s.setParentStatus(route.Status.Parents, status) - } - return modified -} - -// removeHTTPRouteReferences removes the given parent reference sections from an HTTPRoute's status. -func (s *setter) removeHTTPRouteReferences(route *gwv1beta1.HTTPRoute, refs []gwv1beta1.ParentReference) bool { +// removeRouteReferences removes the given parent reference sections from a routes's status. +func (s *setter) removeRouteReferences(route client.Object, refs []gwv1beta1.ParentReference) bool { modified := false for _, parent := range refs { - parents, removed := s.removeParentStatus(route.Status.Parents, parent) - route.Status.Parents = parents + parents, removed := s.removeParentStatus(getRouteParentsStatus(route), parent) + setRouteParentsStatus(route, parents) if removed { modified = true } @@ -46,67 +33,41 @@ func (s *setter) removeHTTPRouteReferences(route *gwv1beta1.HTTPRoute, refs []gw return modified } -// setTCPRouteCondition sets a TCPRoute condition on its status with the given parent. -func (s *setter) setTCPRouteCondition(route *gwv1alpha2.TCPRoute, parent *gwv1beta1.ParentReference, condition metav1.Condition) bool { - condition.LastTransitionTime = metav1.Now() - condition.ObservedGeneration = route.Generation +// setRouteCondition sets an route condition on its status with the given parent. +func (s *setter) setRouteCondition(route client.Object, parent *gwv1beta1.ParentReference, condition metav1.Condition) bool { + condition.LastTransitionTime = timeFunc() + condition.ObservedGeneration = route.GetGeneration() - status := s.getParentStatus(route.Status.Parents, parent) + parents := getRouteParentsStatus(route) + status := s.getParentStatus(parents, parent) conditions, modified := setCondition(status.Conditions, condition) if modified { status.Conditions = conditions - route.Status.Parents = s.setParentStatus(route.Status.Parents, status) - } - return modified -} - -// removeTCPRouteReferences removes the given parent reference sections from a TCPRoute's status. -func (s *setter) removeTCPRouteReferences(route *gwv1alpha2.TCPRoute, refs []gwv1beta1.ParentReference) bool { - modified := false - for _, parent := range refs { - parents, removed := s.removeParentStatus(route.Status.Parents, parent) - route.Status.Parents = parents - if removed { - modified = true - } + setRouteParentsStatus(route, s.setParentStatus(parents, status)) } return modified } -// removeHTTPStatuses removes all statuses set by the given controller from an HTTPRoute's status. -func (s *setter) removeHTTPStatuses(route *gwv1beta1.HTTPRoute) bool { - modified := false - filtered := []gwv1beta1.RouteParentStatus{} - for _, status := range route.Status.Parents { - if string(status.ControllerName) == s.controllerName { - modified = true - continue - } - filtered = append(filtered, status) - } +// setRouteConditionOnAllRefs sets an route condition and its status on all parents. +func (s *setter) setRouteConditionOnAllRefs(route client.Object, condition metav1.Condition) bool { + condition.LastTransitionTime = timeFunc() + condition.ObservedGeneration = route.GetGeneration() - if modified { - route.Status.Parents = filtered - } - return modified -} + parents := getRouteParentsStatus(route) + statuses := common.Filter(getRouteParentsStatus(route), func(status gwv1beta1.RouteParentStatus) bool { + return string(status.ControllerName) != s.controllerName + }) -// removeTCPStatuses removes all statuses set by the given controller from a TCPRoute's status. -func (s *setter) removeTCPStatuses(route *gwv1alpha2.TCPRoute) bool { - modified := false - filtered := []gwv1beta1.RouteParentStatus{} - for _, status := range route.Status.Parents { - if string(status.ControllerName) == s.controllerName { - modified = true - continue + updated := false + for _, status := range statuses { + conditions, modified := setCondition(status.Conditions, condition) + if modified { + updated = true + status.Conditions = conditions + setRouteParentsStatus(route, s.setParentStatus(parents, status)) } - filtered = append(filtered, status) - } - - if modified { - route.Status.Parents = filtered } - return modified + return updated } // getParentStatus returns the section of a status referenced by the given parent reference. @@ -117,7 +78,7 @@ func (s *setter) getParentStatus(statuses []gwv1beta1.RouteParentStatus, parent } for _, status := range statuses { - if parentsEqual(status.ParentRef, parentRef) && string(status.ControllerName) == s.controllerName { + if common.ParentsEqual(status.ParentRef, parentRef) && string(status.ControllerName) == s.controllerName { return status } } @@ -132,7 +93,7 @@ func (s *setter) removeParentStatus(statuses []gwv1beta1.RouteParentStatus, pare found := false filtered := []gwv1beta1.RouteParentStatus{} for _, status := range statuses { - if parentsEqual(status.ParentRef, parent) && string(status.ControllerName) == s.controllerName { + if common.ParentsEqual(status.ParentRef, parent) && string(status.ControllerName) == s.controllerName { found = true continue } @@ -162,19 +123,10 @@ func setCondition(conditions []metav1.Condition, condition metav1.Condition) ([] // setParentStatus updates or inserts the set of parent statuses with the newly modified parent. func (s *setter) setParentStatus(statuses []gwv1beta1.RouteParentStatus, parent gwv1beta1.RouteParentStatus) []gwv1beta1.RouteParentStatus { for i, status := range statuses { - if parentsEqual(status.ParentRef, parent.ParentRef) && status.ControllerName == parent.ControllerName { + if common.ParentsEqual(status.ParentRef, parent.ParentRef) && status.ControllerName == parent.ControllerName { statuses[i] = parent return statuses } } return append(statuses, parent) } - -// parentsEqual checks for equality between two parent references. -func parentsEqual(one, two gwv1beta1.ParentReference) bool { - return bothNilOrEqual(one.Group, two.Group) && - bothNilOrEqual(one.Kind, two.Kind) && - bothNilOrEqual(one.SectionName, two.SectionName) && - bothNilOrEqual(one.Port, two.Port) && - one.Name == two.Name -} diff --git a/control-plane/api-gateway/binding/setter_test.go b/control-plane/api-gateway/binding/setter_test.go index a8eabfb55d..84d3ecc7d5 100644 --- a/control-plane/api-gateway/binding/setter_test.go +++ b/control-plane/api-gateway/binding/setter_test.go @@ -32,10 +32,10 @@ func TestSetter(t *testing.T) { }, }, } - require.True(t, setter.setHTTPRouteCondition(route, &parentRef, condition)) - require.False(t, setter.setHTTPRouteCondition(route, &parentRefDup, condition)) - require.False(t, setter.setHTTPRouteCondition(route, &parentRefDup, condition)) - require.False(t, setter.setHTTPRouteCondition(route, &parentRefDup, condition)) + require.True(t, setter.setRouteCondition(route, &parentRef, condition)) + require.False(t, setter.setRouteCondition(route, &parentRefDup, condition)) + require.False(t, setter.setRouteCondition(route, &parentRefDup, condition)) + require.False(t, setter.setRouteCondition(route, &parentRefDup, condition)) require.Len(t, route.Status.Parents, 1) require.Len(t, route.Status.Parents[0].Conditions, 1) diff --git a/control-plane/api-gateway/binding/snapshot.go b/control-plane/api-gateway/binding/snapshot.go index 5eaf48abb3..18888a6d46 100644 --- a/control-plane/api-gateway/binding/snapshot.go +++ b/control-plane/api-gateway/binding/snapshot.go @@ -4,9 +4,9 @@ package binding import ( + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "github.com/hashicorp/consul/api" - "sigs.k8s.io/controller-runtime/pkg/client" ) // KubernetesSnapshot contains all the operations @@ -15,10 +15,10 @@ type KubernetesSnapshot struct { // Updates is the list of objects that need to have // aspects of their metadata or spec updated in Kubernetes // (i.e. for finalizers or annotations) - Updates []client.Object + Updates *common.KubernetesUpdates // StatusUpdates is the list of objects that need // to have their statuses updated in Kubernetes - StatusUpdates []client.Object + StatusUpdates *common.KubernetesUpdates } // ConsulSnapshot contains all the operations required @@ -26,7 +26,7 @@ type KubernetesSnapshot struct { type ConsulSnapshot struct { // Updates is the list of ConfigEntry objects that should // either be updated or created in Consul - Updates []api.ConfigEntry + Updates []*common.ConsulUpdateOperation // Deletions is a list of references that ought to be // deleted in Consul Deletions []api.ResourceReference @@ -42,9 +42,9 @@ type ConsulSnapshot struct { // needed to complete reconciliation. type Snapshot struct { // Kubernetes holds the snapshot of required Kubernetes operations - Kubernetes KubernetesSnapshot + Kubernetes *KubernetesSnapshot // Consul holds the snapshot of required Consul operations - Consul ConsulSnapshot + Consul *ConsulSnapshot // GatewayClassConfig is the configuration to use for determining // a Gateway deployment, if it is not set, a deployment should be // deleted instead of updated @@ -54,3 +54,13 @@ type Snapshot struct { // objects should be updated, i.e. deployments, roles, services UpsertGatewayDeployment bool } + +func NewSnapshot() *Snapshot { + return &Snapshot{ + Kubernetes: &KubernetesSnapshot{ + Updates: common.NewKubernetesUpdates(), + StatusUpdates: common.NewKubernetesUpdates(), + }, + Consul: &ConsulSnapshot{}, + } +} diff --git a/control-plane/api-gateway/binding/utils.go b/control-plane/api-gateway/binding/utils.go deleted file mode 100644 index 2ba2d01ce5..0000000000 --- a/control-plane/api-gateway/binding/utils.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package binding - -import ( - "reflect" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// pointerTo is a convenience method for taking a pointer -// of an object without having to declare an intermediate variable. -// It's also useful for making sure we don't accidentally take -// the pointer of a range variable directly. -func pointerTo[T any](v T) *T { - return &v -} - -// isNil checks if the argument is nil. It's mainly used to -// check if a generic conforming to a nullable interface is -// actually nil. -func isNil(arg interface{}) bool { - return arg == nil || reflect.ValueOf(arg).IsNil() -} - -// bothNilOrEqual is used to determine if two pointers to comparable -// object are either nil or both point to the same value. -func bothNilOrEqual[T comparable](one, two *T) bool { - if one == nil && two == nil { - return true - } - if one == nil { - return false - } - if two == nil { - return false - } - return *one == *two -} - -// valueOr checks if a string-like pointer is nil, and if it is, -// returns the given value instead. -func valueOr[T ~string](v *T, fallback string) string { - if v == nil { - return fallback - } - return string(*v) -} - -// nilOrEqual checks if a string-like pointer is nil or if it is -// equal to the value provided. -func nilOrEqual[T ~string](v *T, check string) bool { - return v == nil || string(*v) == check -} - -// objectToMeta returns the NamespacedName for the given object. -func objectToMeta[T metav1.Object](object T) types.NamespacedName { - return types.NamespacedName{ - Namespace: object.GetNamespace(), - Name: object.GetName(), - } -} - -// isDeleted checks if the deletion timestamp is set for an object. -func isDeleted(object client.Object) bool { - return !object.GetDeletionTimestamp().IsZero() -} - -// ensureFinalizer ensures that our finalizer is set on an object -// returning whether or not it modified the object. -func ensureFinalizer(object client.Object) bool { - if !object.GetDeletionTimestamp().IsZero() { - return false - } - - finalizers := object.GetFinalizers() - for _, f := range finalizers { - if f == gatewayFinalizer { - return false - } - } - - object.SetFinalizers(append(finalizers, gatewayFinalizer)) - return true -} - -// removeFinalizer ensures that our finalizer is absent from an object -// returning whether or not it modified the object. -func removeFinalizer(object client.Object) bool { - found := false - filtered := []string{} - for _, f := range object.GetFinalizers() { - if f == gatewayFinalizer { - found = true - continue - } - filtered = append(filtered, f) - } - - object.SetFinalizers(filtered) - return found -} diff --git a/control-plane/api-gateway/binding/validation.go b/control-plane/api-gateway/binding/validation.go index c22726f2da..41c9484483 100644 --- a/control-plane/api-gateway/binding/validation.go +++ b/control-plane/api-gateway/binding/validation.go @@ -6,6 +6,7 @@ package binding import ( "strings" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "github.com/hashicorp/consul/api" corev1 "k8s.io/api/core/v1" @@ -13,26 +14,45 @@ import ( klabels "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) +var ( + // the list of kinds we can support by listener protocol. + supportedKindsForProtocol = map[gwv1beta1.ProtocolType][]gwv1beta1.RouteGroupKind{ + gwv1beta1.HTTPProtocolType: {{ + Group: (*gwv1beta1.Group)(&gwv1beta1.GroupVersion.Group), + Kind: "HTTPRoute", + }}, + gwv1beta1.HTTPSProtocolType: {{ + Group: (*gwv1beta1.Group)(&gwv1beta1.GroupVersion.Group), + Kind: "HTTPRoute", + }}, + gwv1beta1.TCPProtocolType: {{ + Group: (*gwv1alpha2.Group)(&gwv1alpha2.GroupVersion.Group), + Kind: "TCPRoute", + }}, + } +) + // validateRefs validates backend references for a route, determining whether or // not they were found in the list of known connect-injected services. -func validateRefs(namespace string, refs []gwv1beta1.BackendRef, services map[types.NamespacedName]api.CatalogService, meshServices map[types.NamespacedName]v1alpha1.MeshService) routeValidationResults { +func validateRefs(route client.Object, refs []gwv1beta1.BackendRef, resources *common.ResourceMap) routeValidationResults { + namespace := route.GetNamespace() + var result routeValidationResults for _, ref := range refs { + backendRef := ref.BackendObjectReference + nsn := types.NamespacedName{ - Name: string(ref.BackendObjectReference.Name), - Namespace: valueOr(ref.BackendObjectReference.Namespace, namespace), + Name: string(backendRef.Name), + Namespace: common.ValueOr(backendRef.Namespace, namespace), } - // TODO: check reference grants - - backendRef := ref.BackendObjectReference - - isServiceRef := nilOrEqual(backendRef.Group, "") && nilOrEqual(backendRef.Kind, "Service") - isMeshServiceRef := derefEqual(backendRef.Group, v1alpha1.ConsulHashicorpGroup) && derefEqual(backendRef.Kind, v1alpha1.MeshServiceKind) + isServiceRef := common.NilOrEqual(backendRef.Group, "") && common.NilOrEqual(backendRef.Kind, common.KindService) + isMeshServiceRef := common.DerefEqual(backendRef.Group, v1alpha1.ConsulHashicorpGroup) && common.DerefEqual(backendRef.Kind, v1alpha1.MeshServiceKind) if !isServiceRef && !isMeshServiceRef { result = append(result, routeValidationResult{ @@ -43,26 +63,31 @@ func validateRefs(namespace string, refs []gwv1beta1.BackendRef, services map[ty continue } - if isServiceRef { - if _, found := services[nsn]; !found { - result = append(result, routeValidationResult{ - namespace: nsn.Namespace, - backend: ref, - err: errRouteBackendNotFound, - }) - continue - } + if isServiceRef && !resources.HasService(nsn) { + result = append(result, routeValidationResult{ + namespace: nsn.Namespace, + backend: ref, + err: errRouteBackendNotFound, + }) + continue } - if isMeshServiceRef { - if _, found := meshServices[nsn]; !found { - result = append(result, routeValidationResult{ - namespace: nsn.Namespace, - backend: ref, - err: errRouteBackendNotFound, - }) - continue - } + if isMeshServiceRef && !resources.HasMeshService(nsn) { + result = append(result, routeValidationResult{ + namespace: nsn.Namespace, + backend: ref, + err: errRouteBackendNotFound, + }) + continue + } + + if !canReferenceBackend(route, ref, resources) { + result = append(result, routeValidationResult{ + namespace: nsn.Namespace, + backend: ref, + err: errRefNotPermitted, + }) + continue } result = append(result, routeValidationResult{ @@ -110,7 +135,7 @@ func (m mergedListeners) validateProtocol() error { var protocol *gwv1beta1.ProtocolType for _, l := range m { if protocol == nil { - protocol = pointerTo(l.listener.Protocol) + protocol = common.PointerTo(l.listener.Protocol) } if *protocol != l.listener.Protocol { return errListenerProtocolConflict @@ -126,7 +151,7 @@ func (m mergedListeners) validateHostname(index int, listener gwv1beta1.Listener if l.index == index { continue } - if bothNilOrEqual(listener.Hostname, l.listener.Hostname) { + if common.BothNilOrEqual(listener.Hostname, l.listener.Hostname) { return errListenerHostnameConflict } } @@ -135,32 +160,36 @@ func (m mergedListeners) validateHostname(index int, listener gwv1beta1.Listener // validateTLS validates that the TLS configuration for a given listener is valid and that // the certificates that it references exist. -func validateTLS(namespace string, tls *gwv1beta1.GatewayTLSConfig, certificates []corev1.Secret) (error, error) { +func validateTLS(gateway gwv1beta1.Gateway, tls *gwv1beta1.GatewayTLSConfig, resources *common.ResourceMap) (error, error) { + namespace := gateway.Namespace + if tls == nil { return nil, nil } - // TODO: Resource Grants - var err error -MAIN_LOOP: - for _, ref := range tls.CertificateRefs { + + for _, cert := range tls.CertificateRefs { // break on the first error - if !nilOrEqual(ref.Group, "") || !nilOrEqual(ref.Kind, "Secret") { + if !common.NilOrEqual(cert.Group, "") || !common.NilOrEqual(cert.Kind, common.KindSecret) { err = errListenerInvalidCertificateRef_NotSupported - break MAIN_LOOP + break } - ns := valueOr(ref.Namespace, namespace) - for _, secret := range certificates { - if secret.Namespace == ns && secret.Name == string(ref.Name) { - continue MAIN_LOOP - } + if !resources.GatewayCanReferenceSecret(gateway, cert) { + err = errRefNotPermitted + break } - // not found, set error - err = errListenerInvalidCertificateRef_NotFound - break MAIN_LOOP + key := common.IndexedNamespacedNameWithDefault(cert.Name, cert.Namespace, namespace) + secret := resources.Certificate(key) + + if secret == nil { + err = errListenerInvalidCertificateRef_NotFound + break + } + + err = validateCertificateData(*secret) } if tls.Mode != nil && *tls.Mode == gwv1beta1.TLSModePassthrough { @@ -171,9 +200,14 @@ MAIN_LOOP: return nil, err } +func validateCertificateData(secret corev1.Secret) error { + _, _, err := common.ParseCertificateData(secret) + return err +} + // validateListeners validates the given listeners both internally and with respect to each // other for purposes of setting "Conflicted" status conditions. -func validateListeners(namespace string, listeners []gwv1beta1.Listener, secrets []corev1.Secret) listenerValidationResults { +func validateListeners(gateway gwv1beta1.Gateway, listeners []gwv1beta1.Listener, resources *common.ResourceMap) listenerValidationResults { var results listenerValidationResults merged := make(map[gwv1beta1.PortNumber]mergedListeners) for i, listener := range listeners { @@ -186,7 +220,7 @@ func validateListeners(namespace string, listeners []gwv1beta1.Listener, secrets for i, listener := range listeners { var result listenerValidationResult - err, refErr := validateTLS(namespace, listener.TLS, secrets) + err, refErr := validateTLS(gateway, listener.TLS, resources) result.refErr = refErr if err != nil { result.acceptedErr = err @@ -290,7 +324,7 @@ func routeKindIsAllowedForListener(kinds []gwv1beta1.RouteGroupKind, gk schema.G } for _, kind := range kinds { - if string(kind.Kind) == gk.Kind && nilOrEqual(kind.Group, gk.Group) { + if string(kind.Kind) == gk.Kind && common.NilOrEqual(kind.Group, gk.Group) { return true } } @@ -311,7 +345,7 @@ func routeKindIsAllowedForListenerExplicit(allowedRoutes *gwv1alpha2.AllowedRout // toNamespaceSet constructs a list of labels used to match a Namespace. func toNamespaceSet(name string, labels map[string]string) klabels.Labels { // If namespace label is not set, implicitly insert it to support older Kubernetes versions - if labels[namespaceNameLabel] == name { + if labels[common.NamespaceNameLabel] == name { // Already set, avoid copies return klabels.Set(labels) } @@ -320,13 +354,6 @@ func toNamespaceSet(name string, labels map[string]string) klabels.Labels { for k, v := range labels { ret[k] = v } - ret[namespaceNameLabel] = name + ret[common.NamespaceNameLabel] = name return klabels.Set(ret) } - -func derefEqual[T ~string](v *T, check string) bool { - if v == nil { - return false - } - return string(*v) == check -} diff --git a/control-plane/api-gateway/binding/validation_test.go b/control-plane/api-gateway/binding/validation_test.go index eaf4388413..da0ed83f95 100644 --- a/control-plane/api-gateway/binding/validation_test.go +++ b/control-plane/api-gateway/binding/validation_test.go @@ -6,13 +6,15 @@ package binding import ( "testing" + logrtest "github.com/go-logr/logr/testing" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul/api" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) @@ -20,132 +22,183 @@ func TestValidateRefs(t *testing.T) { t.Parallel() for name, tt := range map[string]struct { - namespace string - refs []gwv1beta1.BackendObjectReference - services map[types.NamespacedName]api.CatalogService - meshServices map[types.NamespacedName]v1alpha1.MeshService - expectedErrors []error + route client.Object + services map[types.NamespacedName]corev1.Service + referenceGrants []gwv1beta1.ReferenceGrant + meshServices []v1alpha1.MeshService + expectedErrors []error }{ "all pass no namespaces": { - namespace: "test", - refs: []gwv1beta1.BackendObjectReference{{Name: "1"}, {Name: "2"}}, - services: map[types.NamespacedName]api.CatalogService{ + route: testHTTPRouteBackends("route", "test", []gwv1beta1.BackendObjectReference{{Name: "1"}, {Name: "2"}}, nil), + services: map[types.NamespacedName]corev1.Service{ {Name: "1", Namespace: "test"}: {}, {Name: "2", Namespace: "test"}: {}, {Name: "3", Namespace: "test"}: {}, }, - meshServices: map[types.NamespacedName]v1alpha1.MeshService{}, + meshServices: []v1alpha1.MeshService{}, expectedErrors: []error{nil, nil}, }, + "all fails namespaces no reference grants": { + route: testHTTPRouteBackends("route", "test", []gwv1beta1.BackendObjectReference{ + {Name: "1", Namespace: common.PointerTo[gwv1beta1.Namespace]("other")}, + {Name: "2", Namespace: common.PointerTo[gwv1beta1.Namespace]("other")}, + }, nil), + services: map[types.NamespacedName]corev1.Service{ + {Name: "1", Namespace: "other"}: {}, + {Name: "2", Namespace: "other"}: {}, + {Name: "3", Namespace: "other"}: {}, + }, + meshServices: []v1alpha1.MeshService{}, + expectedErrors: []error{errRefNotPermitted, errRefNotPermitted}, + }, "all pass namespaces": { - namespace: "test", - refs: []gwv1beta1.BackendObjectReference{ - {Name: "1", Namespace: pointerTo[gwv1beta1.Namespace]("other")}, - {Name: "2", Namespace: pointerTo[gwv1beta1.Namespace]("other")}, + referenceGrants: []gwv1beta1.ReferenceGrant{ + {ObjectMeta: metav1.ObjectMeta{Namespace: "other", Name: "grant"}, Spec: gwv1beta1.ReferenceGrantSpec{ + From: []gwv1beta1.ReferenceGrantFrom{ + {Group: gwv1beta1.GroupName, Kind: "HTTPRoute", Namespace: gwv1beta1.Namespace("test")}, + }, + To: []gwv1beta1.ReferenceGrantTo{ + {Kind: "Service"}, + }, + }}, }, - services: map[types.NamespacedName]api.CatalogService{ + route: testHTTPRouteBackends("route", "test", []gwv1beta1.BackendObjectReference{ + {Name: "1", Namespace: common.PointerTo[gwv1beta1.Namespace]("other")}, + {Name: "2", Namespace: common.PointerTo[gwv1beta1.Namespace]("other")}, + }, nil), + services: map[types.NamespacedName]corev1.Service{ {Name: "1", Namespace: "other"}: {}, {Name: "2", Namespace: "other"}: {}, {Name: "3", Namespace: "other"}: {}, }, - meshServices: map[types.NamespacedName]v1alpha1.MeshService{}, + meshServices: []v1alpha1.MeshService{}, expectedErrors: []error{nil, nil}, }, - "all pass mixed": { - namespace: "test", - refs: []gwv1beta1.BackendObjectReference{ - {Name: "1", Namespace: pointerTo[gwv1beta1.Namespace]("other")}, + "some pass mixed missing reference grants": { + route: testHTTPRouteBackends("route", "test", []gwv1beta1.BackendObjectReference{ + {Name: "1", Namespace: common.PointerTo[gwv1beta1.Namespace]("other")}, {Name: "2"}, + }, nil), + services: map[types.NamespacedName]corev1.Service{ + {Name: "1", Namespace: "other"}: {}, + {Name: "2", Namespace: "test"}: {}, + {Name: "3", Namespace: "other"}: {}, + }, + meshServices: []v1alpha1.MeshService{}, + expectedErrors: []error{errRefNotPermitted, nil}, + }, + "all pass mixed": { + referenceGrants: []gwv1beta1.ReferenceGrant{ + {ObjectMeta: metav1.ObjectMeta{Namespace: "other", Name: "grant"}, Spec: gwv1beta1.ReferenceGrantSpec{ + From: []gwv1beta1.ReferenceGrantFrom{ + {Group: gwv1beta1.GroupName, Kind: "HTTPRoute", Namespace: gwv1beta1.Namespace("test")}, + }, + To: []gwv1beta1.ReferenceGrantTo{ + {Kind: "Service"}, + }, + }}, }, - services: map[types.NamespacedName]api.CatalogService{ + route: testHTTPRouteBackends("route", "test", []gwv1beta1.BackendObjectReference{ + {Name: "1", Namespace: common.PointerTo[gwv1beta1.Namespace]("other")}, + {Name: "2"}, + }, nil), + services: map[types.NamespacedName]corev1.Service{ {Name: "1", Namespace: "other"}: {}, {Name: "2", Namespace: "test"}: {}, {Name: "3", Namespace: "other"}: {}, }, - meshServices: map[types.NamespacedName]v1alpha1.MeshService{}, + meshServices: []v1alpha1.MeshService{}, expectedErrors: []error{nil, nil}, }, "all fail mixed": { - namespace: "test", - refs: []gwv1beta1.BackendObjectReference{ - {Name: "1"}, - {Name: "2", Namespace: pointerTo[gwv1beta1.Namespace]("other")}, + referenceGrants: []gwv1beta1.ReferenceGrant{ + {ObjectMeta: metav1.ObjectMeta{Namespace: "other", Name: "grant"}, Spec: gwv1beta1.ReferenceGrantSpec{ + From: []gwv1beta1.ReferenceGrantFrom{ + {Group: gwv1beta1.GroupName, Kind: "HTTPRoute", Namespace: gwv1beta1.Namespace("test")}, + }, + To: []gwv1beta1.ReferenceGrantTo{ + {Kind: "Service"}, + }, + }}, }, - services: map[types.NamespacedName]api.CatalogService{ + route: testHTTPRouteBackends("route", "test", []gwv1beta1.BackendObjectReference{ + {Name: "1"}, + {Name: "2", Namespace: common.PointerTo[gwv1beta1.Namespace]("other")}, + }, nil), + services: map[types.NamespacedName]corev1.Service{ {Name: "1", Namespace: "other"}: {}, {Name: "2", Namespace: "test"}: {}, {Name: "3", Namespace: "other"}: {}, }, - meshServices: map[types.NamespacedName]v1alpha1.MeshService{}, + meshServices: []v1alpha1.MeshService{}, expectedErrors: []error{errRouteBackendNotFound, errRouteBackendNotFound}, }, "all fail no namespaces": { - namespace: "test", - refs: []gwv1beta1.BackendObjectReference{ + route: testHTTPRouteBackends("route", "test", []gwv1beta1.BackendObjectReference{ {Name: "1"}, {Name: "2"}, - }, - services: map[types.NamespacedName]api.CatalogService{ + }, nil), + services: map[types.NamespacedName]corev1.Service{ {Name: "1", Namespace: "other"}: {}, {Name: "2", Namespace: "other"}: {}, {Name: "3", Namespace: "other"}: {}, }, - meshServices: map[types.NamespacedName]v1alpha1.MeshService{}, + meshServices: []v1alpha1.MeshService{}, expectedErrors: []error{errRouteBackendNotFound, errRouteBackendNotFound}, }, "all fail namespaces": { - namespace: "test", - refs: []gwv1beta1.BackendObjectReference{ - {Name: "1", Namespace: pointerTo[gwv1beta1.Namespace]("other")}, - {Name: "2", Namespace: pointerTo[gwv1beta1.Namespace]("other")}, - }, - services: map[types.NamespacedName]api.CatalogService{ + route: testHTTPRouteBackends("route", "test", []gwv1beta1.BackendObjectReference{ + {Name: "1", Namespace: common.PointerTo[gwv1beta1.Namespace]("other")}, + {Name: "2", Namespace: common.PointerTo[gwv1beta1.Namespace]("other")}, + }, nil), + services: map[types.NamespacedName]corev1.Service{ {Name: "1", Namespace: "test"}: {}, {Name: "2", Namespace: "test"}: {}, {Name: "3", Namespace: "test"}: {}, }, - meshServices: map[types.NamespacedName]v1alpha1.MeshService{}, + meshServices: []v1alpha1.MeshService{}, expectedErrors: []error{errRouteBackendNotFound, errRouteBackendNotFound}, }, "type failures": { - namespace: "test", - refs: []gwv1beta1.BackendObjectReference{ - {Name: "1", Group: pointerTo[gwv1beta1.Group]("test")}, + route: testHTTPRouteBackends("route", "test", []gwv1beta1.BackendObjectReference{ + {Name: "1", Group: common.PointerTo[gwv1beta1.Group]("test")}, {Name: "2"}, - }, - services: map[types.NamespacedName]api.CatalogService{ + }, nil), + services: map[types.NamespacedName]corev1.Service{ {Name: "1", Namespace: "test"}: {}, {Name: "2", Namespace: "test"}: {}, {Name: "3", Namespace: "test"}: {}, }, - meshServices: map[types.NamespacedName]v1alpha1.MeshService{}, + meshServices: []v1alpha1.MeshService{}, expectedErrors: []error{errRouteInvalidKind, nil}, }, "mesh services": { - namespace: "test", - refs: []gwv1beta1.BackendObjectReference{ + route: testHTTPRouteBackends("route", "test", []gwv1beta1.BackendObjectReference{ { Name: "1", - Group: pointerTo(gwv1beta1.Group(v1alpha1.ConsulHashicorpGroup)), - Kind: pointerTo(gwv1beta1.Kind(v1alpha1.MeshServiceKind)), + Group: common.PointerTo(gwv1beta1.Group(v1alpha1.ConsulHashicorpGroup)), + Kind: common.PointerTo(gwv1beta1.Kind(v1alpha1.MeshServiceKind)), }, - }, - meshServices: map[types.NamespacedName]v1alpha1.MeshService{ - {Name: "1", Namespace: "test"}: {}, - {Name: "2", Namespace: "test"}: {}, - {Name: "3", Namespace: "test"}: {}, + }, nil), + meshServices: []v1alpha1.MeshService{ + {ObjectMeta: metav1.ObjectMeta{Name: "1", Namespace: "test"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "2", Namespace: "test"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "3", Namespace: "test"}}, }, expectedErrors: []error{nil}, }, } { t.Run(name, func(t *testing.T) { - refs := make([]gwv1beta1.BackendRef, len(tt.refs)) - for i, ref := range tt.refs { - refs[i] = gwv1beta1.BackendRef{BackendObjectReference: ref} + refs := getRouteBackends(tt.route) + resources := common.NewResourceMap(common.ResourceTranslator{}, NewReferenceValidator(tt.referenceGrants), logrtest.NewTestLogger(t)) + for _, service := range tt.meshServices { + resources.AddMeshService(service) + } + for id := range tt.services { + resources.AddService(id, id.Name) } - actual := validateRefs(tt.namespace, refs, tt.services, tt.meshServices) - require.Equal(t, len(actual), len(tt.refs)) + actual := validateRefs(tt.route, refs, resources) require.Equal(t, len(actual), len(tt.expectedErrors)) for i, err := range tt.expectedErrors { require.Equal(t, err, actual[i].err) @@ -236,11 +289,11 @@ func TestMergedListeners_ValidateHostname(t *testing.T) { }{ "valid": { mergedListeners: []mergedListener{ - {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("1")}}, - {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("2")}}, - {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("3")}}, - {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("4")}}, - {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("5")}}, + {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("1")}}, + {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("2")}}, + {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("3")}}, + {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("4")}}, + {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("5")}}, {}, }, expected: nil, @@ -248,24 +301,24 @@ func TestMergedListeners_ValidateHostname(t *testing.T) { "invalid nil": { mergedListeners: []mergedListener{ {}, - {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("1")}}, - {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("2")}}, - {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("3")}}, - {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("4")}}, - {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("5")}}, + {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("1")}}, + {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("2")}}, + {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("3")}}, + {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("4")}}, + {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("5")}}, {}, }, expected: errListenerHostnameConflict, }, "invalid set": { mergedListeners: []mergedListener{ - {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("1")}}, - {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("2")}}, - {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("3")}}, - {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("4")}}, - {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("5")}}, + {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("1")}}, + {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("2")}}, + {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("3")}}, + {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("4")}}, + {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("5")}}, {}, - {listener: gwv1beta1.Listener{Hostname: pointerTo[gwv1beta1.Hostname]("1")}}, + {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("1")}}, }, expected: errListenerHostnameConflict, }, @@ -284,25 +337,28 @@ func TestMergedListeners_ValidateHostname(t *testing.T) { func TestValidateTLS(t *testing.T) { t.Parallel() + _, secret := generateTestCertificate(t, "", "") + for name, tt := range map[string]struct { - namespace string + gateway gwv1beta1.Gateway + grants []gwv1beta1.ReferenceGrant tls *gwv1beta1.GatewayTLSConfig certificates []corev1.Secret expectedResolvedRefsErr error expectedAcceptedErr error }{ "no tls": { - namespace: "test", + gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), tls: nil, certificates: nil, expectedResolvedRefsErr: nil, expectedAcceptedErr: nil, }, "not supported certificate": { - namespace: "test", + gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), tls: &gwv1beta1.GatewayTLSConfig{ CertificateRefs: []gwv1beta1.SecretObjectReference{ - {Name: "foo", Namespace: pointerTo[gwv1beta1.Namespace]("other"), Group: pointerTo[gwv1beta1.Group]("test")}, + {Name: "foo", Namespace: common.PointerTo[gwv1beta1.Namespace]("other"), Group: common.PointerTo[gwv1beta1.Group]("test")}, }, }, certificates: []corev1.Secret{ @@ -313,11 +369,36 @@ func TestValidateTLS(t *testing.T) { expectedResolvedRefsErr: errListenerInvalidCertificateRef_NotSupported, expectedAcceptedErr: nil, }, + "not allowed certificate": { + gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), + tls: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{ + {Name: "foo", Namespace: common.PointerTo[gwv1beta1.Namespace]("other")}, + }, + }, + certificates: []corev1.Secret{ + {ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "other"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "other"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "other"}}, + }, + expectedResolvedRefsErr: errRefNotPermitted, + expectedAcceptedErr: nil, + }, "not found certificate": { - namespace: "test", + grants: []gwv1beta1.ReferenceGrant{ + {ObjectMeta: metav1.ObjectMeta{Namespace: "other", Name: "grant"}, Spec: gwv1beta1.ReferenceGrantSpec{ + From: []gwv1beta1.ReferenceGrantFrom{ + {Group: gwv1beta1.GroupName, Kind: "Gateway", Namespace: gwv1beta1.Namespace("default")}, + }, + To: []gwv1beta1.ReferenceGrantTo{ + {Kind: "Secret"}, + }, + }}, + }, + gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), tls: &gwv1beta1.GatewayTLSConfig{ CertificateRefs: []gwv1beta1.SecretObjectReference{ - {Name: "zoiks", Namespace: pointerTo[gwv1beta1.Namespace]("other")}, + {Name: "zoiks", Namespace: common.PointerTo[gwv1beta1.Namespace]("other")}, }, }, certificates: []corev1.Secret{ @@ -329,10 +410,20 @@ func TestValidateTLS(t *testing.T) { expectedAcceptedErr: nil, }, "not found certificate mismatched namespace": { - namespace: "test", + grants: []gwv1beta1.ReferenceGrant{ + {ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "grant"}, Spec: gwv1beta1.ReferenceGrantSpec{ + From: []gwv1beta1.ReferenceGrantFrom{ + {Group: gwv1beta1.GroupName, Kind: "Gateway", Namespace: gwv1beta1.Namespace("default")}, + }, + To: []gwv1beta1.ReferenceGrantTo{ + {Kind: "Secret"}, + }, + }}, + }, + gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), tls: &gwv1beta1.GatewayTLSConfig{ CertificateRefs: []gwv1beta1.SecretObjectReference{ - {Name: "foo", Namespace: pointerTo[gwv1beta1.Namespace]("1")}, + {Name: "foo", Namespace: common.PointerTo[gwv1beta1.Namespace]("foo")}, }, }, certificates: []corev1.Secret{ @@ -344,21 +435,47 @@ func TestValidateTLS(t *testing.T) { expectedAcceptedErr: nil, }, "passthrough mode": { - namespace: "test", + gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), tls: &gwv1beta1.GatewayTLSConfig{ - Mode: pointerTo(gwv1beta1.TLSModePassthrough), + Mode: common.PointerTo(gwv1beta1.TLSModePassthrough), }, certificates: nil, expectedResolvedRefsErr: nil, expectedAcceptedErr: errListenerNoTLSPassthrough, }, "valid targeted namespace": { - namespace: "test", + grants: []gwv1beta1.ReferenceGrant{ + {ObjectMeta: metav1.ObjectMeta{Namespace: "1", Name: "grant"}, Spec: gwv1beta1.ReferenceGrantSpec{ + From: []gwv1beta1.ReferenceGrantFrom{ + {Group: gwv1beta1.GroupName, Kind: "Gateway", Namespace: gwv1beta1.Namespace("default")}, + }, + To: []gwv1beta1.ReferenceGrantTo{ + {Kind: "Secret"}, + }, + }}, + {ObjectMeta: metav1.ObjectMeta{Namespace: "2", Name: "grant"}, Spec: gwv1beta1.ReferenceGrantSpec{ + From: []gwv1beta1.ReferenceGrantFrom{ + {Group: gwv1beta1.GroupName, Kind: "Gateway", Namespace: gwv1beta1.Namespace("default")}, + }, + To: []gwv1beta1.ReferenceGrantTo{ + {Kind: "Secret"}, + }, + }}, + {ObjectMeta: metav1.ObjectMeta{Namespace: "3", Name: "grant"}, Spec: gwv1beta1.ReferenceGrantSpec{ + From: []gwv1beta1.ReferenceGrantFrom{ + {Group: gwv1beta1.GroupName, Kind: "Gateway", Namespace: gwv1beta1.Namespace("default")}, + }, + To: []gwv1beta1.ReferenceGrantTo{ + {Kind: "Secret"}, + }, + }}, + }, + gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), tls: &gwv1beta1.GatewayTLSConfig{ CertificateRefs: []gwv1beta1.SecretObjectReference{ - {Name: "foo", Namespace: pointerTo[gwv1beta1.Namespace]("1")}, - {Name: "bar", Namespace: pointerTo[gwv1beta1.Namespace]("2")}, - {Name: "baz", Namespace: pointerTo[gwv1beta1.Namespace]("3")}, + {Name: "foo", Namespace: common.PointerTo[gwv1beta1.Namespace]("1")}, + {Name: "bar", Namespace: common.PointerTo[gwv1beta1.Namespace]("2")}, + {Name: "baz", Namespace: common.PointerTo[gwv1beta1.Namespace]("3")}, }, }, certificates: []corev1.Secret{ @@ -370,7 +487,7 @@ func TestValidateTLS(t *testing.T) { expectedAcceptedErr: nil, }, "valid same namespace": { - namespace: "test", + gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), tls: &gwv1beta1.GatewayTLSConfig{ CertificateRefs: []gwv1beta1.SecretObjectReference{ {Name: "foo"}, @@ -379,15 +496,15 @@ func TestValidateTLS(t *testing.T) { }, }, certificates: []corev1.Secret{ - {ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "test"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "default"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "default"}}, }, expectedResolvedRefsErr: nil, expectedAcceptedErr: nil, }, "valid empty certs": { - namespace: "test", + gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), tls: &gwv1beta1.GatewayTLSConfig{}, certificates: nil, expectedResolvedRefsErr: nil, @@ -395,7 +512,14 @@ func TestValidateTLS(t *testing.T) { }, } { t.Run(name, func(t *testing.T) { - actualAcceptedError, actualResolvedRefsError := validateTLS(tt.namespace, tt.tls, tt.certificates) + resources := common.NewResourceMap(common.ResourceTranslator{}, NewReferenceValidator(tt.grants), logrtest.NewTestLogger(t)) + for _, certificate := range tt.certificates { + // make the data valid + certificate.Data = secret.Data + resources.ReferenceCountCertificate(certificate) + } + + actualAcceptedError, actualResolvedRefsError := validateTLS(tt.gateway, tt.tls, resources) require.Equal(t, tt.expectedResolvedRefsErr, actualResolvedRefsError) require.Equal(t, tt.expectedAcceptedErr, actualAcceptedError) }) @@ -441,7 +565,7 @@ func TestValidateListeners(t *testing.T) { }, } { t.Run(name, func(t *testing.T) { - require.Equal(t, tt.expectedAcceptedErr, validateListeners("", tt.listeners, nil)[0].acceptedErr) + require.Equal(t, tt.expectedAcceptedErr, validateListeners(gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), tt.listeners, nil)[0].acceptedErr) }) } } @@ -468,32 +592,32 @@ func TestRouteAllowedForListenerNamespaces(t *testing.T) { expected: false, }, "explicit same namespace allowed": { - allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{From: pointerTo(gwv1beta1.NamespacesFromSame)}}, + allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{From: common.PointerTo(gwv1beta1.NamespacesFromSame)}}, gatewayNamespace: "test", routeNamespace: corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test"}}, expected: true, }, "explicit same namespace not allowed": { - allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{From: pointerTo(gwv1beta1.NamespacesFromSame)}}, + allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{From: common.PointerTo(gwv1beta1.NamespacesFromSame)}}, gatewayNamespace: "test", routeNamespace: corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "other"}}, expected: false, }, "all namespace allowed": { - allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{From: pointerTo(gwv1beta1.NamespacesFromAll)}}, + allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{From: common.PointerTo(gwv1beta1.NamespacesFromAll)}}, gatewayNamespace: "test", routeNamespace: corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "other"}}, expected: true, }, "invalid namespace from not allowed": { - allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{From: pointerTo[gwv1beta1.FromNamespaces]("other")}}, + allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{From: common.PointerTo[gwv1beta1.FromNamespaces]("other")}}, gatewayNamespace: "test", routeNamespace: corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test"}}, expected: false, }, "labeled namespace allowed": { allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{ - From: pointerTo(gwv1beta1.NamespacesFromSelector), + From: common.PointerTo(gwv1beta1.NamespacesFromSelector), Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, }}, gatewayNamespace: "test", @@ -504,7 +628,7 @@ func TestRouteAllowedForListenerNamespaces(t *testing.T) { }, "labeled namespace not allowed": { allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{ - From: pointerTo(gwv1beta1.NamespacesFromSelector), + From: common.PointerTo(gwv1beta1.NamespacesFromSelector), Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, }}, gatewayNamespace: "test", @@ -515,7 +639,7 @@ func TestRouteAllowedForListenerNamespaces(t *testing.T) { }, "invalid labeled namespace": { allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{ - From: pointerTo(gwv1beta1.NamespacesFromSelector), + From: common.PointerTo(gwv1beta1.NamespacesFromSelector), Selector: &metav1.LabelSelector{MatchExpressions: []metav1.LabelSelectorRequirement{ {Key: "foo", Operator: "junk", Values: []string{"1"}}, }}, @@ -547,17 +671,17 @@ func TestRouteAllowedForListenerHostname(t *testing.T) { expected: true, }, "empty hostname": { - hostname: pointerTo[gwv1beta1.Hostname]("foo"), + hostname: common.PointerTo[gwv1beta1.Hostname]("foo"), hostnames: nil, expected: true, }, "any hostname match": { - hostname: pointerTo[gwv1beta1.Hostname]("foo"), + hostname: common.PointerTo[gwv1beta1.Hostname]("foo"), hostnames: []gwv1beta1.Hostname{"foo", "bar"}, expected: true, }, "no match": { - hostname: pointerTo[gwv1beta1.Hostname]("foo"), + hostname: common.PointerTo[gwv1beta1.Hostname]("foo"), hostnames: []gwv1beta1.Hostname{"bar"}, expected: false, }, @@ -638,7 +762,7 @@ func TestRouteKindIsAllowedForListener(t *testing.T) { }, "group specified": { kinds: []gwv1beta1.RouteGroupKind{ - {Group: pointerTo[gwv1beta1.Group]("a"), Kind: "b"}, + {Group: common.PointerTo[gwv1beta1.Group]("a"), Kind: "b"}, }, gk: schema.GroupKind{Group: "a", Kind: "b"}, expected: true, @@ -659,7 +783,7 @@ func TestRouteKindIsAllowedForListener(t *testing.T) { }, "group mismatch": { kinds: []gwv1beta1.RouteGroupKind{ - {Group: pointerTo[gwv1beta1.Group]("a"), Kind: "b"}, + {Group: common.PointerTo[gwv1beta1.Group]("a"), Kind: "b"}, }, gk: schema.GroupKind{Group: "d", Kind: "b"}, expected: false, diff --git a/control-plane/api-gateway/cache/consul.go b/control-plane/api-gateway/cache/consul.go index 7b66067123..5c4125a040 100644 --- a/control-plane/api-gateway/cache/consul.go +++ b/control-plane/api-gateway/cache/consul.go @@ -11,14 +11,12 @@ import ( "time" "github.com/go-logr/logr" - "github.com/google/go-cmp/cmp" "golang.org/x/exp/slices" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/event" - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/translation" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul-k8s/control-plane/consul" + "github.com/hashicorp/consul-k8s/control-plane/namespaces" "github.com/hashicorp/consul/api" ) @@ -30,180 +28,11 @@ const ( var Kinds = []string{api.APIGateway, api.HTTPRoute, api.TCPRoute, api.InlineCertificate} type Config struct { - ConsulClientConfig *consul.Config - ConsulServerConnMgr consul.ServerConnectionManager - NamespacesEnabled bool - PeeringEnabled bool - Logger logr.Logger -} - -type resourceCache map[api.ResourceReference]api.ConfigEntry - -func (oldCache resourceCache) diff(newCache resourceCache) []api.ConfigEntry { - diffs := make([]api.ConfigEntry, 0) - - for ref, entry := range newCache { - oldRef, ok := oldCache[ref] - // ref from the new cache doesn't exist in the old one - // this means a resource was added - if !ok { - diffs = append(diffs, entry) - continue - } - - // the entry in the old cache has an older modify index than the ref - // from the new cache - if oldRef.GetModifyIndex() < entry.GetModifyIndex() { - diffs = append(diffs, entry) - } - } - - // get all deleted entries, these are entries present in the old cache - // that are not present in the new - for ref, entry := range oldCache { - if _, ok := newCache[ref]; !ok { - diffs = append(diffs, entry) - } - } - - return diffs -} - -type serviceCache map[api.ResourceReference]*api.CatalogService - -func (oldCache serviceCache) diff(newCache serviceCache) []*api.CatalogService { - diffs := make([]*api.CatalogService, 0) - - for ref, entry := range newCache { - oldRef, ok := oldCache[ref] - // ref from the new cache doesn't exist in the old one - // this means a resource was added - if !ok { - diffs = append(diffs, entry) - continue - } - - // the entry in the old cache has an older modify index than the ref - // from the new cache - if oldRef.ModifyIndex < entry.ModifyIndex { - diffs = append(diffs, entry) - } - } - - // get all deleted entries, these are entries present in the old cache - // that are not present in the new - for ref, entry := range oldCache { - if _, ok := newCache[ref]; !ok { - diffs = append(diffs, entry) - } - } - return diffs -} - -type peeringCache map[api.ResourceReference]*api.Peering - -func (oldCache peeringCache) diff(newCache peeringCache) []*api.Peering { - diffs := make([]*api.Peering, 0) - - for ref, entry := range newCache { - oldRef, ok := oldCache[ref] - // ref from the new cache doesn't exist in the old one - // this means a resource was added - if !ok { - diffs = append(diffs, entry) - continue - } - - // the entry in the old cache has an older modify index than the ref - // from the new cache - if oldRef.ModifyIndex < entry.ModifyIndex { - diffs = append(diffs, entry) - } - } - - // get all deleted entries, these are entries present in the old cache - // that are not present in the new - for ref, entry := range oldCache { - if _, ok := newCache[ref]; !ok { - diffs = append(diffs, entry) - } - } - return diffs -} - -// configEntryObject is used for generic k8s events so we maintain the consul name/namespace. -type configEntryObject struct { - client.Object // embed so we fufill the object interface - - Namespace string - Name string -} - -func (c *configEntryObject) GetNamespace() string { - return c.Namespace -} - -func (c *configEntryObject) GetName() string { - return c.Name -} - -func newConfigEntryObject(namespacedName types.NamespacedName) *configEntryObject { - return &configEntryObject{ - Namespace: namespacedName.Namespace, - Name: namespacedName.Name, - } -} - -// Subscription represents a watcher for events on a specific kind. -type Subscription struct { - translator translation.TranslatorFn - ctx context.Context - cancelCtx context.CancelFunc - events chan event.GenericEvent -} - -func (s *Subscription) Cancel() { - s.cancelCtx() -} - -func (s *Subscription) Events() chan event.GenericEvent { - return s.events -} - -type ServiceTranslatorFn func(*api.CatalogService) []types.NamespacedName - -// ServiceSubscription represents a watcher for events on a specific kind. -type ServiceSubscription struct { - translator ServiceTranslatorFn - ctx context.Context - cancelCtx context.CancelFunc - events chan event.GenericEvent -} - -func (s *ServiceSubscription) Cancel() { - s.cancelCtx() -} - -func (s *ServiceSubscription) Events() chan event.GenericEvent { - return s.events -} - -type PeeringTranslatorFn func(*api.Peering) []types.NamespacedName - -// PeeringsSubscription represents a watcher for events on a specific kind. -type PeeringsSubscription struct { - translator PeeringTranslatorFn - ctx context.Context - cancelCtx context.CancelFunc - events chan event.GenericEvent -} - -func (s *PeeringsSubscription) Cancel() { - s.cancelCtx() -} - -func (s *PeeringsSubscription) Events() chan event.GenericEvent { - return s.events + ConsulClientConfig *consul.Config + ConsulServerConnMgr consul.ServerConnectionManager + NamespacesEnabled bool + CrossNamespaceACLPolicy string + Logger logr.Logger } // Cache subscribes to and caches Consul objects, it also responsible for mainting subscriptions to @@ -213,18 +42,14 @@ type Cache struct { serverMgr consul.ServerConnectionManager logger logr.Logger - cache map[string]resourceCache - serviceCache serviceCache - peeringCache peeringCache - cacheMutex *sync.Mutex + cache map[string]*common.ReferenceMap + cacheMutex *sync.Mutex - subscribers map[string][]*Subscription - serviceSubscribers []*ServiceSubscription - peeringsSubscribers []*PeeringsSubscription - subscriberMutex *sync.Mutex + subscribers map[string][]*Subscription + subscriberMutex *sync.Mutex - namespacesEnabled bool - peeringsEnabled bool + namespacesEnabled bool + crossNamespaceACLPolicy string synced chan struct{} @@ -232,30 +57,24 @@ type Cache struct { } func New(config Config) *Cache { - cache := make(map[string]resourceCache, len(Kinds)) + cache := make(map[string]*common.ReferenceMap, len(Kinds)) for _, kind := range Kinds { - cache[kind] = make(resourceCache) + cache[kind] = common.NewReferenceMap() } config.ConsulClientConfig.APITimeout = apiTimeout return &Cache{ - config: config.ConsulClientConfig, - serverMgr: config.ConsulServerConnMgr, - namespacesEnabled: config.NamespacesEnabled, - peeringsEnabled: config.PeeringEnabled, - cache: cache, - serviceCache: make(serviceCache), - peeringCache: make(peeringCache), - cacheMutex: &sync.Mutex{}, - subscribers: make(map[string][]*Subscription), - serviceSubscribers: make([]*ServiceSubscription, 0), - peeringsSubscribers: make([]*PeeringsSubscription, 0), - subscriberMutex: &sync.Mutex{}, - kinds: Kinds, - // we make a buffered channel that is the length of the kinds which - // are subscribed to + 2 for services + peerings - synced: make(chan struct{}, len(Kinds)+2), - logger: config.Logger, + config: config.ConsulClientConfig, + serverMgr: config.ConsulServerConnMgr, + namespacesEnabled: config.NamespacesEnabled, + cache: cache, + cacheMutex: &sync.Mutex{}, + subscribers: make(map[string][]*Subscription), + subscriberMutex: &sync.Mutex{}, + kinds: Kinds, + synced: make(chan struct{}, len(Kinds)), + logger: config.Logger, + crossNamespaceACLPolicy: config.CrossNamespaceACLPolicy, } } @@ -268,24 +87,10 @@ func (c *Cache) WaitSynced(ctx context.Context) { return } } - // one more for service subscribers - select { - case <-c.synced: - case <-ctx.Done(): - return - } - if c.peeringsEnabled { - // and one more for peerings subscribers - select { - case <-c.synced: - case <-ctx.Done(): - return - } - } } // Subscribe handles adding a new subscription for resources of a given kind. -func (c *Cache) Subscribe(ctx context.Context, kind string, translator translation.TranslatorFn) *Subscription { +func (c *Cache) Subscribe(ctx context.Context, kind string, translator TranslatorFn) *Subscription { c.subscriberMutex.Lock() defer c.subscriberMutex.Unlock() @@ -315,42 +120,6 @@ func (c *Cache) Subscribe(ctx context.Context, kind string, translator translati return sub } -// SubscribeServices handles adding a new subscription for resources of a given kind. -func (c *Cache) SubscribeServices(ctx context.Context, translator ServiceTranslatorFn) *ServiceSubscription { - c.subscriberMutex.Lock() - defer c.subscriberMutex.Unlock() - - ctx, cancel := context.WithCancel(ctx) - events := make(chan event.GenericEvent) - sub := &ServiceSubscription{ - translator: translator, - ctx: ctx, - cancelCtx: cancel, - events: events, - } - - c.serviceSubscribers = append(c.serviceSubscribers, sub) - return sub -} - -// SubscribeServices handles adding a new subscription for resources of a given kind. -func (c *Cache) SubscribePeerings(ctx context.Context, translator PeeringTranslatorFn) *PeeringsSubscription { - c.subscriberMutex.Lock() - defer c.subscriberMutex.Unlock() - - ctx, cancel := context.WithCancel(ctx) - events := make(chan event.GenericEvent) - sub := &PeeringsSubscription{ - translator: translator, - ctx: ctx, - cancelCtx: cancel, - events: events, - } - - c.peeringsSubscribers = append(c.peeringsSubscribers, sub) - return sub -} - // Run starts the cache watch cycle, on the first call it will fill the cache with existing resources. func (c *Cache) Run(ctx context.Context) { wg := &sync.WaitGroup{} @@ -365,20 +134,6 @@ func (c *Cache) Run(ctx context.Context) { }() } - wg.Add(1) - go func() { - defer wg.Done() - c.subscribeToConsulServices(ctx) - }() - - if c.peeringsEnabled { - wg.Add(1) - go func() { - defer wg.Done() - c.subscribeToConsulPeerings(ctx) - }() - } - wg.Wait() } @@ -422,106 +177,16 @@ func (c *Cache) subscribeToConsul(ctx context.Context, kind string) { } } -func (c *Cache) subscribeToConsulServices(ctx context.Context) { - once := &sync.Once{} - - opts := &api.QueryOptions{Connect: true} - - // we need a second set of opts to make sure we don't - // block on the secondary list operations - serviceListOpts := &api.QueryOptions{Connect: true} - -MAIN_LOOP: - for { - select { - case <-ctx.Done(): - return - default: - } - - client, err := consul.NewClientFromConnMgr(c.config, c.serverMgr) - if err != nil { - c.logger.Error(err, "error initializing consul client") - continue - } - - services, meta, err := client.Catalog().Services(opts.WithContext(ctx)) - if err != nil { - c.logger.Error(err, "error fetching services") - continue - } - - flattened := []*api.CatalogService{} - for service := range services { - serviceList, _, err := client.Catalog().Service(service, "", serviceListOpts.WithContext(ctx)) - if err != nil { - c.logger.Error(err, fmt.Sprintf("error fetching service: %s", service)) - continue MAIN_LOOP - } - flattened = append(flattened, serviceList...) - } - - opts.WaitIndex = meta.LastIndex - c.updateAndNotifyServices(ctx, once, flattened) - - select { - case <-ctx.Done(): - return - default: - continue - } - } -} - -func (c *Cache) subscribeToConsulPeerings(ctx context.Context) { - once := &sync.Once{} - - opts := &api.QueryOptions{Connect: true} - if c.namespacesEnabled { - opts.Namespace = namespaceWildcard - } - - for { - select { - case <-ctx.Done(): - return - default: - } - - client, err := consul.NewClientFromConnMgr(c.config, c.serverMgr) - if err != nil { - c.logger.Error(err, "error initializing consul client") - continue - } - - peerings, meta, err := client.Peerings().List(ctx, opts.WithContext(ctx)) - if err != nil { - c.logger.Error(err, "error fetching services") - continue - } - - opts.WaitIndex = meta.LastIndex - c.updateAndNotifyPeerings(ctx, once, peerings) - - select { - case <-ctx.Done(): - return - default: - continue - } - } -} - func (c *Cache) updateAndNotify(ctx context.Context, once *sync.Once, kind string, entries []api.ConfigEntry) { c.cacheMutex.Lock() - cache := make(resourceCache) + cache := common.NewReferenceMap() for _, entry := range entries { - cache[translation.EntryToReference(entry)] = entry + cache.Set(common.EntryToReference(entry), entry) } - diffs := c.cache[kind].diff(cache) + diffs := c.cache[kind].Diff(cache) c.cache[kind] = cache @@ -537,130 +202,6 @@ func (c *Cache) updateAndNotify(ctx context.Context, once *sync.Once, kind strin c.notifySubscribers(ctx, kind, diffs) } -func (c *Cache) updateAndNotifyServices(ctx context.Context, once *sync.Once, services []*api.CatalogService) { - c.cacheMutex.Lock() - - cache := make(serviceCache) - - for _, service := range services { - cache[api.ResourceReference{Name: service.ServiceName, Namespace: service.Namespace, Partition: service.Partition}] = service - } - - diffs := c.serviceCache.diff(cache) - - c.serviceCache = cache - - // we run this the first time the cache is filled to notify the waiter - once.Do(func() { - c.logger.Info("sync mark for services") - c.synced <- struct{}{} - }) - - c.cacheMutex.Unlock() - - // now notify all subscribers - c.notifyServiceSubscribers(ctx, diffs) -} - -func (c *Cache) updateAndNotifyPeerings(ctx context.Context, once *sync.Once, peerings []*api.Peering) { - c.cacheMutex.Lock() - - cache := make(peeringCache) - - for _, peering := range peerings { - cache[api.ResourceReference{Name: peering.Name, Namespace: peering.ID, Partition: peering.Partition}] = peering - } - - diffs := c.peeringCache.diff(cache) - - c.peeringCache = cache - - // we run this the first time the cache is filled to notify the waiter - once.Do(func() { - c.logger.Info("sync mark for peerings") - c.synced <- struct{}{} - }) - - c.cacheMutex.Unlock() - - // now notify all peering subscribers - c.notifyPeeringSubscribers(ctx, diffs) -} - -// notifyServiceSubscribers notifies each subscriber for a given kind on changes to a config entry of that kind. It also -// handles removing any subscribers that have marked themselves as done. -func (c *Cache) notifyServiceSubscribers(ctx context.Context, services []*api.CatalogService) { - c.subscriberMutex.Lock() - defer c.subscriberMutex.Unlock() - - for _, service := range services { - // this will hold the new list of current subscribers after we finish notifying - subscribers := make([]*ServiceSubscription, 0, len(c.serviceSubscribers)) - for _, subscriber := range c.serviceSubscribers { - addSubscriber := false - - for _, namespaceName := range subscriber.translator(service) { - event := event.GenericEvent{ - Object: newConfigEntryObject(namespaceName), - } - - select { - case <-ctx.Done(): - return - case <-subscriber.ctx.Done(): - // don't add this subscriber to current list because it is done - addSubscriber = false - case subscriber.events <- event: - // keep this one since we can send events to it - addSubscriber = true - } - } - - if addSubscriber { - subscribers = append(subscribers, subscriber) - } - } - c.serviceSubscribers = subscribers - } -} - -// notifyPeeringSubscribers notifies each subscriber for a given kind on changes to a config entry of that kind. It also -// handles removing any subscribers that have marked themselves as done. -func (c *Cache) notifyPeeringSubscribers(ctx context.Context, peerings []*api.Peering) { - c.subscriberMutex.Lock() - defer c.subscriberMutex.Unlock() - - for _, peering := range peerings { - // this will hold the new list of current subscribers after we finish notifying - subscribers := make([]*PeeringsSubscription, 0, len(c.peeringsSubscribers)) - for _, subscriber := range c.peeringsSubscribers { - addSubscriber := false - - for _, namespaceName := range subscriber.translator(peering) { - event := event.GenericEvent{ - Object: newConfigEntryObject(namespaceName), - } - - select { - case <-ctx.Done(): - return - case <-subscriber.ctx.Done(): - // don't add this subscriber to current list because it is done - addSubscriber = false - case subscriber.events <- event: - // keep this one since we can send events to it - addSubscriber = true - } - } - - if addSubscriber { - subscribers = append(subscribers, subscriber) - } - } - c.peeringsSubscribers = subscribers - } -} - // notifySubscribers notifies each subscriber for a given kind on changes to a config entry of that kind. It also // handles removing any subscribers that have marked themselves as done. func (c *Cache) notifySubscribers(ctx context.Context, kind string, entries []api.ConfigEntry) { @@ -709,16 +250,11 @@ func (c *Cache) Write(ctx context.Context, entry api.ConfigEntry) error { return nil } - ref := translation.EntryToReference(entry) + ref := common.EntryToReference(entry) - old, ok := entryMap[ref] - if ok { - if cmp.Equal(old, entry, cmp.FilterPath(func(p cmp.Path) bool { - path := p.String() - return strings.HasSuffix(path, "Status") || strings.HasSuffix(path, "ModifyIndex") || strings.HasSuffix(path, "CreateIndex") - }, cmp.Ignore())) { - return nil - } + old := entryMap.Get(ref) + if old != nil && common.EntriesEqual(old, entry) { + return nil } client, err := consul.NewClientFromConnMgr(c.config, c.serverMgr) @@ -726,6 +262,12 @@ func (c *Cache) Write(ctx context.Context, entry api.ConfigEntry) error { return err } + if c.namespacesEnabled { + if _, err := namespaces.EnsureExists(client, entry.GetNamespace(), c.crossNamespaceACLPolicy); err != nil { + return err + } + } + options := &api.WriteOptions{} _, _, err = client.ConfigEntries().Set(entry, options.WithContext(ctx)) @@ -746,12 +288,7 @@ func (c *Cache) Get(ref api.ResourceReference) api.ConfigEntry { return nil } - entry, ok := entryMap[ref] - if !ok { - return nil - } - - return entry + return entryMap.Get(ref) } // Delete handles deleting the config entry from consul, if the current reference of the config entry is stale then @@ -764,8 +301,8 @@ func (c *Cache) Delete(ctx context.Context, ref api.ResourceReference) error { if !ok { return nil } - _, ok = entryMap[ref] - if !ok { + + if entryMap.Get(ref) == nil { c.logger.Info("cached object not found, not deleting") return nil } @@ -786,29 +323,12 @@ func (c *Cache) List(kind string) []api.ConfigEntry { c.cacheMutex.Lock() defer c.cacheMutex.Unlock() - entryMap, ok := c.cache[kind] + refMap, ok := c.cache[kind] if !ok { return nil } - entries := make([]api.ConfigEntry, 0, len(entryMap)) - for _, entry := range entryMap { - entries = append(entries, entry) - } - - return entries -} - -// ListServices returns a list of services from the cache that corresponds to the given kind. -func (c *Cache) ListServices() []api.CatalogService { - c.cacheMutex.Lock() - defer c.cacheMutex.Unlock() - - entries := make([]api.CatalogService, 0, len(c.serviceCache)) - for _, service := range c.serviceCache { - entries = append(entries, *service) - } - return entries + return refMap.Entries() } // LinkPolicy links a mesh write policy to a token associated with the service. diff --git a/control-plane/api-gateway/cache/consul_test.go b/control-plane/api-gateway/cache/consul_test.go index 256aa6b31d..555e13b6c2 100644 --- a/control-plane/api-gateway/cache/consul_test.go +++ b/control-plane/api-gateway/cache/consul_test.go @@ -16,11 +16,12 @@ import ( "github.com/go-logr/logr" logrtest "github.com/go-logr/logr/testing" "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/event" - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/translation" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul-k8s/control-plane/consul" "github.com/hashicorp/consul-k8s/control-plane/helper/test" "github.com/hashicorp/consul/api" @@ -29,21 +30,18 @@ import ( func Test_resourceCache_diff(t *testing.T) { t.Parallel() type args struct { - newCache resourceCache + newCache *common.ReferenceMap } tests := []struct { name string - oldCache resourceCache + oldCache *common.ReferenceMap args args want []api.ConfigEntry }{ { name: "no difference", - oldCache: resourceCache{ - api.ResourceReference{ - Kind: api.HTTPRoute, - Name: "my route", - }: api.ConfigEntry(&api.HTTPRouteConfigEntry{ + oldCache: loadedReferenceMaps([]api.ConfigEntry{ + &api.HTTPRouteConfigEntry{ Kind: api.HTTPRoute, Name: "my route", Parents: []api.ResourceReference{ @@ -123,14 +121,11 @@ func Test_resourceCache_diff(t *testing.T) { Hostnames: []string{"hostname.com"}, Meta: map[string]string{}, Status: api.ConfigEntryStatus{}, - }), - }, + }, + })[api.HTTPRoute], args: args{ - newCache: resourceCache{ - api.ResourceReference{ - Kind: api.HTTPRoute, - Name: "my route", - }: api.ConfigEntry(&api.HTTPRouteConfigEntry{ + newCache: loadedReferenceMaps([]api.ConfigEntry{ + &api.HTTPRouteConfigEntry{ Kind: api.HTTPRoute, Name: "my route", Parents: []api.ResourceReference{ @@ -210,18 +205,15 @@ func Test_resourceCache_diff(t *testing.T) { Hostnames: []string{"hostname.com"}, Meta: map[string]string{}, Status: api.ConfigEntryStatus{}, - }), - }, + }, + })[api.HTTPRoute], }, want: []api.ConfigEntry{}, }, { name: "resource exists in old cache but not new one", - oldCache: resourceCache{ - api.ResourceReference{ - Kind: api.HTTPRoute, - Name: "my route", - }: api.ConfigEntry(&api.HTTPRouteConfigEntry{ + oldCache: loadedReferenceMaps([]api.ConfigEntry{ + &api.HTTPRouteConfigEntry{ Kind: api.HTTPRoute, Name: "my route", Parents: []api.ResourceReference{ @@ -301,11 +293,8 @@ func Test_resourceCache_diff(t *testing.T) { Hostnames: []string{"hostname.com"}, Meta: map[string]string{}, Status: api.ConfigEntryStatus{}, - }), - api.ResourceReference{ - Kind: api.HTTPRoute, - Name: "my route 2", - }: api.ConfigEntry(&api.HTTPRouteConfigEntry{ + }, + &api.HTTPRouteConfigEntry{ Kind: api.HTTPRoute, Name: "my route 2", Parents: []api.ResourceReference{ @@ -385,14 +374,11 @@ func Test_resourceCache_diff(t *testing.T) { Hostnames: []string{"hostname.com"}, Meta: map[string]string{}, Status: api.ConfigEntryStatus{}, - }), - }, + }, + })[api.HTTPRoute], args: args{ - newCache: resourceCache{ - api.ResourceReference{ - Kind: api.HTTPRoute, - Name: "my route", - }: api.ConfigEntry(&api.HTTPRouteConfigEntry{ + newCache: loadedReferenceMaps([]api.ConfigEntry{ + &api.HTTPRouteConfigEntry{ Kind: api.HTTPRoute, Name: "my route", Parents: []api.ResourceReference{ @@ -472,11 +458,11 @@ func Test_resourceCache_diff(t *testing.T) { Hostnames: []string{"hostname.com"}, Meta: map[string]string{}, Status: api.ConfigEntryStatus{}, - }), - }, + }, + })[api.HTTPRoute], }, want: []api.ConfigEntry{ - api.ConfigEntry(&api.HTTPRouteConfigEntry{ + &api.HTTPRouteConfigEntry{ Kind: api.HTTPRoute, Name: "my route 2", Parents: []api.ResourceReference{ @@ -556,16 +542,13 @@ func Test_resourceCache_diff(t *testing.T) { Hostnames: []string{"hostname.com"}, Meta: map[string]string{}, Status: api.ConfigEntryStatus{}, - }), + }, }, }, { name: "resource exists in new cache but not old one", - oldCache: resourceCache{ - api.ResourceReference{ - Kind: api.HTTPRoute, - Name: "my route", - }: api.ConfigEntry(&api.HTTPRouteConfigEntry{ + oldCache: loadedReferenceMaps([]api.ConfigEntry{ + &api.HTTPRouteConfigEntry{ Kind: api.HTTPRoute, Name: "my route", Parents: []api.ResourceReference{ @@ -645,14 +628,11 @@ func Test_resourceCache_diff(t *testing.T) { Hostnames: []string{"hostname.com"}, Meta: map[string]string{}, Status: api.ConfigEntryStatus{}, - }), - }, + }, + })[api.HTTPRoute], args: args{ - newCache: resourceCache{ - api.ResourceReference{ - Kind: api.HTTPRoute, - Name: "my route", - }: api.ConfigEntry(&api.HTTPRouteConfigEntry{ + newCache: loadedReferenceMaps([]api.ConfigEntry{ + &api.HTTPRouteConfigEntry{ Kind: api.HTTPRoute, Name: "my route", Parents: []api.ResourceReference{ @@ -732,12 +712,8 @@ func Test_resourceCache_diff(t *testing.T) { Hostnames: []string{"hostname.com"}, Meta: map[string]string{}, Status: api.ConfigEntryStatus{}, - }), - - api.ResourceReference{ - Kind: api.HTTPRoute, - Name: "my route 2", - }: api.ConfigEntry(&api.HTTPRouteConfigEntry{ + }, + &api.HTTPRouteConfigEntry{ Kind: api.HTTPRoute, Name: "my route 2", Parents: []api.ResourceReference{ @@ -817,11 +793,11 @@ func Test_resourceCache_diff(t *testing.T) { Hostnames: []string{"hostname.com"}, Meta: map[string]string{}, Status: api.ConfigEntryStatus{}, - }), - }, + }, + })[api.HTTPRoute], }, want: []api.ConfigEntry{ - api.ConfigEntry(&api.HTTPRouteConfigEntry{ + &api.HTTPRouteConfigEntry{ Kind: api.HTTPRoute, Name: "my route 2", Parents: []api.ResourceReference{ @@ -901,16 +877,13 @@ func Test_resourceCache_diff(t *testing.T) { Hostnames: []string{"hostname.com"}, Meta: map[string]string{}, Status: api.ConfigEntryStatus{}, - }), + }, }, }, { name: "same ref new cache has a greater modify index", - oldCache: resourceCache{ - api.ResourceReference{ - Kind: api.HTTPRoute, - Name: "my route", - }: api.ConfigEntry(&api.HTTPRouteConfigEntry{ + oldCache: loadedReferenceMaps([]api.ConfigEntry{ + &api.HTTPRouteConfigEntry{ Kind: api.HTTPRoute, Name: "my route", ModifyIndex: 1, @@ -991,14 +964,11 @@ func Test_resourceCache_diff(t *testing.T) { Hostnames: []string{"hostname.com"}, Meta: map[string]string{}, Status: api.ConfigEntryStatus{}, - }), - }, + }, + })[api.HTTPRoute], args: args{ - newCache: resourceCache{ - api.ResourceReference{ - Kind: api.HTTPRoute, - Name: "my route", - }: api.ConfigEntry(&api.HTTPRouteConfigEntry{ + newCache: loadedReferenceMaps([]api.ConfigEntry{ + &api.HTTPRouteConfigEntry{ Kind: api.HTTPRoute, Name: "my route", ModifyIndex: 10, @@ -1079,11 +1049,11 @@ func Test_resourceCache_diff(t *testing.T) { Hostnames: []string{"hostname.com"}, Meta: map[string]string{}, Status: api.ConfigEntryStatus{}, - }), - }, + }, + })[api.HTTPRoute], }, want: []api.ConfigEntry{ - api.ConfigEntry(&api.HTTPRouteConfigEntry{ + &api.HTTPRouteConfigEntry{ Kind: api.HTTPRoute, Name: "my route", ModifyIndex: 10, @@ -1164,7 +1134,7 @@ func Test_resourceCache_diff(t *testing.T) { Hostnames: []string{"hostname.com"}, Meta: map[string]string{}, Status: api.ConfigEntryStatus{}, - }), + }, }, }, } @@ -1172,7 +1142,7 @@ func Test_resourceCache_diff(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() - got := tt.oldCache.diff(tt.args.newCache) + got := tt.oldCache.Diff(tt.args.newCache) if diff := cmp.Diff(got, tt.want); diff != "" { t.Errorf("resourceCache.diff mismatch (-want +got):\n%s", diff) } @@ -1185,7 +1155,7 @@ func TestCache_Subscribe(t *testing.T) { type args struct { ctx context.Context kind string - translator translation.TranslatorFn + translator TranslatorFn } tests := []struct { name string @@ -1327,7 +1297,7 @@ func TestCache_Write(t *testing.T) { }, ConsulServerConnMgr: test.MockConnMgrForIPAndPort(serverURL.Hostname(), port), NamespacesEnabled: false, - Logger: logr.Logger{}, + Logger: logrtest.NewTestLogger(t), }) entry := &api.HTTPRouteConfigEntry{ @@ -1427,7 +1397,7 @@ func TestCache_Get(t *testing.T) { name string args args want api.ConfigEntry - cache map[string]resourceCache + cache map[string]*common.ReferenceMap }{ { name: "entry exists", @@ -1442,26 +1412,18 @@ func TestCache_Get(t *testing.T) { Name: "api-gw", Meta: map[string]string{}, }, - cache: map[string]resourceCache{ - api.APIGateway: { - api.ResourceReference{ - Kind: api.APIGateway, - Name: "api-gw", - }: &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "api-gw", - Meta: map[string]string{}, - }, - api.ResourceReference{ - Kind: api.APIGateway, - Name: "api-gw-2", - }: &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "api-gw-2", - Meta: map[string]string{}, - }, + cache: loadedReferenceMaps([]api.ConfigEntry{ + &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "api-gw", + Meta: map[string]string{}, }, - }, + &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "api-gw-2", + Meta: map[string]string{}, + }, + }), }, { name: "entry does not exist", @@ -1472,26 +1434,18 @@ func TestCache_Get(t *testing.T) { }, }, want: nil, - cache: map[string]resourceCache{ - api.APIGateway: { - api.ResourceReference{ - Kind: api.APIGateway, - Name: "api-gw", - }: &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "api-gw", - Meta: map[string]string{}, - }, - api.ResourceReference{ - Kind: api.APIGateway, - Name: "api-gw-2", - }: &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "api-gw-2", - Meta: map[string]string{}, - }, + cache: loadedReferenceMaps([]api.ConfigEntry{ + &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "api-gw", + Meta: map[string]string{}, }, - }, + &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: "api-gw-2", + Meta: map[string]string{}, + }, + }), }, { name: "kind key does not exist", @@ -1502,18 +1456,13 @@ func TestCache_Get(t *testing.T) { }, }, want: nil, - cache: map[string]resourceCache{ - api.HTTPRoute: { - api.ResourceReference{ - Kind: api.HTTPRoute, - Name: "api-gw", - }: &api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "route", - Meta: map[string]string{}, - }, + cache: loadedReferenceMaps([]api.ConfigEntry{ + &api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, + Name: "route", + Meta: map[string]string{}, }, - }, + }), }, } for _, tt := range tests { @@ -1614,30 +1563,18 @@ func Test_Run(t *testing.T) { NamespacesEnabled: false, Logger: logrtest.NewTestLogger(t), }) - prevCache := make(map[string]resourceCache) + prevCache := make(map[string]*common.ReferenceMap) for kind, cache := range c.cache { - resCache := make(resourceCache) - for resourceRef, entry := range cache { - resCache[resourceRef] = entry + resCache := common.NewReferenceMap() + for _, entry := range cache.Entries() { + resCache.Set(common.EntryToReference(entry), entry) } prevCache[kind] = resCache } - expectedCache := map[string]resourceCache{ - api.APIGateway: { - {Kind: api.APIGateway, Name: gw.Name}: gw, - }, - api.TCPRoute: { - {Kind: api.TCPRoute, Name: tcpRoute.Name}: tcpRoute, - }, - api.HTTPRoute: { - {Kind: api.HTTPRoute, Name: httpRouteOne.Name}: httpRouteOne, - {Kind: api.HTTPRoute, Name: httpRouteTwo.Name}: httpRouteTwo, - }, - api.InlineCertificate: { - {Kind: api.InlineCertificate, Name: inlineCert.Name}: inlineCert, - }, - } + expectedCache := loadedReferenceMaps([]api.ConfigEntry{ + gw, tcpRoute, httpRouteOne, httpRouteTwo, inlineCert, + }) ctx, cancelFn := context.WithCancel(context.Background()) @@ -1696,9 +1633,6 @@ func Test_Run(t *testing.T) { } }) - c.SubscribeServices(ctx, func(cs *api.CatalogService) []types.NamespacedName { return nil }).Cancel() - c.SubscribePeerings(ctx, func(cs *api.Peering) []types.NamespacedName { return nil }).Cancel() - // mark this subscription as ended canceledSub.Cancel() @@ -1736,14 +1670,19 @@ func Test_Run(t *testing.T) { // cancel the context so the Run function exits cancelFn() + sorter := func(x, y api.ConfigEntry) bool { + return x.GetName() < y.GetName() + } // Check cache // expect the cache to have changed - if diff := cmp.Diff(prevCache, c.cache); diff == "" { - t.Error("Expect cache to have changed but it did not") - } + for _, kind := range Kinds { + if diff := cmp.Diff(prevCache[kind].Entries(), c.cache[kind].Entries(), cmpopts.SortSlices(sorter)); diff == "" { + t.Error("Expect cache to have changed but it did not") + } - if diff := cmp.Diff(expectedCache, c.cache); diff != "" { - t.Errorf("Cache.cache mismatch (-want +got):\n%s", diff) + if diff := cmp.Diff(expectedCache[kind].Entries(), c.cache[kind].Entries(), cmpopts.SortSlices(sorter)); diff != "" { + t.Errorf("Cache.cache mismatch (-want +got):\n%s", diff) + } } } @@ -2026,3 +1965,17 @@ func TestCache_Delete(t *testing.T) { }) } } + +func loadedReferenceMaps(entries []api.ConfigEntry) map[string]*common.ReferenceMap { + refs := make(map[string]*common.ReferenceMap) + + for _, entry := range entries { + refMap, ok := refs[entry.GetKind()] + if !ok { + refMap = common.NewReferenceMap() + } + refMap.Set(common.EntryToReference(entry), entry) + refs[entry.GetKind()] = refMap + } + return refs +} diff --git a/control-plane/api-gateway/cache/gateway.go b/control-plane/api-gateway/cache/gateway.go new file mode 100644 index 0000000000..d6d0c27ed2 --- /dev/null +++ b/control-plane/api-gateway/cache/gateway.go @@ -0,0 +1,136 @@ +package cache + +import ( + "context" + "fmt" + "sync" + + "github.com/cenkalti/backoff" + "github.com/go-logr/logr" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" + "github.com/hashicorp/consul-k8s/control-plane/consul" + "github.com/hashicorp/consul/api" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/event" +) + +type GatewayCache struct { + config *consul.Config + serverMgr consul.ServerConnectionManager + logger logr.Logger + + events chan event.GenericEvent + + data map[api.ResourceReference][]api.CatalogService + dataMutex sync.RWMutex + + subscribedGateways map[api.ResourceReference]context.CancelFunc + mutex sync.RWMutex + + ctx context.Context +} + +func NewGatewayCache(ctx context.Context, config Config) *GatewayCache { + return &GatewayCache{ + config: config.ConsulClientConfig, + serverMgr: config.ConsulServerConnMgr, + logger: config.Logger, + events: make(chan event.GenericEvent), + data: make(map[api.ResourceReference][]api.CatalogService), + subscribedGateways: make(map[api.ResourceReference]context.CancelFunc), + ctx: ctx, + } +} + +func (r *GatewayCache) ServicesFor(ref api.ResourceReference) []api.CatalogService { + r.dataMutex.RLock() + defer r.dataMutex.RUnlock() + + return r.data[common.NormalizeMeta(ref)] +} + +func (r *GatewayCache) EnsureSubscribed(ref api.ResourceReference, resource types.NamespacedName) { + r.mutex.Lock() + defer r.mutex.Unlock() + + if _, exists := r.subscribedGateways[common.NormalizeMeta(ref)]; exists { + return + } + + ctx, cancel := context.WithCancel(r.ctx) + r.subscribedGateways[common.NormalizeMeta(ref)] = cancel + go r.subscribeToGateway(ctx, ref, resource) +} + +func (r *GatewayCache) RemoveSubscription(ref api.ResourceReference) { + r.mutex.Lock() + defer r.mutex.Unlock() + + if cancel, exists := r.subscribedGateways[common.NormalizeMeta(ref)]; exists { + cancel() + delete(r.subscribedGateways, common.NormalizeMeta(ref)) + } +} + +func (r *GatewayCache) subscribeToGateway(ctx context.Context, ref api.ResourceReference, resource types.NamespacedName) { + opts := &api.QueryOptions{} + if ref.Namespace != "" { + opts.Namespace = ref.Namespace + } + + var ( + services []*api.CatalogService + meta *api.QueryMeta + ) + + for { + select { + case <-ctx.Done(): + r.dataMutex.Lock() + delete(r.data, ref) + r.dataMutex.Unlock() + return + default: + } + + retryBackoff := backoff.WithMaxRetries(backoff.NewExponentialBackOff(), 10) + + if err := backoff.Retry(func() error { + client, err := consul.NewClientFromConnMgr(r.config, r.serverMgr) + if err != nil { + return err + } + + services, meta, err = client.Catalog().Service(ref.Name, "", opts.WithContext(ctx)) + if err != nil { + return err + } + + return nil + }, backoff.WithContext(retryBackoff, ctx)); err != nil { + r.logger.Error(err, fmt.Sprintf("unable to fetch config entry for gateway: %s/%s", ref.Namespace, ref.Name)) + continue + } + + opts.WaitIndex = meta.LastIndex + + derefed := common.DerefAll(services) + + r.dataMutex.Lock() + r.data[common.NormalizeMeta(ref)] = derefed + r.dataMutex.Unlock() + + event := event.GenericEvent{ + Object: newConfigEntryObject(resource), + } + + select { + case <-ctx.Done(): + r.dataMutex.Lock() + delete(r.data, ref) + r.dataMutex.Unlock() + return + case r.events <- event: + } + } +} diff --git a/control-plane/api-gateway/cache/kubernetes.go b/control-plane/api-gateway/cache/kubernetes.go new file mode 100644 index 0000000000..642a6935fb --- /dev/null +++ b/control-plane/api-gateway/cache/kubernetes.go @@ -0,0 +1,32 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package cache + +import ( + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// configEntryObject is used for generic k8s events so we maintain the consul name/namespace. +type configEntryObject struct { + client.Object // embed so we fufill the object interface + + Namespace string + Name string +} + +func (c *configEntryObject) GetNamespace() string { + return c.Namespace +} + +func (c *configEntryObject) GetName() string { + return c.Name +} + +func newConfigEntryObject(namespacedName types.NamespacedName) *configEntryObject { + return &configEntryObject{ + Namespace: namespacedName.Namespace, + Name: namespacedName.Name, + } +} diff --git a/control-plane/api-gateway/cache/subscription.go b/control-plane/api-gateway/cache/subscription.go new file mode 100644 index 0000000000..8605c95926 --- /dev/null +++ b/control-plane/api-gateway/cache/subscription.go @@ -0,0 +1,30 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package cache + +import ( + "context" + + "github.com/hashicorp/consul/api" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/event" +) + +type TranslatorFn func(api.ConfigEntry) []types.NamespacedName + +// Subscription represents a watcher for events on a specific kind. +type Subscription struct { + translator TranslatorFn + ctx context.Context + cancelCtx context.CancelFunc + events chan event.GenericEvent +} + +func (s *Subscription) Cancel() { + s.cancelCtx() +} + +func (s *Subscription) Events() chan event.GenericEvent { + return s.events +} diff --git a/control-plane/api-gateway/common/constants.go b/control-plane/api-gateway/common/constants.go new file mode 100644 index 0000000000..68abfc96b1 --- /dev/null +++ b/control-plane/api-gateway/common/constants.go @@ -0,0 +1,8 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package common + +const ( + AnnotationGatewayClassConfig = "consul.hashicorp.com/gateway-class-config" +) diff --git a/control-plane/api-gateway/common/diff.go b/control-plane/api-gateway/common/diff.go new file mode 100644 index 0000000000..b58bf23901 --- /dev/null +++ b/control-plane/api-gateway/common/diff.go @@ -0,0 +1,263 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package common + +import ( + "strings" + + "github.com/hashicorp/consul/api" + "golang.org/x/exp/maps" + "golang.org/x/exp/slices" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +func GatewayStatusesEqual(a, b gwv1beta1.GatewayStatus) bool { + return slices.EqualFunc(a.Addresses, b.Addresses, gatewayStatusesAddressesEqual) && + slices.EqualFunc(a.Conditions, b.Conditions, conditionsEqual) && + slices.EqualFunc(a.Listeners, b.Listeners, gatewayStatusesListenersEqual) +} + +func gatewayStatusesAddressesEqual(a, b gwv1beta1.GatewayAddress) bool { + return BothNilOrEqual(a.Type, b.Type) && + a.Value == b.Value +} + +func gatewayStatusesListenersEqual(a, b gwv1beta1.ListenerStatus) bool { + return a.AttachedRoutes == b.AttachedRoutes && + a.Name == b.Name && + slices.EqualFunc(a.SupportedKinds, b.SupportedKinds, routeGroupKindsEqual) && + slices.EqualFunc(a.Conditions, b.Conditions, conditionsEqual) +} + +func routeGroupKindsEqual(a, b gwv1beta1.RouteGroupKind) bool { + return BothNilOrEqual(a.Group, b.Group) && + a.Kind == b.Kind +} + +// this intentionally ignores the last set time so we don't +// always fail a conditional check per-reconciliation. +func conditionsEqual(a, b metav1.Condition) bool { + return a.Type == b.Type && + a.Status == b.Status && + a.Reason == b.Reason && + a.Message == b.Message && + a.ObservedGeneration == b.ObservedGeneration +} + +func EntriesEqual(a, b api.ConfigEntry) bool { + switch aCast := a.(type) { + case *api.APIGatewayConfigEntry: + if bCast, ok := b.(*api.APIGatewayConfigEntry); ok { + return apiGatewaysEqual(aCast, bCast) + } + case *api.HTTPRouteConfigEntry: + if bCast, ok := b.(*api.HTTPRouteConfigEntry); ok { + return httpRoutesEqual(aCast, bCast) + } + case *api.TCPRouteConfigEntry: + if bCast, ok := b.(*api.TCPRouteConfigEntry); ok { + return tcpRoutesEqual(aCast, bCast) + } + case *api.InlineCertificateConfigEntry: + if bCast, ok := b.(*api.InlineCertificateConfigEntry); ok { + return certificatesEqual(aCast, bCast) + } + } + return false +} + +type entryComparator struct { + namespaceA string + partitionA string + namespaceB string + partitionB string +} + +func apiGatewaysEqual(a, b *api.APIGatewayConfigEntry) bool { + if a == nil || b == nil { + return false + } + + return (entryComparator{ + namespaceA: NormalizeEmptyMetadataString(a.Namespace), + partitionA: NormalizeEmptyMetadataString(a.Partition), + namespaceB: NormalizeEmptyMetadataString(b.Namespace), + partitionB: NormalizeEmptyMetadataString(b.Partition), + }).apiGatewaysEqual(*a, *b) +} + +func (e entryComparator) apiGatewaysEqual(a, b api.APIGatewayConfigEntry) bool { + return a.Kind == b.Kind && + a.Name == b.Name && + e.namespaceA == e.namespaceB && + e.partitionA == e.partitionB && + maps.Equal(a.Meta, b.Meta) && + slices.EqualFunc(a.Listeners, b.Listeners, e.apiGatewayListenersEqual) +} + +func (e entryComparator) apiGatewayListenersEqual(a, b api.APIGatewayListener) bool { + return a.Hostname == b.Hostname && + a.Name == b.Name && + a.Port == b.Port && + // normalize the protocol name + strings.EqualFold(a.Protocol, b.Protocol) && + e.apiGatewayListenerTLSConfigurationsEqual(a.TLS, b.TLS) +} + +func (e entryComparator) apiGatewayListenerTLSConfigurationsEqual(a, b api.APIGatewayTLSConfiguration) bool { + return a.MaxVersion == b.MaxVersion && + a.MinVersion == b.MinVersion && + slices.Equal(a.CipherSuites, b.CipherSuites) && + slices.EqualFunc(a.Certificates, b.Certificates, e.resourceReferencesEqual) +} + +func (e entryComparator) resourceReferencesEqual(a, b api.ResourceReference) bool { + return a.Kind == b.Kind && + a.Name == b.Name && + a.SectionName == b.SectionName && + orDefault(a.Namespace, e.namespaceA) == orDefault(b.Namespace, e.namespaceB) && + orDefault(a.Partition, e.partitionA) == orDefault(b.Partition, e.partitionB) +} + +func httpRoutesEqual(a, b *api.HTTPRouteConfigEntry) bool { + if a == nil || b == nil { + return false + } + + return (entryComparator{ + namespaceA: NormalizeEmptyMetadataString(a.Namespace), + partitionA: NormalizeEmptyMetadataString(a.Partition), + namespaceB: NormalizeEmptyMetadataString(b.Namespace), + partitionB: NormalizeEmptyMetadataString(b.Partition), + }).httpRoutesEqual(*a, *b) +} + +func (e entryComparator) httpRoutesEqual(a, b api.HTTPRouteConfigEntry) bool { + return a.Kind == b.Kind && + a.Name == b.Name && + e.namespaceA == e.namespaceB && + e.partitionA == e.partitionB && + maps.Equal(a.Meta, b.Meta) && + slices.Equal(a.Hostnames, b.Hostnames) && + slices.EqualFunc(a.Parents, b.Parents, e.resourceReferencesEqual) && + slices.EqualFunc(a.Rules, b.Rules, e.httpRouteRulesEqual) +} + +func (e entryComparator) httpRouteRulesEqual(a, b api.HTTPRouteRule) bool { + return slices.EqualFunc(a.Filters.Headers, b.Filters.Headers, e.httpHeaderFiltersEqual) && + bothNilOrEqualFunc(a.Filters.URLRewrite, b.Filters.URLRewrite, e.urlRewritesEqual) && + slices.EqualFunc(a.Matches, b.Matches, e.httpMatchesEqual) && + slices.EqualFunc(a.Services, b.Services, e.httpServicesEqual) +} + +func (e entryComparator) httpServicesEqual(a, b api.HTTPService) bool { + return a.Name == b.Name && + a.Weight == b.Weight && + orDefault(a.Namespace, e.namespaceA) == orDefault(b.Namespace, e.namespaceB) && + orDefault(a.Partition, e.partitionA) == orDefault(b.Partition, e.partitionB) && + slices.EqualFunc(a.Filters.Headers, b.Filters.Headers, e.httpHeaderFiltersEqual) && + bothNilOrEqualFunc(a.Filters.URLRewrite, b.Filters.URLRewrite, e.urlRewritesEqual) +} + +func (e entryComparator) httpMatchesEqual(a, b api.HTTPMatch) bool { + return a.Method == b.Method && + slices.EqualFunc(a.Headers, b.Headers, e.httpHeaderMatchesEqual) && + slices.EqualFunc(a.Query, b.Query, e.httpQueryMatchesEqual) && + e.httpPathMatchesEqual(a.Path, b.Path) +} + +func (e entryComparator) httpPathMatchesEqual(a, b api.HTTPPathMatch) bool { + return a.Match == b.Match && a.Value == b.Value +} + +func (e entryComparator) httpHeaderMatchesEqual(a, b api.HTTPHeaderMatch) bool { + return a.Match == b.Match && a.Name == b.Name && a.Value == b.Value +} + +func (e entryComparator) httpQueryMatchesEqual(a, b api.HTTPQueryMatch) bool { + return a.Match == b.Match && a.Name == b.Name && a.Value == b.Value +} + +func (e entryComparator) httpHeaderFiltersEqual(a, b api.HTTPHeaderFilter) bool { + return maps.Equal(a.Add, b.Add) && + maps.Equal(a.Set, b.Set) && + slices.Equal(a.Remove, b.Remove) +} + +func (e entryComparator) urlRewritesEqual(a, b api.URLRewrite) bool { + return a.Path == b.Path +} + +func tcpRoutesEqual(a, b *api.TCPRouteConfigEntry) bool { + if a == nil || b == nil { + return false + } + + return (entryComparator{ + namespaceA: NormalizeEmptyMetadataString(a.Namespace), + partitionA: NormalizeEmptyMetadataString(a.Partition), + namespaceB: NormalizeEmptyMetadataString(b.Namespace), + partitionB: NormalizeEmptyMetadataString(b.Partition), + }).tcpRoutesEqual(*a, *b) +} + +func (e entryComparator) tcpRoutesEqual(a, b api.TCPRouteConfigEntry) bool { + return a.Kind == b.Kind && + a.Name == b.Name && + e.namespaceA == e.namespaceB && + e.partitionA == e.partitionB && + maps.Equal(a.Meta, b.Meta) && + slices.EqualFunc(a.Parents, b.Parents, e.resourceReferencesEqual) && + slices.EqualFunc(a.Services, b.Services, e.tcpRouteServicesEqual) +} + +func (e entryComparator) tcpRouteServicesEqual(a, b api.TCPService) bool { + return a.Name == b.Name && + orDefault(a.Namespace, e.namespaceA) == orDefault(b.Namespace, e.namespaceB) && + orDefault(a.Partition, e.partitionA) == orDefault(b.Partition, e.partitionB) +} + +func certificatesEqual(a, b *api.InlineCertificateConfigEntry) bool { + if a == nil || b == nil { + return false + } + + return (entryComparator{ + namespaceA: NormalizeEmptyMetadataString(a.Namespace), + partitionA: NormalizeEmptyMetadataString(a.Partition), + namespaceB: NormalizeEmptyMetadataString(b.Namespace), + partitionB: NormalizeEmptyMetadataString(b.Partition), + }).certificatesEqual(*a, *b) +} + +func (e entryComparator) certificatesEqual(a, b api.InlineCertificateConfigEntry) bool { + return a.Kind == b.Kind && + a.Name == b.Name && + e.namespaceA == e.namespaceB && + e.partitionA == e.partitionB && + maps.Equal(a.Meta, b.Meta) && + a.Certificate == b.Certificate && + a.PrivateKey == b.PrivateKey +} + +func bothNilOrEqualFunc[T any](one, two *T, fn func(T, T) bool) bool { + if one == nil && two == nil { + return true + } + if one == nil { + return false + } + if two == nil { + return false + } + return fn(*one, *two) +} + +func orDefault[T ~string](v T, fallback string) string { + if v == "" { + return fallback + } + return string(v) +} diff --git a/control-plane/api-gateway/common/finalizers.go b/control-plane/api-gateway/common/finalizers.go new file mode 100644 index 0000000000..e1fe84bdac --- /dev/null +++ b/control-plane/api-gateway/common/finalizers.go @@ -0,0 +1,60 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package common + +import ( + "sigs.k8s.io/controller-runtime/pkg/client" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +const ( + // GatewayFinalizer is the finalizer we add to any gateway object. + GatewayFinalizer = "gateway-finalizer.consul.hashicorp.com" + + // NamespaceNameLabel represents that label added automatically to namespaces in newer Kubernetes clusters. + NamespaceNameLabel = "kubernetes.io/metadata.name" +) + +var ( + // constants extracted for ease of use. + KindGateway = "Gateway" + KindSecret = "Secret" + KindService = "Service" + BetaGroup = gwv1beta1.GroupVersion.Group +) + +// EnsureFinalizer ensures that our finalizer is set on an object +// returning whether or not it modified the object. +func EnsureFinalizer(object client.Object) bool { + if !object.GetDeletionTimestamp().IsZero() { + return false + } + + finalizers := object.GetFinalizers() + for _, f := range finalizers { + if f == GatewayFinalizer { + return false + } + } + + object.SetFinalizers(append(finalizers, GatewayFinalizer)) + return true +} + +// RemoveFinalizer ensures that our finalizer is absent from an object +// returning whether or not it modified the object. +func RemoveFinalizer(object client.Object) bool { + found := false + filtered := []string{} + for _, f := range object.GetFinalizers() { + if f == GatewayFinalizer { + found = true + continue + } + filtered = append(filtered, f) + } + + object.SetFinalizers(filtered) + return found +} diff --git a/control-plane/api-gateway/helm_config.go b/control-plane/api-gateway/common/helm_config.go similarity index 98% rename from control-plane/api-gateway/helm_config.go rename to control-plane/api-gateway/common/helm_config.go index ef7ab22df3..2a6cc8211b 100644 --- a/control-plane/api-gateway/helm_config.go +++ b/control-plane/api-gateway/common/helm_config.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package apigateway +package common import "time" diff --git a/control-plane/api-gateway/common/helpers.go b/control-plane/api-gateway/common/helpers.go new file mode 100644 index 0000000000..b0eeb46510 --- /dev/null +++ b/control-plane/api-gateway/common/helpers.go @@ -0,0 +1,218 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package common + +import ( + "github.com/hashicorp/consul/api" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +func DerefAll[T any](vs []*T) []T { + e := make([]T, 0, len(vs)) + for _, v := range vs { + e = append(e, *v) + } + return e +} + +func EmptyOrEqual(v, check string) bool { + return v == "" || v == check +} + +func NilOrEqual[T ~string](v *T, check string) bool { + return v == nil || string(*v) == check +} + +func IndexedNamespacedNameWithDefault[T ~string, U ~string, V ~string](t T, u *U, v V) types.NamespacedName { + return types.NamespacedName{ + Namespace: DerefStringOr(u, v), + Name: string(t), + } +} + +func ResourceReferenceWithDefault[T ~string, U ~string, V ~string](kind string, name T, section string, u *U, v V, partition string) api.ResourceReference { + return api.ResourceReference{ + Kind: kind, + Name: string(name), + SectionName: section, + Namespace: DerefStringOr(u, v), + Partition: partition, + } +} + +func DerefStringOr[T ~string, U ~string](v *T, val U) string { + if v == nil { + return string(val) + } + return string(*v) +} + +func DerefLookup[T comparable, U any](v *T, lookup map[T]U) U { + var zero U + if v == nil { + return zero + } + return lookup[*v] +} + +func DerefConvertFunc[T any, U any](v *T, fn func(T) U) U { + var zero U + if v == nil { + return zero + } + return fn(*v) +} + +func DerefEqual[T ~string](v *T, check string) bool { + if v == nil { + return false + } + return string(*v) == check +} + +func DerefIntOr[T ~int | ~int32, U ~int](v *T, val U) int { + if v == nil { + return int(val) + } + return int(*v) +} + +func StringLikeSlice[T ~string](vs []T) []string { + converted := []string{} + for _, v := range vs { + converted = append(converted, string(v)) + } + return converted +} + +func ConvertMapValuesToSlice[T comparable, U any](vs map[T]U) []U { + converted := []U{} + for _, v := range vs { + converted = append(converted, v) + } + return converted +} + +func ConvertSliceFunc[T any, U any](vs []T, fn func(T) U) []U { + converted := []U{} + for _, v := range vs { + converted = append(converted, fn(v)) + } + return converted +} + +func ConvertSliceFuncIf[T any, U any](vs []T, fn func(T) (U, bool)) []U { + converted := []U{} + for _, v := range vs { + if c, ok := fn(v); ok { + converted = append(converted, c) + } + } + return converted +} + +func Flatten[T any](vs [][]T) []T { + flattened := []T{} + for _, v := range vs { + flattened = append(flattened, v...) + } + return flattened +} + +func Filter[T any](vs []T, filterFn func(T) bool) []T { + filtered := []T{} + for _, v := range vs { + if !filterFn(v) { + filtered = append(filtered, v) + } + } + return filtered +} + +func DefaultOrEqual(v, fallback, check string) bool { + if v == "" { + return fallback == check + } + return v == check +} + +// ObjectsToReconcileRequests takes a list of objects and returns a list of +// reconcile Requests. +func ObjectsToReconcileRequests[T metav1.Object](objects []T) []reconcile.Request { + requests := make([]reconcile.Request, 0, len(objects)) + + for _, object := range objects { + requests = append(requests, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: object.GetNamespace(), + Name: object.GetName(), + }, + }) + } + return requests +} + +// ParentRefs takes a list of ParentReference objects and returns a list of NamespacedName objects. +func ParentRefs(group, kind, namespace string, refs []gwv1beta1.ParentReference) []types.NamespacedName { + indexed := make([]types.NamespacedName, 0, len(refs)) + for _, parent := range refs { + if NilOrEqual(parent.Group, group) && NilOrEqual(parent.Kind, kind) { + indexed = append(indexed, IndexedNamespacedNameWithDefault(parent.Name, parent.Namespace, namespace)) + } + } + return indexed +} + +// BothNilOrEqual is used to determine if two pointers to comparable +// object are either nil or both point to the same value. +func BothNilOrEqual[T comparable](one, two *T) bool { + if one == nil && two == nil { + return true + } + if one == nil { + return false + } + if two == nil { + return false + } + return *one == *two +} + +// ValueOr checks if a string-like pointer is nil, and if it is, +// returns the given value instead. +func ValueOr[T ~string](v *T, fallback string) string { + if v == nil { + return fallback + } + return string(*v) +} + +// PointerTo is a convenience method for taking a pointer +// of an object without having to declare an intermediate variable. +// It's also useful for making sure we don't accidentally take +// the pointer of a range variable directly. +func PointerTo[T any](v T) *T { + return &v +} + +// ParentsEqual checks for equality between two parent references. +func ParentsEqual(one, two gwv1beta1.ParentReference) bool { + return BothNilOrEqual(one.Group, two.Group) && + BothNilOrEqual(one.Kind, two.Kind) && + BothNilOrEqual(one.SectionName, two.SectionName) && + BothNilOrEqual(one.Port, two.Port) && + one.Name == two.Name +} + +func EntryToReference(entry api.ConfigEntry) api.ResourceReference { + return api.ResourceReference{ + Kind: entry.GetKind(), + Name: entry.GetName(), + Partition: entry.GetPartition(), + Namespace: entry.GetNamespace(), + } +} diff --git a/control-plane/api-gateway/binding/utils_test.go b/control-plane/api-gateway/common/helpers_test.go similarity index 56% rename from control-plane/api-gateway/binding/utils_test.go rename to control-plane/api-gateway/common/helpers_test.go index a7393e6839..62070b434c 100644 --- a/control-plane/api-gateway/binding/utils_test.go +++ b/control-plane/api-gateway/common/helpers_test.go @@ -1,37 +1,17 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package binding +package common import ( "testing" "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) -func TestIsNil(t *testing.T) { - t.Parallel() - - for name, tt := range map[string]struct { - value interface{} - expected bool - }{ - "nil pointer": { - value: (*string)(nil), - expected: true, - }, - } { - t.Run(name, func(t *testing.T) { - require.Equal(t, tt.expected, isNil(tt.value)) - }) - } -} - func TestBothNilOrEqual(t *testing.T) { t.Parallel() @@ -46,28 +26,28 @@ func TestBothNilOrEqual(t *testing.T) { expected: true, }, "second nil": { - first: pointerTo(""), + first: PointerTo(""), second: nil, expected: false, }, "first nil": { first: nil, - second: pointerTo(""), + second: PointerTo(""), expected: false, }, "both equal": { - first: pointerTo(""), - second: pointerTo(""), + first: PointerTo(""), + second: PointerTo(""), expected: true, }, "both not equal": { - first: pointerTo("1"), - second: pointerTo("2"), + first: PointerTo("1"), + second: PointerTo("2"), expected: false, }, } { t.Run(name, func(t *testing.T) { - require.Equal(t, tt.expected, bothNilOrEqual(tt.first, tt.second)) + require.Equal(t, tt.expected, BothNilOrEqual(tt.first, tt.second)) }) } } @@ -86,13 +66,13 @@ func TestValueOr(t *testing.T) { expected: "test", }, "set value": { - value: pointerTo("value"), + value: PointerTo("value"), or: "test", expected: "value", }, } { t.Run(name, func(t *testing.T) { - require.Equal(t, tt.expected, valueOr(tt.value, tt.or)) + require.Equal(t, tt.expected, ValueOr(tt.value, tt.or)) }) } } @@ -111,62 +91,18 @@ func TestNilOrEqual(t *testing.T) { expected: true, }, "equal values": { - value: pointerTo("test"), + value: PointerTo("test"), check: "test", expected: true, }, "unequal values": { - value: pointerTo("value"), + value: PointerTo("value"), check: "test", expected: false, }, } { t.Run(name, func(t *testing.T) { - require.Equal(t, tt.expected, nilOrEqual(tt.value, tt.check)) - }) - } -} - -func TestObjectToMeta(t *testing.T) { - t.Parallel() - - for name, tt := range map[string]struct { - object metav1.Object - expected types.NamespacedName - }{ - "gateway": { - object: &gwv1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{Namespace: "test", Name: "test"}}, - expected: types.NamespacedName{Namespace: "test", Name: "test"}, - }, - "secret": { - object: &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Namespace: "secret", Name: "secret"}}, - expected: types.NamespacedName{Namespace: "secret", Name: "secret"}, - }, - } { - t.Run(name, func(t *testing.T) { - require.Equal(t, tt.expected, objectToMeta(tt.object)) - }) - } -} - -func TestIsDeleted(t *testing.T) { - t.Parallel() - - for name, tt := range map[string]struct { - object client.Object - expected bool - }{ - "deleted gateway": { - object: &gwv1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{DeletionTimestamp: pointerTo(metav1.Now())}}, - expected: true, - }, - "non-deleted http route": { - object: &gwv1beta1.HTTPRoute{}, - expected: false, - }, - } { - t.Run(name, func(t *testing.T) { - require.Equal(t, tt.expected, isDeleted(tt.object)) + require.Equal(t, tt.expected, NilOrEqual(tt.value, tt.check)) }) } } @@ -182,21 +118,21 @@ func TestEnsureFinalizer(t *testing.T) { "gateway no finalizer": { object: &gwv1beta1.Gateway{}, expected: true, - finalizers: []string{gatewayFinalizer}, + finalizers: []string{GatewayFinalizer}, }, "gateway other finalizer": { object: &gwv1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"other"}}}, expected: true, - finalizers: []string{"other", gatewayFinalizer}, + finalizers: []string{"other", GatewayFinalizer}, }, "gateway already has finalizer": { - object: &gwv1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{gatewayFinalizer}}}, + object: &gwv1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{GatewayFinalizer}}}, expected: false, - finalizers: []string{gatewayFinalizer}, + finalizers: []string{GatewayFinalizer}, }, } { t.Run(name, func(t *testing.T) { - require.Equal(t, tt.expected, ensureFinalizer(tt.object)) + require.Equal(t, tt.expected, EnsureFinalizer(tt.object)) require.Equal(t, tt.finalizers, tt.object.GetFinalizers()) }) } @@ -221,18 +157,18 @@ func TestRemoveFinalizer(t *testing.T) { finalizers: []string{"other"}, }, "gateway multiple finalizers": { - object: &gwv1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{gatewayFinalizer, gatewayFinalizer}}}, + object: &gwv1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{GatewayFinalizer, GatewayFinalizer}}}, expected: true, finalizers: []string{}, }, "gateway mixed finalizers": { - object: &gwv1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"other", gatewayFinalizer}}}, + object: &gwv1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"other", GatewayFinalizer}}}, expected: true, finalizers: []string{"other"}, }, } { t.Run(name, func(t *testing.T) { - require.Equal(t, tt.expected, removeFinalizer(tt.object)) + require.Equal(t, tt.expected, RemoveFinalizer(tt.object)) require.Equal(t, tt.finalizers, tt.object.GetFinalizers()) }) } diff --git a/control-plane/api-gateway/labels.go b/control-plane/api-gateway/common/labels.go similarity index 97% rename from control-plane/api-gateway/labels.go rename to control-plane/api-gateway/common/labels.go index 3b4f7c5f1f..3ab7eaf164 100644 --- a/control-plane/api-gateway/labels.go +++ b/control-plane/api-gateway/common/labels.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package apigateway +package common import ( "fmt" diff --git a/control-plane/api-gateway/common/reference.go b/control-plane/api-gateway/common/reference.go new file mode 100644 index 0000000000..78935c11e1 --- /dev/null +++ b/control-plane/api-gateway/common/reference.go @@ -0,0 +1,184 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package common + +import ( + "sync" + + "github.com/hashicorp/consul/api" +) + +// ReferenceMap is contains a map of config entries stored +// by their normalized resource references (with empty string +// for namespaces and partitions stored as "default"). +type ReferenceMap struct { + data map[api.ResourceReference]api.ConfigEntry + ids map[api.ResourceReference]struct{} + mutex sync.RWMutex +} + +// NewReferenceMap constructs a reference map. +func NewReferenceMap() *ReferenceMap { + return &ReferenceMap{ + data: make(map[api.ResourceReference]api.ConfigEntry), + ids: make(map[api.ResourceReference]struct{}), + } +} + +func (r *ReferenceMap) IDs() []api.ResourceReference { + r.mutex.RLock() + defer r.mutex.RUnlock() + + var ids []api.ResourceReference + for id := range r.ids { + ids = append(ids, id) + } + return ids +} + +// Set adds an entry to the reference map. +func (r *ReferenceMap) Set(ref api.ResourceReference, v api.ConfigEntry) { + r.mutex.Lock() + defer r.mutex.Unlock() + + r.ids[ref] = struct{}{} + r.data[NormalizeMeta(ref)] = v +} + +// Get returns an entry from the reference map. +func (r *ReferenceMap) Get(ref api.ResourceReference) api.ConfigEntry { + r.mutex.RLock() + defer r.mutex.RUnlock() + + v, ok := r.data[NormalizeMeta(ref)] + if !ok { + return nil + } + return v +} + +// Entries returns a list of entries stored in the reference map. +func (r *ReferenceMap) Entries() []api.ConfigEntry { + r.mutex.RLock() + defer r.mutex.RUnlock() + + entries := make([]api.ConfigEntry, 0, len(r.data)) + for _, entry := range r.data { + entries = append(entries, entry) + } + return entries +} + +// Delete deletes an entry stored in the reference map. +func (r *ReferenceMap) Delete(ref api.ResourceReference) { + r.mutex.Lock() + defer r.mutex.Unlock() + + delete(r.ids, ref) + delete(r.data, NormalizeMeta(ref)) +} + +// Diff calculates the difference between the stored entries in two reference maps. +func (r *ReferenceMap) Diff(other *ReferenceMap) []api.ConfigEntry { + r.mutex.RLock() + defer r.mutex.RUnlock() + + other.mutex.RLock() + defer other.mutex.RUnlock() + + diffs := make([]api.ConfigEntry, 0) + + for ref, entry := range other.data { + oldRef := r.Get(ref) + // ref from the new cache doesn't exist in the old one + // this means a resource was added + if oldRef == nil { + diffs = append(diffs, entry) + continue + } + + // the entry in the old cache has an older modify index than the ref + // from the new cache + if oldRef.GetModifyIndex() < entry.GetModifyIndex() { + diffs = append(diffs, entry) + } + } + + // get all deleted entries, these are entries present in the old cache + // that are not present in the new + for ref, entry := range r.data { + if other.Get(ref) == nil { + diffs = append(diffs, entry) + } + } + + return diffs +} + +// ReferenceSet is a set of stored references. +type ReferenceSet struct { + data map[api.ResourceReference]struct{} + ids map[api.ResourceReference]struct{} + + mutex sync.RWMutex +} + +// NewReferenceSet constructs a new reference set. +func NewReferenceSet() *ReferenceSet { + return &ReferenceSet{ + data: make(map[api.ResourceReference]struct{}), + ids: make(map[api.ResourceReference]struct{}), + } +} + +// Mark adds a reference to the reference set. +func (r *ReferenceSet) Mark(ref api.ResourceReference) { + r.mutex.Lock() + defer r.mutex.Unlock() + + r.ids[ref] = struct{}{} + r.data[NormalizeMeta(ref)] = struct{}{} +} + +// Contains checks for the inclusion of a reference in the set. +func (r *ReferenceSet) Contains(ref api.ResourceReference) bool { + r.mutex.RLock() + defer r.mutex.RUnlock() + + _, ok := r.data[NormalizeMeta(ref)] + return ok +} + +// Remove drops a reference from the set. +func (r *ReferenceSet) Remove(ref api.ResourceReference) { + r.mutex.Lock() + defer r.mutex.Unlock() + + delete(r.ids, ref) + delete(r.data, NormalizeMeta(ref)) +} + +func (r *ReferenceSet) IDs() []api.ResourceReference { + r.mutex.RLock() + defer r.mutex.RUnlock() + + var ids []api.ResourceReference + for id := range r.ids { + ids = append(ids, id) + } + return ids +} + +func NormalizeMeta(ref api.ResourceReference) api.ResourceReference { + ref.Namespace = NormalizeEmptyMetadataString(ref.Namespace) + ref.Partition = NormalizeEmptyMetadataString(ref.Partition) + return ref +} + +func NormalizeEmptyMetadataString(metaString string) string { + if metaString == "" { + return "default" + } + return metaString +} diff --git a/control-plane/api-gateway/common/resources.go b/control-plane/api-gateway/common/resources.go new file mode 100644 index 0000000000..8696dcacb7 --- /dev/null +++ b/control-plane/api-gateway/common/resources.go @@ -0,0 +1,574 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package common + +import ( + mapset "github.com/deckarep/golang-set" + "github.com/go-logr/logr" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul/api" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +// ConsulUpdateOperation is an operation representing an +// update in Consul. +type ConsulUpdateOperation struct { + // Entry is the ConfigEntry to write to Consul. + Entry api.ConfigEntry + // OnUpdate is an optional callback to fire after running + // the Consul update operation. If specified, then no more + // error handling occurs after the function is called, otherwise + // normal error handling logic applies. + OnUpdate func(err error) +} + +type gvkNamespacedName struct { + gvk string + nsn types.NamespacedName +} + +// KubernetesUpdates holds all update operations (including status) +// that need to be synced to Kubernetes. So long as you're +// modifying the same pointer object passed in to its Add +// function, this de-duplicates any calls to Add, in order +// for us to Add any previously unseen entires, but ignore +// them if they've already been added. +type KubernetesUpdates struct { + operations map[gvkNamespacedName]client.Object +} + +func NewKubernetesUpdates() *KubernetesUpdates { + return &KubernetesUpdates{ + operations: make(map[gvkNamespacedName]client.Object), + } +} + +func (k *KubernetesUpdates) Add(object client.Object) { + k.operations[gvkNamespacedName{ + gvk: object.GetObjectKind().GroupVersionKind().String(), + nsn: client.ObjectKeyFromObject(object), + }] = object +} + +func (k *KubernetesUpdates) Operations() []client.Object { + return ConvertMapValuesToSlice(k.operations) +} + +type ReferenceValidator interface { + GatewayCanReferenceSecret(gateway gwv1beta1.Gateway, secretRef gwv1beta1.SecretObjectReference) bool + HTTPRouteCanReferenceGateway(httproute gwv1beta1.HTTPRoute, parentRef gwv1beta1.ParentReference) bool + HTTPRouteCanReferenceBackend(httproute gwv1beta1.HTTPRoute, backendRef gwv1beta1.BackendRef) bool + TCPRouteCanReferenceGateway(tcpRoute gwv1alpha2.TCPRoute, parentRef gwv1beta1.ParentReference) bool + TCPRouteCanReferenceBackend(tcpRoute gwv1alpha2.TCPRoute, backendRef gwv1beta1.BackendRef) bool +} + +type certificate struct { + secret corev1.Secret + gateways mapset.Set +} + +type httpRoute struct { + route gwv1beta1.HTTPRoute + gateways mapset.Set +} + +type tcpRoute struct { + route gwv1alpha2.TCPRoute + gateways mapset.Set +} + +type consulHTTPRoute struct { + route api.HTTPRouteConfigEntry + gateways mapset.Set +} + +type consulTCPRoute struct { + route api.TCPRouteConfigEntry + gateways mapset.Set +} + +type resourceSet struct { + httpRoutes mapset.Set + tcpRoutes mapset.Set + certificates mapset.Set + + consulObjects *ReferenceSet +} + +type ResourceMap struct { + translator ResourceTranslator + referenceValidator ReferenceValidator + logger logr.Logger + + services map[types.NamespacedName]api.ResourceReference + meshServices map[types.NamespacedName]api.ResourceReference + certificates mapset.Set + + // this acts a a secondary store of what has not yet + // been processed for the sake of garbage collection. + processedCertificates mapset.Set + certificateGateways map[api.ResourceReference]*certificate + tcpRouteGateways map[api.ResourceReference]*tcpRoute + httpRouteGateways map[api.ResourceReference]*httpRoute + gatewayResources map[api.ResourceReference]*resourceSet + + // consul resources for a gateway + consulTCPRoutes map[api.ResourceReference]*consulTCPRoute + consulHTTPRoutes map[api.ResourceReference]*consulHTTPRoute + consulInlineCertificates map[api.ResourceReference]mapset.Set + + // mutations + consulMutations []*ConsulUpdateOperation +} + +func NewResourceMap(translator ResourceTranslator, validator ReferenceValidator, logger logr.Logger) *ResourceMap { + return &ResourceMap{ + translator: translator, + referenceValidator: validator, + logger: logger, + processedCertificates: mapset.NewSet(), + services: make(map[types.NamespacedName]api.ResourceReference), + meshServices: make(map[types.NamespacedName]api.ResourceReference), + certificates: mapset.NewSet(), + consulTCPRoutes: make(map[api.ResourceReference]*consulTCPRoute), + consulHTTPRoutes: make(map[api.ResourceReference]*consulHTTPRoute), + consulInlineCertificates: make(map[api.ResourceReference]mapset.Set), + certificateGateways: make(map[api.ResourceReference]*certificate), + tcpRouteGateways: make(map[api.ResourceReference]*tcpRoute), + httpRouteGateways: make(map[api.ResourceReference]*httpRoute), + gatewayResources: make(map[api.ResourceReference]*resourceSet), + } +} + +func (s *ResourceMap) AddService(id types.NamespacedName, name string) { + // this needs to be not-normalized since it gets written straight + // to Consul's configuration, including in non-enterprise builds. + s.services[id] = api.ResourceReference{ + Name: name, + Namespace: s.translator.Namespace(id.Namespace), + Partition: s.translator.ConsulPartition, + } +} + +func (s *ResourceMap) Service(id types.NamespacedName) api.ResourceReference { + return s.services[id] +} + +func (s *ResourceMap) HasService(id types.NamespacedName) bool { + _, ok := s.services[id] + return ok +} + +func (s *ResourceMap) AddMeshService(service v1alpha1.MeshService) { + // this needs to be not-normalized since it gets written straight + // to Consul's configuration, including in non-enterprise builds. + key := client.ObjectKeyFromObject(&service) + s.meshServices[key] = api.ResourceReference{ + Name: service.Spec.Name, + Namespace: s.translator.Namespace(service.Namespace), + Partition: s.translator.ConsulPartition, + } +} + +func (s *ResourceMap) MeshService(id types.NamespacedName) api.ResourceReference { + return s.meshServices[id] +} + +func (s *ResourceMap) HasMeshService(id types.NamespacedName) bool { + _, ok := s.meshServices[id] + return ok +} + +func (s *ResourceMap) Certificate(key types.NamespacedName) *corev1.Secret { + if !s.certificates.Contains(key) { + return nil + } + consulKey := NormalizeMeta(s.toConsulReference(api.InlineCertificate, key)) + if secret, ok := s.certificateGateways[consulKey]; ok { + return &secret.secret + } + return nil +} + +func (s *ResourceMap) ReferenceCountCertificate(secret corev1.Secret) { + key := client.ObjectKeyFromObject(&secret) + s.certificates.Add(key) + consulKey := NormalizeMeta(s.toConsulReference(api.InlineCertificate, key)) + if _, ok := s.certificateGateways[consulKey]; !ok { + s.certificateGateways[consulKey] = &certificate{ + secret: secret, + gateways: mapset.NewSet(), + } + } +} + +func (s *ResourceMap) ReferenceCountGateway(gateway gwv1beta1.Gateway) { + key := client.ObjectKeyFromObject(&gateway) + consulKey := NormalizeMeta(s.toConsulReference(api.APIGateway, key)) + + set := &resourceSet{ + httpRoutes: mapset.NewSet(), + tcpRoutes: mapset.NewSet(), + certificates: mapset.NewSet(), + consulObjects: NewReferenceSet(), + } + + for _, listener := range gateway.Spec.Listeners { + if listener.TLS == nil || (listener.TLS.Mode != nil && *listener.TLS.Mode != gwv1beta1.TLSModeTerminate) { + continue + } + for _, cert := range listener.TLS.CertificateRefs { + if NilOrEqual(cert.Group, "") && NilOrEqual(cert.Kind, "Secret") { + certificateKey := IndexedNamespacedNameWithDefault(cert.Name, cert.Namespace, gateway.Namespace) + + set.certificates.Add(certificateKey) + + consulCertificateKey := s.toConsulReference(api.InlineCertificate, certificateKey) + certificate, ok := s.certificateGateways[NormalizeMeta(consulCertificateKey)] + if ok { + certificate.gateways.Add(key) + set.consulObjects.Mark(consulCertificateKey) + } + } + } + } + + s.gatewayResources[consulKey] = set +} + +func (s *ResourceMap) ResourcesToGC(key types.NamespacedName) []api.ResourceReference { + consulKey := NormalizeMeta(s.toConsulReference(api.APIGateway, key)) + + resources, ok := s.gatewayResources[consulKey] + if !ok { + return nil + } + + var toGC []api.ResourceReference + + for _, id := range resources.consulObjects.IDs() { + // if any of these objects exist in the below maps + // it means we haven't "popped" it to be created + switch id.Kind { + case api.HTTPRoute: + if route, ok := s.consulHTTPRoutes[NormalizeMeta(id)]; ok && route.gateways.Cardinality() <= 1 { + // we only have a single reference, which will be this gateway, so drop + // the route altogether + toGC = append(toGC, id) + } + case api.TCPRoute: + if route, ok := s.consulTCPRoutes[NormalizeMeta(id)]; ok && route.gateways.Cardinality() <= 1 { + // we only have a single reference, which will be this gateway, so drop + // the route altogether + toGC = append(toGC, id) + } + case api.InlineCertificate: + if s.processedCertificates.Contains(id) { + continue + } + if route, ok := s.certificateGateways[NormalizeMeta(id)]; ok && route.gateways.Cardinality() <= 1 { + // we only have a single reference, which will be this gateway, so drop + // the route altogether + toGC = append(toGC, id) + } + } + } + + return toGC +} + +func (s *ResourceMap) ReferenceCountConsulHTTPRoute(route api.HTTPRouteConfigEntry) { + key := s.objectReference(&route) + + set := &consulHTTPRoute{ + route: route, + gateways: mapset.NewSet(), + } + + for gatewayKey := range s.consulGatewaysForRoute(route.Namespace, route.Parents).Iter() { + if gateway, ok := s.gatewayResources[gatewayKey.(api.ResourceReference)]; ok { + gateway.consulObjects.Mark(key) + } + + set.gateways.Add(gatewayKey) + } + + s.consulHTTPRoutes[NormalizeMeta(key)] = set +} + +func (s *ResourceMap) ReferenceCountConsulTCPRoute(route api.TCPRouteConfigEntry) { + key := s.objectReference(&route) + + set := &consulTCPRoute{ + route: route, + gateways: mapset.NewSet(), + } + + for gatewayKey := range s.consulGatewaysForRoute(route.Namespace, route.Parents).Iter() { + if gateway, ok := s.gatewayResources[gatewayKey.(api.ResourceReference)]; ok { + gateway.consulObjects.Mark(key) + } + + set.gateways.Add(gatewayKey) + } + + s.consulTCPRoutes[NormalizeMeta(key)] = set +} + +func (s *ResourceMap) consulGatewaysForRoute(namespace string, refs []api.ResourceReference) mapset.Set { + gateways := mapset.NewSet() + + for _, parent := range refs { + if EmptyOrEqual(parent.Kind, api.APIGateway) { + key := s.sectionlessParentReference(api.APIGateway, namespace, parent) + gateways.Add(key) + } + } + + return gateways +} + +func (s *ResourceMap) ReferenceCountHTTPRoute(route gwv1beta1.HTTPRoute) { + key := client.ObjectKeyFromObject(&route) + consulKey := NormalizeMeta(s.toConsulReference(api.HTTPRoute, key)) + + set := &httpRoute{ + route: route, + gateways: mapset.NewSet(), + } + + for gatewayKey := range s.gatewaysForRoute(route.Namespace, route.Spec.ParentRefs).Iter() { + set.gateways.Add(gatewayKey.(api.ResourceReference)) + + gateway := s.gatewayResources[gatewayKey.(api.ResourceReference)] + gateway.httpRoutes.Add(consulKey) + } + + s.httpRouteGateways[consulKey] = set +} + +func (s *ResourceMap) ReferenceCountTCPRoute(route gwv1alpha2.TCPRoute) { + key := client.ObjectKeyFromObject(&route) + consulKey := NormalizeMeta(s.toConsulReference(api.TCPRoute, key)) + + set := &tcpRoute{ + route: route, + gateways: mapset.NewSet(), + } + + for gatewayKey := range s.gatewaysForRoute(route.Namespace, route.Spec.ParentRefs).Iter() { + set.gateways.Add(gatewayKey.(api.ResourceReference)) + + gateway := s.gatewayResources[gatewayKey.(api.ResourceReference)] + gateway.tcpRoutes.Add(consulKey) + } + + s.tcpRouteGateways[consulKey] = set +} + +func (s *ResourceMap) gatewaysForRoute(namespace string, refs []gwv1beta1.ParentReference) mapset.Set { + gateways := mapset.NewSet() + + for _, parent := range refs { + if NilOrEqual(parent.Group, gwv1beta1.GroupVersion.Group) && NilOrEqual(parent.Kind, "Gateway") { + key := IndexedNamespacedNameWithDefault(parent.Name, parent.Namespace, namespace) + consulKey := NormalizeMeta(s.toConsulReference(api.APIGateway, key)) + + if _, ok := s.gatewayResources[consulKey]; ok { + gateways.Add(consulKey) + } + } + } + + return gateways +} + +func (s *ResourceMap) TranslateAndMutateHTTPRoute(key types.NamespacedName, onUpdate func(error), mutateFn func(old *api.HTTPRouteConfigEntry, new api.HTTPRouteConfigEntry) api.HTTPRouteConfigEntry) { + consulKey := NormalizeMeta(s.toConsulReference(api.HTTPRoute, key)) + + route, ok := s.httpRouteGateways[consulKey] + if !ok { + return + } + + translated := s.translator.ToHTTPRoute(route.route, s) + + consulRoute, ok := s.consulHTTPRoutes[consulKey] + if ok { + // remove from the consulHTTPRoutes map since we don't want to + // GC it in the end + delete(s.consulHTTPRoutes, consulKey) + mutated := mutateFn(&consulRoute.route, *translated) + s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ + Entry: &mutated, + OnUpdate: onUpdate, + }) + return + } + mutated := mutateFn(nil, *translated) + s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ + Entry: &mutated, + OnUpdate: onUpdate, + }) +} + +func (s *ResourceMap) MutateHTTPRoute(key types.NamespacedName, onUpdate func(error), mutateFn func(api.HTTPRouteConfigEntry) api.HTTPRouteConfigEntry) { + consulKey := NormalizeMeta(s.toConsulReference(api.HTTPRoute, key)) + + consulRoute, ok := s.consulHTTPRoutes[consulKey] + if ok { + // remove from the consulHTTPRoutes map since we don't want to + // GC it in the end + delete(s.consulHTTPRoutes, consulKey) + mutated := mutateFn(consulRoute.route) + // add it to the mutation set + s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ + Entry: &mutated, + OnUpdate: onUpdate, + }) + } +} + +func (s *ResourceMap) CanGCHTTPRouteOnUnbind(id api.ResourceReference) bool { + if set := s.httpRouteGateways[NormalizeMeta(id)]; set != nil { + return set.gateways.Cardinality() <= 1 + } + return true +} + +func (s *ResourceMap) TranslateAndMutateTCPRoute(key types.NamespacedName, onUpdate func(error), mutateFn func(*api.TCPRouteConfigEntry, api.TCPRouteConfigEntry) api.TCPRouteConfigEntry) { + consulKey := NormalizeMeta(s.toConsulReference(api.TCPRoute, key)) + + route, ok := s.tcpRouteGateways[consulKey] + if !ok { + + return + } + + translated := s.translator.ToTCPRoute(route.route, s) + + consulRoute, ok := s.consulTCPRoutes[consulKey] + if ok { + // remove from the consulTCPRoutes map since we don't want to + // GC it in the end + mutated := mutateFn(&consulRoute.route, *translated) + s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ + Entry: &mutated, + OnUpdate: onUpdate, + }) + return + } + mutated := mutateFn(nil, *translated) + s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ + Entry: &mutated, + OnUpdate: onUpdate, + }) +} + +func (s *ResourceMap) MutateTCPRoute(key types.NamespacedName, onUpdate func(error), mutateFn func(api.TCPRouteConfigEntry) api.TCPRouteConfigEntry) { + consulKey := NormalizeMeta(s.toConsulReference(api.TCPRoute, key)) + + consulRoute, ok := s.consulTCPRoutes[consulKey] + if ok { + // remove from the consulTCPRoutes map since we don't want to + // GC it in the end + delete(s.consulTCPRoutes, consulKey) + mutated := mutateFn(consulRoute.route) + // add it to the mutation set + s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ + Entry: &mutated, + OnUpdate: onUpdate, + }) + } +} + +func (s *ResourceMap) CanGCTCPRouteOnUnbind(id api.ResourceReference) bool { + if set := s.tcpRouteGateways[NormalizeMeta(id)]; set != nil { + return set.gateways.Cardinality() <= 1 + } + return true +} + +func (s *ResourceMap) TranslateInlineCertificate(key types.NamespacedName) error { + consulKey := s.toConsulReference(api.InlineCertificate, key) + + certificate, ok := s.certificateGateways[NormalizeMeta(consulKey)] + if !ok { + return nil + } + + consulCertificate, err := s.translator.ToInlineCertificate(certificate.secret) + if err != nil { + return err + } + + // add to the processed set so we don't GC it. + s.processedCertificates.Add(consulKey) + s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ + Entry: consulCertificate, + // just swallow the error and log it since we can't propagate status back on a certificate. + OnUpdate: func(error) { + if err != nil { + s.logger.Error(err, "error syncing certificate to Consul") + } + }, + }) + + return nil +} + +func (s *ResourceMap) Mutations() []*ConsulUpdateOperation { + return s.consulMutations +} + +func (s *ResourceMap) objectReference(o api.ConfigEntry) api.ResourceReference { + return api.ResourceReference{ + Kind: o.GetKind(), + Name: o.GetName(), + Namespace: o.GetNamespace(), + Partition: s.translator.ConsulPartition, + } +} + +func (s *ResourceMap) sectionlessParentReference(kind, namespace string, parent api.ResourceReference) api.ResourceReference { + return NormalizeMeta(api.ResourceReference{ + Kind: kind, + Name: parent.Name, + Namespace: orDefault(parent.Namespace, namespace), + Partition: s.translator.ConsulPartition, + }) +} + +func (s *ResourceMap) toConsulReference(kind string, key types.NamespacedName) api.ResourceReference { + return api.ResourceReference{ + Kind: kind, + Name: key.Name, + Namespace: s.translator.Namespace(key.Namespace), + Partition: s.translator.ConsulPartition, + } +} + +func (s *ResourceMap) GatewayCanReferenceSecret(gateway gwv1beta1.Gateway, ref gwv1beta1.SecretObjectReference) bool { + return s.referenceValidator.GatewayCanReferenceSecret(gateway, ref) +} + +func (s *ResourceMap) HTTPRouteCanReferenceBackend(route gwv1beta1.HTTPRoute, ref gwv1beta1.BackendRef) bool { + return s.referenceValidator.HTTPRouteCanReferenceBackend(route, ref) +} + +func (s *ResourceMap) HTTPRouteCanReferenceGateway(route gwv1beta1.HTTPRoute, ref gwv1beta1.ParentReference) bool { + return s.referenceValidator.HTTPRouteCanReferenceGateway(route, ref) +} + +func (s *ResourceMap) TCPRouteCanReferenceBackend(route gwv1alpha2.TCPRoute, ref gwv1beta1.BackendRef) bool { + return s.referenceValidator.TCPRouteCanReferenceBackend(route, ref) +} + +func (s *ResourceMap) TCPRouteCanReferenceGateway(route gwv1alpha2.TCPRoute, ref gwv1beta1.ParentReference) bool { + return s.referenceValidator.TCPRouteCanReferenceGateway(route, ref) +} diff --git a/control-plane/api-gateway/common/secrets.go b/control-plane/api-gateway/common/secrets.go new file mode 100644 index 0000000000..f7e6064d9f --- /dev/null +++ b/control-plane/api-gateway/common/secrets.go @@ -0,0 +1,68 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package common + +import ( + "crypto/tls" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" + + "github.com/miekg/dns" + corev1 "k8s.io/api/core/v1" +) + +func ParseCertificateData(secret corev1.Secret) (cert string, privateKey string, err error) { + decodedPrivateKey := secret.Data[corev1.TLSPrivateKeyKey] + decodedCertificate := secret.Data[corev1.TLSCertKey] + + privateKeyBlock, _ := pem.Decode(decodedPrivateKey) + if privateKeyBlock == nil { + return "", "", errors.New("failed to parse private key PEM") + } + + certificateBlock, _ := pem.Decode(decodedCertificate) + if certificateBlock == nil { + return "", "", errors.New("failed to parse certificate PEM") + } + + // make sure we have a valid x509 certificate + certificate, err := x509.ParseCertificate(certificateBlock.Bytes) + if err != nil { + return "", "", err + } + + // validate that the cert was generated with the given private key + _, err = tls.X509KeyPair(decodedCertificate, decodedPrivateKey) + if err != nil { + return "", "", err + } + + // validate that each host referenced in the CN, DNSSans, and IPSans + // are valid hostnames + if err := validateCertificateHosts(certificate); err != nil { + return "", "", err + } + + return string(decodedCertificate), string(decodedPrivateKey), nil +} + +func validateCertificateHosts(certificate *x509.Certificate) error { + hosts := []string{certificate.Subject.CommonName} + + hosts = append(hosts, certificate.DNSNames...) + + for _, ip := range certificate.IPAddresses { + hosts = append(hosts, ip.String()) + } + + for _, host := range hosts { + if _, ok := dns.IsDomainName(host); !ok { + return fmt.Errorf("host %q must be a valid DNS hostname", host) + } + } + + return nil +} diff --git a/control-plane/api-gateway/common/translation.go b/control-plane/api-gateway/common/translation.go new file mode 100644 index 0000000000..5e577470d6 --- /dev/null +++ b/control-plane/api-gateway/common/translation.go @@ -0,0 +1,363 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package common + +import ( + "strings" + + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" + "github.com/hashicorp/consul-k8s/control-plane/namespaces" + "github.com/hashicorp/consul/api" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +// ResourceTranslator handles translating K8s resources into Consul config entries. +type ResourceTranslator struct { + EnableConsulNamespaces bool + ConsulDestNamespace string + EnableK8sMirroring bool + MirroringPrefix string + ConsulPartition string +} + +func (t ResourceTranslator) NonNormalizedConfigEntryReference(kind string, id types.NamespacedName) api.ResourceReference { + return api.ResourceReference{ + Kind: kind, + Name: id.Name, + Namespace: t.Namespace(id.Namespace), + Partition: t.ConsulPartition, + } +} + +func (t ResourceTranslator) ConfigEntryReference(kind string, id types.NamespacedName) api.ResourceReference { + return NormalizeMeta(t.NonNormalizedConfigEntryReference(kind, id)) +} + +func (t ResourceTranslator) NormalizedResourceReference(kind, namespace string, ref api.ResourceReference) api.ResourceReference { + return NormalizeMeta(api.ResourceReference{ + Kind: kind, + Name: ref.Name, + SectionName: ref.SectionName, + Namespace: t.Namespace(namespace), + Partition: t.ConsulPartition, + }) +} + +func (t ResourceTranslator) Namespace(namespace string) string { + return namespaces.ConsulNamespace(namespace, t.EnableK8sMirroring, t.ConsulDestNamespace, t.EnableK8sMirroring, t.MirroringPrefix) +} + +// ToAPIGateway translates a kuberenetes API gateway into a Consul APIGateway Config Entry. +func (t ResourceTranslator) ToAPIGateway(gateway gwv1beta1.Gateway, resources *ResourceMap) *api.APIGatewayConfigEntry { + namespace := t.Namespace(gateway.Namespace) + + listeners := ConvertSliceFuncIf(gateway.Spec.Listeners, func(listener gwv1beta1.Listener) (api.APIGatewayListener, bool) { + return t.toAPIGatewayListener(gateway, listener, resources) + }) + + return &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, + Name: gateway.Name, + Namespace: namespace, + Partition: t.ConsulPartition, + Meta: map[string]string{ + constants.MetaKeyKubeNS: gateway.Namespace, + constants.MetaKeyKubeName: gateway.Name, + }, + Listeners: listeners, + } +} + +var listenerProtocolMap = map[string]string{ + "https": "http", + "http": "http", + "tcp": "tcp", +} + +func (t ResourceTranslator) toAPIGatewayListener(gateway gwv1beta1.Gateway, listener gwv1beta1.Listener, resources *ResourceMap) (api.APIGatewayListener, bool) { + namespace := gateway.Namespace + + var certificates []api.ResourceReference + + if listener.TLS != nil { + for _, ref := range listener.TLS.CertificateRefs { + if !resources.GatewayCanReferenceSecret(gateway, ref) { + return api.APIGatewayListener{}, false + } + + if !NilOrEqual(ref.Group, "") || !NilOrEqual(ref.Kind, "Secret") { + // only translate the valid types we support + continue + } + + ref := IndexedNamespacedNameWithDefault(ref.Name, ref.Namespace, namespace) + if resources.Certificate(ref) != nil { + certificates = append(certificates, t.NonNormalizedConfigEntryReference(api.InlineCertificate, ref)) + } + } + } + + return api.APIGatewayListener{ + Name: string(listener.Name), + Hostname: DerefStringOr(listener.Hostname, ""), + Port: int(listener.Port), + Protocol: listenerProtocolMap[strings.ToLower(string(listener.Protocol))], + TLS: api.APIGatewayTLSConfiguration{ + Certificates: certificates, + }, + }, true +} + +func (t ResourceTranslator) ToHTTPRoute(route gwv1beta1.HTTPRoute, resources *ResourceMap) *api.HTTPRouteConfigEntry { + namespace := t.Namespace(route.Namespace) + + // we don't translate parent refs + + hostnames := StringLikeSlice(route.Spec.Hostnames) + rules := ConvertSliceFuncIf(route.Spec.Rules, func(rule gwv1beta1.HTTPRouteRule) (api.HTTPRouteRule, bool) { + return t.translateHTTPRouteRule(route, rule, resources) + }) + + return &api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, + Name: route.Name, + Namespace: namespace, + Partition: t.ConsulPartition, + Meta: map[string]string{ + constants.MetaKeyKubeNS: route.Namespace, + constants.MetaKeyKubeName: route.Name, + }, + Hostnames: hostnames, + Rules: rules, + } +} + +func (t ResourceTranslator) translateHTTPRouteRule(route gwv1beta1.HTTPRoute, rule gwv1beta1.HTTPRouteRule, resources *ResourceMap) (api.HTTPRouteRule, bool) { + services := ConvertSliceFuncIf(rule.BackendRefs, func(ref gwv1beta1.HTTPBackendRef) (api.HTTPService, bool) { + return t.translateHTTPBackendRef(route, ref, resources) + }) + + if len(services) == 0 { + return api.HTTPRouteRule{}, false + } + + matches := ConvertSliceFunc(rule.Matches, t.translateHTTPMatch) + filters := t.translateHTTPFilters(rule.Filters) + + return api.HTTPRouteRule{ + Services: services, + Matches: matches, + Filters: filters, + }, true +} + +func (t ResourceTranslator) translateHTTPBackendRef(route gwv1beta1.HTTPRoute, ref gwv1beta1.HTTPBackendRef, resources *ResourceMap) (api.HTTPService, bool) { + id := types.NamespacedName{ + Name: string(ref.Name), + Namespace: DerefStringOr(ref.Namespace, route.Namespace), + } + + isServiceRef := NilOrEqual(ref.Group, "") && NilOrEqual(ref.Kind, "Service") + + if isServiceRef && resources.HasService(id) && resources.HTTPRouteCanReferenceBackend(route, ref.BackendRef) { + filters := t.translateHTTPFilters(ref.Filters) + service := resources.Service(id) + + return api.HTTPService{ + Name: service.Name, + Namespace: service.Namespace, + Partition: t.ConsulPartition, + Filters: filters, + Weight: DerefIntOr(ref.Weight, 1), + }, true + } + + isMeshServiceRef := DerefEqual(ref.Group, v1alpha1.ConsulHashicorpGroup) && DerefEqual(ref.Kind, v1alpha1.MeshServiceKind) + if isMeshServiceRef && resources.HasMeshService(id) && resources.HTTPRouteCanReferenceBackend(route, ref.BackendRef) { + filters := t.translateHTTPFilters(ref.Filters) + service := resources.MeshService(id) + + return api.HTTPService{ + Name: service.Name, + Namespace: service.Namespace, + Partition: t.ConsulPartition, + Filters: filters, + Weight: DerefIntOr(ref.Weight, 1), + }, true + } + + return api.HTTPService{}, false +} + +var headerMatchTypeTranslation = map[gwv1beta1.HeaderMatchType]api.HTTPHeaderMatchType{ + gwv1beta1.HeaderMatchExact: api.HTTPHeaderMatchExact, + gwv1beta1.HeaderMatchRegularExpression: api.HTTPHeaderMatchRegularExpression, +} + +var headerPathMatchTypeTranslation = map[gwv1beta1.PathMatchType]api.HTTPPathMatchType{ + gwv1beta1.PathMatchExact: api.HTTPPathMatchExact, + gwv1beta1.PathMatchPathPrefix: api.HTTPPathMatchPrefix, + gwv1beta1.PathMatchRegularExpression: api.HTTPPathMatchRegularExpression, +} + +var queryMatchTypeTranslation = map[gwv1beta1.QueryParamMatchType]api.HTTPQueryMatchType{ + gwv1beta1.QueryParamMatchExact: api.HTTPQueryMatchExact, + gwv1beta1.QueryParamMatchRegularExpression: api.HTTPQueryMatchRegularExpression, +} + +func (t ResourceTranslator) translateHTTPMatch(match gwv1beta1.HTTPRouteMatch) api.HTTPMatch { + headers := ConvertSliceFunc(match.Headers, t.translateHTTPHeaderMatch) + queries := ConvertSliceFunc(match.QueryParams, t.translateHTTPQueryMatch) + + return api.HTTPMatch{ + Headers: headers, + Query: queries, + Path: DerefConvertFunc(match.Path, t.translateHTTPPathMatch), + Method: api.HTTPMatchMethod(DerefStringOr(match.Method, "")), + } +} + +func (t ResourceTranslator) translateHTTPPathMatch(match gwv1beta1.HTTPPathMatch) api.HTTPPathMatch { + return api.HTTPPathMatch{ + Match: DerefLookup(match.Type, headerPathMatchTypeTranslation), + Value: DerefStringOr(match.Value, ""), + } +} + +func (t ResourceTranslator) translateHTTPHeaderMatch(match gwv1beta1.HTTPHeaderMatch) api.HTTPHeaderMatch { + return api.HTTPHeaderMatch{ + Name: string(match.Name), + Value: match.Value, + Match: DerefLookup(match.Type, headerMatchTypeTranslation), + } +} + +func (t ResourceTranslator) translateHTTPQueryMatch(match gwv1beta1.HTTPQueryParamMatch) api.HTTPQueryMatch { + return api.HTTPQueryMatch{ + Name: string(match.Name), + Value: match.Value, + Match: DerefLookup(match.Type, queryMatchTypeTranslation), + } +} + +func (t ResourceTranslator) translateHTTPFilters(filters []gwv1beta1.HTTPRouteFilter) api.HTTPFilters { + var urlRewrite *api.URLRewrite + consulFilter := api.HTTPHeaderFilter{ + Add: make(map[string]string), + Set: make(map[string]string), + } + + for _, filter := range filters { + consulFilter.Remove = append(consulFilter.Remove, filter.RequestHeaderModifier.Remove...) + + for _, toAdd := range filter.RequestHeaderModifier.Add { + consulFilter.Add[string(toAdd.Name)] = toAdd.Value + } + + for _, toSet := range filter.RequestHeaderModifier.Set { + consulFilter.Set[string(toSet.Name)] = toSet.Value + } + + // we drop any path rewrites that are not prefix matches as we don't support those + if filter.URLRewrite != nil && + filter.URLRewrite.Path != nil && + filter.URLRewrite.Path.Type == gwv1beta1.PrefixMatchHTTPPathModifier { + urlRewrite = &api.URLRewrite{Path: DerefStringOr(filter.URLRewrite.Path.ReplacePrefixMatch, "")} + } + } + return api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{consulFilter}, + URLRewrite: urlRewrite, + } +} + +func (t ResourceTranslator) ToTCPRoute(route gwv1alpha2.TCPRoute, resources *ResourceMap) *api.TCPRouteConfigEntry { + namespace := t.Namespace(route.Namespace) + + // we don't translate parent refs + + backendRefs := ConvertSliceFunc(route.Spec.Rules, func(rule gwv1alpha2.TCPRouteRule) []gwv1beta1.BackendRef { return rule.BackendRefs }) + flattenedRefs := Flatten(backendRefs) + services := ConvertSliceFuncIf(flattenedRefs, func(ref gwv1beta1.BackendRef) (api.TCPService, bool) { + return t.translateTCPRouteRule(route, ref, resources) + }) + + return &api.TCPRouteConfigEntry{ + Kind: api.TCPRoute, + Name: route.Name, + Namespace: namespace, + Partition: t.ConsulPartition, + Meta: map[string]string{ + constants.MetaKeyKubeNS: route.Namespace, + constants.MetaKeyKubeName: route.Name, + }, + Services: services, + } +} + +func (t ResourceTranslator) translateTCPRouteRule(route gwv1alpha2.TCPRoute, ref gwv1beta1.BackendRef, resources *ResourceMap) (api.TCPService, bool) { + // we ignore weight for now + + id := types.NamespacedName{ + Name: string(ref.Name), + Namespace: DerefStringOr(ref.Namespace, route.Namespace), + } + + isServiceRef := NilOrEqual(ref.Group, "") && NilOrEqual(ref.Kind, "Service") + if isServiceRef && resources.HasService(id) && resources.TCPRouteCanReferenceBackend(route, ref) { + service := resources.Service(id) + + return api.TCPService{ + Name: service.Name, + Namespace: service.Namespace, + }, true + } + + isMeshServiceRef := DerefEqual(ref.Group, v1alpha1.ConsulHashicorpGroup) && DerefEqual(ref.Kind, v1alpha1.MeshServiceKind) + if isMeshServiceRef && resources.HasMeshService(id) && resources.TCPRouteCanReferenceBackend(route, ref) { + service := resources.MeshService(id) + + return api.TCPService{ + Name: service.Name, + Namespace: service.Namespace, + }, true + } + + return api.TCPService{}, false +} + +func (t ResourceTranslator) ToInlineCertificate(secret corev1.Secret) (*api.InlineCertificateConfigEntry, error) { + certificate, privateKey, err := ParseCertificateData(secret) + if err != nil { + return nil, err + } + + namespace := t.Namespace(secret.Namespace) + + return &api.InlineCertificateConfigEntry{ + Kind: api.InlineCertificate, + Name: secret.Name, + Namespace: namespace, + Partition: t.ConsulPartition, + Certificate: strings.TrimSpace(certificate), + PrivateKey: strings.TrimSpace(privateKey), + Meta: map[string]string{ + constants.MetaKeyKubeNS: secret.Namespace, + constants.MetaKeyKubeName: secret.Name, + }, + }, nil +} + +func EntryToNamespacedName(entry api.ConfigEntry) types.NamespacedName { + meta := entry.GetMeta() + + return types.NamespacedName{ + Namespace: meta[constants.MetaKeyKubeNS], + Name: meta[constants.MetaKeyKubeName], + } +} diff --git a/control-plane/api-gateway/translation/config_entry_translation_test.go b/control-plane/api-gateway/common/translation_test.go similarity index 51% rename from control-plane/api-gateway/translation/config_entry_translation_test.go rename to control-plane/api-gateway/common/translation_test.go index 39abe4b613..029e4affa7 100644 --- a/control-plane/api-gateway/translation/config_entry_translation_test.go +++ b/control-plane/api-gateway/common/translation_test.go @@ -1,24 +1,51 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package translation +package common import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" "testing" "time" "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + logrtest "github.com/go-logr/logr/testing" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" "github.com/hashicorp/consul/api" - capi "github.com/hashicorp/consul/api" ) -func TestTranslator_GatewayToAPIGateway(t *testing.T) { +type fakeReferenceValidator struct{} + +func (v fakeReferenceValidator) GatewayCanReferenceSecret(gateway gwv1beta1.Gateway, secretRef gwv1beta1.SecretObjectReference) bool { + return true +} +func (v fakeReferenceValidator) HTTPRouteCanReferenceGateway(httproute gwv1beta1.HTTPRoute, parentRef gwv1beta1.ParentReference) bool { + return true +} +func (v fakeReferenceValidator) HTTPRouteCanReferenceBackend(httproute gwv1beta1.HTTPRoute, backendRef gwv1beta1.BackendRef) bool { + return true +} +func (v fakeReferenceValidator) TCPRouteCanReferenceGateway(tcpRoute gwv1alpha2.TCPRoute, parentRef gwv1beta1.ParentReference) bool { + return true +} +func (v fakeReferenceValidator) TCPRouteCanReferenceBackend(tcpRoute gwv1alpha2.TCPRoute, backendRef gwv1beta1.BackendRef) bool { + return true +} + +func TestTranslator_ToAPIGateway(t *testing.T) { t.Parallel() k8sObjectName := "my-k8s-gw" k8sNamespace := "my-k8s-namespace" @@ -30,12 +57,13 @@ func TestTranslator_GatewayToAPIGateway(t *testing.T) { listenerOneName := "listener-one" listenerOneHostname := "*.consul.io" listenerOnePort := 3366 - listenerOneProtocol := "https" + listenerOneProtocol := "http" // listener one tls config listenerOneCertName := "one-cert" - listenerOneCertK8sNamespace := "one-cert-k8s-ns" - listenerOneCertConsulNamespace := "one-cert-consul-ns" + listenerOneCertK8sNamespace := "one-cert-ns" + listenerOneCertConsulNamespace := "one-cert-ns" + listenerOneCert := generateTestCertificate(t, "one-cert-ns", "one-cert") // listener one status listenerOneLastTransmissionTime := time.Now() @@ -44,12 +72,13 @@ func TestTranslator_GatewayToAPIGateway(t *testing.T) { listenerTwoName := "listener-two" listenerTwoHostname := "*.consul.io" listenerTwoPort := 5432 - listenerTwoProtocol := "https" + listenerTwoProtocol := "http" // listener one tls config listenerTwoCertName := "two-cert" - listenerTwoCertK8sNamespace := "two-cert-k8s-ns" - listenerTwoCertConsulNamespace := "two-cert-consul-ns" + listenerTwoCertK8sNamespace := "two-cert-ns" + listenerTwoCertConsulNamespace := "two-cert-ns" + listenerTwoCert := generateTestCertificate(t, "two-cert-ns", "two-cert") // listener two status listenerTwoLastTransmissionTime := time.Now() @@ -59,23 +88,13 @@ func TestTranslator_GatewayToAPIGateway(t *testing.T) { expectedGWName string listenerOneK8sCertRefs []gwv1beta1.SecretObjectReference }{ - "when gw name is not overriden by annotations": { + "gw name": { annotations: make(map[string]string), expectedGWName: k8sObjectName, listenerOneK8sCertRefs: []gwv1beta1.SecretObjectReference{ { Name: gwv1beta1.ObjectName(listenerOneCertName), - Namespace: ptrTo(gwv1beta1.Namespace(listenerOneCertK8sNamespace)), - }, - }, - }, - "when gw name is overriden by annotations": { - annotations: map[string]string{AnnotationGateway: "my-new-gw-name"}, - expectedGWName: "my-new-gw-name", - listenerOneK8sCertRefs: []gwv1beta1.SecretObjectReference{ - { - Name: gwv1beta1.ObjectName(listenerOneCertName), - Namespace: ptrTo(gwv1beta1.Namespace(listenerOneCertK8sNamespace)), + Namespace: PointerTo(gwv1beta1.Namespace(listenerOneCertK8sNamespace)), }, }, }, @@ -85,11 +104,11 @@ func TestTranslator_GatewayToAPIGateway(t *testing.T) { listenerOneK8sCertRefs: []gwv1beta1.SecretObjectReference{ { Name: gwv1beta1.ObjectName(listenerOneCertName), - Namespace: ptrTo(gwv1beta1.Namespace(listenerOneCertK8sNamespace)), + Namespace: PointerTo(gwv1beta1.Namespace(listenerOneCertK8sNamespace)), }, { Name: gwv1beta1.ObjectName("cert that won't exist in the translated type"), - Namespace: ptrTo(gwv1beta1.Namespace(listenerOneCertK8sNamespace)), + Namespace: PointerTo(gwv1beta1.Namespace(listenerOneCertK8sNamespace)), }, }, }, @@ -113,7 +132,7 @@ func TestTranslator_GatewayToAPIGateway(t *testing.T) { Listeners: []gwv1beta1.Listener{ { Name: gwv1beta1.SectionName(listenerOneName), - Hostname: ptrTo(gwv1beta1.Hostname(listenerOneHostname)), + Hostname: PointerTo(gwv1beta1.Hostname(listenerOneHostname)), Port: gwv1beta1.PortNumber(listenerOnePort), Protocol: gwv1beta1.ProtocolType(listenerOneProtocol), TLS: &gwv1beta1.GatewayTLSConfig{ @@ -122,14 +141,14 @@ func TestTranslator_GatewayToAPIGateway(t *testing.T) { }, { Name: gwv1beta1.SectionName(listenerTwoName), - Hostname: ptrTo(gwv1beta1.Hostname(listenerTwoHostname)), + Hostname: PointerTo(gwv1beta1.Hostname(listenerTwoHostname)), Port: gwv1beta1.PortNumber(listenerTwoPort), Protocol: gwv1beta1.ProtocolType(listenerTwoProtocol), TLS: &gwv1beta1.GatewayTLSConfig{ CertificateRefs: []gwv1beta1.SecretObjectReference{ { Name: gwv1beta1.ObjectName(listenerTwoCertName), - Namespace: ptrTo(gwv1beta1.Namespace(listenerTwoCertK8sNamespace)), + Namespace: PointerTo(gwv1beta1.Namespace(listenerTwoCertK8sNamespace)), }, }, }, @@ -178,25 +197,23 @@ func TestTranslator_GatewayToAPIGateway(t *testing.T) { }, } - expectedConfigEntry := capi.APIGatewayConfigEntry{ - Kind: capi.APIGateway, + expectedConfigEntry := &api.APIGatewayConfigEntry{ + Kind: api.APIGateway, Name: tc.expectedGWName, Meta: map[string]string{ - metaKeyManagedBy: metaValueManagedBy, - metaKeyKubeNS: k8sNamespace, - metaKeyKubeServiceName: k8sObjectName, - metaKeyKubeName: k8sObjectName, + constants.MetaKeyKubeNS: k8sNamespace, + constants.MetaKeyKubeName: k8sObjectName, }, - Listeners: []capi.APIGatewayListener{ + Listeners: []api.APIGatewayListener{ { Name: listenerOneName, Hostname: listenerOneHostname, Port: listenerOnePort, Protocol: listenerOneProtocol, - TLS: capi.APIGatewayTLSConfiguration{ - Certificates: []capi.ResourceReference{ + TLS: api.APIGatewayTLSConfiguration{ + Certificates: []api.ResourceReference{ { - Kind: capi.InlineCertificate, + Kind: api.InlineCertificate, Name: listenerOneCertName, Namespace: listenerOneCertConsulNamespace, }, @@ -208,10 +225,10 @@ func TestTranslator_GatewayToAPIGateway(t *testing.T) { Hostname: listenerTwoHostname, Port: listenerTwoPort, Protocol: listenerTwoProtocol, - TLS: capi.APIGatewayTLSConfiguration{ - Certificates: []capi.ResourceReference{ + TLS: api.APIGatewayTLSConfiguration{ + Certificates: []api.ResourceReference{ { - Kind: capi.InlineCertificate, + Kind: api.InlineCertificate, Name: listenerTwoCertName, Namespace: listenerTwoCertConsulNamespace, }, @@ -219,28 +236,21 @@ func TestTranslator_GatewayToAPIGateway(t *testing.T) { }, }, }, - Status: capi.ConfigEntryStatus{}, + Status: api.ConfigEntryStatus{}, Namespace: k8sNamespace, } - translator := K8sToConsulTranslator{ + translator := ResourceTranslator{ EnableConsulNamespaces: true, ConsulDestNamespace: "", EnableK8sMirroring: true, MirroringPrefix: "", } - certs := map[types.NamespacedName]api.ResourceReference{ - {Name: listenerOneCertName, Namespace: listenerOneCertK8sNamespace}: { - Name: listenerOneCertName, - Namespace: listenerOneCertConsulNamespace, - }, - {Name: listenerTwoCertName, Namespace: listenerTwoCertK8sNamespace}: { - Name: listenerTwoCertName, - Namespace: listenerTwoCertConsulNamespace, - }, - } + resources := NewResourceMap(translator, fakeReferenceValidator{}, logrtest.NewTestLogger(t)) + resources.ReferenceCountCertificate(listenerOneCert) + resources.ReferenceCountCertificate(listenerTwoCert) - actualConfigEntry := translator.GatewayToAPIGateway(input, certs) + actualConfigEntry := translator.ToAPIGateway(input, resources) if diff := cmp.Diff(expectedConfigEntry, actualConfigEntry); diff != "" { t.Errorf("Translator.GatewayToAPIGateway() mismatch (-want +got):\n%s", diff) @@ -249,17 +259,16 @@ func TestTranslator_GatewayToAPIGateway(t *testing.T) { } } -func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { +func TestTranslator_ToHTTPRoute(t *testing.T) { t.Parallel() type args struct { k8sHTTPRoute gwv1beta1.HTTPRoute - parentRefs map[types.NamespacedName]api.ResourceReference - services map[types.NamespacedName]api.CatalogService - meshServices map[types.NamespacedName]v1alpha1.MeshService + services []types.NamespacedName + meshServices []v1alpha1.MeshService } tests := map[string]struct { args args - want capi.HTTPRouteConfigEntry + want api.HTTPRouteConfigEntry }{ "base test": { args: args{ @@ -273,10 +282,10 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { CommonRouteSpec: gwv1beta1.CommonRouteSpec{ ParentRefs: []gwv1beta1.ParentReference{ { - Namespace: ptrTo(gwv1beta1.Namespace("k8s-gw-ns")), + Namespace: PointerTo(gwv1beta1.Namespace("k8s-gw-ns")), Name: gwv1beta1.ObjectName("api-gw"), - Kind: ptrTo(gwv1beta1.Kind("Gateway")), - SectionName: ptrTo(gwv1beta1.SectionName("listener-1")), + Kind: PointerTo(gwv1beta1.Kind("Gateway")), + SectionName: PointerTo(gwv1beta1.SectionName("listener-1")), }, }, }, @@ -289,24 +298,24 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { Matches: []gwv1beta1.HTTPRouteMatch{ { Path: &gwv1beta1.HTTPPathMatch{ - Type: ptrTo(gwv1beta1.PathMatchPathPrefix), - Value: ptrTo("/v1"), + Type: PointerTo(gwv1beta1.PathMatchPathPrefix), + Value: PointerTo("/v1"), }, Headers: []gwv1beta1.HTTPHeaderMatch{ { - Type: ptrTo(gwv1beta1.HeaderMatchExact), + Type: PointerTo(gwv1beta1.HeaderMatchExact), Name: "my header match", Value: "the value", }, }, QueryParams: []gwv1beta1.HTTPQueryParamMatch{ { - Type: ptrTo(gwv1beta1.QueryParamMatchExact), + Type: PointerTo(gwv1beta1.QueryParamMatchExact), Name: "search", Value: "term", }, }, - Method: ptrTo(gwv1beta1.HTTPMethodGet), + Method: PointerTo(gwv1beta1.HTTPMethodGet), }, }, Filters: []gwv1beta1.HTTPRouteFilter{ @@ -333,7 +342,7 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ Path: &gwv1beta1.HTTPPathModifier{ Type: gwv1beta1.PrefixMatchHTTPPathModifier, - ReplacePrefixMatch: ptrTo("v1"), + ReplacePrefixMatch: PointerTo("v1"), }, }, }, @@ -343,9 +352,9 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { BackendRef: gwv1beta1.BackendRef{ BackendObjectReference: gwv1beta1.BackendObjectReference{ Name: "service one", - Namespace: ptrTo(gwv1beta1.Namespace("some ns")), + Namespace: PointerTo(gwv1beta1.Namespace("other")), }, - Weight: ptrTo(int32(45)), + Weight: PointerTo(int32(45)), }, Filters: []gwv1beta1.HTTPRouteFilter{ { @@ -371,7 +380,7 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ Path: &gwv1beta1.HTTPPathModifier{ Type: gwv1beta1.PrefixMatchHTTPPathModifier, - ReplacePrefixMatch: ptrTo("path"), + ReplacePrefixMatch: PointerTo("path"), }, }, }, @@ -382,29 +391,17 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { }, }, }, - parentRefs: map[types.NamespacedName]api.ResourceReference{ - {Name: "api-gw", Namespace: "k8s-gw-ns"}: {Name: "api-gw", Partition: "part-1", Namespace: "ns"}, - }, - services: map[types.NamespacedName]api.CatalogService{ - {Name: "service one", Namespace: "some ns"}: {ServiceName: "service one", Namespace: "other"}, + services: []types.NamespacedName{ + {Name: "service one", Namespace: "other"}, }, }, - want: capi.HTTPRouteConfigEntry{ - Kind: capi.HTTPRoute, + want: api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, Name: "k8s-http-route", - Parents: []capi.ResourceReference{ - { - Kind: capi.APIGateway, - Name: "api-gw", - SectionName: "listener-1", - Partition: "part-1", - Namespace: "ns", - }, - }, - Rules: []capi.HTTPRouteRule{ + Rules: []api.HTTPRouteRule{ { - Filters: capi.HTTPFilters{ - Headers: []capi.HTTPHeaderFilter{ + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ { Add: map[string]string{ "add it on": "the value", @@ -416,37 +413,37 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { }, }, }, - URLRewrite: &capi.URLRewrite{Path: "v1"}, + URLRewrite: &api.URLRewrite{Path: "v1"}, }, - Matches: []capi.HTTPMatch{ + Matches: []api.HTTPMatch{ { - Headers: []capi.HTTPHeaderMatch{ + Headers: []api.HTTPHeaderMatch{ { - Match: capi.HTTPHeaderMatchExact, + Match: api.HTTPHeaderMatchExact, Name: "my header match", Value: "the value", }, }, - Method: capi.HTTPMatchMethodGet, - Path: capi.HTTPPathMatch{ - Match: capi.HTTPPathMatchPrefix, + Method: api.HTTPMatchMethodGet, + Path: api.HTTPPathMatch{ + Match: api.HTTPPathMatchPrefix, Value: "/v1", }, - Query: []capi.HTTPQueryMatch{ + Query: []api.HTTPQueryMatch{ { - Match: capi.HTTPQueryMatchExact, + Match: api.HTTPQueryMatchExact, Name: "search", Value: "term", }, }, }, }, - Services: []capi.HTTPService{ + Services: []api.HTTPService{ { Name: "service one", Weight: 45, - Filters: capi.HTTPFilters{ - Headers: []capi.HTTPHeaderFilter{ + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ { Add: map[string]string{ "svc - add it on": "svc - the value", @@ -458,7 +455,7 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { }, }, }, - URLRewrite: &capi.URLRewrite{ + URLRewrite: &api.URLRewrite{ Path: "path", }, }, @@ -472,231 +469,8 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { "consul.io", }, Meta: map[string]string{ - metaKeyManagedBy: metaValueManagedBy, - metaKeyKubeNS: "k8s-ns", - metaKeyKubeServiceName: "k8s-http-route", - metaKeyKubeName: "k8s-http-route", - }, - Namespace: "k8s-ns", - }, - }, - "with httproute name override": { - args: args{ - k8sHTTPRoute: gwv1beta1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Name: "k8s-http-route", - Namespace: "k8s-ns", - Annotations: map[string]string{ - AnnotationHTTPRoute: "overrrrride", - }, - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - { - Namespace: ptrTo(gwv1beta1.Namespace("k8s-gw-ns")), - Name: gwv1beta1.ObjectName("api-gw"), - Kind: ptrTo(gwv1beta1.Kind("Gateway")), - SectionName: ptrTo(gwv1beta1.SectionName("listener-1")), - }, - }, - }, - Hostnames: []gwv1beta1.Hostname{ - "host-name.example.com", - "consul.io", - }, - Rules: []gwv1beta1.HTTPRouteRule{ - { - Matches: []gwv1beta1.HTTPRouteMatch{ - { - Path: &gwv1beta1.HTTPPathMatch{ - Type: ptrTo(gwv1beta1.PathMatchPathPrefix), - Value: ptrTo("/v1"), - }, - Headers: []gwv1beta1.HTTPHeaderMatch{ - { - Type: ptrTo(gwv1beta1.HeaderMatchExact), - Name: "my header match", - Value: "the value", - }, - }, - QueryParams: []gwv1beta1.HTTPQueryParamMatch{ - { - Type: ptrTo(gwv1beta1.QueryParamMatchExact), - Name: "search", - Value: "term", - }, - }, - Method: ptrTo(gwv1beta1.HTTPMethodGet), - }, - }, - Filters: []gwv1beta1.HTTPRouteFilter{ - { - RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ - Set: []gwv1beta1.HTTPHeader{ - { - Name: "Magic", - Value: "v2", - }, - { - Name: "Another One", - Value: "dj khaled", - }, - }, - Add: []gwv1beta1.HTTPHeader{ - { - Name: "add it on", - Value: "the value", - }, - }, - Remove: []string{"time to go"}, - }, - URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ - Path: &gwv1beta1.HTTPPathModifier{ - Type: gwv1beta1.PrefixMatchHTTPPathModifier, - ReplacePrefixMatch: ptrTo("v1"), - }, - }, - }, - }, - BackendRefs: []gwv1beta1.HTTPBackendRef{ - { - BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "service one", - Namespace: ptrTo(gwv1beta1.Namespace("some ns")), - }, - Weight: ptrTo(int32(45)), - }, - Filters: []gwv1beta1.HTTPRouteFilter{ - { - RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ - Set: []gwv1beta1.HTTPHeader{ - { - Name: "svc - Magic", - Value: "svc - v2", - }, - { - Name: "svc - Another One", - Value: "svc - dj khaled", - }, - }, - Add: []gwv1beta1.HTTPHeader{ - { - Name: "svc - add it on", - Value: "svc - the value", - }, - }, - Remove: []string{"svc - time to go"}, - }, - URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ - Path: &gwv1beta1.HTTPPathModifier{ - Type: gwv1beta1.PrefixMatchHTTPPathModifier, - ReplacePrefixMatch: ptrTo("path"), - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - parentRefs: map[types.NamespacedName]api.ResourceReference{ - {Name: "api-gw", Namespace: "k8s-gw-ns"}: {Name: "api-gw", Partition: "part-1", Namespace: "ns"}, - }, - services: map[types.NamespacedName]api.CatalogService{ - {Name: "service one", Namespace: "some ns"}: {ServiceName: "service one", Namespace: "some ns"}, - }, - }, - want: capi.HTTPRouteConfigEntry{ - Kind: capi.HTTPRoute, - Name: "overrrrride", - Parents: []capi.ResourceReference{ - { - Kind: capi.APIGateway, - Name: "api-gw", - SectionName: "listener-1", - Partition: "part-1", - Namespace: "ns", - }, - }, - Rules: []capi.HTTPRouteRule{ - { - Filters: capi.HTTPFilters{ - Headers: []capi.HTTPHeaderFilter{ - { - Add: map[string]string{ - "add it on": "the value", - }, - Remove: []string{"time to go"}, - Set: map[string]string{ - "Magic": "v2", - "Another One": "dj khaled", - }, - }, - }, - URLRewrite: &capi.URLRewrite{Path: "v1"}, - }, - Matches: []capi.HTTPMatch{ - { - Headers: []capi.HTTPHeaderMatch{ - { - Match: capi.HTTPHeaderMatchExact, - Name: "my header match", - Value: "the value", - }, - }, - Method: capi.HTTPMatchMethodGet, - Path: capi.HTTPPathMatch{ - Match: capi.HTTPPathMatchPrefix, - Value: "/v1", - }, - Query: []capi.HTTPQueryMatch{ - { - Match: capi.HTTPQueryMatchExact, - Name: "search", - Value: "term", - }, - }, - }, - }, - Services: []capi.HTTPService{ - { - Name: "service one", - Weight: 45, - Filters: capi.HTTPFilters{ - Headers: []capi.HTTPHeaderFilter{ - { - Add: map[string]string{ - "svc - add it on": "svc - the value", - }, - Remove: []string{"svc - time to go"}, - Set: map[string]string{ - "svc - Magic": "svc - v2", - "svc - Another One": "svc - dj khaled", - }, - }, - }, - URLRewrite: &capi.URLRewrite{ - Path: "path", - }, - }, - Namespace: "some ns", - }, - }, - }, - }, - Hostnames: []string{ - "host-name.example.com", - "consul.io", - }, - Meta: map[string]string{ - metaKeyManagedBy: metaValueManagedBy, - metaKeyKubeNS: "k8s-ns", - metaKeyKubeServiceName: "k8s-http-route", - metaKeyKubeName: "k8s-http-route", + constants.MetaKeyKubeNS: "k8s-ns", + constants.MetaKeyKubeName: "k8s-http-route", }, Namespace: "k8s-ns", }, @@ -707,18 +481,15 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "k8s-http-route", Namespace: "k8s-ns", - Annotations: map[string]string{ - AnnotationHTTPRoute: "overrrrride", - }, }, Spec: gwv1beta1.HTTPRouteSpec{ CommonRouteSpec: gwv1beta1.CommonRouteSpec{ ParentRefs: []gwv1beta1.ParentReference{ { - Namespace: ptrTo(gwv1beta1.Namespace("k8s-gw-ns")), + Namespace: PointerTo(gwv1beta1.Namespace("k8s-gw-ns")), Name: gwv1beta1.ObjectName("api-gw"), - SectionName: ptrTo(gwv1beta1.SectionName("listener-1")), - Kind: ptrTo(gwv1beta1.Kind("Gateway")), + SectionName: PointerTo(gwv1beta1.SectionName("listener-1")), + Kind: PointerTo(gwv1beta1.Kind("Gateway")), }, }, }, @@ -731,24 +502,24 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { Matches: []gwv1beta1.HTTPRouteMatch{ { Path: &gwv1beta1.HTTPPathMatch{ - Type: ptrTo(gwv1beta1.PathMatchPathPrefix), - Value: ptrTo("/v1"), + Type: PointerTo(gwv1beta1.PathMatchPathPrefix), + Value: PointerTo("/v1"), }, Headers: []gwv1beta1.HTTPHeaderMatch{ { - Type: ptrTo(gwv1beta1.HeaderMatchExact), + Type: PointerTo(gwv1beta1.HeaderMatchExact), Name: "my header match", Value: "the value", }, }, QueryParams: []gwv1beta1.HTTPQueryParamMatch{ { - Type: ptrTo(gwv1beta1.QueryParamMatchExact), + Type: PointerTo(gwv1beta1.QueryParamMatchExact), Name: "search", Value: "term", }, }, - Method: ptrTo(gwv1beta1.HTTPMethodGet), + Method: PointerTo(gwv1beta1.HTTPMethodGet), }, }, Filters: []gwv1beta1.HTTPRouteFilter{ @@ -776,7 +547,7 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ Path: &gwv1beta1.HTTPPathModifier{ Type: gwv1beta1.FullPathHTTPPathModifier, - ReplaceFullPath: ptrTo("v1"), + ReplaceFullPath: PointerTo("v1"), }, }, }, @@ -786,9 +557,9 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { BackendRef: gwv1beta1.BackendRef{ BackendObjectReference: gwv1beta1.BackendObjectReference{ Name: "service one", - Namespace: ptrTo(gwv1beta1.Namespace("some ns")), + Namespace: PointerTo(gwv1beta1.Namespace("some ns")), }, - Weight: ptrTo(int32(45)), + Weight: PointerTo(int32(45)), }, Filters: []gwv1beta1.HTTPRouteFilter{ { @@ -814,7 +585,7 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ Path: &gwv1beta1.HTTPPathModifier{ Type: gwv1beta1.PrefixMatchHTTPPathModifier, - ReplacePrefixMatch: ptrTo("path"), + ReplacePrefixMatch: PointerTo("path"), }, }, }, @@ -825,29 +596,17 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { }, }, }, - parentRefs: map[types.NamespacedName]api.ResourceReference{ - {Name: "api-gw", Namespace: "k8s-gw-ns"}: {Name: "api-gw", Partition: "part-1", Namespace: "ns"}, - }, - services: map[types.NamespacedName]api.CatalogService{ - {Name: "service one", Namespace: "some ns"}: {ServiceName: "service one", Namespace: "some ns"}, + services: []types.NamespacedName{ + {Name: "service one", Namespace: "some ns"}, }, }, - want: capi.HTTPRouteConfigEntry{ - Kind: capi.HTTPRoute, - Name: "overrrrride", - Parents: []capi.ResourceReference{ - { - Kind: capi.APIGateway, - Name: "api-gw", - SectionName: "listener-1", - Partition: "part-1", - Namespace: "ns", - }, - }, - Rules: []capi.HTTPRouteRule{ + want: api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, + Name: "k8s-http-route", + Rules: []api.HTTPRouteRule{ { - Filters: capi.HTTPFilters{ - Headers: []capi.HTTPHeaderFilter{ + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ { Add: map[string]string{ "add it on": "the value", @@ -860,35 +619,35 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { }, }, }, - Matches: []capi.HTTPMatch{ + Matches: []api.HTTPMatch{ { - Headers: []capi.HTTPHeaderMatch{ + Headers: []api.HTTPHeaderMatch{ { - Match: capi.HTTPHeaderMatchExact, + Match: api.HTTPHeaderMatchExact, Name: "my header match", Value: "the value", }, }, - Method: capi.HTTPMatchMethodGet, - Path: capi.HTTPPathMatch{ - Match: capi.HTTPPathMatchPrefix, + Method: api.HTTPMatchMethodGet, + Path: api.HTTPPathMatch{ + Match: api.HTTPPathMatchPrefix, Value: "/v1", }, - Query: []capi.HTTPQueryMatch{ + Query: []api.HTTPQueryMatch{ { - Match: capi.HTTPQueryMatchExact, + Match: api.HTTPQueryMatchExact, Name: "search", Value: "term", }, }, }, }, - Services: []capi.HTTPService{ + Services: []api.HTTPService{ { Name: "service one", Weight: 45, - Filters: capi.HTTPFilters{ - Headers: []capi.HTTPHeaderFilter{ + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ { Add: map[string]string{ "svc - add it on": "svc - the value", @@ -900,7 +659,7 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { }, }, }, - URLRewrite: &capi.URLRewrite{ + URLRewrite: &api.URLRewrite{ Path: "path", }, }, @@ -914,15 +673,12 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { "consul.io", }, Meta: map[string]string{ - metaKeyManagedBy: metaValueManagedBy, - metaKeyKubeNS: "k8s-ns", - metaKeyKubeServiceName: "k8s-http-route", - metaKeyKubeName: "k8s-http-route", + constants.MetaKeyKubeNS: "k8s-ns", + constants.MetaKeyKubeName: "k8s-http-route", }, Namespace: "k8s-ns", }, }, - "parent ref that is not registered with consul is dropped": { args: args{ k8sHTTPRoute: gwv1beta1.HTTPRoute{ @@ -935,17 +691,17 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { CommonRouteSpec: gwv1beta1.CommonRouteSpec{ ParentRefs: []gwv1beta1.ParentReference{ { - Namespace: ptrTo(gwv1beta1.Namespace("k8s-gw-ns")), + Namespace: PointerTo(gwv1beta1.Namespace("k8s-gw-ns")), Name: gwv1beta1.ObjectName("api-gw"), - Kind: ptrTo(gwv1beta1.Kind("Gateway")), - SectionName: ptrTo(gwv1beta1.SectionName("listener-1")), + Kind: PointerTo(gwv1beta1.Kind("Gateway")), + SectionName: PointerTo(gwv1beta1.SectionName("listener-1")), }, { - Namespace: ptrTo(gwv1beta1.Namespace("k8s-gw-ns")), + Namespace: PointerTo(gwv1beta1.Namespace("k8s-gw-ns")), Name: gwv1beta1.ObjectName("consul don't know about me"), - Kind: ptrTo(gwv1beta1.Kind("Gateway")), - SectionName: ptrTo(gwv1beta1.SectionName("listener-1")), + Kind: PointerTo(gwv1beta1.Kind("Gateway")), + SectionName: PointerTo(gwv1beta1.SectionName("listener-1")), }, }, }, @@ -958,24 +714,24 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { Matches: []gwv1beta1.HTTPRouteMatch{ { Path: &gwv1beta1.HTTPPathMatch{ - Type: ptrTo(gwv1beta1.PathMatchPathPrefix), - Value: ptrTo("/v1"), + Type: PointerTo(gwv1beta1.PathMatchPathPrefix), + Value: PointerTo("/v1"), }, Headers: []gwv1beta1.HTTPHeaderMatch{ { - Type: ptrTo(gwv1beta1.HeaderMatchExact), + Type: PointerTo(gwv1beta1.HeaderMatchExact), Name: "my header match", Value: "the value", }, }, QueryParams: []gwv1beta1.HTTPQueryParamMatch{ { - Type: ptrTo(gwv1beta1.QueryParamMatchExact), + Type: PointerTo(gwv1beta1.QueryParamMatchExact), Name: "search", Value: "term", }, }, - Method: ptrTo(gwv1beta1.HTTPMethodGet), + Method: PointerTo(gwv1beta1.HTTPMethodGet), }, }, Filters: []gwv1beta1.HTTPRouteFilter{ @@ -1002,7 +758,7 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ Path: &gwv1beta1.HTTPPathModifier{ Type: gwv1beta1.PrefixMatchHTTPPathModifier, - ReplacePrefixMatch: ptrTo("v1"), + ReplacePrefixMatch: PointerTo("v1"), }, }, }, @@ -1012,9 +768,9 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { BackendRef: gwv1beta1.BackendRef{ BackendObjectReference: gwv1beta1.BackendObjectReference{ Name: "service one", - Namespace: ptrTo(gwv1beta1.Namespace("some ns")), + Namespace: PointerTo(gwv1beta1.Namespace("some ns")), }, - Weight: ptrTo(int32(45)), + Weight: PointerTo(int32(45)), }, Filters: []gwv1beta1.HTTPRouteFilter{ { @@ -1040,7 +796,7 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ Path: &gwv1beta1.HTTPPathModifier{ Type: gwv1beta1.PrefixMatchHTTPPathModifier, - ReplacePrefixMatch: ptrTo("path"), + ReplacePrefixMatch: PointerTo("path"), }, }, }, @@ -1051,29 +807,17 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { }, }, }, - parentRefs: map[types.NamespacedName]api.ResourceReference{ - {Name: "api-gw", Namespace: "k8s-gw-ns"}: {Name: "api-gw", Partition: "part-1", Namespace: "ns"}, - }, - services: map[types.NamespacedName]api.CatalogService{ - {Name: "service one", Namespace: "some ns"}: {ServiceName: "service one", Namespace: "some ns"}, + services: []types.NamespacedName{ + {Name: "service one", Namespace: "some ns"}, }, }, - want: capi.HTTPRouteConfigEntry{ - Kind: capi.HTTPRoute, + want: api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, Name: "k8s-http-route", - Parents: []capi.ResourceReference{ - { - Kind: capi.APIGateway, - Name: "api-gw", - SectionName: "listener-1", - Partition: "part-1", - Namespace: "ns", - }, - }, - Rules: []capi.HTTPRouteRule{ + Rules: []api.HTTPRouteRule{ { - Filters: capi.HTTPFilters{ - Headers: []capi.HTTPHeaderFilter{ + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ { Add: map[string]string{ "add it on": "the value", @@ -1085,37 +829,37 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { }, }, }, - URLRewrite: &capi.URLRewrite{Path: "v1"}, + URLRewrite: &api.URLRewrite{Path: "v1"}, }, - Matches: []capi.HTTPMatch{ + Matches: []api.HTTPMatch{ { - Headers: []capi.HTTPHeaderMatch{ + Headers: []api.HTTPHeaderMatch{ { - Match: capi.HTTPHeaderMatchExact, + Match: api.HTTPHeaderMatchExact, Name: "my header match", Value: "the value", }, }, - Method: capi.HTTPMatchMethodGet, - Path: capi.HTTPPathMatch{ - Match: capi.HTTPPathMatchPrefix, + Method: api.HTTPMatchMethodGet, + Path: api.HTTPPathMatch{ + Match: api.HTTPPathMatchPrefix, Value: "/v1", }, - Query: []capi.HTTPQueryMatch{ + Query: []api.HTTPQueryMatch{ { - Match: capi.HTTPQueryMatchExact, + Match: api.HTTPQueryMatchExact, Name: "search", Value: "term", }, }, }, }, - Services: []capi.HTTPService{ + Services: []api.HTTPService{ { Name: "service one", Weight: 45, - Filters: capi.HTTPFilters{ - Headers: []capi.HTTPHeaderFilter{ + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ { Add: map[string]string{ "svc - add it on": "svc - the value", @@ -1127,7 +871,7 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { }, }, }, - URLRewrite: &capi.URLRewrite{ + URLRewrite: &api.URLRewrite{ Path: "path", }, }, @@ -1141,10 +885,8 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { "consul.io", }, Meta: map[string]string{ - metaKeyManagedBy: metaValueManagedBy, - metaKeyKubeNS: "k8s-ns", - metaKeyKubeServiceName: "k8s-http-route", - metaKeyKubeName: "k8s-http-route", + constants.MetaKeyKubeNS: "k8s-ns", + constants.MetaKeyKubeName: "k8s-http-route", }, Namespace: "k8s-ns", }, @@ -1161,9 +903,9 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { CommonRouteSpec: gwv1beta1.CommonRouteSpec{ ParentRefs: []gwv1beta1.ParentReference{ { - Namespace: ptrTo(gwv1beta1.Namespace("k8s-gw-ns")), + Namespace: PointerTo(gwv1beta1.Namespace("k8s-gw-ns")), Name: gwv1beta1.ObjectName("api-gw"), - Kind: ptrTo(gwv1beta1.Kind("Gateway")), + Kind: PointerTo(gwv1beta1.Kind("Gateway")), }, }, }, @@ -1176,24 +918,24 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { Matches: []gwv1beta1.HTTPRouteMatch{ { Path: &gwv1beta1.HTTPPathMatch{ - Type: ptrTo(gwv1beta1.PathMatchPathPrefix), - Value: ptrTo("/v1"), + Type: PointerTo(gwv1beta1.PathMatchPathPrefix), + Value: PointerTo("/v1"), }, Headers: []gwv1beta1.HTTPHeaderMatch{ { - Type: ptrTo(gwv1beta1.HeaderMatchExact), + Type: PointerTo(gwv1beta1.HeaderMatchExact), Name: "my header match", Value: "the value", }, }, QueryParams: []gwv1beta1.HTTPQueryParamMatch{ { - Type: ptrTo(gwv1beta1.QueryParamMatchExact), + Type: PointerTo(gwv1beta1.QueryParamMatchExact), Name: "search", Value: "term", }, }, - Method: ptrTo(gwv1beta1.HTTPMethodGet), + Method: PointerTo(gwv1beta1.HTTPMethodGet), }, }, Filters: []gwv1beta1.HTTPRouteFilter{ @@ -1220,7 +962,7 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ Path: &gwv1beta1.HTTPPathModifier{ Type: gwv1beta1.PrefixMatchHTTPPathModifier, - ReplacePrefixMatch: ptrTo("v1"), + ReplacePrefixMatch: PointerTo("v1"), }, }, }, @@ -1231,7 +973,7 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { BackendRef: gwv1beta1.BackendRef{ BackendObjectReference: gwv1beta1.BackendObjectReference{ Name: "service two", - Namespace: ptrTo(gwv1beta1.Namespace("some ns")), + Namespace: PointerTo(gwv1beta1.Namespace("some ns")), }, }, }, @@ -1239,9 +981,9 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { BackendRef: gwv1beta1.BackendRef{ BackendObjectReference: gwv1beta1.BackendObjectReference{ Name: "some-service-part-three", - Namespace: ptrTo(gwv1beta1.Namespace("svc-ns")), - Group: ptrTo(gwv1beta1.Group(v1alpha1.ConsulHashicorpGroup)), - Kind: ptrTo(gwv1beta1.Kind(v1alpha1.MeshServiceKind)), + Namespace: PointerTo(gwv1beta1.Namespace("svc-ns")), + Group: PointerTo(gwv1beta1.Group(v1alpha1.ConsulHashicorpGroup)), + Kind: PointerTo(gwv1beta1.Kind(v1alpha1.MeshServiceKind)), }, }, }, @@ -1249,9 +991,9 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { BackendRef: gwv1beta1.BackendRef{ BackendObjectReference: gwv1beta1.BackendObjectReference{ Name: "service one", - Namespace: ptrTo(gwv1beta1.Namespace("some ns")), + Namespace: PointerTo(gwv1beta1.Namespace("some ns")), }, - Weight: ptrTo(int32(45)), + Weight: PointerTo(int32(45)), }, Filters: []gwv1beta1.HTTPRouteFilter{ { @@ -1277,7 +1019,7 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ Path: &gwv1beta1.HTTPPathModifier{ Type: gwv1beta1.PrefixMatchHTTPPathModifier, - ReplacePrefixMatch: ptrTo("path"), + ReplacePrefixMatch: PointerTo("path"), }, }, }, @@ -1288,32 +1030,20 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { }, }, }, - parentRefs: map[types.NamespacedName]api.ResourceReference{ - {Name: "api-gw", Namespace: "k8s-gw-ns"}: {Name: "api-gw", Partition: "part-1", Namespace: "ns"}, - }, - services: map[types.NamespacedName]api.CatalogService{ - {Name: "service one", Namespace: "some ns"}: {ServiceName: "service one", Namespace: "some ns"}, + services: []types.NamespacedName{ + {Name: "service one", Namespace: "some ns"}, }, - meshServices: map[types.NamespacedName]v1alpha1.MeshService{ - {Name: "some-service-part-three", Namespace: "svc-ns"}: {Spec: v1alpha1.MeshServiceSpec{Name: "some-service-part-three"}}, + meshServices: []v1alpha1.MeshService{ + {ObjectMeta: metav1.ObjectMeta{Name: "some-service-part-three", Namespace: "svc-ns"}, Spec: v1alpha1.MeshServiceSpec{Name: "some-override"}}, }, }, - want: capi.HTTPRouteConfigEntry{ - Kind: capi.HTTPRoute, + want: api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, Name: "k8s-http-route", - Parents: []capi.ResourceReference{ - { - Kind: capi.APIGateway, - Name: "api-gw", - SectionName: "", - Partition: "part-1", - Namespace: "ns", - }, - }, - Rules: []capi.HTTPRouteRule{ + Rules: []api.HTTPRouteRule{ { - Filters: capi.HTTPFilters{ - Headers: []capi.HTTPHeaderFilter{ + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ { Add: map[string]string{ "add it on": "the value", @@ -1325,38 +1055,38 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { }, }, }, - URLRewrite: &capi.URLRewrite{Path: "v1"}, + URLRewrite: &api.URLRewrite{Path: "v1"}, }, - Matches: []capi.HTTPMatch{ + Matches: []api.HTTPMatch{ { - Headers: []capi.HTTPHeaderMatch{ + Headers: []api.HTTPHeaderMatch{ { - Match: capi.HTTPHeaderMatchExact, + Match: api.HTTPHeaderMatchExact, Name: "my header match", Value: "the value", }, }, - Method: capi.HTTPMatchMethodGet, - Path: capi.HTTPPathMatch{ - Match: capi.HTTPPathMatchPrefix, + Method: api.HTTPMatchMethodGet, + Path: api.HTTPPathMatch{ + Match: api.HTTPPathMatchPrefix, Value: "/v1", }, - Query: []capi.HTTPQueryMatch{ + Query: []api.HTTPQueryMatch{ { - Match: capi.HTTPQueryMatchExact, + Match: api.HTTPQueryMatchExact, Name: "search", Value: "term", }, }, }, }, - Services: []capi.HTTPService{ - {Name: "some-service-part-three", Filters: capi.HTTPFilters{Headers: []capi.HTTPHeaderFilter{{Add: make(map[string]string), Remove: make([]string, 0), Set: make(map[string]string)}}}}, + Services: []api.HTTPService{ + {Name: "some-override", Namespace: "svc-ns", Weight: 1, Filters: api.HTTPFilters{Headers: []api.HTTPHeaderFilter{{Add: make(map[string]string), Set: make(map[string]string)}}}}, { Name: "service one", Weight: 45, - Filters: capi.HTTPFilters{ - Headers: []capi.HTTPHeaderFilter{ + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ { Add: map[string]string{ "svc - add it on": "svc - the value", @@ -1368,7 +1098,7 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { }, }, }, - URLRewrite: &capi.URLRewrite{ + URLRewrite: &api.URLRewrite{ Path: "path", }, }, @@ -1382,10 +1112,8 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { "consul.io", }, Meta: map[string]string{ - metaKeyManagedBy: metaValueManagedBy, - metaKeyKubeNS: "k8s-ns", - metaKeyKubeServiceName: "k8s-http-route", - metaKeyKubeName: "k8s-http-route", + constants.MetaKeyKubeNS: "k8s-ns", + constants.MetaKeyKubeName: "k8s-http-route", }, Namespace: "k8s-ns", }, @@ -1393,29 +1121,37 @@ func TestTranslator_HTTPRouteToHTTPRoute(t *testing.T) { } for name, tc := range tests { t.Run(name, func(t *testing.T) { - tr := K8sToConsulTranslator{ + tr := ResourceTranslator{ EnableConsulNamespaces: true, EnableK8sMirroring: true, } - got := tr.HTTPRouteToHTTPRoute(&tc.args.k8sHTTPRoute, tc.args.parentRefs, tc.args.services, tc.args.meshServices) + + resources := NewResourceMap(tr, fakeReferenceValidator{}, logrtest.NewTestLogger(t)) + for _, service := range tc.args.services { + resources.AddService(service, service.Name) + } + for _, service := range tc.args.meshServices { + resources.AddMeshService(service) + } + + got := tr.ToHTTPRoute(tc.args.k8sHTTPRoute, resources) if diff := cmp.Diff(&tc.want, got); diff != "" { - t.Errorf("Translator.HTTPRouteToHTTPRoute() mismatch (-want +got):\n%s", diff) + t.Errorf("Translator.ToHTTPRoute() mismatch (-want +got):\n%s", diff) } }) } } -func TestTranslator_TCPRouteToTCPRoute(t *testing.T) { +func TestTranslator_ToTCPRoute(t *testing.T) { t.Parallel() type args struct { k8sRoute gwv1alpha2.TCPRoute - parentRefs map[types.NamespacedName]api.ResourceReference - services map[types.NamespacedName]api.CatalogService - meshServices map[types.NamespacedName]v1alpha1.MeshService + services []types.NamespacedName + meshServices []v1alpha1.MeshService } tests := map[string]struct { args args - want capi.TCPRouteConfigEntry + want api.TCPRouteConfigEntry }{ "base test": { args: args{ @@ -1425,23 +1161,13 @@ func TestTranslator_TCPRouteToTCPRoute(t *testing.T) { Namespace: "k8s-ns", }, Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - { - Namespace: ptrTo(gwv1beta1.Namespace("another-ns")), - Name: "mygw", - SectionName: ptrTo(gwv1beta1.SectionName("listener-one")), - Kind: ptrTo(gwv1beta1.Kind("Gateway")), - }, - }, - }, Rules: []gwv1alpha2.TCPRouteRule{ { BackendRefs: []gwv1beta1.BackendRef{ { BackendObjectReference: gwv1beta1.BackendObjectReference{ Name: "some-service", - Namespace: ptrTo(gwv1beta1.Namespace("svc-ns")), + Namespace: PointerTo(gwv1beta1.Namespace("svc-ns")), }, Weight: new(int32), }, @@ -1452,7 +1178,16 @@ func TestTranslator_TCPRouteToTCPRoute(t *testing.T) { { BackendObjectReference: gwv1beta1.BackendObjectReference{ Name: "some-service-part-two", - Namespace: ptrTo(gwv1beta1.Namespace("svc-ns")), + Namespace: PointerTo(gwv1beta1.Namespace("svc-ns")), + }, + Weight: new(int32), + }, + { + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Group: PointerTo(gwv1beta1.Group(v1alpha1.ConsulHashicorpGroup)), + Kind: PointerTo(gwv1beta1.Kind(v1alpha1.MeshServiceKind)), + Name: "some-service-part-three", + Namespace: PointerTo(gwv1beta1.Namespace("svc-ns")), }, Weight: new(int32), }, @@ -1461,38 +1196,19 @@ func TestTranslator_TCPRouteToTCPRoute(t *testing.T) { }, }, }, - parentRefs: map[types.NamespacedName]api.ResourceReference{ - { - Namespace: "another-ns", - Name: "mygw", - }: { - Name: "mygw", - Namespace: "another-ns", - Partition: "", - }, + services: []types.NamespacedName{ + {Name: "some-service", Namespace: "svc-ns"}, + {Name: "some-service-part-two", Namespace: "svc-ns"}, }, - services: map[types.NamespacedName]api.CatalogService{ - {Name: "some-service", Namespace: "svc-ns"}: {ServiceName: "some-service", Namespace: "svc-ns"}, - {Name: "some-service-part-two", Namespace: "svc-ns"}: {ServiceName: "some-service-part-two", Namespace: "svc-ns"}, - }, - meshServices: map[types.NamespacedName]v1alpha1.MeshService{ - {Name: "some-service-part-three", Namespace: "svc-ns"}: {Spec: v1alpha1.MeshServiceSpec{Name: "some-service-part-three"}}, + meshServices: []v1alpha1.MeshService{ + {ObjectMeta: metav1.ObjectMeta{Name: "some-service-part-three", Namespace: "svc-ns"}, Spec: v1alpha1.MeshServiceSpec{Name: "some-override"}}, }, }, - want: capi.TCPRouteConfigEntry{ - Kind: capi.TCPRoute, + want: api.TCPRouteConfigEntry{ + Kind: api.TCPRoute, Name: "tcp-route", Namespace: "k8s-ns", - Parents: []capi.ResourceReference{ - { - Kind: capi.APIGateway, - Name: "mygw", - SectionName: "listener-one", - Partition: "", - Namespace: "another-ns", - }, - }, - Services: []capi.TCPService{ + Services: []api.TCPService{ { Name: "some-service", Partition: "", @@ -1503,134 +1219,86 @@ func TestTranslator_TCPRouteToTCPRoute(t *testing.T) { Partition: "", Namespace: "svc-ns", }, - }, - Meta: map[string]string{ - metaKeyManagedBy: metaValueManagedBy, - metaKeyKubeNS: "k8s-ns", - metaKeyKubeServiceName: "tcp-route", - metaKeyKubeName: "tcp-route", - }, - }, - }, - - "overwrite the route name via annotaions": { - args: args{ - k8sRoute: gwv1alpha2.TCPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Name: "tcp-route", - Namespace: "k8s-ns", - Annotations: map[string]string{ - AnnotationTCPRoute: "replaced-name", - }, - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - { - Namespace: ptrTo(gwv1beta1.Namespace("another-ns")), - Name: "mygw", - SectionName: ptrTo(gwv1beta1.SectionName("listener-one")), - Kind: ptrTo(gwv1beta1.Kind("Gateway")), - }, - }, - }, - Rules: []gwv1alpha2.TCPRouteRule{ - { - BackendRefs: []gwv1beta1.BackendRef{ - { - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "some-service", - Namespace: ptrTo(gwv1beta1.Namespace("svc-ns")), - }, - Weight: new(int32), - }, - }, - }, - { - BackendRefs: []gwv1beta1.BackendRef{ - { - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "some-service-part-two", - Namespace: ptrTo(gwv1beta1.Namespace("svc-ns")), - }, - Weight: new(int32), - }, - }, - }, - { - BackendRefs: []gwv1beta1.BackendRef{ - { - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "some-service-part-three", - Namespace: ptrTo(gwv1beta1.Namespace("svc-ns")), - Group: ptrTo(gwv1beta1.Group(v1alpha1.ConsulHashicorpGroup)), - Kind: ptrTo(gwv1beta1.Kind(v1alpha1.MeshServiceKind)), - }, - Weight: new(int32), - }, - }, - }, - }, - }, - }, - parentRefs: map[types.NamespacedName]api.ResourceReference{ { - Namespace: "another-ns", - Name: "mygw", - }: { - Name: "mygw", - Namespace: "another-ns", + Name: "some-override", Partition: "", + Namespace: "svc-ns", }, }, - services: map[types.NamespacedName]api.CatalogService{ - {Name: "some-service", Namespace: "svc-ns"}: {ServiceName: "some-service", Namespace: "other"}, - }, - meshServices: map[types.NamespacedName]v1alpha1.MeshService{ - {Name: "some-service-part-three", Namespace: "svc-ns"}: {Spec: v1alpha1.MeshServiceSpec{Name: "some-service-part-three"}}, - }, - }, - want: capi.TCPRouteConfigEntry{ - Kind: capi.TCPRoute, - Name: "replaced-name", - Namespace: "k8s-ns", - Parents: []capi.ResourceReference{ - { - Kind: capi.APIGateway, - Name: "mygw", - SectionName: "listener-one", - Partition: "", - Namespace: "another-ns", - }, - }, - Services: []capi.TCPService{ - { - Name: "some-service", - Partition: "", - Namespace: "other", - }, - {Name: "some-service-part-three"}, - }, Meta: map[string]string{ - metaKeyManagedBy: metaValueManagedBy, - metaKeyKubeNS: "k8s-ns", - metaKeyKubeServiceName: "tcp-route", - metaKeyKubeName: "tcp-route", + constants.MetaKeyKubeNS: "k8s-ns", + constants.MetaKeyKubeName: "tcp-route", }, }, }, } for name, tt := range tests { t.Run(name, func(t *testing.T) { - tr := K8sToConsulTranslator{ + tr := ResourceTranslator{ EnableConsulNamespaces: true, EnableK8sMirroring: true, } - got := tr.TCPRouteToTCPRoute(&tt.args.k8sRoute, tt.args.parentRefs, tt.args.services, tt.args.meshServices) + resources := NewResourceMap(tr, fakeReferenceValidator{}, logrtest.NewTestLogger(t)) + for _, service := range tt.args.services { + resources.AddService(service, service.Name) + } + for _, service := range tt.args.meshServices { + resources.AddMeshService(service) + } + + got := tr.ToTCPRoute(tt.args.k8sRoute, resources) if diff := cmp.Diff(&tt.want, got); diff != "" { t.Errorf("Translator.TCPRouteToTCPRoute() mismatch (-want +got):\n%s", diff) } }) } } + +func generateTestCertificate(t *testing.T, namespace, name string) corev1.Secret { + privateKey, err := rsa.GenerateKey(rand.Reader, 1024) + require.NoError(t, err) + + usage := x509.KeyUsageCertSign + expiration := time.Now().AddDate(10, 0, 0) + + cert := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: "consul.test", + }, + IsCA: true, + NotBefore: time.Now().Add(-10 * time.Minute), + NotAfter: expiration, + SubjectKeyId: []byte{1, 2, 3, 4, 6}, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: usage, + BasicConstraintsValid: true, + } + caCert := cert + caPrivateKey := privateKey + + data, err := x509.CreateCertificate(rand.Reader, cert, caCert, &privateKey.PublicKey, caPrivateKey) + require.NoError(t, err) + + certBytes := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: data, + }) + + privateKeyBytes := pem.EncodeToMemory(&pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(privateKey), + }) + + return corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + Data: map[string][]byte{ + corev1.TLSCertKey: certBytes, + corev1.TLSPrivateKeyKey: privateKeyBytes, + }, + } +} diff --git a/control-plane/api-gateway/controllers/gateway_controller.go b/control-plane/api-gateway/controllers/gateway_controller.go index e29e40b28e..691c25bf72 100644 --- a/control-plane/api-gateway/controllers/gateway_controller.go +++ b/control-plane/api-gateway/controllers/gateway_controller.go @@ -6,7 +6,10 @@ package controllers import ( "context" "reflect" + "strconv" + "strings" + mapset "github.com/deckarep/golang-set" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" "github.com/go-logr/logr" @@ -21,234 +24,178 @@ import ( "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" - "sigs.k8s.io/gateway-api/apis/v1alpha2" gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - apigateway "github.com/hashicorp/consul-k8s/control-plane/api-gateway" "github.com/hashicorp/consul-k8s/control-plane/api-gateway/binding" "github.com/hashicorp/consul-k8s/control-plane/api-gateway/cache" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul-k8s/control-plane/api-gateway/gatekeeper" - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/translation" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "github.com/hashicorp/consul-k8s/control-plane/consul" "github.com/hashicorp/consul/api" ) -const ( - gatewayFinalizer = "gateway-finalizer.consul.hashicorp.com" - - kindGateway = "Gateway" -) - // GatewayControllerConfig holds the values necessary for configuring the GatewayController. type GatewayControllerConfig struct { - HelmConfig apigateway.HelmConfig - ConsulClientConfig *consul.Config - ConsulServerConnMgr consul.ServerConnectionManager - NamespacesEnabled bool - Partition string + HelmConfig common.HelmConfig + ConsulClientConfig *consul.Config + ConsulServerConnMgr consul.ServerConnectionManager + NamespacesEnabled bool + CrossNamespaceACLPolicy string + Partition string + AllowK8sNamespacesSet mapset.Set + DenyK8sNamespacesSet mapset.Set } // GatewayController reconciles a Gateway object. // The Gateway is responsible for defining the behavior of API gateways. type GatewayController struct { - HelmConfig apigateway.HelmConfig - Log logr.Logger - Translator translation.K8sToConsulTranslator - cache *cache.Cache + HelmConfig common.HelmConfig + Log logr.Logger + Translator common.ResourceTranslator + cache *cache.Cache + gatewayCache *cache.GatewayCache + allowK8sNamespacesSet mapset.Set + denyK8sNamespacesSet mapset.Set client.Client } // Reconcile handles the reconciliation loop for Gateway objects. func (r *GatewayController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + consulKey := r.Translator.ConfigEntryReference(api.APIGateway, req.NamespacedName) + nonNormalizedConsulKey := r.Translator.NonNormalizedConfigEntryReference(api.APIGateway, req.NamespacedName) + + var gateway gwv1beta1.Gateway + log := r.Log.WithValues("gateway", req.NamespacedName) log.Info("Reconciling Gateway") - // If gateway does not exist, log an error. - var gw gwv1beta1.Gateway - err := r.Client.Get(ctx, req.NamespacedName, &gw) - if err != nil { - if k8serrors.IsNotFound(err) { - return ctrl.Result{}, nil + // get the gateway + if err := r.Client.Get(ctx, req.NamespacedName, &gateway); err != nil { + if !k8serrors.IsNotFound(err) { + log.Error(err, "unable to get Gateway") } - log.Error(err, "unable to get Gateway") - return ctrl.Result{}, err + return ctrl.Result{}, client.IgnoreNotFound(err) } - // If gateway class on the gateway does not exist, log an error. - gwc := &gwv1beta1.GatewayClass{} - err = r.Client.Get(ctx, types.NamespacedName{Name: string(gw.Spec.GatewayClassName)}, gwc) + // get the gateway class + gatewayClass, err := r.getGatewayClassForGateway(ctx, gateway) if err != nil { - if !k8serrors.IsNotFound(err) { - log.Error(err, "unable to get GatewayClass") - return ctrl.Result{}, err - } - gwc = nil + log.Error(err, "unable to get GatewayClass") + return ctrl.Result{}, err } - gwcc, err := getConfigForGatewayClass(ctx, r.Client, gwc) + // get the gateway class config + gatewayClassConfig, err := r.getConfigForGatewayClass(ctx, gatewayClass) if err != nil { log.Error(err, "error fetching the gateway class config") return ctrl.Result{}, err } - // fetch all namespaces - namespaceList := &corev1.NamespaceList{} - if err := r.Client.List(ctx, namespaceList); err != nil { + // get all namespaces + namespaces, err := r.getNamespaces(ctx) + if err != nil { log.Error(err, "unable to list Namespaces") return ctrl.Result{}, err } - namespaces := map[string]corev1.Namespace{} - for _, namespace := range namespaceList.Items { - namespaces[namespace.Name] = namespace - } - - // fetch all gateways we control for reference counting - gwcList := &gwv1beta1.GatewayClassList{} - if err := r.Client.List(ctx, gwcList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(GatewayClass_ControllerNameIndex, GatewayClassControllerName), - }); err != nil { - log.Error(err, "unable to list GatewayClasses") - return ctrl.Result{}, err - } - // fetch related gateway pods - labels := apigateway.LabelsForGateway(&gw) - podList := &corev1.PodList{} - if err := r.Client.List(ctx, podList, client.MatchingLabels(labels)); err != nil { - log.Error(err, "unable to list Pods for Gateway") + // get all reference grants + grants, err := r.getReferenceGrants(ctx) + if err != nil { + log.Error(err, "unable to list ReferenceGrants") return ctrl.Result{}, err } - // fetch related gateway services - service := &corev1.Service{} - // we use the implicit association of a service name/namespace with a corresponding - // gateway - if err := r.Client.Get(ctx, req.NamespacedName, service); err != nil { - if !k8serrors.IsNotFound(err) { - log.Error(err, "unable to fetch service for Gateway") - return ctrl.Result{}, err - } - // if we got a 404, then nil out the service - service = nil + // get related gateway service + service, err := r.getDeployedGatewayService(ctx, req.NamespacedName) + if err != nil { + log.Error(err, "unable to fetch service for Gateway") } - gwList := &gwv1beta1.GatewayList{} - if err := r.Client.List(ctx, gwList); err != nil { - log.Error(err, "unable to list Gateways") + // get related gateway pods + pods, err := r.getDeployedGatewayPods(ctx, gateway) + if err != nil { + log.Error(err, "unable to list Pods for Gateway") return ctrl.Result{}, err } - controlled := map[types.NamespacedName]gwv1beta1.Gateway{} - for _, gwc := range gwcList.Items { - for _, gw := range gwList.Items { - if string(gw.Spec.GatewayClassName) == gwc.Name { - controlled[types.NamespacedName{Namespace: gw.Namespace, Name: gw.Name}] = gw - } - } - } + // construct our resource map + referenceValidator := binding.NewReferenceValidator(grants) + resources := common.NewResourceMap(r.Translator, referenceValidator, log) - // fetch all MeshServices - meshServiceList := &v1alpha1.MeshServiceList{} - if err := r.Client.List(ctx, meshServiceList); err != nil { - log.Error(err, "unable to list MeshServices") + if err := r.fetchCertificatesForGateway(ctx, resources, gateway); err != nil { + log.Error(err, "unable to fetch certificates for gateway") return ctrl.Result{}, err } - // fetch all secrets referenced by this gateway - secretList := &corev1.SecretList{} - if err := r.Client.List(ctx, secretList); err != nil { - log.Error(err, "unable to list Secrets") + if err := r.fetchControlledGateways(ctx, resources); err != nil { + log.Error(err, "unable to fetch controlled gateways") return ctrl.Result{}, err } - listenerCerts := make(map[types.NamespacedName]struct{}) - for _, listener := range gw.Spec.Listeners { - if listener.TLS != nil { - for _, ref := range listener.TLS.CertificateRefs { - if nilOrEqual(ref.Group, "") && nilOrEqual(ref.Kind, "Secret") { - listenerCerts[indexedNamespacedNameWithDefault(ref.Name, ref.Namespace, gw.Namespace)] = struct{}{} - } - } - } - } - - filteredSecrets := []corev1.Secret{} - for _, secret := range secretList.Items { - namespacedName := types.NamespacedName{Namespace: secret.Namespace, Name: secret.Name} - if _, ok := listenerCerts[namespacedName]; ok { - filteredSecrets = append(filteredSecrets, secret) - } - } - - // fetch all http routes referencing this gateway - httpRouteList := &gwv1beta1.HTTPRouteList{} - if err := r.Client.List(ctx, httpRouteList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(HTTPRoute_GatewayIndex, req.String()), - }); err != nil { + // get all http routes referencing this gateway + httpRoutes, err := r.getRelatedHTTPRoutes(ctx, req.NamespacedName, resources) + if err != nil { log.Error(err, "unable to list HTTPRoutes") return ctrl.Result{}, err } - // fetch all tcp routes referencing this gateway - tcpRouteList := &gwv1alpha2.TCPRouteList{} - if err := r.Client.List(ctx, tcpRouteList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(TCPRoute_GatewayIndex, req.String()), - }); err != nil { + // get all tcp routes referencing this gateway + tcpRoutes, err := r.getRelatedTCPRoutes(ctx, req.NamespacedName, resources) + if err != nil { log.Error(err, "unable to list TCPRoutes") return ctrl.Result{}, err } - configEntry := r.cache.Get(r.Translator.ReferenceForGateway(&gw)) - - var consulGateway *api.APIGatewayConfigEntry - if configEntry != nil { - consulGateway = configEntry.(*api.APIGatewayConfigEntry) + if err := r.fetchServicesForRoutes(ctx, resources, tcpRoutes, httpRoutes); err != nil { + log.Error(err, "unable to fetch services for routes") + return ctrl.Result{}, err } - httpRoutes := r.cache.List(api.HTTPRoute) - tcpRoutes := r.cache.List(api.TCPRoute) - inlineCertificates := r.cache.List(api.InlineCertificate) - services := r.cache.ListServices() + + // fetch all consul objects from cache + consulServices := r.getConsulServices(consulKey) + consulGateway := r.getConsulGateway(consulKey) + consulHTTPRoutes := r.getConsulHTTPRoutes(consulKey, resources) + consulTCPRoutes := r.getConsulTCPRoutes(consulKey, resources) + consulInlineCertificates := r.getConsulInlineCertificates() binder := binding.NewBinder(binding.BinderConfig{ + Logger: log, Translator: r.Translator, ControllerName: GatewayClassControllerName, - GatewayClassConfig: gwcc, - GatewayClass: gwc, - Gateway: gw, - Pods: podList.Items, + Namespaces: namespaces, + GatewayClassConfig: gatewayClassConfig, + GatewayClass: gatewayClass, + Gateway: gateway, + Pods: pods, Service: service, - HTTPRoutes: httpRouteList.Items, - TCPRoutes: tcpRouteList.Items, - MeshServices: meshServiceList.Items, - Secrets: filteredSecrets, + HTTPRoutes: httpRoutes, + TCPRoutes: tcpRoutes, + Resources: resources, ConsulGateway: consulGateway, - ConsulHTTPRoutes: derefAll(configEntriesTo[*api.HTTPRouteConfigEntry](httpRoutes)), - ConsulTCPRoutes: derefAll(configEntriesTo[*api.TCPRouteConfigEntry](tcpRoutes)), - ConsulInlineCertificates: derefAll(configEntriesTo[*api.InlineCertificateConfigEntry](inlineCertificates)), - ConnectInjectedServices: services, - GatewayServices: consulServicesForGateway(gw, services), - Namespaces: namespaces, - ControlledGateways: controlled, + ConsulHTTPRoutes: consulHTTPRoutes, + ConsulTCPRoutes: consulTCPRoutes, + ConsulInlineCertificates: consulInlineCertificates, + ConsulGatewayServices: consulServices, }) updates := binder.Snapshot() if updates.UpsertGatewayDeployment { - log.Info("updating gatekeeper") - err := r.updateGatekeeperResources(ctx, log, &gw, gwcc) + err := r.updateGatekeeperResources(ctx, log, &gateway, updates.GatewayClassConfig) if err != nil { log.Error(err, "unable to update gateway resources") return ctrl.Result{}, err } + r.gatewayCache.EnsureSubscribed(nonNormalizedConsulKey, req.NamespacedName) } else { - log.Info("deleting gatekeeper") - err := r.deleteGatekeeperResources(ctx, log, &gw) + err := r.deleteGatekeeperResources(ctx, log, &gateway) if err != nil { log.Error(err, "unable to delete gateway resources") return ctrl.Result{}, err } + r.gatewayCache.RemoveSubscription(nonNormalizedConsulKey) } for _, deletion := range updates.Consul.Deletions { @@ -260,8 +207,16 @@ func (r *GatewayController) Reconcile(ctx context.Context, req ctrl.Request) (ct } for _, update := range updates.Consul.Updates { - log.Info("updating in Consul", "kind", update.GetKind(), "namespace", update.GetNamespace(), "name", update.GetName()) - if err := r.cache.Write(ctx, update); err != nil { + entry := update.Entry + log.Info("updating in Consul", "kind", entry.GetKind(), "namespace", entry.GetNamespace(), "name", entry.GetName()) + err := r.cache.Write(ctx, entry) + if update.OnUpdate != nil { + // swallow any potential error with our handler if one is provided + update.OnUpdate(err) + continue + } + + if err != nil { log.Error(err, "error updating config entry") return ctrl.Result{}, err } @@ -283,7 +238,7 @@ func (r *GatewayController) Reconcile(ctx context.Context, req ctrl.Request) (ct } } - for _, update := range updates.Kubernetes.Updates { + for _, update := range updates.Kubernetes.Updates.Operations() { log.Info("update in Kubernetes", "kind", update.GetObjectKind().GroupVersionKind().Kind, "namespace", update.GetNamespace(), "name", update.GetName()) if err := r.updateAndResetStatus(ctx, update); err != nil { log.Error(err, "error updating object") @@ -291,7 +246,7 @@ func (r *GatewayController) Reconcile(ctx context.Context, req ctrl.Request) (ct } } - for _, update := range updates.Kubernetes.StatusUpdates { + for _, update := range updates.Kubernetes.StatusUpdates.Operations() { log.Info("update status in Kubernetes", "kind", update.GetObjectKind().GroupVersionKind().Kind, "namespace", update.GetNamespace(), "name", update.GetName()) if err := r.Client.Status().Update(ctx, update); err != nil { log.Error(err, "error updating status") @@ -303,17 +258,12 @@ func (r *GatewayController) Reconcile(ctx context.Context, req ctrl.Request) (ct // mesh == read on the provisioned gateway token if needed, figure out some other // way of handling it. if updates.UpsertGatewayDeployment { - reference := r.Translator.ReferenceForGateway(&gw) - if err := r.cache.LinkPolicy(ctx, reference.Name, reference.Namespace); err != nil { + if err := r.cache.LinkPolicy(ctx, nonNormalizedConsulKey.Name, nonNormalizedConsulKey.Namespace); err != nil { log.Error(err, "error linking token policy") return ctrl.Result{}, err } } - /* TODO: - 1.ReferenceGrants - */ - return ctrl.Result{}, nil } @@ -328,14 +278,6 @@ func (r *GatewayController) updateAndResetStatus(ctx context.Context, o client.O return nil } -func derefAll[T any](vs []*T) []T { - e := make([]T, len(vs)) - for _, v := range vs { - e = append(e, *v) - } - return e -} - func configEntriesTo[T api.ConfigEntry](entries []api.ConfigEntry) []T { es := []T{} for _, e := range entries { @@ -369,21 +311,31 @@ func (r *GatewayController) updateGatekeeperResources(ctx context.Context, log l // SetupWithGatewayControllerManager registers the controller with the given manager. func SetupGatewayControllerWithManager(ctx context.Context, mgr ctrl.Manager, config GatewayControllerConfig) (*cache.Cache, error) { - c := cache.New(cache.Config{ - ConsulClientConfig: config.ConsulClientConfig, - ConsulServerConnMgr: config.ConsulServerConnMgr, - NamespacesEnabled: config.NamespacesEnabled, - PeeringEnabled: config.HelmConfig.PeeringEnabled, - Logger: mgr.GetLogger(), - }) - - translator := translation.NewConsulToNamespaceNameTranslator(c) + cacheConfig := cache.Config{ + ConsulClientConfig: config.ConsulClientConfig, + ConsulServerConnMgr: config.ConsulServerConnMgr, + NamespacesEnabled: config.NamespacesEnabled, + CrossNamespaceACLPolicy: config.CrossNamespaceACLPolicy, + Logger: mgr.GetLogger(), + } + c := cache.New(cacheConfig) + gwc := cache.NewGatewayCache(ctx, cacheConfig) r := &GatewayController{ Client: mgr.GetClient(), Log: mgr.GetLogger(), HelmConfig: config.HelmConfig, - cache: c, + Translator: common.ResourceTranslator{ + EnableConsulNamespaces: config.HelmConfig.EnableNamespaces, + ConsulDestNamespace: config.HelmConfig.ConsulDestinationNamespace, + EnableK8sMirroring: config.HelmConfig.EnableNamespaceMirroring, + MirroringPrefix: config.HelmConfig.NamespaceMirroringPrefix, + ConsulPartition: config.HelmConfig.ConsulPartition, + }, + denyK8sNamespacesSet: config.DenyK8sNamespacesSet, + allowK8sNamespacesSet: config.AllowK8sNamespacesSet, + cache: c, + gatewayCache: gwc, } return c, ctrl.NewControllerManagedBy(mgr). @@ -391,6 +343,10 @@ func SetupGatewayControllerWithManager(ctx context.Context, mgr ctrl.Manager, co Owns(&appsv1.Deployment{}). Owns(&corev1.Service{}). Owns(&corev1.Pod{}). + Watches( + source.NewKindWithCache(&gwv1beta1.ReferenceGrant{}, mgr.GetCache()), + handler.EnqueueRequestsFromMapFunc(r.transformReferenceGrant(ctx)), + ). Watches( source.NewKindWithCache(&gwv1beta1.GatewayClass{}, mgr.GetCache()), handler.EnqueueRequestsFromMapFunc(r.transformGatewayClass(ctx)), @@ -416,44 +372,31 @@ func SetupGatewayControllerWithManager(ctx context.Context, mgr ctrl.Manager, co handler.EnqueueRequestsFromMapFunc(r.transformMeshService(ctx)), ). Watches( - // Subscribe to changes from Consul Connect Services - &source.Channel{Source: c.SubscribeServices(ctx, r.transformConsulService(ctx)).Events()}, - &handler.EnqueueRequestForObject{}, - ). - Watches( - // Subscribe to changes from Consul Peering Services - &source.Channel{Source: c.SubscribePeerings(ctx, r.transformConsulPeering(ctx)).Events()}, - &handler.EnqueueRequestForObject{}, + source.NewKindWithCache(&corev1.Endpoints{}, mgr.GetCache()), + handler.EnqueueRequestsFromMapFunc(r.transformEndpoints(ctx)), ). Watches( // Subscribe to changes from Consul for APIGateways - &source.Channel{Source: c.Subscribe(ctx, api.APIGateway, translator.BuildConsulGatewayTranslator(ctx)).Events()}, + &source.Channel{Source: c.Subscribe(ctx, api.APIGateway, r.transformConsulGateway).Events()}, &handler.EnqueueRequestForObject{}, ). Watches( // Subscribe to changes from Consul for HTTPRoutes - &source.Channel{Source: c.Subscribe(ctx, api.HTTPRoute, translator.BuildConsulHTTPRouteTranslator(ctx)).Events()}, + &source.Channel{Source: c.Subscribe(ctx, api.HTTPRoute, r.transformConsulHTTPRoute(ctx)).Events()}, &handler.EnqueueRequestForObject{}, ). Watches( // Subscribe to changes from Consul for TCPRoutes - &source.Channel{Source: c.Subscribe(ctx, api.TCPRoute, translator.BuildConsulTCPRouteTranslator(ctx)).Events()}, + &source.Channel{Source: c.Subscribe(ctx, api.TCPRoute, r.transformConsulTCPRoute(ctx)).Events()}, &handler.EnqueueRequestForObject{}, ). Watches( // Subscribe to changes from Consul for InlineCertificates - &source.Channel{Source: c.Subscribe(ctx, api.InlineCertificate, translator.BuildConsulInlineCertificateTranslator(ctx, r.transformSecret)).Events()}, + &source.Channel{Source: c.Subscribe(ctx, api.InlineCertificate, r.transformConsulInlineCertificate(ctx)).Events()}, &handler.EnqueueRequestForObject{}, ).Complete(r) } -func serviceToNamespacedName(s *api.CatalogService) types.NamespacedName { - return types.NamespacedName{ - Namespace: s.ServiceMeta[constants.MetaKeyKubeNS], - Name: s.ServiceMeta[constants.MetaKeyKubeServiceName], - } -} - // transformGatewayClass will check the list of GatewayClass objects for a matching // class, then return a list of reconcile Requests for it. func (r *GatewayController) transformGatewayClass(ctx context.Context) func(o client.Object) []reconcile.Request { @@ -465,7 +408,7 @@ func (r *GatewayController) transformGatewayClass(ctx context.Context) func(o cl }); err != nil { return nil } - return objectsToRequests(pointersOf(gatewayList.Items)) + return common.ObjectsToReconcileRequests(pointersOf(gatewayList.Items)) } } @@ -474,7 +417,7 @@ func (r *GatewayController) transformGatewayClass(ctx context.Context) func(o cl func (r *GatewayController) transformHTTPRoute(ctx context.Context) func(o client.Object) []reconcile.Request { return func(o client.Object) []reconcile.Request { route := o.(*gwv1beta1.HTTPRoute) - return refsToRequests(parentRefs(gwv1beta1.GroupVersion.Group, kindGateway, route.Namespace, route.Spec.ParentRefs)) + return refsToRequests(common.ParentRefs(common.BetaGroup, common.KindGateway, route.Namespace, route.Spec.ParentRefs)) } } @@ -483,7 +426,7 @@ func (r *GatewayController) transformHTTPRoute(ctx context.Context) func(o clien func (r *GatewayController) transformTCPRoute(ctx context.Context) func(o client.Object) []reconcile.Request { return func(o client.Object) []reconcile.Request { route := o.(*gwv1alpha2.TCPRoute) - return refsToRequests(parentRefs(gwv1beta1.GroupVersion.Group, kindGateway, route.Namespace, route.Spec.ParentRefs)) + return refsToRequests(common.ParentRefs(common.BetaGroup, common.KindGateway, route.Namespace, route.Spec.ParentRefs)) } } @@ -498,7 +441,7 @@ func (r *GatewayController) transformSecret(ctx context.Context) func(o client.O }); err != nil { return nil } - return objectsToRequests(pointersOf(gatewayList.Items)) + return common.ObjectsToReconcileRequests(pointersOf(gatewayList.Items)) } } @@ -506,140 +449,153 @@ func (r *GatewayController) transformSecret(ctx context.Context) func(o client.O // class, then return a list of reconcile Requests for Gateways referring to it. func (r *GatewayController) transformReferenceGrant(ctx context.Context) func(o client.Object) []reconcile.Request { return func(o client.Object) []reconcile.Request { - // just reconcile all gateways within the namespace - grant := o.(*gwv1beta1.ReferenceGrant) + // just re-reconcile all gateways for now ideally this will filter down to gateways + // affected, but technically the blast radius is gateways in the namespace + referencing + // the namespace + the routes that bind to them. gatewayList := &gwv1beta1.GatewayList{} - if err := r.Client.List(ctx, gatewayList, &client.ListOptions{ - Namespace: grant.Namespace, - }); err != nil { + if err := r.Client.List(ctx, gatewayList); err != nil { return nil } - return objectsToRequests(pointersOf(gatewayList.Items)) + + return common.ObjectsToReconcileRequests(pointersOf(gatewayList.Items)) } } -// transformConsulService will return a list of gateways that are referenced -// by a TCPRoute or HTTPRoute that references the Consul service. -func (r *GatewayController) transformConsulService(ctx context.Context) func(service *api.CatalogService) []types.NamespacedName { - return func(service *api.CatalogService) []types.NamespacedName { - nsn := serviceToNamespacedName(service) +// transformMeshService will return a list of gateways that are referenced +// by a TCPRoute or HTTPRoute that references the mesh service. +func (r *GatewayController) transformMeshService(ctx context.Context) func(o client.Object) []reconcile.Request { + return func(o client.Object) []reconcile.Request { + service := o.(*v1alpha1.MeshService) + key := client.ObjectKeyFromObject(service).String() - if nsn.Namespace != "" && nsn.Name != "" { - key := nsn.String() + return r.gatewaysForRoutesReferencing(ctx, TCPRoute_MeshServiceIndex, HTTPRoute_MeshServiceIndex, key) + } +} - requestSet := make(map[types.NamespacedName]struct{}) - tcpRouteList := &gwv1alpha2.TCPRouteList{} - if err := r.Client.List(ctx, tcpRouteList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(TCPRoute_ServiceIndex, key), - }); err != nil { - r.Log.Error(err, "unable to list TCPRoutes") - } - for _, route := range tcpRouteList.Items { - for _, ref := range parentRefs(gwv1beta1.GroupVersion.Group, kindGateway, route.Namespace, route.Spec.ParentRefs) { - requestSet[ref] = struct{}{} - } - } +// transformConsulGateway will return a list of gateways that this corresponds to. +func (r *GatewayController) transformConsulGateway(entry api.ConfigEntry) []types.NamespacedName { + return []types.NamespacedName{common.EntryToNamespacedName(entry)} +} - httpRouteList := &gwv1alpha2.HTTPRouteList{} - if err := r.Client.List(ctx, httpRouteList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(HTTPRoute_ServiceIndex, key), - }); err != nil { - r.Log.Error(err, "unable to list HTTPRoutes") - } - for _, route := range httpRouteList.Items { - for _, ref := range parentRefs(gwv1beta1.GroupVersion.Group, kindGateway, route.Namespace, route.Spec.ParentRefs) { - requestSet[ref] = struct{}{} - } - } +// transformConsulHTTPRoute will return a list of gateways that need to be reconciled. +func (r *GatewayController) transformConsulHTTPRoute(ctx context.Context) func(entry api.ConfigEntry) []types.NamespacedName { + return func(entry api.ConfigEntry) []types.NamespacedName { + parents := mapset.NewSet() + for _, parent := range entry.(*api.HTTPRouteConfigEntry).Parents { + parents.Add(api.ResourceReference{ + Kind: parent.Kind, + Name: parent.Name, + Namespace: parent.Namespace, + Partition: parent.Partition, + }) + } - requests := []types.NamespacedName{} - for request := range requestSet { - requests = append(requests, request) + var gateways []types.NamespacedName + for parent := range parents.Iter() { + if gateway := r.cache.Get(parent.(api.ResourceReference)); gateway != nil { + gateways = append(gateways, common.EntryToNamespacedName(gateway)) } - return requests } - - return nil + return gateways } } -// transformConsulPeering will return a list of gateways that are referenced -// by a TCPRoute or HTTPRoute that references the Consul peering. -func (r *GatewayController) transformConsulPeering(ctx context.Context) func(service *api.Peering) []types.NamespacedName { - return func(peering *api.Peering) []types.NamespacedName { - meshServiceList := &v1alpha1.MeshServiceList{} - - if err := r.Client.List(ctx, meshServiceList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(MeshService_PeerIndex, peering.Name), - }); err != nil { - r.Log.Error(err, "unable to list TCPRoutes") +func (r *GatewayController) transformConsulTCPRoute(ctx context.Context) func(entry api.ConfigEntry) []types.NamespacedName { + return func(entry api.ConfigEntry) []types.NamespacedName { + parents := mapset.NewSet() + for _, parent := range entry.(*api.TCPRouteConfigEntry).Parents { + parents.Add(api.ResourceReference{ + Kind: parent.Kind, + Name: parent.Name, + Namespace: parent.Namespace, + Partition: parent.Partition, + }) } - flattened := []types.NamespacedName{} - for _, meshService := range meshServiceList.Items { - for _, request := range r.transformMeshService(ctx)(&meshService) { - flattened = append(flattened, request.NamespacedName) + var gateways []types.NamespacedName + for parent := range parents.Iter() { + if gateway := r.cache.Get(parent.(api.ResourceReference)); gateway != nil { + gateways = append(gateways, common.EntryToNamespacedName(gateway)) } } - - return flattened + return gateways } } -// transformMeshService will return a list of gateways that are referenced -// by a TCPRoute or HTTPRoute that references the mesh service. -func (r *GatewayController) transformMeshService(ctx context.Context) func(o client.Object) []reconcile.Request { - return func(o client.Object) []reconcile.Request { - service := o.(*v1alpha1.MeshService) - key := client.ObjectKeyFromObject(service).String() - - requestSet := make(map[types.NamespacedName]struct{}) - - tcpRouteList := &gwv1alpha2.TCPRouteList{} - if err := r.Client.List(ctx, tcpRouteList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(TCPRoute_MeshServiceIndex, key), - }); err != nil { - r.Log.Error(err, "unable to list TCPRoutes") +func (r *GatewayController) transformConsulInlineCertificate(ctx context.Context) func(entry api.ConfigEntry) []types.NamespacedName { + return func(entry api.ConfigEntry) []types.NamespacedName { + certificateKey := api.ResourceReference{ + Kind: entry.GetKind(), + Name: entry.GetName(), + Namespace: entry.GetNamespace(), + Partition: entry.GetPartition(), } - for _, route := range tcpRouteList.Items { - for _, ref := range parentRefs(gwv1beta1.GroupVersion.Group, kindGateway, route.Namespace, route.Spec.ParentRefs) { - requestSet[ref] = struct{}{} + + var gateways []types.NamespacedName + for _, entry := range r.cache.List(api.APIGateway) { + gateway := entry.(*api.APIGatewayConfigEntry) + if gatewayReferencesCertificate(certificateKey, gateway) { + gateways = append(gateways, common.EntryToNamespacedName(gateway)) } } - httpRouteList := &gwv1beta1.HTTPRouteList{} - if err := r.Client.List(ctx, httpRouteList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(HTTPRoute_MeshServiceIndex, key), - }); err != nil { - r.Log.Error(err, "unable to list HTTPRoutes") - } - for _, route := range httpRouteList.Items { - for _, ref := range parentRefs(gwv1beta1.GroupVersion.Group, kindGateway, route.Namespace, route.Spec.ParentRefs) { - requestSet[ref] = struct{}{} + return gateways + } +} + +func gatewayReferencesCertificate(certificateKey api.ResourceReference, gateway *api.APIGatewayConfigEntry) bool { + for _, listener := range gateway.Listeners { + for _, cert := range listener.TLS.Certificates { + if cert == certificateKey { + return true } } + } + return false +} - requests := []reconcile.Request{} - for request := range requestSet { - requests = append(requests, reconcile.Request{NamespacedName: request}) - } - return requests +// transformEndpoints will return a list of gateways that are referenced +// by a TCPRoute or HTTPRoute that references the service. +func (r *GatewayController) transformEndpoints(ctx context.Context) func(o client.Object) []reconcile.Request { + return func(o client.Object) []reconcile.Request { + key := client.ObjectKeyFromObject(o).String() + + return r.gatewaysForRoutesReferencing(ctx, TCPRoute_ServiceIndex, HTTPRoute_ServiceIndex, key) } } -// objectsToRequests takes a list of objects and returns a list of -// reconcile Requests. -func objectsToRequests[T metav1.Object](objects []T) []reconcile.Request { - requests := make([]reconcile.Request, 0, len(objects)) +// gatewaysForRoutesReferencing returns a mapping of all gateways that are referenced by routes that +// have a backend associated with the given key and index. +func (r *GatewayController) gatewaysForRoutesReferencing(ctx context.Context, tcpIndex, httpIndex, key string) []reconcile.Request { + requestSet := make(map[types.NamespacedName]struct{}) - // TODO: is it possible to receive empty objects? - for _, object := range objects { - requests = append(requests, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Namespace: object.GetNamespace(), - Name: object.GetName(), - }, - }) + tcpRouteList := &gwv1alpha2.TCPRouteList{} + if err := r.Client.List(ctx, tcpRouteList, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(tcpIndex, key), + }); err != nil { + r.Log.Error(err, "unable to list TCPRoutes") + } + for _, route := range tcpRouteList.Items { + for _, ref := range common.ParentRefs(common.BetaGroup, common.KindGateway, route.Namespace, route.Spec.ParentRefs) { + requestSet[ref] = struct{}{} + } + } + + httpRouteList := &gwv1beta1.HTTPRouteList{} + if err := r.Client.List(ctx, httpRouteList, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(httpIndex, key), + }); err != nil { + r.Log.Error(err, "unable to list HTTPRoutes") + } + for _, route := range httpRouteList.Items { + for _, ref := range common.ParentRefs(common.BetaGroup, common.KindGateway, route.Namespace, route.Spec.ParentRefs) { + requestSet[ref] = struct{}{} + } + } + + requests := []reconcile.Request{} + for request := range requestSet { + requests = append(requests, reconcile.Request{NamespacedName: request}) } return requests } @@ -670,110 +626,364 @@ func refsToRequests(objects []types.NamespacedName) []reconcile.Request { return requests } -// parentRefs takes a list of ParentReference objects and returns a list of NamespacedName objects. -func parentRefs(group, kind, namespace string, refs []gwv1beta1.ParentReference) []types.NamespacedName { - indexed := make([]types.NamespacedName, 0, len(refs)) - for _, parent := range refs { - if nilOrEqual(parent.Group, group) && nilOrEqual(parent.Kind, kind) { - indexed = append(indexed, indexedNamespacedNameWithDefault(parent.Name, parent.Namespace, namespace)) - } +// kubernetes helpers + +func (c *GatewayController) getNamespaces(ctx context.Context) (map[string]corev1.Namespace, error) { + var list corev1.NamespaceList + + if err := c.Client.List(ctx, &list); err != nil { + return nil, err } - return indexed + namespaces := map[string]corev1.Namespace{} + for _, namespace := range list.Items { + namespaces[namespace.Name] = namespace + } + + return namespaces, nil } -func nilOrEqual[T ~string](v *T, check string) bool { - return v == nil || string(*v) == check +func (c *GatewayController) getReferenceGrants(ctx context.Context) ([]gwv1beta1.ReferenceGrant, error) { + var list gwv1beta1.ReferenceGrantList + + if err := c.Client.List(ctx, &list); err != nil { + return nil, err + } + + return list.Items, nil } -func indexedNamespacedNameWithDefault[T ~string, U ~string, V ~string](t T, u *U, v V) types.NamespacedName { - return types.NamespacedName{ - Namespace: derefStringOr(u, v), - Name: string(t), +func (c *GatewayController) getDeployedGatewayService(ctx context.Context, gateway types.NamespacedName) (*corev1.Service, error) { + service := &corev1.Service{} + + // we use the implicit association of a service name/namespace with a corresponding gateway + if err := c.Client.Get(ctx, gateway, service); err != nil { + return nil, client.IgnoreNotFound(err) } + + return service, nil } -func derefStringOr[T ~string, U ~string](v *T, val U) string { - if v == nil { - return string(val) +func (c *GatewayController) getDeployedGatewayPods(ctx context.Context, gateway gwv1beta1.Gateway) ([]corev1.Pod, error) { + labels := common.LabelsForGateway(&gateway) + + var list corev1.PodList + + if err := c.Client.List(ctx, &list, client.MatchingLabels(labels)); err != nil { + return nil, err } - return string(*v) + + return list.Items, nil } -func (r *GatewayController) getAllRefsForGateway(ctx context.Context, gw *gwv1beta1.Gateway) ([]metav1.Object, error) { - objs := make([]metav1.Object, 0) +func (c *GatewayController) getRelatedHTTPRoutes(ctx context.Context, gateway types.NamespacedName, resources *common.ResourceMap) ([]gwv1beta1.HTTPRoute, error) { + var list gwv1beta1.HTTPRouteList - // handle http routes - httpRouteList := &gwv1beta1.HTTPRouteList{} - err := r.Client.List(ctx, httpRouteList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(HTTPRoute_GatewayIndex, types.NamespacedName{Name: gw.Name, Namespace: gw.Namespace}.String()), - }) - if err != nil { + if err := c.Client.List(ctx, &list, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(HTTPRoute_GatewayIndex, gateway.String()), + }); err != nil { return nil, err } - for _, route := range httpRouteList.Items { - objs = append(objs, &route) + + for _, route := range list.Items { + resources.ReferenceCountHTTPRoute(route) } - // handle tcp routes - tcpRouteList := &v1alpha2.TCPRouteList{} - err = r.Client.List(ctx, tcpRouteList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(TCPRoute_GatewayIndex, types.NamespacedName{Name: gw.Name, Namespace: gw.Namespace}.String()), - }) - if err != nil { + + return list.Items, nil +} + +func (c *GatewayController) getRelatedTCPRoutes(ctx context.Context, gateway types.NamespacedName, resources *common.ResourceMap) ([]gwv1alpha2.TCPRoute, error) { + var list gwv1alpha2.TCPRouteList + + if err := c.Client.List(ctx, &list, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(TCPRoute_GatewayIndex, gateway.String()), + }); err != nil { return nil, err } - for _, route := range tcpRouteList.Items { - objs = append(objs, &route) - } - // handle secrets - for _, listener := range gw.Spec.Listeners { - for _, secret := range listener.TLS.CertificateRefs { - secretObj := &corev1.Secret{} - err = r.Client.Get(ctx, indexedNamespacedNameWithDefault(secret.Name, secret.Namespace, gw.Namespace), secretObj) - if err != nil { - continue - } - objs = append(objs, secretObj) - } + for _, route := range list.Items { + resources.ReferenceCountTCPRoute(route) } - return objs, nil + return list.Items, nil } -// getConfigForGatewayClass returns the relevant GatewayClassConfig for the GatewayClass. -func getConfigForGatewayClass(ctx context.Context, client client.Client, gwc *gwv1beta1.GatewayClass) (*v1alpha1.GatewayClassConfig, error) { - if gwc == nil { +func (c *GatewayController) getConfigForGatewayClass(ctx context.Context, gatewayClassConfig *gwv1beta1.GatewayClass) (*v1alpha1.GatewayClassConfig, error) { + if gatewayClassConfig == nil { // if we don't have a gateway class we can't fetch the corresponding config return nil, nil } config := &v1alpha1.GatewayClassConfig{} - if ref := gwc.Spec.ParametersRef; ref != nil { + if ref := gatewayClassConfig.Spec.ParametersRef; ref != nil { if string(ref.Group) != v1alpha1.GroupVersion.Group || ref.Kind != v1alpha1.GatewayClassConfigKind || - gwc.Spec.ControllerName != GatewayClassControllerName { + gatewayClassConfig.Spec.ControllerName != GatewayClassControllerName { // we don't have supported params, so return nil return nil, nil } - err := client.Get(ctx, types.NamespacedName{Name: ref.Name}, config) - if err != nil { - if k8serrors.IsNotFound(err) { - return nil, nil - } - return nil, err + if err := c.Client.Get(ctx, types.NamespacedName{Name: ref.Name}, config); err != nil { + return nil, client.IgnoreNotFound(err) } } return config, nil } -func consulServicesForGateway(gateway gwv1beta1.Gateway, services []api.CatalogService) []api.CatalogService { - filtered := []api.CatalogService{} - for _, service := range services { - kubeService := serviceToNamespacedName(&service) - if gateway.Name == kubeService.Name && gateway.Namespace == kubeService.Namespace { - filtered = append(filtered, service) +func (c *GatewayController) getGatewayClassForGateway(ctx context.Context, gateway gwv1beta1.Gateway) (*gwv1beta1.GatewayClass, error) { + var gatewayClass gwv1beta1.GatewayClass + if err := c.Client.Get(ctx, types.NamespacedName{Name: string(gateway.Spec.GatewayClassName)}, &gatewayClass); err != nil { + return nil, client.IgnoreNotFound(err) + } + return &gatewayClass, nil +} + +// resource map construction routines + +func (c *GatewayController) fetchControlledGateways(ctx context.Context, resources *common.ResourceMap) error { + set := mapset.NewSet() + + list := gwv1beta1.GatewayClassList{} + if err := c.Client.List(ctx, &list, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(GatewayClass_ControllerNameIndex, GatewayClassControllerName), + }); err != nil { + return err + } + for _, gatewayClass := range list.Items { + set.Add(gatewayClass.Name) + } + + gateways := &gwv1beta1.GatewayList{} + if err := c.Client.List(ctx, gateways); err != nil { + return err + } + + for _, gateway := range gateways.Items { + if set.Contains(string(gateway.Spec.GatewayClassName)) { + resources.ReferenceCountGateway(gateway) + } + } + return nil +} + +func (c *GatewayController) fetchCertificatesForGateway(ctx context.Context, resources *common.ResourceMap, gateway gwv1beta1.Gateway) error { + certificates := mapset.NewSet() + + for _, listener := range gateway.Spec.Listeners { + if listener.TLS != nil { + for _, cert := range listener.TLS.CertificateRefs { + if common.NilOrEqual(cert.Group, "") && common.NilOrEqual(cert.Kind, common.KindSecret) { + certificates.Add(common.IndexedNamespacedNameWithDefault(cert.Name, cert.Namespace, gateway.Namespace)) + } + } + } + } + + for key := range certificates.Iter() { + if err := c.fetchSecret(ctx, resources, key.(types.NamespacedName)); err != nil { + return err + } + } + + return nil +} + +func (c *GatewayController) fetchSecret(ctx context.Context, resources *common.ResourceMap, key types.NamespacedName) error { + var secret corev1.Secret + if err := c.Client.Get(ctx, key, &secret); err != nil { + return client.IgnoreNotFound(err) + } + + resources.ReferenceCountCertificate(secret) + + return nil +} + +func (c *GatewayController) fetchServicesForRoutes(ctx context.Context, resources *common.ResourceMap, tcpRoutes []gwv1alpha2.TCPRoute, httpRoutes []gwv1beta1.HTTPRoute) error { + serviceBackends := mapset.NewSet() + meshServiceBackends := mapset.NewSet() + + for _, route := range httpRoutes { + for _, rule := range route.Spec.Rules { + for _, backend := range rule.BackendRefs { + if common.DerefEqual(backend.Group, v1alpha1.ConsulHashicorpGroup) && + common.DerefEqual(backend.Kind, v1alpha1.MeshServiceKind) { + meshServiceBackends.Add(common.IndexedNamespacedNameWithDefault(backend.Name, backend.Namespace, route.Namespace)) + } else if common.NilOrEqual(backend.Group, "") && common.NilOrEqual(backend.Kind, "Service") { + serviceBackends.Add(common.IndexedNamespacedNameWithDefault(backend.Name, backend.Namespace, route.Namespace)) + } + } + } + } + + for _, route := range tcpRoutes { + for _, rule := range route.Spec.Rules { + for _, backend := range rule.BackendRefs { + if common.DerefEqual(backend.Group, v1alpha1.ConsulHashicorpGroup) && + common.DerefEqual(backend.Kind, v1alpha1.MeshServiceKind) { + meshServiceBackends.Add(common.IndexedNamespacedNameWithDefault(backend.Name, backend.Namespace, route.Namespace)) + } else if common.NilOrEqual(backend.Group, "") && common.NilOrEqual(backend.Kind, "Service") { + serviceBackends.Add(common.IndexedNamespacedNameWithDefault(backend.Name, backend.Namespace, route.Namespace)) + } + } + } + } + + for key := range meshServiceBackends.Iter() { + if err := c.fetchMeshService(ctx, resources, key.(types.NamespacedName)); err != nil { + return err + } + } + + for key := range serviceBackends.Iter() { + if err := c.fetchServicesForEndpoints(ctx, resources, key.(types.NamespacedName)); err != nil { + return err + } + } + return nil +} + +func (c *GatewayController) fetchMeshService(ctx context.Context, resources *common.ResourceMap, key types.NamespacedName) error { + var service v1alpha1.MeshService + if err := c.Client.Get(ctx, key, &service); err != nil { + return client.IgnoreNotFound(err) + } + + resources.AddMeshService(service) + + return nil +} + +func (c *GatewayController) fetchServicesForEndpoints(ctx context.Context, resources *common.ResourceMap, key types.NamespacedName) error { + if shouldIgnore(key.Namespace, c.denyK8sNamespacesSet, c.allowK8sNamespacesSet) { + return nil + } + + var endpoints corev1.Endpoints + if err := c.Client.Get(ctx, key, &endpoints); err != nil { + return client.IgnoreNotFound(err) + } + + if isLabeledIgnore(endpoints.Labels) { + return nil + } + + for _, subset := range endpoints.Subsets { + for _, address := range subset.Addresses { + if address.TargetRef != nil && address.TargetRef.Kind == "Pod" { + objectKey := types.NamespacedName{Name: address.TargetRef.Name, Namespace: address.TargetRef.Namespace} + + var pod corev1.Pod + if err := c.Client.Get(ctx, objectKey, &pod); err != nil { + if k8serrors.IsNotFound(err) { + continue + } + return err + } + + resources.AddService(key, serviceName(pod, endpoints)) + } + } + } + + return nil +} + +// cache routines + +func (c *GatewayController) getConsulServices(ref api.ResourceReference) []api.CatalogService { + return c.gatewayCache.ServicesFor(ref) +} + +func (c *GatewayController) getConsulGateway(ref api.ResourceReference) *api.APIGatewayConfigEntry { + if entry := c.cache.Get(ref); entry != nil { + return entry.(*api.APIGatewayConfigEntry) + } + return nil +} + +func (c *GatewayController) getConsulHTTPRoutes(ref api.ResourceReference, resources *common.ResourceMap) []api.HTTPRouteConfigEntry { + var filtered []api.HTTPRouteConfigEntry + + for _, route := range configEntriesTo[*api.HTTPRouteConfigEntry](c.cache.List(api.HTTPRoute)) { + if routeReferencesGateway(route.Namespace, ref, route.Parents) { + filtered = append(filtered, *route) + resources.ReferenceCountConsulHTTPRoute(*route) + } + } + return filtered +} + +func (c *GatewayController) getConsulTCPRoutes(ref api.ResourceReference, resources *common.ResourceMap) []api.TCPRouteConfigEntry { + var filtered []api.TCPRouteConfigEntry + + for _, route := range configEntriesTo[*api.TCPRouteConfigEntry](c.cache.List(api.TCPRoute)) { + if routeReferencesGateway(route.Namespace, ref, route.Parents) { + filtered = append(filtered, *route) + resources.ReferenceCountConsulTCPRoute(*route) } } return filtered } + +func (c *GatewayController) getConsulInlineCertificates() []api.InlineCertificateConfigEntry { + var filtered []api.InlineCertificateConfigEntry + + for _, cert := range configEntriesTo[*api.InlineCertificateConfigEntry](c.cache.List(api.InlineCertificate)) { + filtered = append(filtered, *cert) + } + return filtered +} + +func routeReferencesGateway(namespace string, ref api.ResourceReference, refs []api.ResourceReference) bool { + // we don't need to check partition here since they're all in the same partition + if namespace == "" { + namespace = "default" + } + + for _, parent := range refs { + if common.EmptyOrEqual(parent.Kind, api.APIGateway) { + if common.DefaultOrEqual(parent.Namespace, namespace, ref.Namespace) && + parent.Name == ref.Name { + return true + } + } + } + + return false +} + +func serviceName(pod corev1.Pod, serviceEndpoints corev1.Endpoints) string { + svcName := serviceEndpoints.Name + // If the annotation has a comma, it is a multi port Pod. In that case we always use the name of the endpoint. + if serviceNameFromAnnotation, ok := pod.Annotations[constants.AnnotationService]; ok && serviceNameFromAnnotation != "" && !strings.Contains(serviceNameFromAnnotation, ",") { + svcName = serviceNameFromAnnotation + } + return svcName +} + +func isLabeledIgnore(labels map[string]string) bool { + value, labelExists := labels[constants.LabelServiceIgnore] + shouldIgnore, err := strconv.ParseBool(value) + + return shouldIgnore && labelExists && err == nil +} + +// shouldIgnore ignores namespaces where we don't connect-inject. +func shouldIgnore(namespace string, denySet, allowSet mapset.Set) bool { + // Ignores system namespaces. + if namespace == metav1.NamespaceSystem || namespace == metav1.NamespacePublic || namespace == "local-path-storage" { + return true + } + + // Ignores deny list. + if denySet.Contains(namespace) { + return true + } + + // Ignores if not in allow list or allow list is not *. + if !allowSet.Contains("*") && !allowSet.Contains(namespace) { + return true + } + + return false +} diff --git a/control-plane/api-gateway/controllers/gateway_controller_test.go b/control-plane/api-gateway/controllers/gateway_controller_test.go deleted file mode 100644 index d04a0b2e44..0000000000 --- a/control-plane/api-gateway/controllers/gateway_controller_test.go +++ /dev/null @@ -1,464 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers - -import ( - "context" - "fmt" - "net/http" - "net/http/httptest" - "net/url" - "strconv" - "testing" - - appsv1 "k8s.io/api/apps/v1" - rbac "k8s.io/api/rbac/v1" - - logrtest "github.com/go-logr/logr/testr" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/cache" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul-k8s/control-plane/consul" - "github.com/hashicorp/consul-k8s/control-plane/helper/test" - "github.com/hashicorp/consul/api" -) - -const ( - TestGatewayClassConfigName = "test-gateway-class-config" - TestAnnotationConfigKey = "api-gateway.consul.hashicorp.com/config" - TestGatewayClassName = "test-gateway-class" - TestGatewayName = "test-gateway" - TestNamespace = "test-namespace" -) - -func stubConsulCache(t *testing.T) *cache.Cache { - t.Helper() - - consulServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case "/v1/acl/policies": - fmt.Fprintln(w, `[]`) - case "/v1/acl/tokens": - fmt.Fprintln(w, `[]`) - case "/v1/config": - fmt.Fprintln(w, `[]`) - case "/v1/catalog/services": - fmt.Fprintln(w, `{}`) - default: - w.WriteHeader(500) - fmt.Fprintln(w, "Mock Server not configured for this route: "+r.URL.Path) - } - })) - t.Cleanup(consulServer.Close) - - serverURL, err := url.Parse(consulServer.URL) - require.NoError(t, err) - - port, err := strconv.Atoi(serverURL.Port()) - require.NoError(t, err) - - return cache.New(cache.Config{ - ConsulClientConfig: &consul.Config{ - APIClientConfig: &api.Config{}, - HTTPPort: port, - GRPCPort: port, - APITimeout: 0, - }, - ConsulServerConnMgr: test.MockConnMgrForIPAndPort(serverURL.Hostname(), port), - NamespacesEnabled: false, - Logger: logrtest.New(t), - }) -} - -func TestGatewayReconcileGatekeeperUpdates(t *testing.T) { - t.Parallel() - - namespace := "test-namespace" - name := "test-gateway" - - req := ctrl.Request{ - NamespacedName: types.NamespacedName{ - Namespace: namespace, - Name: name, - }, - } - - basicGatewayClass, basicGatewayClassConfig := getBasicGatewayClassAndConfig() - - cases := map[string]struct { - gateway *gwv1beta1.Gateway - k8sObjects []runtime.Object - expectedError error - }{ - "successful update of gateway": { - gateway: &gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - Finalizers: []string{gatewayFinalizer}, - Annotations: map[string]string{ - TestAnnotationConfigKey: `{"serviceType":"serviceType"}`, - }, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: TestGatewayClassName, - }, - }, - k8sObjects: []runtime.Object{ - &basicGatewayClass, - &basicGatewayClassConfig, - }, - expectedError: nil, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - s := runtime.NewScheme() - require.NoError(t, gwv1beta1.Install(s)) - require.NoError(t, v1alpha1.AddToScheme(s)) - require.NoError(t, gwv1alpha2.AddToScheme(s)) - require.NoError(t, rbac.AddToScheme(s)) - require.NoError(t, corev1.AddToScheme(s)) - require.NoError(t, appsv1.AddToScheme(s)) - - objs := tc.k8sObjects - if tc.gateway != nil { - objs = append(objs, tc.gateway) - } - - fakeClient := registerFieldIndexersForTest(fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(objs...)).Build() - - r := &GatewayController{ - cache: stubConsulCache(t), - Client: fakeClient, - Log: logrtest.New(t), - } - - _, err := r.Reconcile(context.Background(), req) - - require.Equal(t, tc.expectedError, err) - deployment := appsv1.Deployment{} - r.Client.Get(context.TODO(), types.NamespacedName{ - Namespace: TestNamespace, - Name: TestGatewayName, - }, &deployment) - require.NotEmpty(t, deployment) - require.Equal(t, TestGatewayName, deployment.ObjectMeta.Name) - }) - } -} - -func TestGatewayReconcileGatekeeperDeletes(t *testing.T) { - t.Parallel() - - req := ctrl.Request{ - NamespacedName: types.NamespacedName{ - Namespace: TestNamespace, - Name: TestGatewayName, - }, - } - - basicGatewayClass, basicGatewayClassConfig := getBasicGatewayClassAndConfig() - cases := map[string]struct { - gateway *gwv1beta1.Gateway - k8sObjects []runtime.Object - expectedError error - }{ - "successful change of gatewayclass on gateway": { - gateway: &gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: TestNamespace, - Name: TestGatewayName, - Finalizers: []string{gatewayFinalizer}, - Annotations: map[string]string{ - TestAnnotationConfigKey: `{"serviceType":"serviceType"}`, - }, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: TestGatewayClassName, - }, - }, - k8sObjects: []runtime.Object{ - &basicGatewayClass, - &basicGatewayClassConfig, - }, - expectedError: nil, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - s := runtime.NewScheme() - require.NoError(t, gwv1beta1.Install(s)) - require.NoError(t, v1alpha1.AddToScheme(s)) - require.NoError(t, gwv1alpha2.AddToScheme(s)) - require.NoError(t, rbac.AddToScheme(s)) - require.NoError(t, corev1.AddToScheme(s)) - require.NoError(t, appsv1.AddToScheme(s)) - - objs := tc.k8sObjects - if tc.gateway != nil { - objs = append(objs, tc.gateway) - } - - fakeClient := registerFieldIndexersForTest(fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(objs...)).Build() - - r := &GatewayController{ - cache: stubConsulCache(t), - Client: fakeClient, - Log: logrtest.New(t), - } - - _, err := r.Reconcile(context.Background(), req) - - require.Equal(t, tc.expectedError, err) - deployment := appsv1.Deployment{} - r.Client.Get(context.TODO(), types.NamespacedName{ - Namespace: TestNamespace, - Name: TestGatewayName, - }, &deployment) - require.NotEmpty(t, deployment) - require.Equal(t, TestGatewayName, deployment.ObjectMeta.Name) - }) - } -} - -func TestObjectsToRequests(t *testing.T) { - t.Parallel() - - name := "test-gatewayclass" - - namespacedName := types.NamespacedName{ - Namespace: TestNamespace, - Name: name, - } - - cases := map[string]struct { - objects []metav1.Object - expectedResult []reconcile.Request - }{ - "successful conversion of gateway to request": { - objects: []metav1.Object{ - &gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: TestNamespace, - Name: name, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: GatewayClassControllerName, - }, - }, - }, - expectedResult: []reconcile.Request{ - { - NamespacedName: namespacedName, - }, - }, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - requests := objectsToRequests(tc.objects) - - require.Equal(t, tc.expectedResult, requests) - }) - } -} - -func TestGatewayController_getAllRefsForGateway(t *testing.T) { - t.Parallel() - s := runtime.NewScheme() - require.NoError(t, gwv1beta1.Install(s)) - require.NoError(t, gwv1alpha2.Install(s)) - require.NoError(t, corev1.AddToScheme(s)) - require.NoError(t, v1alpha1.AddToScheme(s)) - - secret := &corev1.Secret{ - TypeMeta: metav1.TypeMeta{ - Kind: "Secret", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "secret squirrel", - }, - } - gw := &gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-gw", - Annotations: map[string]string{}, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: "", - Listeners: []gwv1beta1.Listener{ - { - Name: "l1", - TLS: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{ - { - Kind: pointerTo(gwv1beta1.Kind("Secret")), - Name: "secret squirrel", - }, - }, - Options: map[gwv1beta1.AnnotationKey]gwv1beta1.AnnotationValue{}, - }, - AllowedRoutes: &gwv1beta1.AllowedRoutes{}, - }, - }, - Addresses: []gwv1beta1.GatewayAddress{}, - }, - Status: gwv1beta1.GatewayStatus{}, - } - gwc := &gwv1beta1.GatewayClass{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-gw-class", - }, - Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: "", - ParametersRef: &gwv1beta1.ParametersReference{ - Group: Group, - Kind: v1alpha1.GatewayClassConfigKind, - Name: "the config", - }, - Description: new(string), - }, - } - gwcConfig := &v1alpha1.GatewayClassConfig{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "the config", - }, - Spec: v1alpha1.GatewayClassConfigSpec{ - ServiceType: pointerTo(corev1.ServiceType("serviceType")), - NodeSelector: map[string]string{ - "selector": "of node", - }, - Tolerations: []v1.Toleration{ - { - Key: "key", - Operator: "op", - Value: "120", - Effect: "to the moon", - TolerationSeconds: new(int64), - }, - }, - CopyAnnotations: v1alpha1.CopyAnnotationsSpec{ - Service: []string{"service"}, - }, - }, - } - - httpRouteOnGateway := &gwv1beta1.HTTPRoute{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "route 1", - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - { - Name: gwv1beta1.ObjectName(gw.Name), - }, - }, - }, - }, - } - - httpRouteNotOnGateway := &gwv1beta1.HTTPRoute{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "route not on gateway", - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - { - Name: gwv1beta1.ObjectName("not on the gateway"), - }, - }, - }, - }, - Status: gwv1beta1.HTTPRouteStatus{}, - } - - tcpRoute := &gwv1alpha2.TCPRoute{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "tcp route", - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - { - Name: gwv1beta1.ObjectName(gw.Name), - }, - }, - }, - }, - } - - objs := []runtime.Object{gw, gwc, gwcConfig, httpRouteOnGateway, httpRouteNotOnGateway, tcpRoute, secret} - - fakeClient := registerFieldIndexersForTest(fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(objs...)).Build() - controller := GatewayController{ - Client: fakeClient, - } - - ctx := context.Background() - - actual, err := controller.getAllRefsForGateway(ctx, gw) - - require.NoError(t, err) - expectedEntries := []metav1.Object{httpRouteOnGateway, tcpRoute, secret} - - require.ElementsMatch(t, expectedEntries, actual) -} - -func getBasicGatewayClassAndConfig() (gwv1beta1.GatewayClass, v1alpha1.GatewayClassConfig) { - serviceType := corev1.ServiceType("NodePort") - basicGatewayClass := gwv1beta1.GatewayClass{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "", - Name: TestGatewayClassName, - Finalizers: []string{ - gatewayClassFinalizer, - }, - }, - Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: GatewayClassControllerName, - ParametersRef: &gwv1beta1.ParametersReference{ - Group: "consul.hashicorp.com", - Kind: "GatewayClassConfig", - Name: TestGatewayClassConfigName, - Namespace: nil, - }, - }, - } - - basicGatewayClassConfig := v1alpha1.GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: TestGatewayClassConfigName, - }, - Spec: v1alpha1.GatewayClassConfigSpec{ - ServiceType: &serviceType, - CopyAnnotations: v1alpha1.CopyAnnotationsSpec{ - Service: []string{"serviceType"}, - }, - }, - } - - return basicGatewayClass, basicGatewayClassConfig -} diff --git a/control-plane/api-gateway/controllers/index.go b/control-plane/api-gateway/controllers/index.go index 3ef0e65c81..cff6dfac12 100644 --- a/control-plane/api-gateway/controllers/index.go +++ b/control-plane/api-gateway/controllers/index.go @@ -11,6 +11,7 @@ import ( gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" ) @@ -149,9 +150,9 @@ func gatewayForSecret(o client.Object) []string { continue } for _, cert := range listener.TLS.CertificateRefs { - if nilOrEqual(cert.Group, "") && nilOrEqual(cert.Kind, "Secret") { + if common.NilOrEqual(cert.Group, "") && common.NilOrEqual(cert.Kind, "Secret") { // If an explicit Secret namespace is not provided, use the Gateway namespace to lookup the provided Secret Name. - secretReferences = append(secretReferences, indexedNamespacedNameWithDefault(cert.Name, cert.Namespace, gateway.Namespace).String()) + secretReferences = append(secretReferences, common.IndexedNamespacedNameWithDefault(cert.Name, cert.Namespace, gateway.Namespace).String()) } } } @@ -174,8 +175,8 @@ func servicesForHTTPRoute(o client.Object) []string { for _, rule := range route.Spec.Rules { BACKEND_LOOP: for _, ref := range rule.BackendRefs { - if nilOrEqual(ref.Group, "") && nilOrEqual(ref.Kind, "Service") { - backendRef := indexedNamespacedNameWithDefault(ref.Name, ref.Namespace, route.Namespace).String() + if common.NilOrEqual(ref.Group, "") && common.NilOrEqual(ref.Kind, "Service") { + backendRef := common.IndexedNamespacedNameWithDefault(ref.Name, ref.Namespace, route.Namespace).String() for _, member := range refs { if member == backendRef { continue BACKEND_LOOP @@ -194,9 +195,8 @@ func meshServicesForHTTPRoute(o client.Object) []string { for _, rule := range route.Spec.Rules { BACKEND_LOOP: for _, ref := range rule.BackendRefs { - if ref.Group != nil && string(*ref.Group) == v1alpha1.ConsulHashicorpGroup && - ref.Kind != nil && string(*ref.Kind) == v1alpha1.MeshServiceKind { - backendRef := indexedNamespacedNameWithDefault(ref.Name, ref.Namespace, route.Namespace).String() + if common.DerefEqual(ref.Group, v1alpha1.ConsulHashicorpGroup) && common.DerefEqual(ref.Kind, v1alpha1.MeshServiceKind) { + backendRef := common.IndexedNamespacedNameWithDefault(ref.Name, ref.Namespace, route.Namespace).String() for _, member := range refs { if member == backendRef { continue BACKEND_LOOP @@ -215,8 +215,8 @@ func servicesForTCPRoute(o client.Object) []string { for _, rule := range route.Spec.Rules { BACKEND_LOOP: for _, ref := range rule.BackendRefs { - if nilOrEqual(ref.Group, "") && nilOrEqual(ref.Kind, "Service") { - backendRef := indexedNamespacedNameWithDefault(ref.Name, ref.Namespace, route.Namespace).String() + if common.NilOrEqual(ref.Group, "") && common.NilOrEqual(ref.Kind, common.KindService) { + backendRef := common.IndexedNamespacedNameWithDefault(ref.Name, ref.Namespace, route.Namespace).String() for _, member := range refs { if member == backendRef { continue BACKEND_LOOP @@ -235,9 +235,8 @@ func meshServicesForTCPRoute(o client.Object) []string { for _, rule := range route.Spec.Rules { BACKEND_LOOP: for _, ref := range rule.BackendRefs { - if ref.Group != nil && string(*ref.Group) == v1alpha1.ConsulHashicorpGroup && - ref.Kind != nil && string(*ref.Kind) == v1alpha1.MeshServiceKind { - backendRef := indexedNamespacedNameWithDefault(ref.Name, ref.Namespace, route.Namespace).String() + if common.DerefEqual(ref.Group, v1alpha1.ConsulHashicorpGroup) && common.DerefEqual(ref.Kind, v1alpha1.MeshServiceKind) { + backendRef := common.IndexedNamespacedNameWithDefault(ref.Name, ref.Namespace, route.Namespace).String() for _, member := range refs { if member == backendRef { continue BACKEND_LOOP @@ -253,9 +252,9 @@ func meshServicesForTCPRoute(o client.Object) []string { func gatewaysForRoute(namespace string, refs []gwv1beta1.ParentReference) []string { var references []string for _, parent := range refs { - if nilOrEqual(parent.Group, gwv1beta1.GroupVersion.Group) && nilOrEqual(parent.Kind, "Gateway") { + if common.NilOrEqual(parent.Group, common.BetaGroup) && common.NilOrEqual(parent.Kind, common.KindGateway) { // If an explicit Gateway namespace is not provided, use the Route namespace to lookup the provided Gateway Namespace. - references = append(references, indexedNamespacedNameWithDefault(parent.Name, parent.Namespace, namespace).String()) + references = append(references, common.IndexedNamespacedNameWithDefault(parent.Name, parent.Namespace, namespace).String()) } } return references diff --git a/control-plane/api-gateway/gatekeeper/dataplane.go b/control-plane/api-gateway/gatekeeper/dataplane.go index 6ef7cc04ec..90cd186743 100644 --- a/control-plane/api-gateway/gatekeeper/dataplane.go +++ b/control-plane/api-gateway/gatekeeper/dataplane.go @@ -10,7 +10,7 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/utils/pointer" - apigateway "github.com/hashicorp/consul-k8s/control-plane/api-gateway" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" "github.com/hashicorp/consul-k8s/control-plane/namespaces" "k8s.io/apimachinery/pkg/util/intstr" @@ -25,7 +25,7 @@ const ( volumeName = "consul-connect-inject-data" ) -func consulDataplaneContainer(config apigateway.HelmConfig, name, namespace string) (corev1.Container, error) { +func consulDataplaneContainer(config common.HelmConfig, name, namespace string) (corev1.Container, error) { // Extract the service account token's volume mount. var ( err error @@ -113,7 +113,7 @@ func consulDataplaneContainer(config apigateway.HelmConfig, name, namespace stri return container, nil } -func getDataplaneArgs(namespace string, config apigateway.HelmConfig, bearerTokenFile string, name string) ([]string, error) { +func getDataplaneArgs(namespace string, config common.HelmConfig, bearerTokenFile string, name string) ([]string, error) { proxyIDFileName := "/consul/connect-inject/proxyid" envoyConcurrency := defaultEnvoyProxyConcurrency diff --git a/control-plane/api-gateway/gatekeeper/deployment.go b/control-plane/api-gateway/gatekeeper/deployment.go index 0c46dbb0a5..eecfa31349 100644 --- a/control-plane/api-gateway/gatekeeper/deployment.go +++ b/control-plane/api-gateway/gatekeeper/deployment.go @@ -6,10 +6,10 @@ package gatekeeper import ( "context" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "k8s.io/apimachinery/pkg/types" - apigateway "github.com/hashicorp/consul-k8s/control-plane/api-gateway" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" @@ -24,7 +24,7 @@ const ( defaultInstances int32 = 1 ) -func (g *Gatekeeper) upsertDeployment(ctx context.Context, gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config apigateway.HelmConfig) error { +func (g *Gatekeeper) upsertDeployment(ctx context.Context, gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config common.HelmConfig) error { // Get Deployment if it exists. existingDeployment := &appsv1.Deployment{} exists := false @@ -84,7 +84,7 @@ func (g *Gatekeeper) deleteDeployment(ctx context.Context, nsname types.Namespac return err } -func (g *Gatekeeper) deployment(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config apigateway.HelmConfig, currentReplicas *int32) (*appsv1.Deployment, error) { +func (g *Gatekeeper) deployment(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config common.HelmConfig, currentReplicas *int32) (*appsv1.Deployment, error) { initContainer, err := initContainer(config, gateway.Name, gateway.Namespace) if err != nil { return nil, err @@ -99,16 +99,16 @@ func (g *Gatekeeper) deployment(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayC ObjectMeta: metav1.ObjectMeta{ Name: gateway.Name, Namespace: gateway.Namespace, - Labels: apigateway.LabelsForGateway(&gateway), + Labels: common.LabelsForGateway(&gateway), }, Spec: appsv1.DeploymentSpec{ Replicas: deploymentReplicas(gcc, currentReplicas), Selector: &metav1.LabelSelector{ - MatchLabels: apigateway.LabelsForGateway(&gateway), + MatchLabels: common.LabelsForGateway(&gateway), }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: apigateway.LabelsForGateway(&gateway), + Labels: common.LabelsForGateway(&gateway), Annotations: map[string]string{ "consul.hashicorp.com/connect-inject": "false", }, @@ -135,7 +135,7 @@ func (g *Gatekeeper) deployment(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayC Weight: 1, PodAffinityTerm: corev1.PodAffinityTerm{ LabelSelector: &metav1.LabelSelector{ - MatchLabels: apigateway.LabelsForGateway(&gateway), + MatchLabels: common.LabelsForGateway(&gateway), }, TopologyKey: "kubernetes.io/hostname", }, diff --git a/control-plane/api-gateway/gatekeeper/gatekeeper.go b/control-plane/api-gateway/gatekeeper/gatekeeper.go index e333b6a8ea..23f96acb3d 100644 --- a/control-plane/api-gateway/gatekeeper/gatekeeper.go +++ b/control-plane/api-gateway/gatekeeper/gatekeeper.go @@ -8,7 +8,7 @@ import ( "fmt" "github.com/go-logr/logr" - apigateway "github.com/hashicorp/consul-k8s/control-plane/api-gateway" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" @@ -30,7 +30,7 @@ func New(log logr.Logger, client client.Client) *Gatekeeper { } // Upsert creates or updates the resources for handling routing of network traffic. -func (g *Gatekeeper) Upsert(ctx context.Context, gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config apigateway.HelmConfig) error { +func (g *Gatekeeper) Upsert(ctx context.Context, gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config common.HelmConfig) error { g.Log.Info(fmt.Sprintf("Upsert Gateway Deployment %s/%s", gateway.Namespace, gateway.Name)) if err := g.upsertRole(ctx, gateway, gcc, config); err != nil { @@ -83,7 +83,7 @@ func (g Gatekeeper) namespacedName(gateway gwv1beta1.Gateway) types.NamespacedNa } } -func (g Gatekeeper) serviceAccountName(gateway gwv1beta1.Gateway, config apigateway.HelmConfig) string { +func (g Gatekeeper) serviceAccountName(gateway gwv1beta1.Gateway, config common.HelmConfig) string { if config.AuthMethod == "" { return "" } diff --git a/control-plane/api-gateway/gatekeeper/gatekeeper_test.go b/control-plane/api-gateway/gatekeeper/gatekeeper_test.go index 92a92d66f2..cc19f68d47 100644 --- a/control-plane/api-gateway/gatekeeper/gatekeeper_test.go +++ b/control-plane/api-gateway/gatekeeper/gatekeeper_test.go @@ -9,7 +9,7 @@ import ( "testing" logrtest "github.com/go-logr/logr/testr" - apigateway "github.com/hashicorp/consul-k8s/control-plane/api-gateway" + common "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" @@ -44,7 +44,7 @@ var ( { Name: "Listener 2", Port: 8081, - Protocol: "UDP", + Protocol: "TCP", }, } ) @@ -52,7 +52,7 @@ var ( type testCase struct { gateway gwv1beta1.Gateway gatewayClassConfig v1alpha1.GatewayClassConfig - helmConfig apigateway.HelmConfig + helmConfig common.HelmConfig initialResources resources finalResources resources @@ -85,15 +85,15 @@ func TestUpsert(t *testing.T) { }, Spec: v1alpha1.GatewayClassConfigSpec{ DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: ptrTo(int32(3)), - MaxInstances: ptrTo(int32(3)), - MinInstances: ptrTo(int32(1)), + DefaultInstances: common.PointerTo(int32(3)), + MaxInstances: common.PointerTo(int32(3)), + MinInstances: common.PointerTo(int32(1)), }, CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - ServiceType: (*corev1.ServiceType)(ptrTo("NodePort")), + ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), }, }, - helmConfig: apigateway.HelmConfig{}, + helmConfig: common.HelmConfig{}, initialResources: resources{}, finalResources: resources{ deployments: []*appsv1.Deployment{ @@ -120,15 +120,15 @@ func TestUpsert(t *testing.T) { }, Spec: v1alpha1.GatewayClassConfigSpec{ DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: ptrTo(int32(3)), - MaxInstances: ptrTo(int32(3)), - MinInstances: ptrTo(int32(1)), + DefaultInstances: common.PointerTo(int32(3)), + MaxInstances: common.PointerTo(int32(3)), + MinInstances: common.PointerTo(int32(1)), }, CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - ServiceType: (*corev1.ServiceType)(ptrTo("NodePort")), + ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), }, }, - helmConfig: apigateway.HelmConfig{}, + helmConfig: common.HelmConfig{}, initialResources: resources{}, finalResources: resources{ deployments: []*appsv1.Deployment{ @@ -144,7 +144,7 @@ func TestUpsert(t *testing.T) { }, { Name: "Listener 2", - Protocol: "UDP", + Protocol: "TCP", Port: 8081, }, }, "1"), @@ -168,15 +168,15 @@ func TestUpsert(t *testing.T) { }, Spec: v1alpha1.GatewayClassConfigSpec{ DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: ptrTo(int32(3)), - MaxInstances: ptrTo(int32(3)), - MinInstances: ptrTo(int32(1)), + DefaultInstances: common.PointerTo(int32(3)), + MaxInstances: common.PointerTo(int32(3)), + MinInstances: common.PointerTo(int32(1)), }, CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - ServiceType: (*corev1.ServiceType)(ptrTo("NodePort")), + ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), }, }, - helmConfig: apigateway.HelmConfig{ + helmConfig: common.HelmConfig{ AuthMethod: "method", }, initialResources: resources{}, @@ -196,7 +196,7 @@ func TestUpsert(t *testing.T) { }, { Name: "Listener 2", - Protocol: "UDP", + Protocol: "TCP", Port: 8081, }, }, "1"), @@ -222,15 +222,15 @@ func TestUpsert(t *testing.T) { }, Spec: v1alpha1.GatewayClassConfigSpec{ DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: ptrTo(int32(3)), - MaxInstances: ptrTo(int32(3)), - MinInstances: ptrTo(int32(1)), + DefaultInstances: common.PointerTo(int32(3)), + MaxInstances: common.PointerTo(int32(3)), + MinInstances: common.PointerTo(int32(1)), }, CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - ServiceType: (*corev1.ServiceType)(ptrTo("NodePort")), + ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), }, }, - helmConfig: apigateway.HelmConfig{ + helmConfig: common.HelmConfig{ AuthMethod: "method", }, initialResources: resources{ @@ -269,7 +269,7 @@ func TestUpsert(t *testing.T) { }, { Name: "Listener 2", - Protocol: "UDP", + Protocol: "TCP", Port: 8081, }, }, "2"), @@ -297,15 +297,15 @@ func TestUpsert(t *testing.T) { }, Spec: v1alpha1.GatewayClassConfigSpec{ DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: ptrTo(int32(3)), - MaxInstances: ptrTo(int32(3)), - MinInstances: ptrTo(int32(1)), + DefaultInstances: common.PointerTo(int32(3)), + MaxInstances: common.PointerTo(int32(3)), + MinInstances: common.PointerTo(int32(1)), }, CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - ServiceType: (*corev1.ServiceType)(ptrTo("NodePort")), + ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), }, }, - helmConfig: apigateway.HelmConfig{ + helmConfig: common.HelmConfig{ AuthMethod: "method", }, initialResources: resources{ @@ -324,7 +324,7 @@ func TestUpsert(t *testing.T) { }, { Name: "Listener 2", - Protocol: "UDP", + Protocol: "TCP", Port: 8081, }, }, "1"), @@ -370,15 +370,15 @@ func TestUpsert(t *testing.T) { }, Spec: v1alpha1.GatewayClassConfigSpec{ DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: ptrTo(int32(5)), - MaxInstances: ptrTo(int32(7)), - MinInstances: ptrTo(int32(1)), + DefaultInstances: common.PointerTo(int32(5)), + MaxInstances: common.PointerTo(int32(7)), + MinInstances: common.PointerTo(int32(1)), }, CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - ServiceType: (*corev1.ServiceType)(ptrTo("NodePort")), + ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), }, }, - helmConfig: apigateway.HelmConfig{}, + helmConfig: common.HelmConfig{}, initialResources: resources{ deployments: []*appsv1.Deployment{ configureDeployment(name, namespace, labels, 5, nil, nil, "", "1"), @@ -438,15 +438,15 @@ func TestDelete(t *testing.T) { }, Spec: v1alpha1.GatewayClassConfigSpec{ DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: ptrTo(int32(3)), - MaxInstances: ptrTo(int32(3)), - MinInstances: ptrTo(int32(1)), + DefaultInstances: common.PointerTo(int32(3)), + MaxInstances: common.PointerTo(int32(3)), + MinInstances: common.PointerTo(int32(1)), }, CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - ServiceType: (*corev1.ServiceType)(ptrTo("NodePort")), + ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), }, }, - helmConfig: apigateway.HelmConfig{}, + helmConfig: common.HelmConfig{}, initialResources: resources{ deployments: []*appsv1.Deployment{ configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), @@ -475,15 +475,15 @@ func TestDelete(t *testing.T) { }, Spec: v1alpha1.GatewayClassConfigSpec{ DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: ptrTo(int32(3)), - MaxInstances: ptrTo(int32(3)), - MinInstances: ptrTo(int32(1)), + DefaultInstances: common.PointerTo(int32(3)), + MaxInstances: common.PointerTo(int32(3)), + MinInstances: common.PointerTo(int32(1)), }, CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - ServiceType: (*corev1.ServiceType)(ptrTo("NodePort")), + ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), }, }, - helmConfig: apigateway.HelmConfig{}, + helmConfig: common.HelmConfig{}, initialResources: resources{ deployments: []*appsv1.Deployment{ configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), @@ -498,7 +498,7 @@ func TestDelete(t *testing.T) { }, { Name: "Listener 2", - Protocol: "UDP", + Protocol: "TCP", Port: 8081, }, }, "1"), @@ -528,15 +528,15 @@ func TestDelete(t *testing.T) { }, Spec: v1alpha1.GatewayClassConfigSpec{ DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: ptrTo(int32(3)), - MaxInstances: ptrTo(int32(3)), - MinInstances: ptrTo(int32(1)), + DefaultInstances: common.PointerTo(int32(3)), + MaxInstances: common.PointerTo(int32(3)), + MinInstances: common.PointerTo(int32(1)), }, CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - ServiceType: (*corev1.ServiceType)(ptrTo("NodePort")), + ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), }, }, - helmConfig: apigateway.HelmConfig{ + helmConfig: common.HelmConfig{ AuthMethod: "method", }, initialResources: resources{ @@ -555,7 +555,7 @@ func TestDelete(t *testing.T) { }, { Name: "Listener 2", - Protocol: "UDP", + Protocol: "TCP", Port: 8081, }, }, "1"), @@ -770,8 +770,8 @@ func configureDeployment(name, namespace string, labels map[string]string, repli APIVersion: "gateway.networking.k8s.io/v1beta1", Kind: "Gateway", Name: name, - Controller: ptrTo(true), - BlockOwnerDeletion: ptrTo(true), + Controller: common.PointerTo(true), + BlockOwnerDeletion: common.PointerTo(true), }, }, }, @@ -828,8 +828,8 @@ func configureRole(name, namespace string, labels map[string]string, resourceVer APIVersion: "gateway.networking.k8s.io/v1beta1", Kind: "Gateway", Name: name, - Controller: ptrTo(true), - BlockOwnerDeletion: ptrTo(true), + Controller: common.PointerTo(true), + BlockOwnerDeletion: common.PointerTo(true), }, }, }, @@ -854,8 +854,8 @@ func configureService(name, namespace string, labels, annotations map[string]str APIVersion: "gateway.networking.k8s.io/v1beta1", Kind: "Gateway", Name: name, - Controller: ptrTo(true), - BlockOwnerDeletion: ptrTo(true), + Controller: common.PointerTo(true), + BlockOwnerDeletion: common.PointerTo(true), }, }, }, @@ -883,14 +883,10 @@ func configureServiceAccount(name, namespace string, labels map[string]string, r APIVersion: "gateway.networking.k8s.io/v1beta1", Kind: "Gateway", Name: name, - Controller: ptrTo(true), - BlockOwnerDeletion: ptrTo(true), + Controller: common.PointerTo(true), + BlockOwnerDeletion: common.PointerTo(true), }, }, }, } } - -func ptrTo[T any](t T) *T { - return &t -} diff --git a/control-plane/api-gateway/gatekeeper/init.go b/control-plane/api-gateway/gatekeeper/init.go index 4f53769c11..831380cb52 100644 --- a/control-plane/api-gateway/gatekeeper/init.go +++ b/control-plane/api-gateway/gatekeeper/init.go @@ -11,7 +11,7 @@ import ( corev1 "k8s.io/api/core/v1" - apigateway "github.com/hashicorp/consul-k8s/control-plane/api-gateway" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul-k8s/control-plane/namespaces" "k8s.io/utils/pointer" ) @@ -33,7 +33,7 @@ type initContainerCommandData struct { // containerInit returns the init container spec for connect-init that polls for the service and the connect proxy service to be registered // so that it can save the proxy service id to the shared volume and boostrap Envoy with the proxy-id. -func initContainer(config apigateway.HelmConfig, name, namespace string) (corev1.Container, error) { +func initContainer(config common.HelmConfig, name, namespace string) (corev1.Container, error) { data := initContainerCommandData{ AuthMethod: config.AuthMethod, LogLevel: config.LogLevel, diff --git a/control-plane/api-gateway/gatekeeper/role.go b/control-plane/api-gateway/gatekeeper/role.go index 604c503be5..688c425f5f 100644 --- a/control-plane/api-gateway/gatekeeper/role.go +++ b/control-plane/api-gateway/gatekeeper/role.go @@ -10,7 +10,7 @@ import ( "k8s.io/apimachinery/pkg/types" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - apigateway "github.com/hashicorp/consul-k8s/control-plane/api-gateway" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" rbac "k8s.io/api/rbac/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" @@ -18,7 +18,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" ) -func (g *Gatekeeper) upsertRole(ctx context.Context, gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config apigateway.HelmConfig) error { +func (g *Gatekeeper) upsertRole(ctx context.Context, gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config common.HelmConfig) error { if config.AuthMethod == "" { return g.deleteRole(ctx, types.NamespacedName{Namespace: gateway.Namespace, Name: gateway.Name}) } @@ -74,7 +74,7 @@ func (g *Gatekeeper) role(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassCo ObjectMeta: metav1.ObjectMeta{ Name: gateway.Name, Namespace: gateway.Namespace, - Labels: apigateway.LabelsForGateway(&gateway), + Labels: common.LabelsForGateway(&gateway), }, Rules: []rbac.PolicyRule{}, } diff --git a/control-plane/api-gateway/gatekeeper/service.go b/control-plane/api-gateway/gatekeeper/service.go index 1191879390..149dc067e3 100644 --- a/control-plane/api-gateway/gatekeeper/service.go +++ b/control-plane/api-gateway/gatekeeper/service.go @@ -6,10 +6,10 @@ package gatekeeper import ( "context" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "k8s.io/apimachinery/pkg/types" - apigateway "github.com/hashicorp/consul-k8s/control-plane/api-gateway" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" k8serrors "k8s.io/apimachinery/pkg/api/errors" @@ -26,7 +26,7 @@ var ( } ) -func (g *Gatekeeper) upsertService(ctx context.Context, gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config apigateway.HelmConfig) error { +func (g *Gatekeeper) upsertService(ctx context.Context, gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config common.HelmConfig) error { if gcc.Spec.ServiceType == nil { return g.deleteService(ctx, types.NamespacedName{Namespace: gateway.Namespace, Name: gateway.Name}) } @@ -68,8 +68,9 @@ func (g *Gatekeeper) service(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClas ports := []corev1.ServicePort{} for _, listener := range gateway.Spec.Listeners { ports = append(ports, corev1.ServicePort{ - Name: string(listener.Name), - Protocol: corev1.Protocol(listener.Protocol), + Name: string(listener.Name), + // only TCP-based services are supported for now + Protocol: corev1.ProtocolTCP, Port: int32(listener.Port), }) } @@ -90,11 +91,11 @@ func (g *Gatekeeper) service(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClas ObjectMeta: metav1.ObjectMeta{ Name: gateway.Name, Namespace: gateway.Namespace, - Labels: apigateway.LabelsForGateway(&gateway), + Labels: common.LabelsForGateway(&gateway), Annotations: annotations, }, Spec: corev1.ServiceSpec{ - Selector: apigateway.LabelsForGateway(&gateway), + Selector: common.LabelsForGateway(&gateway), Type: *gcc.Spec.ServiceType, Ports: ports, }, diff --git a/control-plane/api-gateway/gatekeeper/serviceaccount.go b/control-plane/api-gateway/gatekeeper/serviceaccount.go index 207f4485c4..864047fb1b 100644 --- a/control-plane/api-gateway/gatekeeper/serviceaccount.go +++ b/control-plane/api-gateway/gatekeeper/serviceaccount.go @@ -7,17 +7,17 @@ import ( "context" "errors" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "k8s.io/apimachinery/pkg/types" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - apigateway "github.com/hashicorp/consul-k8s/control-plane/api-gateway" corev1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ctrl "sigs.k8s.io/controller-runtime" ) -func (g *Gatekeeper) upsertServiceAccount(ctx context.Context, gateway gwv1beta1.Gateway, config apigateway.HelmConfig) error { +func (g *Gatekeeper) upsertServiceAccount(ctx context.Context, gateway gwv1beta1.Gateway, config common.HelmConfig) error { if config.AuthMethod == "" { return g.deleteServiceAccount(ctx, types.NamespacedName{Namespace: gateway.Namespace, Name: gateway.Name}) } @@ -74,7 +74,7 @@ func (g *Gatekeeper) serviceAccount(gateway gwv1beta1.Gateway) *corev1.ServiceAc ObjectMeta: metav1.ObjectMeta{ Name: gateway.Name, Namespace: gateway.Namespace, - Labels: apigateway.LabelsForGateway(&gateway), + Labels: common.LabelsForGateway(&gateway), }, } } diff --git a/control-plane/api-gateway/translation/config_entry_translation.go b/control-plane/api-gateway/translation/config_entry_translation.go deleted file mode 100644 index 9501859f8b..0000000000 --- a/control-plane/api-gateway/translation/config_entry_translation.go +++ /dev/null @@ -1,516 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -// Package translation handles translating resources between different types -package translation - -import ( - "strings" - - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul-k8s/control-plane/namespaces" - "github.com/hashicorp/consul/api" - capi "github.com/hashicorp/consul/api" -) - -const ( - metaKeyManagedBy = "managed-by" - metaValueManagedBy = "consul-k8s-gateway-controller" - metaKeyKubeNS = "k8s-namespace" - metaKeyKubeServiceName = "k8s-service-name" - metaKeyKubeName = "k8s-name" - - // AnnotationGateway is the annotation used to override the gateway name. - AnnotationGateway = "consul.hashicorp.com/gateway" - // AnnotationHTTPRoute is the annotation used to override the http route name. - AnnotationHTTPRoute = "consul.hashicorp.com/http-route" - // AnnotationTCPRoute is the annotation used to override the tcp route name. - AnnotationTCPRoute = "consul.hashicorp.com/tcp-route" - // AnnotationInlineCertificate is the annotation used to override the inline certificate name. - AnnotationInlineCertificate = "consul.hashicorp.com/inline-certificate" -) - -func translateListenerProtocol[T ~string](protocol T) string { - return strings.ToLower(string(protocol)) -} - -// K8sToConsulTranslator handles translating K8s resources into Consul config entries. -type K8sToConsulTranslator struct { - EnableConsulNamespaces bool - ConsulDestNamespace string - EnableK8sMirroring bool - MirroringPrefix string - ConsulPartition string -} - -// GatewayToAPIGateway translates a kuberenetes API gateway into a Consul APIGateway Config Entry. -func (t K8sToConsulTranslator) GatewayToAPIGateway(k8sGW gwv1beta1.Gateway, certs map[types.NamespacedName]api.ResourceReference) capi.APIGatewayConfigEntry { - listeners := make([]capi.APIGatewayListener, 0, len(k8sGW.Spec.Listeners)) - for _, listener := range k8sGW.Spec.Listeners { - var certificates []capi.ResourceReference - if listener.TLS != nil { - certificates = make([]capi.ResourceReference, 0, len(listener.TLS.CertificateRefs)) - for _, certificate := range listener.TLS.CertificateRefs { - k8sNS := "" - if certificate.Namespace != nil { - k8sNS = string(*certificate.Namespace) - } - nsn := types.NamespacedName{Name: string(certificate.Name), Namespace: k8sNS} - certRef, ok := certs[nsn] - if !ok { - // we don't have a ref for this certificate in consul - // drop the ref from the created gateway - continue - } - c := capi.ResourceReference{ - Kind: capi.InlineCertificate, - Name: certRef.Name, - Partition: certRef.Partition, - Namespace: certRef.Namespace, - } - certificates = append(certificates, c) - } - } - hostname := "" - if listener.Hostname != nil { - hostname = string(*listener.Hostname) - } - l := capi.APIGatewayListener{ - Name: string(listener.Name), - Hostname: hostname, - Port: int(listener.Port), - Protocol: translateListenerProtocol(listener.Protocol), - TLS: capi.APIGatewayTLSConfiguration{ - Certificates: certificates, - }, - } - - listeners = append(listeners, l) - } - gwName := k8sGW.Name - - if gwNameFromAnnotation, ok := k8sGW.Annotations[AnnotationGateway]; ok && gwNameFromAnnotation != "" && !strings.Contains(gwNameFromAnnotation, ",") { - gwName = gwNameFromAnnotation - } - - return capi.APIGatewayConfigEntry{ - Kind: capi.APIGateway, - Name: gwName, - Meta: map[string]string{ - metaKeyManagedBy: metaValueManagedBy, - metaKeyKubeNS: k8sGW.GetObjectMeta().GetNamespace(), - metaKeyKubeServiceName: k8sGW.GetObjectMeta().GetName(), - metaKeyKubeName: k8sGW.GetObjectMeta().GetName(), - }, - Listeners: listeners, - Partition: t.ConsulPartition, - Namespace: t.getConsulNamespace(k8sGW.GetObjectMeta().GetNamespace()), - } -} - -func (t K8sToConsulTranslator) ReferenceForGateway(k8sGW *gwv1beta1.Gateway) api.ResourceReference { - gwName := k8sGW.Name - if gwNameFromAnnotation, ok := k8sGW.Annotations[AnnotationGateway]; ok && gwNameFromAnnotation != "" && !strings.Contains(gwNameFromAnnotation, ",") { - gwName = gwNameFromAnnotation - } - return api.ResourceReference{ - Kind: api.APIGateway, - Name: gwName, - Namespace: t.getConsulNamespace(k8sGW.GetObjectMeta().GetNamespace()), - } -} - -// HTTPRouteToHTTPRoute translates a k8s HTTPRoute into a Consul HTTPRoute Config Entry. -func (t K8sToConsulTranslator) HTTPRouteToHTTPRoute(k8sHTTPRoute *gwv1beta1.HTTPRoute, parentRefs map[types.NamespacedName]api.ResourceReference, k8sServices map[types.NamespacedName]api.CatalogService, meshServices map[types.NamespacedName]v1alpha1.MeshService) *capi.HTTPRouteConfigEntry { - routeName := k8sHTTPRoute.Name - if routeNameFromAnnotation, ok := k8sHTTPRoute.Annotations[AnnotationHTTPRoute]; ok && routeNameFromAnnotation != "" && !strings.Contains(routeNameFromAnnotation, ",") { - routeName = routeNameFromAnnotation - } - - consulHTTPRoute := &capi.HTTPRouteConfigEntry{ - Kind: capi.HTTPRoute, - Name: routeName, - Meta: map[string]string{ - metaKeyManagedBy: metaValueManagedBy, - metaKeyKubeNS: k8sHTTPRoute.GetObjectMeta().GetNamespace(), - metaKeyKubeServiceName: k8sHTTPRoute.GetObjectMeta().GetName(), - metaKeyKubeName: k8sHTTPRoute.GetObjectMeta().GetName(), - }, - Partition: t.ConsulPartition, - - Namespace: t.getConsulNamespace(k8sHTTPRoute.GetObjectMeta().GetNamespace()), - } - - // translate hostnames - hostnames := make([]string, 0, len(k8sHTTPRoute.Spec.Hostnames)) - for _, k8Hostname := range k8sHTTPRoute.Spec.Hostnames { - hostnames = append(hostnames, string(k8Hostname)) - } - consulHTTPRoute.Hostnames = hostnames - - // translate parent refs - consulHTTPRoute.Parents = translateRouteParentRefs(k8sHTTPRoute.Spec.CommonRouteSpec.ParentRefs, parentRefs) - - // translate rules - consulHTTPRoute.Rules = t.translateHTTPRouteRules(k8sHTTPRoute.Namespace, k8sHTTPRoute.Spec.Rules, k8sServices, meshServices) - - return consulHTTPRoute -} - -func (t K8sToConsulTranslator) ReferenceForHTTPRoute(k8sHTTPRoute *gwv1beta1.HTTPRoute) api.ResourceReference { - routeName := k8sHTTPRoute.Name - if routeNameFromAnnotation, ok := k8sHTTPRoute.Annotations[AnnotationHTTPRoute]; ok && routeNameFromAnnotation != "" && !strings.Contains(routeNameFromAnnotation, ",") { - routeName = routeNameFromAnnotation - } - return api.ResourceReference{ - Kind: api.HTTPRoute, - Name: routeName, - Namespace: t.getConsulNamespace(k8sHTTPRoute.GetObjectMeta().GetNamespace()), - } -} - -// translates parent refs for Routes into Consul Resource References. -func translateRouteParentRefs(k8sParentRefs []gwv1beta1.ParentReference, parentRefs map[types.NamespacedName]api.ResourceReference) []capi.ResourceReference { - parents := make([]capi.ResourceReference, 0, len(k8sParentRefs)) - for _, k8sParentRef := range k8sParentRefs { - namespace := "" - if k8sParentRef.Namespace != nil { - namespace = string(*k8sParentRef.Namespace) - } - parentRef, ok := parentRefs[types.NamespacedName{Name: string(k8sParentRef.Name), Namespace: namespace}] - if !(ok && isRefAPIGateway(k8sParentRef)) { - // we drop any parent refs that consul does not know about - continue - } - sectionName := "" - if k8sParentRef.SectionName != nil { - sectionName = string(*k8sParentRef.SectionName) - } - ref := capi.ResourceReference{ - Kind: capi.APIGateway, // Will this ever not be a gateway? is that something we need to handle? - Name: parentRef.Name, - SectionName: sectionName, - Partition: parentRef.Partition, - Namespace: parentRef.Namespace, - } - parents = append(parents, ref) - } - return parents -} - -// isRefAPIGateway checks if the parent resource is an APIGateway. -func isRefAPIGateway(ref gwv1beta1.ParentReference) bool { - return ref.Kind != nil && *ref.Kind == gwv1beta1.Kind("Gateway") || ref.Group != nil && string(*ref.Group) == gwv1beta1.GroupName -} - -// translate the rules portion of a HTTPRoute. -func (t K8sToConsulTranslator) translateHTTPRouteRules(namespace string, k8sRules []gwv1beta1.HTTPRouteRule, k8sServices map[types.NamespacedName]api.CatalogService, meshServices map[types.NamespacedName]v1alpha1.MeshService) []capi.HTTPRouteRule { - rules := make([]capi.HTTPRouteRule, 0, len(k8sRules)) - for _, k8sRule := range k8sRules { - rule := capi.HTTPRouteRule{} - // translate matches - rule.Matches = translateHTTPMatches(k8sRule.Matches) - - // translate filters - rule.Filters = translateHTTPFilters(k8sRule.Filters) - - // translate services - rule.Services = t.translateHTTPServices(namespace, k8sRule.BackendRefs, k8sServices, meshServices) - - rules = append(rules, rule) - } - return rules -} - -var headerMatchTypeTranslation = map[gwv1beta1.HeaderMatchType]capi.HTTPHeaderMatchType{ - gwv1beta1.HeaderMatchExact: capi.HTTPHeaderMatchExact, - gwv1beta1.HeaderMatchRegularExpression: capi.HTTPHeaderMatchRegularExpression, -} - -var headerPathMatchTypeTranslation = map[gwv1beta1.PathMatchType]capi.HTTPPathMatchType{ - gwv1beta1.PathMatchExact: capi.HTTPPathMatchExact, - gwv1beta1.PathMatchPathPrefix: capi.HTTPPathMatchPrefix, - gwv1beta1.PathMatchRegularExpression: capi.HTTPPathMatchRegularExpression, -} - -var queryMatchTypeTranslation = map[gwv1beta1.QueryParamMatchType]capi.HTTPQueryMatchType{ - gwv1beta1.QueryParamMatchExact: capi.HTTPQueryMatchExact, - gwv1beta1.QueryParamMatchRegularExpression: capi.HTTPQueryMatchRegularExpression, -} - -// translate the http matches section. -func translateHTTPMatches(k8sMatches []gwv1beta1.HTTPRouteMatch) []capi.HTTPMatch { - matches := make([]capi.HTTPMatch, 0, len(k8sMatches)) - for _, k8sMatch := range k8sMatches { - // translate header matches - headers := make([]capi.HTTPHeaderMatch, 0, len(k8sMatch.Headers)) - for _, k8sHeader := range k8sMatch.Headers { - header := capi.HTTPHeaderMatch{ - Name: string(k8sHeader.Name), - Value: k8sHeader.Value, - } - if k8sHeader.Type != nil { - header.Match = headerMatchTypeTranslation[*k8sHeader.Type] - } - headers = append(headers, header) - } - - // translate query matches - queries := make([]capi.HTTPQueryMatch, 0, len(k8sMatch.QueryParams)) - for _, k8sQuery := range k8sMatch.QueryParams { - query := capi.HTTPQueryMatch{ - Name: k8sQuery.Name, - Value: k8sQuery.Value, - } - if k8sQuery.Type != nil { - query.Match = queryMatchTypeTranslation[*k8sQuery.Type] - } - queries = append(queries, query) - } - - match := capi.HTTPMatch{ - Headers: headers, - Query: queries, - } - if k8sMatch.Method != nil { - match.Method = capi.HTTPMatchMethod(*k8sMatch.Method) - } - if k8sMatch.Path != nil { - if k8sMatch.Path.Type != nil { - match.Path.Match = headerPathMatchTypeTranslation[*k8sMatch.Path.Type] - } - if k8sMatch.Path.Value != nil { - match.Path.Value = string(*k8sMatch.Path.Value) - } - } - matches = append(matches, match) - } - return matches -} - -// translate the http filters section. -func translateHTTPFilters(k8sFilters []gwv1beta1.HTTPRouteFilter) capi.HTTPFilters { - add := make(map[string]string) - set := make(map[string]string) - remove := make([]string, 0) - var urlRewrite *capi.URLRewrite - for _, k8sFilter := range k8sFilters { - for _, adder := range k8sFilter.RequestHeaderModifier.Add { - add[string(adder.Name)] = adder.Value - } - - for _, setter := range k8sFilter.RequestHeaderModifier.Set { - set[string(setter.Name)] = setter.Value - } - - remove = append(remove, k8sFilter.RequestHeaderModifier.Remove...) - - // we drop any path rewrites that are not prefix matches as we don't support those - if k8sFilter.URLRewrite != nil && k8sFilter.URLRewrite.Path.Type == gwv1beta1.PrefixMatchHTTPPathModifier { - urlRewrite = &capi.URLRewrite{Path: *k8sFilter.URLRewrite.Path.ReplacePrefixMatch} - } - - } - filter := capi.HTTPFilters{ - Headers: []capi.HTTPHeaderFilter{ - { - Add: add, - Remove: remove, - Set: set, - }, - }, - URLRewrite: urlRewrite, - } - - return filter -} - -// translate the backendrefs into services. -func (t K8sToConsulTranslator) translateHTTPServices(namespace string, k8sBackendRefs []gwv1beta1.HTTPBackendRef, k8sServices map[types.NamespacedName]api.CatalogService, meshServices map[types.NamespacedName]v1alpha1.MeshService) []capi.HTTPService { - services := make([]capi.HTTPService, 0, len(k8sBackendRefs)) - - for _, k8sRef := range k8sBackendRefs { - backendRef := k8sRef.BackendObjectReference - - nsn := types.NamespacedName{ - Name: string(backendRef.Name), - Namespace: valueOr(backendRef.Namespace, namespace), - } - - isServiceRef := nilOrEqual(backendRef.Group, "") && nilOrEqual(backendRef.Kind, "Service") - isMeshServiceRef := derefEqual(backendRef.Group, v1alpha1.ConsulHashicorpGroup) && derefEqual(backendRef.Kind, v1alpha1.MeshServiceKind) - - k8sService, k8sServiceFound := k8sServices[nsn] - meshService, meshServiceFound := meshServices[nsn] - - if isServiceRef && k8sServiceFound { - service := capi.HTTPService{ - Name: strings.TrimSuffix(k8sService.ServiceName, "-sidecar-proxy"), - Namespace: t.getConsulNamespace(k8sService.Namespace), - Filters: translateHTTPFilters(k8sRef.Filters), - } - if k8sRef.Weight != nil { - service.Weight = int(*k8sRef.Weight) - } - services = append(services, service) - } else if isMeshServiceRef && meshServiceFound { - service := capi.HTTPService{ - Name: meshService.Spec.Name, - Namespace: t.getConsulNamespace(meshService.Namespace), - Filters: translateHTTPFilters(k8sRef.Filters), - } - if k8sRef.Weight != nil { - service.Weight = int(*k8sRef.Weight) - } - services = append(services, service) - } - } - - return services -} - -// TCPRouteToTCPRoute translates a Kuberenetes TCPRoute into a Consul TCPRoute Config Entry. -func (t K8sToConsulTranslator) TCPRouteToTCPRoute(k8sRoute *gwv1alpha2.TCPRoute, parentRefs map[types.NamespacedName]api.ResourceReference, k8sServices map[types.NamespacedName]api.CatalogService, meshServices map[types.NamespacedName]v1alpha1.MeshService) *capi.TCPRouteConfigEntry { - routeName := k8sRoute.Name - if routeNameFromAnnotation, ok := k8sRoute.Annotations[AnnotationTCPRoute]; ok && routeNameFromAnnotation != "" && !strings.Contains(routeNameFromAnnotation, ",") { - routeName = routeNameFromAnnotation - } - - consulRoute := &capi.TCPRouteConfigEntry{ - Kind: capi.TCPRoute, - Name: routeName, - Meta: map[string]string{ - metaKeyManagedBy: metaValueManagedBy, - metaKeyKubeNS: k8sRoute.GetObjectMeta().GetNamespace(), - metaKeyKubeServiceName: k8sRoute.GetObjectMeta().GetName(), - metaKeyKubeName: k8sRoute.GetObjectMeta().GetName(), - }, - Partition: t.ConsulPartition, - - Namespace: t.getConsulNamespace(k8sRoute.GetObjectMeta().GetNamespace()), - } - - // translate parent refs - consulRoute.Parents = translateRouteParentRefs(k8sRoute.Spec.CommonRouteSpec.ParentRefs, parentRefs) - - // translate the services - consulRoute.Services = make([]capi.TCPService, 0) - for _, rule := range k8sRoute.Spec.Rules { - for _, k8sref := range rule.BackendRefs { - backendRef := k8sref.BackendObjectReference - - nsn := types.NamespacedName{ - Name: string(backendRef.Name), - Namespace: valueOr(backendRef.Namespace, k8sRoute.Namespace), - } - - isServiceRef := nilOrEqual(backendRef.Group, "") && nilOrEqual(backendRef.Kind, "Service") - isMeshServiceRef := derefEqual(backendRef.Group, v1alpha1.ConsulHashicorpGroup) && derefEqual(backendRef.Kind, v1alpha1.MeshServiceKind) - - k8sService, k8sServiceFound := k8sServices[nsn] - meshService, meshServiceFound := meshServices[nsn] - - if isServiceRef && k8sServiceFound { - service := capi.TCPService{ - Name: strings.TrimSuffix(k8sService.ServiceName, "-sidecar-proxy"), - Namespace: t.getConsulNamespace(k8sService.Namespace), - } - consulRoute.Services = append(consulRoute.Services, service) - } else if isMeshServiceRef && meshServiceFound { - service := capi.TCPService{ - Name: meshService.Spec.Name, - Namespace: t.getConsulNamespace(meshService.Namespace), - } - consulRoute.Services = append(consulRoute.Services, service) - } - } - } - - return consulRoute -} - -func (t K8sToConsulTranslator) ReferenceForTCPRoute(k8sTCPRoute *gwv1alpha2.TCPRoute) api.ResourceReference { - routeName := k8sTCPRoute.Name - if routeNameFromAnnotation, ok := k8sTCPRoute.Annotations[AnnotationTCPRoute]; ok && routeNameFromAnnotation != "" && !strings.Contains(routeNameFromAnnotation, ",") { - routeName = routeNameFromAnnotation - } - return api.ResourceReference{ - Kind: api.TCPRoute, - Name: routeName, - Namespace: t.getConsulNamespace(k8sTCPRoute.GetObjectMeta().GetNamespace()), - } -} - -// SecretToInlineCertificate translates a Kuberenetes Secret into a Consul Inline Certificate Config Entry. -func (t K8sToConsulTranslator) SecretToInlineCertificate(k8sSecret corev1.Secret) capi.InlineCertificateConfigEntry { - namespace := t.getConsulNamespace(k8sSecret.GetObjectMeta().GetNamespace()) - return capi.InlineCertificateConfigEntry{ - Kind: capi.InlineCertificate, - Namespace: namespace, - Name: k8sSecret.Name, - Certificate: k8sSecret.StringData[corev1.TLSCertKey], - PrivateKey: k8sSecret.StringData[corev1.TLSPrivateKeyKey], - Meta: map[string]string{ - metaKeyManagedBy: metaValueManagedBy, - metaKeyKubeNS: namespace, - metaKeyKubeServiceName: string(k8sSecret.Name), - metaKeyKubeName: string(k8sSecret.Name), - }, - } -} - -func (t K8sToConsulTranslator) ReferenceForSecret(k8sSecret corev1.Secret) api.ResourceReference { - return api.ResourceReference{ - Kind: api.InlineCertificate, - Name: k8sSecret.Name, - Namespace: t.getConsulNamespace(k8sSecret.GetObjectMeta().GetNamespace()), - } -} - -func EntryToNamespacedName(entry capi.ConfigEntry) types.NamespacedName { - meta := entry.GetMeta() - return types.NamespacedName{ - Name: meta[metaKeyKubeName], - Namespace: meta[metaKeyKubeNS], - } -} - -func (t K8sToConsulTranslator) getConsulNamespace(k8sNS string) string { - return namespaces.ConsulNamespace(k8sNS, t.EnableK8sMirroring, t.ConsulDestNamespace, t.EnableK8sMirroring, t.MirroringPrefix) -} - -func EntryToReference(entry capi.ConfigEntry) capi.ResourceReference { - return capi.ResourceReference{ - Kind: entry.GetKind(), - Name: entry.GetName(), - Partition: entry.GetPartition(), - Namespace: entry.GetNamespace(), - } -} - -func ptrTo[T any](v T) *T { - return &v -} - -func derefEqual[T ~string](v *T, check string) bool { - if v == nil { - return false - } - return string(*v) == check -} - -func nilOrEqual[T ~string](v *T, check string) bool { - return v == nil || string(*v) == check -} - -func valueOr[T ~string](v *T, fallback string) string { - if v == nil { - return fallback - } - return string(*v) -} diff --git a/control-plane/api-gateway/translation/k8s_cache_translation.go b/control-plane/api-gateway/translation/k8s_cache_translation.go deleted file mode 100644 index 88c0eca3b9..0000000000 --- a/control-plane/api-gateway/translation/k8s_cache_translation.go +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package translation - -import ( - "context" - - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - - "github.com/hashicorp/consul/api" -) - -type TranslatorFn func(api.ConfigEntry) []types.NamespacedName - -type secretTransfomer func(context.Context) func(client.Object) []reconcile.Request - -type resourceGetter interface { - Get(api.ResourceReference) api.ConfigEntry -} - -// ConsulToNamespaceNameTranslator handles translating consul config entries to k8s namespaced names. -type ConsulToNamespaceNameTranslator struct { - cache resourceGetter -} - -// NewConsulToNamespaceNameTranslator creates an instance of the ConsulToNSNTranslator. -func NewConsulToNamespaceNameTranslator(cache resourceGetter) ConsulToNamespaceNameTranslator { - return ConsulToNamespaceNameTranslator{cache: cache} -} - -// BuildConsulGatewayTranslator creates a slice k8s types.NamespacedName from the meta fields of the api gateway config entry. -func (c ConsulToNamespaceNameTranslator) BuildConsulGatewayTranslator(ctx context.Context) TranslatorFn { - return func(config api.ConfigEntry) []types.NamespacedName { - meta, ok := metaToK8sNamespacedName(config) - if !ok { - return nil - } - - return []types.NamespacedName{meta} - } -} - -// BuildConsulHTTPRouteTranslator creates a slice of k8s types.NamespacedName from the meta fields of the http route parent refs. -func (c ConsulToNamespaceNameTranslator) BuildConsulHTTPRouteTranslator(ctx context.Context) TranslatorFn { - return func(config api.ConfigEntry) []types.NamespacedName { - route, ok := config.(*api.HTTPRouteConfigEntry) - if !ok { - return nil - } - - return consulRefsToNSN(c.cache, route.Parents) - } -} - -// BuildConsulTCPRouteTranslator creates a slice of k8s types.NamespacedName from the meta fields of the tcp route parent refs. -func (c ConsulToNamespaceNameTranslator) BuildConsulTCPRouteTranslator(ctx context.Context) TranslatorFn { - return func(config api.ConfigEntry) []types.NamespacedName { - route, ok := config.(*api.TCPRouteConfigEntry) - if !ok { - return nil - } - - return consulRefsToNSN(c.cache, route.Parents) - } -} - -// BuildConsulInlineCertificateTranslator creates a slice of k8s types.NamespacedName from the meta fields of the secret. It does this -// by using a secret transformer function to get a list of reconcile requests from k8s for the given secret and then converts -// those requests to the slice of NamespaceName. -func (c ConsulToNamespaceNameTranslator) BuildConsulInlineCertificateTranslator(ctx context.Context, secretTransformer secretTransfomer) TranslatorFn { - return func(config api.ConfigEntry) []types.NamespacedName { - meta, ok := metaToK8sNamespacedName(config) - if !ok { - return nil - } - - return requestsToRefs(secretTransformer(ctx)(&corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: meta.Name, - Namespace: meta.Namespace, - }, - })) - } -} - -func metaToK8sNamespacedName(config api.ConfigEntry) (types.NamespacedName, bool) { - meta := config.GetMeta() - - namespace, ok := meta[metaKeyKubeNS] - if !ok { - return types.NamespacedName{}, false - } - - name, ok := meta[metaKeyKubeName] - if !ok { - return types.NamespacedName{}, false - } - - return types.NamespacedName{ - Namespace: namespace, - Name: name, - }, true -} - -func consulRefsToNSN(cache resourceGetter, refs []api.ResourceReference) []types.NamespacedName { - nsnSet := make(map[types.NamespacedName]struct{}) - - for _, ref := range refs { - if parent := cache.Get(ref); parent != nil { - if k8sNSN, ok := metaToK8sNamespacedName(parent); ok { - nsnSet[k8sNSN] = struct{}{} - } - } - } - nsns := make([]types.NamespacedName, 0, len(nsnSet)) - - for nsn := range nsnSet { - nsns = append(nsns, nsn) - } - return nsns -} - -func requestsToRefs(objects []reconcile.Request) []types.NamespacedName { - var refs []types.NamespacedName - for _, object := range objects { - refs = append(refs, object.NamespacedName) - } - return refs -} diff --git a/control-plane/api-gateway/translation/k8s_cache_translation_test.go b/control-plane/api-gateway/translation/k8s_cache_translation_test.go deleted file mode 100644 index 52f2ca0eb3..0000000000 --- a/control-plane/api-gateway/translation/k8s_cache_translation_test.go +++ /dev/null @@ -1,460 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package translation - -import ( - "context" - "sort" - "testing" - - "github.com/google/go-cmp/cmp" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - - "github.com/hashicorp/consul/api" -) - -func Test_ConsulToNamespaceNameTranslator_TranslateConsulGateway(t *testing.T) { - t.Parallel() - type args struct { - config *api.APIGatewayConfigEntry - } - tests := []struct { - name string - args args - want []types.NamespacedName - }{ - { - name: "when name and namespace are set", - args: args{ - config: &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "api-gw", - Meta: map[string]string{ - metaKeyKubeNS: "my-ns", - metaKeyKubeName: "api-gw-name", - }, - }, - }, - want: []types.NamespacedName{ - { - Namespace: "my-ns", - Name: "api-gw-name", - }, - }, - }, - { - name: "when name is not set and namespace is set", - args: args{ - config: &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "api-gw", - Meta: map[string]string{ - metaKeyKubeNS: "my-ns", - }, - }, - }, - want: nil, - }, - { - name: "when name is set and namespace is not set", - args: args{ - config: &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "api-gw", - Meta: map[string]string{ - metaKeyKubeName: "api-gw-name", - }, - }, - }, - want: nil, - }, - { - name: "when both name and namespace are not set", - args: args{ - config: &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "api-gw", - Meta: map[string]string{}, - }, - }, - want: nil, - }, - } - - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - translator := ConsulToNamespaceNameTranslator{} - fn := translator.BuildConsulGatewayTranslator(context.Background()) - got := fn(tt.args.config) - if diff := cmp.Diff(got, tt.want, sortTransformer()); diff != "" { - t.Errorf("ConsulToNSNTranslator.TranslateConsulGateway() mismatch (-want +got):\n%s", diff) - } - }) - } -} - -func TestConsulToNamespaceNameTranslator_TranslateConsulHTTPRoute(t *testing.T) { - t.Parallel() - type fields struct { - cache resourceGetter - } - tests := []struct { - name string - fields fields - parentRefs []api.ResourceReference - want []types.NamespacedName - }{ - { - name: "all refs in cache", - fields: fields{ - cache: buildMockCache(map[api.ResourceReference]api.ConfigEntry{ - { - Kind: api.APIGateway, - Name: "api-gw-1", - Namespace: "ns", - }: api.ConfigEntry(&api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "api-gw-1", - Meta: map[string]string{ - metaKeyKubeNS: "ns", - metaKeyKubeName: "api-gw-1", - }, - Namespace: "ns", - }), - { - Kind: api.APIGateway, - Name: "api-gw-2", - Namespace: "ns", - }: api.ConfigEntry(&api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "api-gw-2", - Meta: map[string]string{ - metaKeyKubeNS: "ns", - metaKeyKubeName: "api-gw-2", - }, - Namespace: "ns", - }), - }), - }, - parentRefs: []api.ResourceReference{ - { - Kind: api.APIGateway, - Name: "api-gw-1", - Namespace: "ns", - }, - - { - Kind: api.APIGateway, - Name: "api-gw-2", - Namespace: "ns", - }, - }, - want: []types.NamespacedName{ - { - Namespace: "ns", - Name: "api-gw-1", - }, - { - Namespace: "ns", - Name: "api-gw-2", - }, - }, - }, - { - name: "some refs not in cache", - fields: fields{ - cache: buildMockCache(map[api.ResourceReference]api.ConfigEntry{ - { - Kind: api.APIGateway, - Name: "api-gw-1", - Namespace: "ns", - }: api.ConfigEntry(&api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "api-gw-1", - Meta: map[string]string{ - metaKeyKubeNS: "ns", - metaKeyKubeName: "api-gw-1", - }, - Namespace: "ns", - }), - }), - }, - parentRefs: []api.ResourceReference{ - { - Kind: api.APIGateway, - Name: "api-gw-1", - Namespace: "ns", - }, - { - Kind: api.APIGateway, - Name: "api-gw-2", - Namespace: "ns", - }, - }, - want: []types.NamespacedName{ - { - Namespace: "ns", - Name: "api-gw-1", - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := ConsulToNamespaceNameTranslator{ - cache: tt.fields.cache, - } - config := &api.HTTPRouteConfigEntry{ - Parents: tt.parentRefs, - } - got := c.BuildConsulHTTPRouteTranslator(context.Background())(config) - if diff := cmp.Diff(got, tt.want, sortTransformer()); diff != "" { - t.Errorf("ConsulToNSNTranslator.TranslateConsulHTTPRoute() mismatch (-want +got):\n%s", diff) - } - }) - } -} - -func TestConsulToNamespaceNameTranslator_TranslateConsulTCPRoute(t *testing.T) { - t.Parallel() - type fields struct { - cache resourceGetter - } - tests := []struct { - name string - fields fields - parentRefs []api.ResourceReference - want []types.NamespacedName - }{ - { - name: "all refs in cache", - fields: fields{ - cache: buildMockCache(map[api.ResourceReference]api.ConfigEntry{ - { - Kind: api.APIGateway, - Name: "api-gw-1", - Namespace: "ns", - }: api.ConfigEntry(&api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "api-gw-1", - Meta: map[string]string{ - metaKeyKubeNS: "ns", - metaKeyKubeName: "api-gw-1", - }, - Namespace: "ns", - }), - { - Kind: api.APIGateway, - Name: "api-gw-2", - Namespace: "ns", - }: api.ConfigEntry(&api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "api-gw-2", - Meta: map[string]string{ - metaKeyKubeNS: "ns", - metaKeyKubeName: "api-gw-2", - }, - Namespace: "ns", - }), - }), - }, - parentRefs: []api.ResourceReference{ - { - Kind: api.APIGateway, - Name: "api-gw-1", - Namespace: "ns", - }, - - { - Kind: api.APIGateway, - Name: "api-gw-2", - Namespace: "ns", - }, - }, - want: []types.NamespacedName{ - { - Namespace: "ns", - Name: "api-gw-1", - }, - { - Namespace: "ns", - Name: "api-gw-2", - }, - }, - }, - { - name: "some refs not in cache", - fields: fields{ - cache: buildMockCache(map[api.ResourceReference]api.ConfigEntry{ - { - Kind: api.APIGateway, - Name: "api-gw-1", - Namespace: "ns", - }: api.ConfigEntry(&api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "api-gw-1", - Meta: map[string]string{ - metaKeyKubeNS: "ns", - metaKeyKubeName: "api-gw-1", - }, - Namespace: "ns", - }), - }), - }, - parentRefs: []api.ResourceReference{ - { - Kind: api.APIGateway, - Name: "api-gw-1", - Namespace: "ns", - }, - { - Kind: api.APIGateway, - Name: "api-gw-2", - Namespace: "ns", - }, - }, - want: []types.NamespacedName{ - { - Namespace: "ns", - Name: "api-gw-1", - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := ConsulToNamespaceNameTranslator{ - cache: tt.fields.cache, - } - config := &api.TCPRouteConfigEntry{ - Parents: tt.parentRefs, - } - got := c.BuildConsulTCPRouteTranslator(context.Background())(config) - if diff := cmp.Diff(got, tt.want, sortTransformer()); diff != "" { - t.Errorf("ConsulToNSNTranslator.TranslateConsulTCPRoute() mismatch (-want +got):\n%s", diff) - } - }) - } -} - -func Test_ConsulToNamespaceNameTranslator_TranslateInlineCertificate(t *testing.T) { - t.Parallel() - type args struct { - config *api.InlineCertificateConfigEntry - } - tests := []struct { - name string - args args - want []types.NamespacedName - }{ - { - name: "when name and namespace are set", - args: args{ - config: &api.InlineCertificateConfigEntry{ - Kind: api.InlineCertificate, - Name: "secret", - Meta: map[string]string{ - metaKeyKubeNS: "my-ns", - metaKeyKubeName: "secret", - }, - }, - }, - want: []types.NamespacedName{ - { - Namespace: "my-ns", - Name: "secret", - }, - }, - }, - { - name: "when name is not set and namespace is set", - args: args{ - config: &api.InlineCertificateConfigEntry{ - Kind: api.InlineCertificate, - Name: "secret", - Meta: map[string]string{ - metaKeyKubeNS: "my-ns", - }, - }, - }, - want: nil, - }, - { - name: "when name is set and namespace is not set", - args: args{ - config: &api.InlineCertificateConfigEntry{ - Kind: api.APIGateway, - Name: "secret", - Meta: map[string]string{ - metaKeyKubeName: "secret", - }, - }, - }, - want: nil, - }, - { - name: "when both name and namespace are not set", - args: args{ - config: &api.InlineCertificateConfigEntry{ - Kind: api.InlineCertificate, - Name: "secret", - Meta: map[string]string{}, - }, - }, - want: nil, - }, - } - - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - transformer := func(ctx context.Context) func(client.Object) []reconcile.Request { - return func(o client.Object) []reconcile.Request { - return []reconcile.Request{ - { - NamespacedName: types.NamespacedName{Name: o.GetName(), Namespace: o.GetNamespace()}, - }, - } - } - } - - translator := ConsulToNamespaceNameTranslator{} - fn := translator.BuildConsulInlineCertificateTranslator(context.Background(), transformer) - got := fn(tt.args.config) - if diff := cmp.Diff(got, tt.want, sortTransformer()); diff != "" { - t.Errorf("ConsulToNSNTranslator.TranslateConsulInlineCertificate() mismatch (-want +got):\n%s", diff) - } - }) - } -} - -func sortTransformer() cmp.Option { - return cmp.Transformer("Sort", func(in []types.NamespacedName) []types.NamespacedName { - sort.Slice(in, func(i int, j int) bool { - return in[i].Name < in[j].Name - }) - return in - }) -} - -type mockCache struct { - c map[api.ResourceReference]api.ConfigEntry -} - -func (m mockCache) Get(ref api.ResourceReference) api.ConfigEntry { - val, ok := m.c[ref] - if !ok { - return nil - } - return val -} - -func buildMockCache(c map[api.ResourceReference]api.ConfigEntry) mockCache { - return mockCache{c: c} -} diff --git a/control-plane/connect-inject/constants/constants.go b/control-plane/connect-inject/constants/constants.go index 48596f9d4b..8987a9f5e8 100644 --- a/control-plane/connect-inject/constants/constants.go +++ b/control-plane/connect-inject/constants/constants.go @@ -16,6 +16,9 @@ const ( // MetaKeyKubeNS is the meta key name for Kubernetes namespace used for the Consul services. MetaKeyKubeNS = "k8s-namespace" + // MetaKeyKubeName is the meta key name for Kubernetes object name used for a Consul object. + MetaKeyKubeName = "k8s-name" + // MetaKeyKubeServiceName is the meta key name for Kubernetes service name used for the Consul services. MetaKeyKubeServiceName = "k8s-service-name" diff --git a/control-plane/subcommand/inject-connect/command.go b/control-plane/subcommand/inject-connect/command.go index 676681baea..c969b3058b 100644 --- a/control-plane/subcommand/inject-connect/command.go +++ b/control-plane/subcommand/inject-connect/command.go @@ -15,7 +15,7 @@ import ( "sync" "syscall" - apigateway "github.com/hashicorp/consul-k8s/control-plane/api-gateway" + gatewaycommon "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" gatewaycontrollers "github.com/hashicorp/consul-k8s/control-plane/api-gateway/controllers" apicommon "github.com/hashicorp/consul-k8s/control-plane/api/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" @@ -485,8 +485,8 @@ func (c *Command) Run(args []string) int { } cache, err := gatewaycontrollers.SetupGatewayControllerWithManager(ctx, mgr, gatewaycontrollers.GatewayControllerConfig{ - HelmConfig: apigateway.HelmConfig{ - ConsulConfig: apigateway.ConsulConfig{ + HelmConfig: gatewaycommon.HelmConfig{ + ConsulConfig: gatewaycommon.ConsulConfig{ Address: c.consul.Addresses, GRPCPort: consulConfig.GRPCPort, HTTPPort: consulConfig.HTTPPort, @@ -508,10 +508,13 @@ func (c *Command) Run(args []string) int { ConsulPartition: c.consul.Partition, ConsulCACert: string(caCertPem), }, - ConsulClientConfig: consulConfig, - ConsulServerConnMgr: watcher, - NamespacesEnabled: c.flagEnableNamespaces, - Partition: c.consul.Partition, + AllowK8sNamespacesSet: allowK8sNamespaces, + DenyK8sNamespacesSet: denyK8sNamespaces, + ConsulClientConfig: consulConfig, + ConsulServerConnMgr: watcher, + NamespacesEnabled: c.flagEnableNamespaces, + CrossNamespaceACLPolicy: c.flagCrossNamespaceACLPolicy, + Partition: c.consul.Partition, }) if err != nil { setupLog.Error(err, "unable to create controller", "controller", "Gateway") From 18f2cd53700734fafb7575bd85d30639384b04b2 Mon Sep 17 00:00:00 2001 From: Paul Glass Date: Wed, 31 May 2023 12:15:17 -0500 Subject: [PATCH 181/340] Update ServiceIntentions CRD for JWT auth (#2213) --- .changelog/2213.txt | 3 + .../templates/crd-serviceintentions.yaml | 75 +++++++ .../api/v1alpha1/serviceintentions_types.go | 96 ++++++++- .../v1alpha1/serviceintentions_types_test.go | 190 ++++++++++++++++++ ...onsul.hashicorp.com_serviceintentions.yaml | 75 +++++++ 5 files changed, 437 insertions(+), 2 deletions(-) create mode 100644 .changelog/2213.txt diff --git a/.changelog/2213.txt b/.changelog/2213.txt new file mode 100644 index 0000000000..c09c2e0397 --- /dev/null +++ b/.changelog/2213.txt @@ -0,0 +1,3 @@ +```release-note:feature +helm: Update the ServiceIntentions CRD to support `JWT` fields. +``` diff --git a/charts/consul/templates/crd-serviceintentions.yaml b/charts/consul/templates/crd-serviceintentions.yaml index f16d0a0d8a..335d2eff7a 100644 --- a/charts/consul/templates/crd-serviceintentions.yaml +++ b/charts/consul/templates/crd-serviceintentions.yaml @@ -74,6 +74,43 @@ spec: have intentions defined. type: string type: object + jwt: + description: JWT specifies the configuration to validate a JSON Web + Token for all incoming requests. + properties: + providers: + description: Providers is a list of providers to consider when + verifying a JWT. + items: + properties: + name: + description: Name is the name of the JWT provider. There + MUST be a corresponding "jwt-provider" config entry with + this name. + type: string + verifyClaims: + description: VerifyClaims is a list of additional claims + to verify in a JWT's payload. + items: + properties: + path: + description: Path is the path to the claim in the + token JSON. + items: + type: string + type: array + value: + description: Value is the expected value at the given + path. If the type at the path is a list then we + verify that this value is contained in the list. + If the type at the path is a string then we verify + that this value matches. + type: string + type: object + type: array + type: object + type: array + type: object sources: description: Sources is the list of all intention sources and the authorization granted to those sources. The order of this list does @@ -183,6 +220,44 @@ spec: match on the HTTP request path. type: string type: object + jwt: + description: JWT specifies configuration to validate a + JSON Web Token for incoming requests. + properties: + providers: + description: Providers is a list of providers to consider + when verifying a JWT. + items: + properties: + name: + description: Name is the name of the JWT provider. + There MUST be a corresponding "jwt-provider" + config entry with this name. + type: string + verifyClaims: + description: VerifyClaims is a list of additional + claims to verify in a JWT's payload. + items: + properties: + path: + description: Path is the path to the claim + in the token JSON. + items: + type: string + type: array + value: + description: Value is the expected value + at the given path. If the type at the + path is a list then we verify that this + value is contained in the list. If the + type at the path is a string then we + verify that this value matches. + type: string + type: object + type: array + type: object + type: array + type: object type: object type: array samenessGroup: diff --git a/control-plane/api/v1alpha1/serviceintentions_types.go b/control-plane/api/v1alpha1/serviceintentions_types.go index 74e02eb617..d393f72a2d 100644 --- a/control-plane/api/v1alpha1/serviceintentions_types.go +++ b/control-plane/api/v1alpha1/serviceintentions_types.go @@ -59,6 +59,8 @@ type ServiceIntentionsSpec struct { // The order of this list does not matter, but out of convenience Consul will always store this // reverse sorted by intention precedence, as that is the order that they will be evaluated at enforcement time. Sources SourceIntentions `json:"sources,omitempty"` + // JWT specifies the configuration to validate a JSON Web Token for all incoming requests. + JWT *IntentionJWTRequirement `json:"jwt,omitempty"` } type IntentionDestination struct { @@ -108,6 +110,8 @@ type IntentionPermission struct { Action IntentionAction `json:"action,omitempty"` // HTTP is a set of HTTP-specific authorization criteria. HTTP *IntentionHTTPPermission `json:"http,omitempty"` + // JWT specifies configuration to validate a JSON Web Token for incoming requests. + JWT *IntentionJWTRequirement `json:"jwt,omitempty"` } type IntentionHTTPPermission struct { @@ -142,6 +146,30 @@ type IntentionHTTPHeaderPermission struct { Invert bool `json:"invert,omitempty"` } +type IntentionJWTRequirement struct { + // Providers is a list of providers to consider when verifying a JWT. + Providers []*IntentionJWTProvider `json:"providers,omitempty"` +} + +type IntentionJWTProvider struct { + // Name is the name of the JWT provider. There MUST be a corresponding + // "jwt-provider" config entry with this name. + Name string `json:"name,omitempty"` + + // VerifyClaims is a list of additional claims to verify in a JWT's payload. + VerifyClaims []*IntentionJWTClaimVerification `json:"verifyClaims,omitempty"` +} + +type IntentionJWTClaimVerification struct { + // Path is the path to the claim in the token JSON. + Path []string `json:"path,omitempty"` + + // Value is the expected value at the given path. If the type at the path + // is a list then we verify that this value is contained in the list. If + // the type at the path is a string then we verify that this value matches. + Value string `json:"value,omitempty"` +} + // IntentionAction is the action that the intention represents. This // can be "allow" or "deny" to allowlist or denylist intentions. type IntentionAction string @@ -226,6 +254,7 @@ func (in *ServiceIntentions) ToConsul(datacenter string) api.ConfigEntry { Name: in.Spec.Destination.Name, Namespace: in.Spec.Destination.Namespace, Sources: in.Spec.Sources.toConsul(), + JWT: in.Spec.JWT.toConsul(), Meta: meta(datacenter), } } @@ -297,6 +326,8 @@ func (in *ServiceIntentions) Validate(consulMeta common.ConsulMeta) error { errs = append(errs, in.validateNamespaces(consulMeta.NamespacesEnabled)...) + errs = append(errs, in.Spec.JWT.validate(path.Child("jwt"))...) + if len(errs) > 0 { return apierrors.NewInvalid( schema.GroupKind{Group: ConsulHashicorpGroup, Kind: common.ServiceIntentions}, @@ -394,6 +425,7 @@ func (in IntentionPermissions) toConsul() []*capi.IntentionPermission { consulIntentionPermissions = append(consulIntentionPermissions, &capi.IntentionPermission{ Action: permission.Action.toConsul(), HTTP: permission.HTTP.toConsul(), + JWT: permission.JWT.toConsul(), }) } return consulIntentionPermissions @@ -429,15 +461,54 @@ func (in IntentionHTTPHeaderPermissions) toConsul() []capi.IntentionHTTPHeaderPe return headerPermissions } +func (in *IntentionJWTRequirement) toConsul() *capi.IntentionJWTRequirement { + if in == nil { + return nil + } + var providers []*capi.IntentionJWTProvider + for _, p := range in.Providers { + providers = append(providers, p.toConsul()) + } + return &capi.IntentionJWTRequirement{ + Providers: providers, + } +} + +func (in *IntentionJWTProvider) toConsul() *capi.IntentionJWTProvider { + if in == nil { + return nil + } + var claims []*capi.IntentionJWTClaimVerification + for _, c := range in.VerifyClaims { + claims = append(claims, c.toConsul()) + } + return &capi.IntentionJWTProvider{ + Name: in.Name, + VerifyClaims: claims, + } +} + +func (in *IntentionJWTClaimVerification) toConsul() *capi.IntentionJWTClaimVerification { + if in == nil { + return nil + } + return &capi.IntentionJWTClaimVerification{ + Path: in.Path, + Value: in.Value, + } +} + func (in IntentionPermissions) validate(path *field.Path) field.ErrorList { var errs field.ErrorList for i, permission := range in { - if err := permission.Action.validate(path.Child("permissions").Index(i)); err != nil { + permPath := path.Child("permissions").Index(i) + if err := permission.Action.validate(permPath); err != nil { errs = append(errs, err) } if permission.HTTP != nil { - errs = append(errs, permission.HTTP.validate(path.Child("permissions").Index(i))...) + errs = append(errs, permission.HTTP.validate(permPath)...) } + errs = append(errs, permission.JWT.validate(permPath.Child("jwt"))...) } return errs } @@ -540,6 +611,27 @@ func numNotEmpty(ss ...string) int { return count } +func (in *IntentionJWTRequirement) validate(path *field.Path) field.ErrorList { + var errs field.ErrorList + if in == nil { + return errs + } + + for i, p := range in.Providers { + if err := p.validate(path.Child("providers").Index(i)); err != nil { + errs = append(errs, err) + } + } + return errs +} + +func (in *IntentionJWTProvider) validate(path *field.Path) *field.Error { + if in != nil && in.Name == "" { + return field.Invalid(path.Child("name"), in.Name, "JWT provider name is required") + } + return nil +} + // sourceIntentionSortKey returns a string that can be used to sort intention // sources. func sourceIntentionSortKey(ixn *capi.SourceIntention) string { diff --git a/control-plane/api/v1alpha1/serviceintentions_types_test.go b/control-plane/api/v1alpha1/serviceintentions_types_test.go index f445dfb246..8d0a6d907a 100644 --- a/control-plane/api/v1alpha1/serviceintentions_types_test.go +++ b/control-plane/api/v1alpha1/serviceintentions_types_test.go @@ -165,11 +165,37 @@ func TestServiceIntentions_MatchesConsul(t *testing.T) { "PUT", }, }, + JWT: &IntentionJWTRequirement{ + Providers: []*IntentionJWTProvider{ + { + Name: "okta-nested", + VerifyClaims: []*IntentionJWTClaimVerification{ + { + Path: []string{"perms", "role"}, + Value: "admin-nested", + }, + }, + }, + }, + }, }, }, Description: "an L7 config", }, }, + JWT: &IntentionJWTRequirement{ + Providers: []*IntentionJWTProvider{ + { + Name: "okta", + VerifyClaims: []*IntentionJWTClaimVerification{ + { + Path: []string{"perms", "role"}, + Value: "admin", + }, + }, + }, + }, + }, }, }, Theirs: &capi.ServiceIntentionsConfigEntry{ @@ -220,11 +246,37 @@ func TestServiceIntentions_MatchesConsul(t *testing.T) { "PUT", }, }, + JWT: &capi.IntentionJWTRequirement{ + Providers: []*capi.IntentionJWTProvider{ + { + Name: "okta-nested", + VerifyClaims: []*capi.IntentionJWTClaimVerification{ + { + Path: []string{"perms", "role"}, + Value: "admin-nested", + }, + }, + }, + }, + }, }, }, Description: "an L7 config", }, }, + JWT: &capi.IntentionJWTRequirement{ + Providers: []*capi.IntentionJWTProvider{ + { + Name: "okta", + VerifyClaims: []*capi.IntentionJWTClaimVerification{ + { + Path: []string{"perms", "role"}, + Value: "admin", + }, + }, + }, + }, + }, Meta: nil, }, Matches: true, @@ -376,11 +428,37 @@ func TestServiceIntentions_ToConsul(t *testing.T) { "PUT", }, }, + JWT: &IntentionJWTRequirement{ + Providers: []*IntentionJWTProvider{ + { + Name: "okta-nested", + VerifyClaims: []*IntentionJWTClaimVerification{ + { + Path: []string{"perms", "role"}, + Value: "admin-nested", + }, + }, + }, + }, + }, }, }, Description: "an L7 config", }, }, + JWT: &IntentionJWTRequirement{ + Providers: []*IntentionJWTProvider{ + { + Name: "okta", + VerifyClaims: []*IntentionJWTClaimVerification{ + { + Path: []string{"perms", "role"}, + Value: "admin", + }, + }, + }, + }, + }, }, }, Exp: &capi.ServiceIntentionsConfigEntry{ @@ -436,11 +514,37 @@ func TestServiceIntentions_ToConsul(t *testing.T) { "PUT", }, }, + JWT: &capi.IntentionJWTRequirement{ + Providers: []*capi.IntentionJWTProvider{ + { + Name: "okta-nested", + VerifyClaims: []*capi.IntentionJWTClaimVerification{ + { + Path: []string{"perms", "role"}, + Value: "admin-nested", + }, + }, + }, + }, + }, }, }, Description: "an L7 config", }, }, + JWT: &capi.IntentionJWTRequirement{ + Providers: []*capi.IntentionJWTProvider{ + { + Name: "okta", + VerifyClaims: []*capi.IntentionJWTClaimVerification{ + { + Path: []string{"perms", "role"}, + Value: "admin", + }, + }, + }, + }, + }, Meta: map[string]string{ common.SourceKey: common.SourceValue, common.DatacenterKey: "datacenter", @@ -741,11 +845,37 @@ func TestServiceIntentions_Validate(t *testing.T) { "PUT", }, }, + JWT: &IntentionJWTRequirement{ + Providers: []*IntentionJWTProvider{ + { + Name: "okta-nested", + VerifyClaims: []*IntentionJWTClaimVerification{ + { + Path: []string{"perms", "role"}, + Value: "admin-nested", + }, + }, + }, + }, + }, }, }, Description: "an L7 config", }, }, + JWT: &IntentionJWTRequirement{ + Providers: []*IntentionJWTProvider{ + { + Name: "okta", + VerifyClaims: []*IntentionJWTClaimVerification{ + { + Path: []string{"perms", "role"}, + Value: "admin", + }, + }, + }, + }, + }, }, }, namespacesEnabled: true, @@ -1622,6 +1752,66 @@ func TestServiceIntentions_Validate(t *testing.T) { `samenessgroup cannot use or contain wildcard '*'`, }, }, + "invalid empty jwt provider name at top-level": { + input: &ServiceIntentions{ + ObjectMeta: metav1.ObjectMeta{ + Name: "does-not-matter", + }, + Spec: ServiceIntentionsSpec{ + Destination: IntentionDestination{ + Name: "dest-service", + }, + Sources: SourceIntentions{ + { + Name: "bar", + Action: "allow", + }, + }, + JWT: &IntentionJWTRequirement{ + Providers: []*IntentionJWTProvider{ + { + Name: "", + }, + }, + }, + }, + }, + expectedErrMsgs: []string{ + `spec.jwt.providers[0].name: Invalid value: "": JWT provider name is required`, + }, + }, + "invalid empty jwt provider name in permissions": { + input: &ServiceIntentions{ + ObjectMeta: metav1.ObjectMeta{ + Name: "does-not-matter", + }, + Spec: ServiceIntentionsSpec{ + Destination: IntentionDestination{ + Name: "dest-service", + }, + Sources: SourceIntentions{ + { + Name: "bar", + Permissions: IntentionPermissions{ + { + Action: "allow", + JWT: &IntentionJWTRequirement{ + Providers: []*IntentionJWTProvider{ + { + Name: "", + }, + }, + }, + }, + }, + }, + }, + }, + }, + expectedErrMsgs: []string{ + `spec.sources[0].permissions[0].jwt.providers[0].name: Invalid value: "": JWT provider name is required`, + }, + }, } for name, testCase := range cases { t.Run(name, func(t *testing.T) { diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml index fac2b31b97..cd28173ba8 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml @@ -70,6 +70,43 @@ spec: have intentions defined. type: string type: object + jwt: + description: JWT specifies the configuration to validate a JSON Web + Token for all incoming requests. + properties: + providers: + description: Providers is a list of providers to consider when + verifying a JWT. + items: + properties: + name: + description: Name is the name of the JWT provider. There + MUST be a corresponding "jwt-provider" config entry with + this name. + type: string + verifyClaims: + description: VerifyClaims is a list of additional claims + to verify in a JWT's payload. + items: + properties: + path: + description: Path is the path to the claim in the + token JSON. + items: + type: string + type: array + value: + description: Value is the expected value at the given + path. If the type at the path is a list then we + verify that this value is contained in the list. + If the type at the path is a string then we verify + that this value matches. + type: string + type: object + type: array + type: object + type: array + type: object sources: description: Sources is the list of all intention sources and the authorization granted to those sources. The order of this list does @@ -179,6 +216,44 @@ spec: match on the HTTP request path. type: string type: object + jwt: + description: JWT specifies configuration to validate a + JSON Web Token for incoming requests. + properties: + providers: + description: Providers is a list of providers to consider + when verifying a JWT. + items: + properties: + name: + description: Name is the name of the JWT provider. + There MUST be a corresponding "jwt-provider" + config entry with this name. + type: string + verifyClaims: + description: VerifyClaims is a list of additional + claims to verify in a JWT's payload. + items: + properties: + path: + description: Path is the path to the claim + in the token JSON. + items: + type: string + type: array + value: + description: Value is the expected value + at the given path. If the type at the + path is a list then we verify that this + value is contained in the list. If the + type at the path is a string then we + verify that this value matches. + type: string + type: object + type: array + type: object + type: array + type: object type: object type: array samenessGroup: From aaaed6780ad223327c1997fb5e5be5f3a2b9345f Mon Sep 17 00:00:00 2001 From: Connor Date: Wed, 31 May 2023 15:59:02 -0500 Subject: [PATCH 182/340] Fix setting args for the telemetry-collector (#2224) * Fix setting args for the telemetry-collector Either the docker container or the execution method for the telemetry-collector is making the args not get included on the process. Switch to putting it directly in the command so we can ensure this works as expected * Fix bats test --- .../consul/templates/telemetry-collector-deployment.yaml | 9 ++++----- charts/consul/test/unit/connect-inject-deployment.bats | 2 +- .../consul/test/unit/telemetry-collector-deployment.bats | 4 ++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/charts/consul/templates/telemetry-collector-deployment.yaml b/charts/consul/templates/telemetry-collector-deployment.yaml index cb0cb67852..62b8868f1f 100644 --- a/charts/consul/templates/telemetry-collector-deployment.yaml +++ b/charts/consul/templates/telemetry-collector-deployment.yaml @@ -222,11 +222,10 @@ spec: {{- end }} {{- end }} - consul-telemetry-collector agent - {{- if .Values.telemetryCollector.customExporterConfig }} - args: - - -config-file-path /consul/config/config.json - {{ end }} + consul-telemetry-collector agent \ + {{- if .Values.telemetryCollector.customExporterConfig }} + -config-file-path /consul/config/config.json \ + {{ end }} volumeMounts: {{- if .Values.telemetryCollector.customExporterConfig }} - name: config diff --git a/charts/consul/test/unit/connect-inject-deployment.bats b/charts/consul/test/unit/connect-inject-deployment.bats index e7d5b3bf48..26e3038759 100755 --- a/charts/consul/test/unit/connect-inject-deployment.bats +++ b/charts/consul/test/unit/connect-inject-deployment.bats @@ -216,7 +216,7 @@ load _helpers local cmd=$(helm template \ -s templates/connect-inject-deployment.yaml \ --set 'connectInject.enabled=true' \ - --set 'connectInject.metrics.enableTelemetryCollector=true' \ + --set 'global.metrics.enableTelemetryCollector=true' \ . | tee /dev/stderr | yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) diff --git a/charts/consul/test/unit/telemetry-collector-deployment.bats b/charts/consul/test/unit/telemetry-collector-deployment.bats index 1e2758e638..705447621e 100755 --- a/charts/consul/test/unit/telemetry-collector-deployment.bats +++ b/charts/consul/test/unit/telemetry-collector-deployment.bats @@ -921,7 +921,7 @@ load _helpers --set 'telemetryCollector.image=bar' \ --set 'telemetryCollector.customExporterConfig="foo"' \ . | tee /dev/stderr | - yq '.spec.template.spec.containers[0].args') + yq '.spec.template.spec.containers[0].command') local actual=$(echo $flags | yq -r '. | any(contains("-config-file-path /consul/config/config.json"))') [ "${actual}" = "true" ] @@ -1073,4 +1073,4 @@ MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ local actual=$(echo $object | yq -r 'map(select(.name == "foo")) | .[0].value' | tee /dev/stderr) [ "${actual}" = "bar" ] -} \ No newline at end of file +} From 0c28b9b000cb5dced3011ae10f5cd858f2754026 Mon Sep 17 00:00:00 2001 From: chappie <6537530+chapmanc@users.noreply.github.com> Date: Wed, 31 May 2023 20:42:56 -0700 Subject: [PATCH 183/340] Fix telemetry collector issue and fix for bat test (#2223) --- .../endpoints/endpoints_controller.go | 7 +- .../endpoints/endpoints_controller_test.go | 104 +++++++++++++++++- 2 files changed, 103 insertions(+), 8 deletions(-) diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go index 584abd48c6..eeaeeab485 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go @@ -487,7 +487,7 @@ func (r *Controller) createServiceRegistrations(pod corev1.Pod, serviceEndpoints proxyConfig.Config[envoyPrometheusBindAddr] = prometheusScrapeListener } - if r.EnableTelemetryCollector { + if r.EnableTelemetryCollector && proxyConfig.Config != nil { proxyConfig.Config[envoyTelemetryCollectorBindSocketDir] = "/consul/connect-inject" } @@ -671,6 +671,9 @@ func (r *Controller) createGatewayRegistrations(pod corev1.Pod, serviceEndpoints ID: pod.Name, Address: pod.Status.PodIP, Meta: meta, + Proxy: &api.AgentServiceConnectProxyConfig{ + Config: map[string]interface{}{}, + }, } gatewayServiceName, ok := pod.Annotations[constants.AnnotationGatewayConsulServiceName] @@ -770,7 +773,7 @@ func (r *Controller) createGatewayRegistrations(pod corev1.Pod, serviceEndpoints } } - if r.EnableTelemetryCollector { + if r.EnableTelemetryCollector && service.Proxy != nil && service.Proxy.Config != nil { service.Proxy.Config[envoyTelemetryCollectorBindSocketDir] = "/consul/service" } diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go index aad7694b2e..acf62b2b0e 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go @@ -996,6 +996,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { expectedProxySvcInstances []*api.CatalogService expectedHealthChecks []*api.HealthCheck metricsEnabled bool + telemetryCollectorDisabled bool nodeMeta map[string]string expErr string }{ @@ -1078,6 +1079,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { DestinationServiceID: "pod1-service-created", LocalServiceAddress: "", LocalServicePort: 0, + Config: map[string]any{"envoy_telemetry_collector_bind_socket_dir": string("/consul/connect-inject")}, }, ServiceMeta: map[string]string{constants.MetaKeyPodName: "pod1", metaKeyKubeServiceName: "service-created", constants.MetaKeyKubeNS: "default", metaKeyManagedBy: constants.ManagedByValue, metaKeySyntheticNode: "true"}, ServiceTags: []string{}, @@ -1163,7 +1165,9 @@ func TestReconcileCreateEndpoint(t *testing.T) { Port: 443, }, }, - ServiceProxy: &api.AgentServiceConnectProxyConfig{}, + ServiceProxy: &api.AgentServiceConnectProxyConfig{ + Config: map[string]any{"envoy_telemetry_collector_bind_socket_dir": string("/consul/service")}, + }, NodeMeta: map[string]string{ "synthetic-node": "true", "test-node": "true", @@ -1216,6 +1220,80 @@ func TestReconcileCreateEndpoint(t *testing.T) { } return []runtime.Object{gateway, endpoint} }, + expectedConsulSvcInstances: []*api.CatalogService{ + { + ServiceID: "mesh-gateway", + ServiceName: "mesh-gateway", + ServiceAddress: "1.2.3.4", + ServicePort: 8443, + ServiceMeta: map[string]string{constants.MetaKeyPodName: "mesh-gateway", metaKeyKubeServiceName: "mesh-gateway", constants.MetaKeyKubeNS: "default", metaKeyManagedBy: constants.ManagedByValue, metaKeySyntheticNode: "true"}, + ServiceTags: []string{}, + ServiceTaggedAddresses: map[string]api.ServiceAddress{ + "lan": { + Address: "1.2.3.4", + Port: 8443, + }, + "wan": { + Address: "2.3.4.5", + Port: 443, + }, + }, + ServiceProxy: &api.AgentServiceConnectProxyConfig{ + Config: map[string]interface{}{ + "envoy_prometheus_bind_addr": "1.2.3.4:20200", + "envoy_telemetry_collector_bind_socket_dir": "/consul/service", + }, + }, + }, + }, + expectedHealthChecks: []*api.HealthCheck{ + { + CheckID: "default/mesh-gateway", + ServiceName: "mesh-gateway", + ServiceID: "mesh-gateway", + Name: consulKubernetesCheckName, + Status: api.HealthPassing, + Output: kubernetesSuccessReasonMsg, + Type: consulKubernetesCheckType, + }, + }, + metricsEnabled: true, + }, + { + name: "Mesh_Gateway_with_Metrics_enabled_and_telemetry_collector_disabled", + svcName: "mesh-gateway", + consulSvcName: "mesh-gateway", + telemetryCollectorDisabled: true, + k8sObjects: func() []runtime.Object { + gateway := createGatewayPod("mesh-gateway", "1.2.3.4", map[string]string{ + constants.AnnotationGatewayConsulServiceName: "mesh-gateway", + constants.AnnotationGatewayWANSource: "Static", + constants.AnnotationGatewayWANAddress: "2.3.4.5", + constants.AnnotationGatewayWANPort: "443", + constants.AnnotationMeshGatewayContainerPort: "8443", + constants.AnnotationGatewayKind: meshGateway}) + endpoint := &corev1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mesh-gateway", + Namespace: "default", + }, + Subsets: []corev1.EndpointSubset{ + { + Addresses: []corev1.EndpointAddress{ + { + IP: "1.2.3.4", + TargetRef: &corev1.ObjectReference{ + Kind: "Pod", + Name: "mesh-gateway", + Namespace: "default", + }, + }, + }, + }, + }, + } + return []runtime.Object{gateway, endpoint} + }, expectedConsulSvcInstances: []*api.CatalogService{ { ServiceID: "mesh-gateway", @@ -1298,8 +1376,10 @@ func TestReconcileCreateEndpoint(t *testing.T) { metaKeyManagedBy: constants.ManagedByValue, metaKeySyntheticNode: "true", }, - ServiceTags: []string{}, - ServiceProxy: &api.AgentServiceConnectProxyConfig{}, + ServiceTags: []string{}, + ServiceProxy: &api.AgentServiceConnectProxyConfig{ + Config: map[string]any{"envoy_telemetry_collector_bind_socket_dir": string("/consul/service")}, + }, }, }, expectedHealthChecks: []*api.HealthCheck{ @@ -1362,7 +1442,8 @@ func TestReconcileCreateEndpoint(t *testing.T) { ServiceTags: []string{}, ServiceProxy: &api.AgentServiceConnectProxyConfig{ Config: map[string]interface{}{ - "envoy_prometheus_bind_addr": "1.2.3.4:20200", + "envoy_prometheus_bind_addr": "1.2.3.4:20200", + "envoy_telemetry_collector_bind_socket_dir": "/consul/service", }, }, }, @@ -1462,6 +1543,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { "address": "0.0.0.0", }, }, + "envoy_telemetry_collector_bind_socket_dir": "/consul/service", }, }, }, @@ -1562,7 +1644,8 @@ func TestReconcileCreateEndpoint(t *testing.T) { "address": "0.0.0.0", }, }, - "envoy_prometheus_bind_addr": "1.2.3.4:20200", + "envoy_prometheus_bind_addr": "1.2.3.4:20200", + "envoy_telemetry_collector_bind_socket_dir": "/consul/service", }, }, }, @@ -1647,6 +1730,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { DestinationServiceID: "pod1-service-created", LocalServiceAddress: "", LocalServicePort: 0, + Config: map[string]any{"envoy_telemetry_collector_bind_socket_dir": string("/consul/connect-inject")}, }, ServiceMeta: map[string]string{constants.MetaKeyPodName: "pod1", metaKeyKubeServiceName: "service-created", constants.MetaKeyKubeNS: "default", metaKeyManagedBy: constants.ManagedByValue, metaKeySyntheticNode: "true"}, ServiceTags: []string{}, @@ -1661,6 +1745,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { DestinationServiceID: "pod2-service-created", LocalServiceAddress: "", LocalServicePort: 0, + Config: map[string]any{"envoy_telemetry_collector_bind_socket_dir": string("/consul/connect-inject")}, }, ServiceMeta: map[string]string{constants.MetaKeyPodName: "pod2", metaKeyKubeServiceName: "service-created", constants.MetaKeyKubeNS: "default", metaKeyManagedBy: constants.ManagedByValue, metaKeySyntheticNode: "true"}, ServiceTags: []string{}, @@ -1786,6 +1871,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { DestinationServiceID: "pod1-service-created", LocalServiceAddress: "", LocalServicePort: 0, + Config: map[string]any{"envoy_telemetry_collector_bind_socket_dir": string("/consul/connect-inject")}, }, ServiceMeta: map[string]string{constants.MetaKeyPodName: "pod1", metaKeyKubeServiceName: "service-created", constants.MetaKeyKubeNS: "default", metaKeyManagedBy: constants.ManagedByValue, metaKeySyntheticNode: "true"}, ServiceTags: []string{}, @@ -1800,6 +1886,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { DestinationServiceID: "pod2-service-created", LocalServiceAddress: "", LocalServicePort: 0, + Config: map[string]any{"envoy_telemetry_collector_bind_socket_dir": string("/consul/connect-inject")}, }, ServiceMeta: map[string]string{constants.MetaKeyPodName: "pod2", metaKeyKubeServiceName: "service-created", constants.MetaKeyKubeNS: "default", metaKeyManagedBy: constants.ManagedByValue, metaKeySyntheticNode: "true"}, ServiceTags: []string{}, @@ -1912,7 +1999,8 @@ func TestReconcileCreateEndpoint(t *testing.T) { }, }, Config: map[string]interface{}{ - "envoy_prometheus_bind_addr": "0.0.0.0:12345", + "envoy_prometheus_bind_addr": "0.0.0.0:12345", + "envoy_telemetry_collector_bind_socket_dir": "/consul/connect-inject", }, }, ServiceMeta: map[string]string{ @@ -2013,6 +2101,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { DestinationServiceID: "pod1-service-created", LocalServiceAddress: "", LocalServicePort: 0, + Config: map[string]any{"envoy_telemetry_collector_bind_socket_dir": string("/consul/connect-inject")}, }, ServiceMeta: map[string]string{constants.MetaKeyPodName: "pod1", metaKeyKubeServiceName: "service-created", constants.MetaKeyKubeNS: "default", metaKeyManagedBy: constants.ManagedByValue, metaKeySyntheticNode: "true"}, ServiceTags: []string{}, @@ -2072,6 +2161,9 @@ func TestReconcileCreateEndpoint(t *testing.T) { EnableGatewayMetrics: true, } } + + ep.EnableTelemetryCollector = !tt.telemetryCollectorDisabled + namespacedName := types.NamespacedName{ Namespace: "default", Name: tt.svcName, From 8d51935f399cd9ffb1e456872c4366a32342e053 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Thu, 1 Jun 2023 10:34:33 -0400 Subject: [PATCH 184/340] Get consul-dataplane image from helm chart (#2232) --- Makefile | 5 +++++ .../build-support/scripts/consul-dataplane-version.sh | 7 +++++++ 2 files changed, 12 insertions(+) create mode 100755 control-plane/build-support/scripts/consul-dataplane-version.sh diff --git a/Makefile b/Makefile index 02527e4d76..151f7868de 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ VERSION = $(shell ./control-plane/build-support/scripts/version.sh control-plane/version/version.go) CONSUL_IMAGE_VERSION = $(shell ./control-plane/build-support/scripts/consul-version.sh charts/consul/values.yaml) +CONSUL_DATAPLANE_IMAGE_VERSION = $(shell ./control-plane/build-support/scripts/consul-dataplane-version.sh charts/consul/values.yaml) # ===========> Helm Targets @@ -164,6 +165,10 @@ version: consul-version: @echo $(CONSUL_IMAGE_VERSION) +consul-dataplane-version: + @echo $(CONSUL_DATAPLANE_IMAGE_VERSION) + + # ===========> Release Targets prepare-release: ## Sets the versions, updates changelog to prepare this repository to release diff --git a/control-plane/build-support/scripts/consul-dataplane-version.sh b/control-plane/build-support/scripts/consul-dataplane-version.sh new file mode 100755 index 0000000000..906ee54a1f --- /dev/null +++ b/control-plane/build-support/scripts/consul-dataplane-version.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 +FILE=$1 +VERSION=$(yq .global.imageConsulDataplane $FILE) + +echo "${VERSION}" From 9dfc3d0f6867128b879d9dbf752c557ea97a2516 Mon Sep 17 00:00:00 2001 From: Andrew Stucki Date: Thu, 1 Jun 2023 12:04:56 -0400 Subject: [PATCH 185/340] Add acceptance test cleanup for API Gateway resources (#2237) --- acceptance/framework/consul/helm_cluster.go | 56 ++++++ acceptance/go.mod | 45 +++-- acceptance/go.sum | 192 +++++++------------- 3 files changed, 149 insertions(+), 144 deletions(-) diff --git a/acceptance/framework/consul/helm_cluster.go b/acceptance/framework/consul/helm_cluster.go index e03e3615fc..613b69da91 100644 --- a/acceptance/framework/consul/helm_cluster.go +++ b/acceptance/framework/consul/helm_cluster.go @@ -18,6 +18,7 @@ import ( "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/logger" "github.com/hashicorp/consul-k8s/acceptance/framework/portforward" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "github.com/hashicorp/consul/api" "github.com/hashicorp/consul/sdk/testutil/retry" "github.com/stretchr/testify/require" @@ -26,7 +27,11 @@ import ( rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/selection" "k8s.io/client-go/kubernetes" + "sigs.k8s.io/controller-runtime/pkg/client" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) // HelmCluster implements Cluster and uses Helm @@ -44,6 +49,7 @@ type HelmCluster struct { ctx environment.TestContext helmOptions *helm.Options releaseName string + runtimeClient client.Client kubernetesClient kubernetes.Interface noCleanupOnFailure bool debugDirectory string @@ -98,6 +104,7 @@ func NewHelmCluster( ctx: ctx, helmOptions: opts, releaseName: releaseName, + runtimeClient: ctx.ControllerRuntimeClient(t), kubernetesClient: ctx.KubernetesClient(t), noCleanupOnFailure: cfg.NoCleanupOnFailure, debugDirectory: cfg.DebugDirectory, @@ -154,6 +161,9 @@ func (h *HelmCluster) Destroy(t *testing.T) { // Retry because sometimes certain resources (like PVC) take time to delete // in cloud providers. retry.RunWith(&retry.Counter{Wait: 2 * time.Second, Count: 600}, t, func(r *retry.R) { + requirement, err := labels.NewRequirement("release", selection.Equals, []string{h.releaseName}) + require.NoError(r, err) + // Force delete any pods that have h.releaseName in their name because sometimes // graceful termination takes a long time and since this is an uninstall // we don't care that they're stopped gracefully. @@ -233,6 +243,34 @@ func (h *HelmCluster) Destroy(t *testing.T) { } } + // Forcibly delete all gateway classes and remove their finalizers. + err = h.runtimeClient.DeleteAllOf(context.Background(), &gwv1beta1.GatewayClass{}, client.HasLabels{"release=" + h.releaseName}) + require.NoError(r, err) + + var gatewayClassList gwv1beta1.GatewayClassList + err = h.runtimeClient.List(context.Background(), &gatewayClassList, &client.ListOptions{ + LabelSelector: labels.NewSelector().Add(*requirement), + }) + require.NoError(r, err) + for _, item := range gatewayClassList.Items { + item.SetFinalizers([]string{}) + require.NoError(r, h.runtimeClient.Update(context.Background(), &item)) + } + + // Forcibly delete all gateway class configs and remove their finalizers. + err = h.runtimeClient.DeleteAllOf(context.Background(), &v1alpha1.GatewayClassConfig{}, client.HasLabels{"release=" + h.releaseName}) + require.NoError(r, err) + + var gatewayClassConfigList v1alpha1.GatewayClassConfigList + err = h.runtimeClient.List(context.Background(), &gatewayClassConfigList, &client.ListOptions{ + LabelSelector: labels.NewSelector().Add(*requirement), + }) + require.NoError(r, err) + for _, item := range gatewayClassConfigList.Items { + item.SetFinalizers([]string{}) + require.NoError(r, h.runtimeClient.Update(context.Background(), &item)) + } + // Verify all Consul Pods are deleted. pods, err = h.kubernetesClient.CoreV1().Pods(h.helmOptions.KubectlOptions.Namespace).List(context.Background(), metav1.ListOptions{LabelSelector: "release=" + h.releaseName}) require.NoError(r, err) @@ -291,6 +329,24 @@ func (h *HelmCluster) Destroy(t *testing.T) { r.Errorf("Found job which should have been deleted: %s", job.Name) } } + + // Verify all Gateway Classes are deleted. + err = h.runtimeClient.List(context.Background(), &gatewayClassList, &client.ListOptions{ + LabelSelector: labels.NewSelector().Add(*requirement), + }) + require.NoError(r, err) + for _, gatewayClass := range gatewayClassList.Items { + r.Errorf("Found gateway class which should have been deleted: %s", gatewayClass.Name) + } + + // Verify all Gateway Class Configs are deleted. + err = h.runtimeClient.List(context.Background(), &gatewayClassConfigList, &client.ListOptions{ + LabelSelector: labels.NewSelector().Add(*requirement), + }) + require.NoError(r, err) + for _, gatewayClassConfig := range gatewayClassConfigList.Items { + r.Errorf("Found gateway class config which should have been deleted: %s", gatewayClassConfig.Name) + } }) } diff --git a/acceptance/go.mod b/acceptance/go.mod index c53caf4952..88507a2d54 100644 --- a/acceptance/go.mod +++ b/acceptance/go.mod @@ -4,12 +4,13 @@ go 1.20 require ( github.com/gruntwork-io/terratest v0.31.2 - github.com/hashicorp/consul-k8s/control-plane v0.0.0-20221117191905-0b1cc2b631e3 + github.com/hashicorp/consul-k8s/control-plane v0.0.0-20230601034256-0c28b9b000cb github.com/hashicorp/consul/api v1.10.1-0.20230530193107-04a0d0133ae4 github.com/hashicorp/consul/sdk v0.13.1 github.com/hashicorp/go-uuid v1.0.3 github.com/hashicorp/go-version v1.6.0 - github.com/hashicorp/vault/api v1.2.0 + github.com/hashicorp/serf v0.10.1 + github.com/hashicorp/vault/api v1.8.3 github.com/stretchr/testify v1.8.2 gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.26.3 @@ -22,7 +23,7 @@ require ( require ( github.com/armon/go-metrics v0.4.1 // indirect github.com/armon/go-radix v1.0.0 // indirect - github.com/aws/aws-sdk-go v1.30.27 // indirect + github.com/aws/aws-sdk-go v1.44.262 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect github.com/cenkalti/backoff/v3 v3.0.0 // indirect @@ -42,41 +43,43 @@ require ( github.com/go-sql-driver/mysql v1.5.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/golang/snappy v0.0.1 // indirect + github.com/golang/snappy v0.0.4 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.1.0 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gruntwork-io/gruntwork-cli v0.7.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-bexpr v0.1.11 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v1.2.2 // indirect - github.com/hashicorp/go-immutable-radix v1.3.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-plugin v1.0.1 // indirect + github.com/hashicorp/go-plugin v1.4.5 // indirect github.com/hashicorp/go-retryablehttp v0.6.6 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1 // indirect - github.com/hashicorp/go-secure-stdlib/strutil v0.1.1 // indirect + github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 // indirect + github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect + github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect github.com/hashicorp/go-sockaddr v1.0.2 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/hashicorp/serf v0.10.1 // indirect - github.com/hashicorp/vault/sdk v0.2.1 // indirect + github.com/hashicorp/vault/sdk v0.7.0 // indirect github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect github.com/imdario/mergo v0.3.12 // indirect - github.com/jmespath/go-jmespath v0.3.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect - github.com/miekg/dns v1.1.41 // indirect + github.com/miekg/dns v1.1.50 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.0 // indirect - github.com/mitchellh/mapstructure v1.4.2 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/pointerstructure v1.2.1 // indirect github.com/mitchellh/reflectwalk v1.0.0 // indirect github.com/moby/spdystream v0.2.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -95,19 +98,21 @@ require ( github.com/ryanuber/go-glob v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/urfave/cli v1.22.2 // indirect - go.uber.org/atomic v1.7.0 // indirect - golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect - golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect + go.uber.org/atomic v1.9.0 // indirect + golang.org/x/crypto v0.1.0 // indirect + golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect + golang.org/x/mod v0.9.0 // indirect golang.org/x/net v0.8.0 // indirect golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect golang.org/x/sys v0.6.0 // indirect golang.org/x/term v0.6.0 // indirect - golang.org/x/text v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect - gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect + golang.org/x/tools v0.7.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect - google.golang.org/grpc v1.48.0 // indirect + google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect + google.golang.org/grpc v1.49.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/square/go-jose.v2 v2.5.1 // indirect diff --git a/acceptance/go.sum b/acceptance/go.sum index 82037c23a0..c0a0a6d870 100644 --- a/acceptance/go.sum +++ b/acceptance/go.sum @@ -1,4 +1,3 @@ -bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -72,8 +71,6 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190822182118-27a4ced34534/go.mod h1:iroGtC8B3tQiqtds1l+mgk/BBOrxbqjH+eUfFQYRc14= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= -github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -88,8 +85,6 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs= -github.com/armon/go-metrics v0.3.3/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= @@ -98,10 +93,9 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.16.26/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.30.27 h1:9gPjZWVDSoQrBO2AvqrWObS6KAZByfEJxQoCYo4ZfK0= -github.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go v1.44.262 h1:gyXpcJptWoNkK+DiAiaBltlreoWKQXjAIh6FRh60F+I= +github.com/aws/aws-sdk-go v1.44.262/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -129,17 +123,7 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= -github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20200709052629-daa8e1ccc0bc/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= -github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= @@ -167,7 +151,6 @@ github.com/docker/cli v0.0.0-20200109221225-a4f60165b7a3/go.mod h1:JLrzqnKDaYBop github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v1.4.2-0.20200319182547-c7ad2b866182/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= @@ -189,10 +172,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF 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.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= @@ -204,17 +185,13 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= -github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= github.com/frankban/quicktest v1.13.0 h1:yNZif1OkDfNoDfb9zZa9aXIpejNR4F23Wely0c+Qdqk= -github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.0.2-0.20180813162953-d98b870cc4e0/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= @@ -226,8 +203,6 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-ldap/ldap/v3 v3.1.3/go.mod h1:3rbOH3jRS2u6jg2rJnKAMLE/xQyCKIveG2Sa/Cohzb8= -github.com/go-ldap/ldap/v3 v3.1.10/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= @@ -259,10 +234,7 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= -github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= @@ -301,8 +273,8 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= @@ -347,7 +319,6 @@ github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTV github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -360,63 +331,55 @@ github.com/gruntwork-io/gruntwork-cli v0.7.0 h1:YgSAmfCj9c61H+zuvHwKfYUwlMhu5arn github.com/gruntwork-io/gruntwork-cli v0.7.0/go.mod h1:jp6Z7NcLF2avpY8v71fBx6hds9eOFPELSuD/VPv7w00= github.com/gruntwork-io/terratest v0.31.2 h1:xvYHA80MUq5kx670dM18HInewOrrQrAN+XbVVtytUHg= github.com/gruntwork-io/terratest v0.31.2/go.mod h1:EEgJie28gX/4AD71IFqgMj6e99KP5mi81hEtzmDjxTo= -github.com/hashicorp/consul-k8s/control-plane v0.0.0-20221117191905-0b1cc2b631e3 h1:4wROIZB8Y4cN/wPILChc2zQ/q00z1VyJitdgyLbITdU= -github.com/hashicorp/consul-k8s/control-plane v0.0.0-20221117191905-0b1cc2b631e3/go.mod h1:j9Db/whkzvNC+KP2GftY0HxxleLm9swxXjlu3tYaOAw= +github.com/hashicorp/consul-k8s/control-plane v0.0.0-20230601034256-0c28b9b000cb h1:9GUvDoKVoV3IW78QyfoNY4bRcKxcn26wTGLoBrz92N4= +github.com/hashicorp/consul-k8s/control-plane v0.0.0-20230601034256-0c28b9b000cb/go.mod h1:jKzTEgDc/np2gX/KPdfdm1mEUfZLrU8gc71XN3B15VI= github.com/hashicorp/consul/api v1.10.1-0.20230530193107-04a0d0133ae4 h1:6kUTk+YBgA5X5b3gNAoI18WEK4/t75LcWSimEgmpFdg= github.com/hashicorp/consul/api v1.10.1-0.20230530193107-04a0d0133ae4/go.mod h1:tXfrC6o0yFTgAW46xd5Ic8STHc9oIBcRVBcwhX5KNCQ= -github.com/hashicorp/consul/api v1.20.0 h1:9IHTjNVSZ7MIwjlW3N3a7iGiykCMDpxZu8jsxFJh0yc= -github.com/hashicorp/consul/api v1.20.0/go.mod h1:nR64eD44KQ59Of/ECwt2vUmIK2DKsDzAwTmwmLl8Wpo= github.com/hashicorp/consul/sdk v0.13.1 h1:EygWVWWMczTzXGpO93awkHFzfUka6hLYJ0qhETd+6lY= github.com/hashicorp/consul/sdk v0.13.1/go.mod h1:SW/mM4LbKfqmMvcFu8v+eiQQ7oitXEFeiBe9StxERb0= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-bexpr v0.1.11 h1:6DqdA/KBjurGby9yTY0bmkathya0lfwF2SeuubCI7dY= +github.com/hashicorp/go-bexpr v0.1.11/go.mod h1:f03lAo0duBlDIUMGCuad8oLcgejw4m7U+N8T+6Kz1AE= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.2.2 h1:ihRI7YFwcZdiSD7SIenIhHfQH3OuDvWerAUBZbeQS3M= github.com/hashicorp/go-hclog v1.2.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.3.0 h1:8exGP7ego3OmkfksihtSouGMZ+hQrhxx+FVELeXpVPE= -github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.0.1 h1:4OtAfUGbnKC6yS48p0CtMX2oFYtzFZVv6rok3cRWgnE= -github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= +github.com/hashicorp/go-plugin v1.4.5 h1:oTE/oQR4eghggRg8VY7PAz3dr++VwDNBGCcOfIvHpBo= +github.com/hashicorp/go-plugin v1.4.5/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-retryablehttp v0.6.2/go.mod h1:gEx6HMUGxYYhJScX7W1Il64m6cc2C1mDaW3NQ9sY1FY= github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= -github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1 h1:78ki3QBevHwYrVxnyVeaEz+7WtifHhauYF23es/0KlI= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.1 h1:nd0HIW15E6FG1MsnArYaHfuw9C2zgzM8LxkG5Ty/788= +github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 h1:cCRo8gK7oq6A2L6LICkUZ+/a5rLiRXFMf1Qd4xSwxTc= +github.com/hashicorp/go-secure-stdlib/mlock v0.1.1/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -432,12 +395,10 @@ github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= -github.com/hashicorp/vault/api v1.0.5-0.20200519221902-385fac77e20f/go.mod h1:euTFbi2YJgwcju3imEt919lhJKF68nN1cQPq3aA+kBE= -github.com/hashicorp/vault/api v1.2.0 h1:ysGFc6XRGbv05NsWPzuO5VTv68Lj8jtwATxRLFOpP9s= -github.com/hashicorp/vault/api v1.2.0/go.mod h1:dAjw0T5shMnrfH7Q/Mst+LrcTKvStZBVs1PICEDpUqY= -github.com/hashicorp/vault/sdk v0.1.14-0.20200519221530-14615acda45f/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10= -github.com/hashicorp/vault/sdk v0.2.1 h1:S4O6Iv/dyKlE9AUTXGa7VOvZmsCvg36toPKgV4f2P4M= -github.com/hashicorp/vault/sdk v0.2.1/go.mod h1:WfUiO1vYzfBkz1TmoE4ZGU7HD0T0Cl/rZwaxjBkgN4U= +github.com/hashicorp/vault/api v1.8.3 h1:cHQOLcMhBR+aVI0HzhPxO62w2+gJhIrKguQNONPzu6o= +github.com/hashicorp/vault/api v1.8.3/go.mod h1:4g/9lj9lmuJQMtT6CmVMHC5FW1yENaVv+Nv4ZfG8fAg= +github.com/hashicorp/vault/sdk v0.7.0 h1:2pQRO40R1etpKkia5fb4kjrdYMx3BHklPxl1pxpxDHg= +github.com/hashicorp/vault/sdk v0.7.0/go.mod h1:KyfArJkhooyba7gYCKSq8v66QdqJmnbAxtV/OX1+JTs= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -448,10 +409,13 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= -github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -503,7 +467,6 @@ github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= @@ -517,25 +480,25 @@ github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfr github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= +github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.14.0 h1:/x0XQ6h+3U3nAyk1yx+bHPURrKa9sVVvYbuqZ7pIAtI= github.com/mitchellh/go-testing-interface v1.14.0/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo= -github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/pointerstructure v1.2.1 h1:ZhBBeX8tSlRpu/FFhXH4RC4OJzFlqsQhoHZAz4x7TIw= +github.com/mitchellh/pointerstructure v1.2.1/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= @@ -569,24 +532,17 @@ github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGV github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E= -github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/oracle/oci-go-sdk v7.1.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -599,7 +555,6 @@ github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prY github.com/pquerna/otp v1.2.0 h1:/A3+Jn+cagqayeR3iHs/L62m5ue7710D35zl1zJ1kok= github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= @@ -613,7 +568,6 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= @@ -621,9 +575,7 @@ github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9 github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= @@ -650,7 +602,6 @@ github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -659,12 +610,10 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -692,7 +641,6 @@ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo= @@ -706,6 +654,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -715,19 +664,16 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= -golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -735,10 +681,10 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -751,8 +697,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= -golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o= +golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -765,7 +711,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -775,12 +720,14 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -808,7 +755,6 @@ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= @@ -817,8 +763,11 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -840,7 +789,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -849,29 +799,24 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -890,7 +835,6 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -910,11 +854,15 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -926,13 +874,13 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -951,7 +899,6 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -959,7 +906,6 @@ golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -990,13 +936,16 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= 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= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= -gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= +gomodules.xyz/jsonpatch/v2 v2.3.0 h1:8NFhfS6gzxNqjLIYnZxg319wZ5Qjnx4m/CcX+Klzazc= +gomodules.xyz/jsonpatch/v2 v2.3.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= @@ -1056,13 +1005,11 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 h1:hrbNEivu7Zn1pxvHk6MBrq9iE22woVILTHqexqBxe6I= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= @@ -1076,9 +1023,9 @@ google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w= -google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= 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= @@ -1092,9 +1039,9 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= 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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1105,13 +1052,11 @@ gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= @@ -1131,7 +1076,6 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= 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-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From 494837ef1b15320cf2d5ff11dbf5b5bb582fa205 Mon Sep 17 00:00:00 2001 From: malizz Date: Thu, 1 Jun 2023 10:37:29 -0700 Subject: [PATCH 186/340] improve code readability and fix flaky tests re acl token generation (#2210) --- .../create-federation-secret/command_test.go | 148 +++++++++--------- 1 file changed, 78 insertions(+), 70 deletions(-) diff --git a/control-plane/subcommand/create-federation-secret/command_test.go b/control-plane/subcommand/create-federation-secret/command_test.go index c401f1fc7e..16939a2868 100644 --- a/control-plane/subcommand/create-federation-secret/command_test.go +++ b/control-plane/subcommand/create-federation-secret/command_test.go @@ -323,7 +323,7 @@ func TestRun_ACLs_K8SNamespaces_ResourcePrefixes(tt *testing.T) { // Set up Consul server with TLS. caFile, certFile, keyFile := test.GenerateServerCerts(t) - a, err := testutil.NewTestServerConfigT(t, func(cfg *testutil.TestServerConfig) { + testserver, err := testutil.NewTestServerConfigT(t, func(cfg *testutil.TestServerConfig) { cfg.CAFile = caFile cfg.CertFile = certFile cfg.KeyFile = keyFile @@ -333,11 +333,11 @@ func TestRun_ACLs_K8SNamespaces_ResourcePrefixes(tt *testing.T) { } }) require.NoError(t, err) - defer a.Stop() + defer testserver.Stop() // Construct Consul client. client, err := api.NewClient(&api.Config{ - Address: a.HTTPSAddr, + Address: testserver.HTTPSAddr, Scheme: "https", TLSConfig: api.TLSConfig{ CAFile: caFile, @@ -362,7 +362,7 @@ func TestRun_ACLs_K8SNamespaces_ResourcePrefixes(tt *testing.T) { // Redefine the client with the bootstrap token set so // subsequent calls will succeed. client, err = api.NewClient(&api.Config{ - Address: a.HTTPSAddr, + Address: testserver.HTTPSAddr, Scheme: "https", TLSConfig: api.TLSConfig{ CAFile: caFile, @@ -447,7 +447,7 @@ func TestRun_ACLs_K8SNamespaces_ResourcePrefixes(tt *testing.T) { "-ca-file", caFile, "-server-ca-cert-file", caFile, "-server-ca-key-file", keyFile, - "-http-addr", fmt.Sprintf("https://%s", a.HTTPSAddr), + "-http-addr", fmt.Sprintf("https://%s", testserver.HTTPSAddr), "-consul-api-timeout", "10s", } if c.aclsEnabled { @@ -506,27 +506,31 @@ func TestRun_WaitsForMeshGatewayInstances(t *testing.T) { // Set up Consul server with TLS. caFile, certFile, keyFile := test.GenerateServerCerts(t) - a, err := testutil.NewTestServerConfigT(t, func(c *testutil.TestServerConfig) { + testserver, err := testutil.NewTestServerConfigT(t, func(c *testutil.TestServerConfig) { c.CAFile = caFile c.CertFile = certFile c.KeyFile = keyFile }) require.NoError(t, err) - defer a.Stop() + defer testserver.Stop() // Create a mesh gateway instance after a delay. meshGWIP := "192.168.0.1" meshGWPort := 443 go func() { - time.Sleep(500 * time.Millisecond) - client, err := api.NewClient(&api.Config{ - Address: a.HTTPSAddr, - Scheme: "https", - TLSConfig: api.TLSConfig{ - CAFile: caFile, - }, + var client *api.Client + timer := &retry.Timer{Timeout: 10 * time.Second, Wait: 500 * time.Millisecond} + retry.RunWith(timer, t, func(r *retry.R) { + client, err = api.NewClient(&api.Config{ + Address: testserver.HTTPSAddr, + Scheme: "https", + TLSConfig: api.TLSConfig{ + CAFile: caFile, + }, + }) + require.NoError(t, err) }) - require.NoError(t, err) + err = client.Agent().ServiceRegister(&api.AgentServiceRegistration{ Name: "mesh-gateway", TaggedAddresses: map[string]api.ServiceAddress{ @@ -554,7 +558,7 @@ func TestRun_WaitsForMeshGatewayInstances(t *testing.T) { "-ca-file", caFile, "-server-ca-cert-file", certFile, "-server-ca-key-file", keyFile, - "-http-addr", fmt.Sprintf("https://%s", a.HTTPSAddr), + "-http-addr", fmt.Sprintf("https://%s", testserver.HTTPSAddr), "-consul-api-timeout", "10s", }) require.Equal(t, 0, exitCode, ui.ErrorWriter.String()) @@ -575,15 +579,15 @@ func TestRun_MeshGatewayNoWANAddr(t *testing.T) { // Set up Consul server with TLS. caFile, certFile, keyFile := test.GenerateServerCerts(t) - a, err := testutil.NewTestServerConfigT(t, func(c *testutil.TestServerConfig) { + testserver, err := testutil.NewTestServerConfigT(t, func(c *testutil.TestServerConfig) { c.CAFile = caFile c.CertFile = certFile c.KeyFile = keyFile }) require.NoError(t, err) - defer a.Stop() + defer testserver.Stop() client, err := api.NewClient(&api.Config{ - Address: a.HTTPSAddr, + Address: testserver.HTTPSAddr, Scheme: "https", TLSConfig: api.TLSConfig{ CAFile: caFile, @@ -608,7 +612,7 @@ func TestRun_MeshGatewayNoWANAddr(t *testing.T) { "-ca-file", caFile, "-server-ca-cert-file", caFile, "-server-ca-key-file", keyFile, - "-http-addr", fmt.Sprintf("https://%s", a.HTTPSAddr), + "-http-addr", fmt.Sprintf("https://%s", testserver.HTTPSAddr), "-consul-api-timeout", "10s", }) require.Equal(t, 1, exitCode, ui.ErrorWriter.String()) @@ -646,17 +650,17 @@ func TestRun_MeshGatewayUniqueAddrs(tt *testing.T) { // Set up Consul server with TLS. caFile, certFile, keyFile := test.GenerateServerCerts(t) - a, err := testutil.NewTestServerConfigT(t, func(c *testutil.TestServerConfig) { + testserver, err := testutil.NewTestServerConfigT(t, func(c *testutil.TestServerConfig) { c.CAFile = caFile c.CertFile = certFile c.KeyFile = keyFile }) require.NoError(t, err) - defer a.Stop() + defer testserver.Stop() // Create mesh gateway instances. client, err := api.NewClient(&api.Config{ - Address: a.HTTPSAddr, + Address: testserver.HTTPSAddr, Scheme: "https", TLSConfig: api.TLSConfig{ CAFile: caFile, @@ -694,7 +698,7 @@ func TestRun_MeshGatewayUniqueAddrs(tt *testing.T) { "-ca-file", caFile, "-server-ca-cert-file", caFile, "-server-ca-key-file", keyFile, - "-http-addr", fmt.Sprintf("https://%s", a.HTTPSAddr), + "-http-addr", fmt.Sprintf("https://%s", testserver.HTTPSAddr), "-consul-api-timeout", "10s", }) require.Equal(t, 0, exitCode, ui.ErrorWriter.String()) @@ -725,7 +729,7 @@ func TestRun_ReplicationSecretDelay(t *testing.T) { // Set up Consul server with TLS. caFile, certFile, keyFile := test.GenerateServerCerts(t) - a, err := testutil.NewTestServerConfigT(t, func(cfg *testutil.TestServerConfig) { + testserver, err := testutil.NewTestServerConfigT(t, func(cfg *testutil.TestServerConfig) { cfg.CAFile = caFile cfg.CertFile = certFile cfg.KeyFile = keyFile @@ -733,11 +737,11 @@ func TestRun_ReplicationSecretDelay(t *testing.T) { cfg.ACL.DefaultPolicy = "deny" }) require.NoError(t, err) - defer a.Stop() + defer testserver.Stop() // Construct Consul client. client, err := api.NewClient(&api.Config{ - Address: a.HTTPSAddr, + Address: testserver.HTTPSAddr, Scheme: "https", TLSConfig: api.TLSConfig{ CAFile: caFile, @@ -762,7 +766,7 @@ func TestRun_ReplicationSecretDelay(t *testing.T) { // Redefine the client with the bootstrap token set so // subsequent calls will succeed. client, err = api.NewClient(&api.Config{ - Address: a.HTTPSAddr, + Address: testserver.HTTPSAddr, Scheme: "https", TLSConfig: api.TLSConfig{ CAFile: caFile, @@ -807,20 +811,22 @@ func TestRun_ReplicationSecretDelay(t *testing.T) { // Create replication token secret after a delay. go func() { - time.Sleep(400 * time.Millisecond) - _, err := k8s.CoreV1().Secrets("default").Create( - context.Background(), - &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "prefix-" + common.ACLReplicationTokenName + "-acl-token", - Labels: map[string]string{common.CLILabelKey: common.CLILabelValue}, - }, - Data: map[string][]byte{ - common.ACLTokenSecretKey: []byte(replicationToken), + timer := &retry.Timer{Timeout: 6 * time.Second, Wait: 400 * time.Millisecond} + retry.RunWith(timer, t, func(r *retry.R) { + _, err := k8s.CoreV1().Secrets("default").Create( + context.Background(), + &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "prefix-" + common.ACLReplicationTokenName + "-acl-token", + Labels: map[string]string{common.CLILabelKey: common.CLILabelValue}, + }, + Data: map[string][]byte{ + common.ACLTokenSecretKey: []byte(replicationToken), + }, }, - }, - metav1.CreateOptions{}) - require.NoError(t, err) + metav1.CreateOptions{}) + require.NoError(t, err) + }) }() // Run the command. @@ -836,7 +842,7 @@ func TestRun_ReplicationSecretDelay(t *testing.T) { "-ca-file", caFile, "-server-ca-cert-file", caFile, "-server-ca-key-file", keyFile, - "-http-addr", fmt.Sprintf("https://%s", a.HTTPSAddr), + "-http-addr", fmt.Sprintf("https://%s", testserver.HTTPSAddr), "-export-replication-token", "-consul-api-timeout", "10s", } @@ -860,17 +866,17 @@ func TestRun_UpdatesSecret(t *testing.T) { // Set up Consul server with TLS. caFile, certFile, keyFile := test.GenerateServerCerts(t) - a, err := testutil.NewTestServerConfigT(t, func(c *testutil.TestServerConfig) { + testserver, err := testutil.NewTestServerConfigT(t, func(c *testutil.TestServerConfig) { c.CAFile = caFile c.CertFile = certFile c.KeyFile = keyFile }) require.NoError(t, err) - defer a.Stop() + defer testserver.Stop() // Create a mesh gateway instance. client, err := api.NewClient(&api.Config{ - Address: a.HTTPSAddr, + Address: testserver.HTTPSAddr, Scheme: "https", TLSConfig: api.TLSConfig{ CAFile: caFile, @@ -907,7 +913,7 @@ func TestRun_UpdatesSecret(t *testing.T) { "-ca-file", caFile, "-server-ca-cert-file", certFile, "-server-ca-key-file", keyFile, - "-http-addr", fmt.Sprintf("https://%s", a.HTTPSAddr), + "-http-addr", fmt.Sprintf("https://%s", testserver.HTTPSAddr), "-consul-api-timeout", "10s", }) require.Equal(t, 0, exitCode, ui.ErrorWriter.String()) @@ -949,7 +955,7 @@ func TestRun_UpdatesSecret(t *testing.T) { "-ca-file", caFile, "-server-ca-cert-file", caFile, "-server-ca-key-file", keyFile, - "-http-addr", fmt.Sprintf("https://%s", a.HTTPSAddr), + "-http-addr", fmt.Sprintf("https://%s", testserver.HTTPSAddr), "-consul-api-timeout", "10s", }) require.Equal(t, 0, exitCode, ui.ErrorWriter.String()) @@ -978,31 +984,33 @@ func TestRun_ConsulClientDelay(t *testing.T) { k8s := fake.NewSimpleClientset() // Set up Consul server with TLS. Start after a 500ms delay. - var a *testutil.TestServer + var testserver *testutil.TestServer wg := sync.WaitGroup{} wg.Add(1) go func() { defer wg.Done() - time.Sleep(500 * time.Millisecond) - var err error - a, err = testutil.NewTestServerConfigT(t, func(cfg *testutil.TestServerConfig) { - cfg.CAFile = caFile - cfg.CertFile = certFile - cfg.KeyFile = keyFile - cfg.Ports = &testutil.TestPortConfig{ - DNS: randomPorts[0], - HTTP: randomPorts[1], - HTTPS: randomPorts[2], - SerfLan: randomPorts[3], - SerfWan: randomPorts[4], - Server: randomPorts[5], - } + timer := &retry.Timer{Timeout: 10 * time.Second, Wait: 500 * time.Millisecond} + retry.RunWith(timer, t, func(r *retry.R) { + var err error + testserver, err = testutil.NewTestServerConfigT(t, func(cfg *testutil.TestServerConfig) { + cfg.CAFile = caFile + cfg.CertFile = certFile + cfg.KeyFile = keyFile + cfg.Ports = &testutil.TestPortConfig{ + DNS: randomPorts[0], + HTTP: randomPorts[1], + HTTPS: randomPorts[2], + SerfLan: randomPorts[3], + SerfWan: randomPorts[4], + Server: randomPorts[5], + } + }) + require.NoError(t, err) }) - require.NoError(t, err) // Construct Consul client. client, err := api.NewClient(&api.Config{ - Address: a.HTTPSAddr, + Address: testserver.HTTPSAddr, Scheme: "https", TLSConfig: api.TLSConfig{ CAFile: caFile, @@ -1025,8 +1033,8 @@ func TestRun_ConsulClientDelay(t *testing.T) { require.NoError(t, err) }() defer func() { - if a != nil { - a.Stop() + if testserver != nil { + testserver.Stop() } }() @@ -1065,17 +1073,17 @@ func TestRun_Autoencrypt(t *testing.T) { // Set up Consul server with TLS. caFile, certFile, keyFile := test.GenerateServerCerts(t) - a, err := testutil.NewTestServerConfigT(t, func(c *testutil.TestServerConfig) { + testserver, err := testutil.NewTestServerConfigT(t, func(c *testutil.TestServerConfig) { c.CAFile = caFile c.CertFile = certFile c.KeyFile = keyFile }) require.NoError(t, err) - defer a.Stop() + defer testserver.Stop() // Create a mesh gateway instance. client, err := api.NewClient(&api.Config{ - Address: a.HTTPSAddr, + Address: testserver.HTTPSAddr, Scheme: "https", TLSConfig: api.TLSConfig{ CAFile: caFile, @@ -1111,7 +1119,7 @@ func TestRun_Autoencrypt(t *testing.T) { // was being used as the CA (since it's not a CA). "-server-ca-cert-file", keyFile, "-server-ca-key-file", keyFile, - "-http-addr", fmt.Sprintf("https://%s", a.HTTPSAddr), + "-http-addr", fmt.Sprintf("https://%s", testserver.HTTPSAddr), "-consul-api-timeout", "10s", }) require.Equal(t, 0, exitCode, ui.ErrorWriter.String()) From 10c582f1f3ed0e6fb8271a6d3aaa67140a1ab095 Mon Sep 17 00:00:00 2001 From: chappie <6537530+chapmanc@users.noreply.github.com> Date: Thu, 1 Jun 2023 12:52:25 -0700 Subject: [PATCH 187/340] Increase timeout and backoff for retry on flaky test (#2242) --- control-plane/subcommand/get-consul-client-ca/command_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/control-plane/subcommand/get-consul-client-ca/command_test.go b/control-plane/subcommand/get-consul-client-ca/command_test.go index 95ff4dbfbb..68bdd918b1 100644 --- a/control-plane/subcommand/get-consul-client-ca/command_test.go +++ b/control-plane/subcommand/get-consul-client-ca/command_test.go @@ -203,9 +203,10 @@ func TestRun_ConsulServerAvailableLater(t *testing.T) { }) require.NoError(t, err) + retrier := &retry.Timer{Timeout: 20 * time.Second, Wait: 1 * time.Second} // get the actual ca cert from consul var expectedCARoot string - retry.Run(t, func(r *retry.R) { + retry.RunWith(retrier, t, func(r *retry.R) { roots, _, err := client.Agent().ConnectCARoots(nil) require.NoError(r, err) require.NotNil(r, roots) From 46055a324f8b1fcfb6e6debf95f5d059d7b50fe4 Mon Sep 17 00:00:00 2001 From: Joshua Timmons Date: Thu, 1 Jun 2023 19:22:48 -0400 Subject: [PATCH 188/340] Add fake demo/crds to get around that expectation in chart install (#2245) --- charts/demo/crds/blank | 4 ++++ charts/demo/templates/intentions.yaml | 11 +++++++++++ charts/embed_chart.go | 4 ++-- 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 charts/demo/crds/blank diff --git a/charts/demo/crds/blank b/charts/demo/crds/blank new file mode 100644 index 0000000000..a6560dc716 --- /dev/null +++ b/charts/demo/crds/blank @@ -0,0 +1,4 @@ +This is here purely so we can embed it so the HelmDeployment that references does not fail. Otherwise, at the time of writing, we get: + +==> Installing Consul demo application + ! open demo/crds: file does not exist \ No newline at end of file diff --git a/charts/demo/templates/intentions.yaml b/charts/demo/templates/intentions.yaml index a268e4fa4b..ef36025b16 100644 --- a/charts/demo/templates/intentions.yaml +++ b/charts/demo/templates/intentions.yaml @@ -58,6 +58,17 @@ spec: --- apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceIntentions +metadata: + name: consul-telemetry-collector +spec: + destination: + name: 'consul-telemetry-collector' + sources: + - name: '*' + action: allow +--- +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ServiceIntentions metadata: name: deny-all spec: diff --git a/charts/embed_chart.go b/charts/embed_chart.go index bb3898a421..72650e8819 100644 --- a/charts/embed_chart.go +++ b/charts/embed_chart.go @@ -15,9 +15,9 @@ import "embed" // // The embed directive does not include files with underscores unless explicitly listed, which is why _helpers.tpl is // explicitly embedded. -// + //go:embed consul/Chart.yaml consul/values.yaml consul/crds consul/templates consul/templates/_helpers.tpl var ConsulHelmChart embed.FS -//go:embed demo/Chart.yaml demo/values.yaml demo/templates +//go:embed demo/Chart.yaml demo/values.yaml demo/crds/blank demo/templates var DemoHelmChart embed.FS From d4b8c733f639f58c7bed43755d880d4fc6c26f22 Mon Sep 17 00:00:00 2001 From: chappie <6537530+chapmanc@users.noreply.github.com> Date: Thu, 1 Jun 2023 20:08:04 -0700 Subject: [PATCH 189/340] NET-4285 add check for pointer (#2246) --- .../api/v1alpha1/servicedefaults_types.go | 10 ++- .../v1alpha1/servicedefaults_types_test.go | 85 +++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/control-plane/api/v1alpha1/servicedefaults_types.go b/control-plane/api/v1alpha1/servicedefaults_types.go index cfb8865bdf..2f3b95a297 100644 --- a/control-plane/api/v1alpha1/servicedefaults_types.go +++ b/control-plane/api/v1alpha1/servicedefaults_types.go @@ -7,6 +7,7 @@ import ( "fmt" "net" "strings" + "time" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" @@ -465,13 +466,20 @@ func (in *PassiveHealthCheck) toConsul() *capi.PassiveHealthCheck { if in == nil { return nil } + var baseEjectiontime *time.Duration + if in.BaseEjectionTime == nil { + dur := time.Second * 30 + baseEjectiontime = &dur + } else { + baseEjectiontime = &in.BaseEjectionTime.Duration + } return &capi.PassiveHealthCheck{ Interval: in.Interval.Duration, MaxFailures: in.MaxFailures, EnforcingConsecutive5xx: in.EnforcingConsecutive5xx, MaxEjectionPercent: in.MaxEjectionPercent, - BaseEjectionTime: &in.BaseEjectionTime.Duration, + BaseEjectionTime: baseEjectiontime, } } diff --git a/control-plane/api/v1alpha1/servicedefaults_types_test.go b/control-plane/api/v1alpha1/servicedefaults_types_test.go index 0a5a59e4d1..31a41f3f06 100644 --- a/control-plane/api/v1alpha1/servicedefaults_types_test.go +++ b/control-plane/api/v1alpha1/servicedefaults_types_test.go @@ -330,6 +330,91 @@ func TestServiceDefaults_ToConsul(t *testing.T) { } } +func TestPasstiveHealthCheckConsul(t *testing.T) { + baseDur := time.Second * 30 + baseEjection := time.Second * 60 + baseInt := uint32(1) + for name, tc := range map[string]struct { + input *PassiveHealthCheck + output *capi.PassiveHealthCheck + }{ + "basenil": {}, + "base": { + input: &PassiveHealthCheck{}, + output: &capi.PassiveHealthCheck{BaseEjectionTime: &baseDur}, + }, + "with_interval": { + input: &PassiveHealthCheck{ + Interval: metav1.Duration{Duration: baseDur}, + }, + output: &capi.PassiveHealthCheck{ + Interval: time.Second * 30, + BaseEjectionTime: &baseDur, + }, + }, + "with_interval_maxfailures": { + input: &PassiveHealthCheck{ + Interval: metav1.Duration{Duration: baseDur}, + MaxFailures: 100, + }, + output: &capi.PassiveHealthCheck{ + MaxFailures: 100, + Interval: time.Second * 30, + BaseEjectionTime: &baseDur, + }, + }, + "with_interval_maxfailures_enforcing": { + input: &PassiveHealthCheck{ + Interval: metav1.Duration{Duration: baseDur}, + MaxFailures: 100, + EnforcingConsecutive5xx: &baseInt, + }, + output: &capi.PassiveHealthCheck{ + MaxFailures: 100, + Interval: time.Second * 30, + BaseEjectionTime: &baseDur, + EnforcingConsecutive5xx: &baseInt, + }, + }, + "with_interval_maxfailures_enforcing_maxejection": { + input: &PassiveHealthCheck{ + Interval: metav1.Duration{Duration: baseDur}, + MaxFailures: 100, + EnforcingConsecutive5xx: &baseInt, + MaxEjectionPercent: &baseInt, + }, + output: &capi.PassiveHealthCheck{ + MaxFailures: 100, + Interval: time.Second * 30, + BaseEjectionTime: &baseDur, + EnforcingConsecutive5xx: &baseInt, + MaxEjectionPercent: &baseInt, + }, + }, + "with_interval_maxfailures_enforcing_maxejection_baseejection": { + input: &PassiveHealthCheck{ + Interval: metav1.Duration{Duration: baseDur}, + MaxFailures: 100, + EnforcingConsecutive5xx: &baseInt, + MaxEjectionPercent: &baseInt, + BaseEjectionTime: &metav1.Duration{Duration: baseEjection}, + }, + output: &capi.PassiveHealthCheck{ + MaxFailures: 100, + Interval: time.Second * 30, + BaseEjectionTime: &baseEjection, + EnforcingConsecutive5xx: &baseInt, + MaxEjectionPercent: &baseInt, + }, + }, + } { + t.Run(name, func(t *testing.T) { + output := tc.input.toConsul() + require.Equal(t, tc.output, output) + }) + } +} + func TestServiceDefaults_MatchesConsul(t *testing.T) { cases := map[string]struct { internal *ServiceDefaults From 7b6e5eba6e49c97abb11d9832d2f7f6a6cab6d6d Mon Sep 17 00:00:00 2001 From: Derek Menteer <105233703+hashi-derek@users.noreply.github.com> Date: Fri, 2 Jun 2023 09:11:34 -0500 Subject: [PATCH 190/340] Persist virtual-ips for intentions / service-defaults. (#2222) --- .../controllers/configentry_controller.go | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/control-plane/controllers/configentry_controller.go b/control-plane/controllers/configentry_controller.go index 374af7451e..133afc0a1c 100644 --- a/control-plane/controllers/configentry_controller.go +++ b/control-plane/controllers/configentry_controller.go @@ -270,7 +270,7 @@ func (r *ConfigEntryController) ReconcileEntry(ctx context.Context, crdCtrl Cont // For resolvers and splitters, we need to set the ClusterIP of the matching service to Consul so that transparent // proxy works correctly. Do not fail the reconcile if assigning the virtual IP returns an error. - if needsVirtualIPAssignment(configEntry) { + if needsVirtualIPAssignment(r.DatacenterName, configEntry) { err = assignServiceVirtualIP(ctx, logger, consulClient, crdCtrl, req.NamespacedName, configEntry, r.DatacenterName) if err != nil { logger.Error(err, "failed assigning service virtual ip") @@ -392,10 +392,27 @@ func (r *ConfigEntryController) nonMatchingMigrationError(kubeEntry common.Confi } // needsVirtualIPAssignment checks to see if a configEntry type needs to be assigned a virtual IP. -func needsVirtualIPAssignment(configEntry common.ConfigEntryResource) bool { - kubeKind := configEntry.KubeKind() - if kubeKind == common.ServiceResolver || kubeKind == common.ServiceRouter || kubeKind == common.ServiceSplitter { +func needsVirtualIPAssignment(datacenterName string, configEntry common.ConfigEntryResource) bool { + switch configEntry.KubeKind() { + case common.ServiceResolver: return true + case common.ServiceRouter: + return true + case common.ServiceSplitter: + return true + case common.ServiceDefaults: + return true + case common.ServiceIntentions: + entry := configEntry.ToConsul(datacenterName) + intention, ok := entry.(*capi.ServiceIntentionsConfigEntry) + if !ok { + return false + } + // We should not persist virtual ips if the destination is a wildcard + // in any form, since that would target multiple services. + return !strings.Contains(intention.Name, "*") && + !strings.Contains(intention.Namespace, "*") && + !strings.Contains(intention.Partition, "*") } return false } @@ -422,13 +439,14 @@ func assignServiceVirtualIP(ctx context.Context, logger logr.Logger, consulClien return err } + consulType := configEntry.ToConsul(datacenter) wo := &capi.WriteOptions{ - Namespace: configEntry.ToConsul(datacenter).GetNamespace(), - Partition: configEntry.ToConsul(datacenter).GetPartition(), + Namespace: consulType.GetNamespace(), + Partition: consulType.GetPartition(), } logger.Info("adding manual ip to virtual ip table in Consul", "name", service.Name) - _, _, err := consulClient.Internal().AssignServiceVirtualIP(ctx, configEntry.KubernetesName(), []string{service.Spec.ClusterIP}, wo) + _, _, err := consulClient.Internal().AssignServiceVirtualIP(ctx, consulType.GetName(), []string{service.Spec.ClusterIP}, wo) if err != nil { // Maintain backwards compatibility with older versions of Consul that do not support the manual VIP improvements. With the older version, the mesh // will still work. From b922ef2d3760744143f8cf606891bd091b46091a Mon Sep 17 00:00:00 2001 From: Andrew Stucki Date: Fri, 2 Jun 2023 11:28:18 -0400 Subject: [PATCH 191/340] Allow API Gateways to bind to privileged ports (#2253) --- acceptance/tests/api-gateway/api_gateway_test.go | 2 +- .../fixtures/bases/api-gateway/apigateway.yaml | 6 +++--- control-plane/api-gateway/gatekeeper/dataplane.go | 15 +++++++++++---- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/acceptance/tests/api-gateway/api_gateway_test.go b/acceptance/tests/api-gateway/api_gateway_test.go index 006f1456d3..94a91e79c2 100644 --- a/acceptance/tests/api-gateway/api_gateway_test.go +++ b/acceptance/tests/api-gateway/api_gateway_test.go @@ -175,7 +175,7 @@ func TestAPIGateway_Basic(t *testing.T) { // finally we check that we can actually route to the service via the gateway k8sOptions := ctx.KubectlOptions(t) - targetAddress := fmt.Sprintf("http://%s:8080/", gatewayAddress) + targetAddress := fmt.Sprintf("http://%s/", gatewayAddress) if c.secure { // check that intentions keep our connection from happening diff --git a/acceptance/tests/fixtures/bases/api-gateway/apigateway.yaml b/acceptance/tests/fixtures/bases/api-gateway/apigateway.yaml index f0e4eddc03..2a355e1b2f 100644 --- a/acceptance/tests/fixtures/bases/api-gateway/apigateway.yaml +++ b/acceptance/tests/fixtures/bases/api-gateway/apigateway.yaml @@ -9,19 +9,19 @@ spec: gatewayClassName: gateway-class listeners: - protocol: HTTP - port: 8080 + port: 80 name: http allowedRoutes: namespaces: from: "All" - protocol: TCP - port: 8081 + port: 81 name: tcp allowedRoutes: namespaces: from: "All" - protocol: HTTPS - port: 8082 + port: 443 name: https tls: certificateRefs: diff --git a/control-plane/api-gateway/gatekeeper/dataplane.go b/control-plane/api-gateway/gatekeeper/dataplane.go index 90cd186743..36829e2b7c 100644 --- a/control-plane/api-gateway/gatekeeper/dataplane.go +++ b/control-plane/api-gateway/gatekeeper/dataplane.go @@ -17,9 +17,10 @@ import ( ) const ( + allCapabilities = "all" + netBindCapability = "NET_BIND_SERVICE" consulDataplaneDNSBindHost = "127.0.0.1" consulDataplaneDNSBindPort = 8600 - sidecarUserAndGroupID = 5995 defaultPrometheusScrapePath = "/metrics" defaultEnvoyProxyConcurrency = 1 volumeName = "consul-connect-inject-data" @@ -103,10 +104,16 @@ func consulDataplaneContainer(config common.HelmConfig, name, namespace string) // skip setting the security context and let OpenShift set it for us. if !config.EnableOpenShift { container.SecurityContext = &corev1.SecurityContext{ - RunAsUser: pointer.Int64(sidecarUserAndGroupID), - RunAsGroup: pointer.Int64(sidecarUserAndGroupID), - RunAsNonRoot: pointer.Bool(true), ReadOnlyRootFilesystem: pointer.Bool(true), + // We have to run as root if we want to bind to any + // sort of privileged ports. The drop "all" is intended + // to drop any Linux capabilities you'd get as root + // other than NET_BIND_SERVICE. + RunAsUser: pointer.Int64(0), + Capabilities: &corev1.Capabilities{ + Add: []corev1.Capability{netBindCapability}, + Drop: []corev1.Capability{allCapabilities}, + }, } } From f9ad99446a23222f732b3f9858bcdb8f3e47f842 Mon Sep 17 00:00:00 2001 From: Andrew Stucki Date: Fri, 2 Jun 2023 11:46:43 -0400 Subject: [PATCH 192/340] API Gateway lifecycle acceptance tests (#2248) * initial test * More lifecycle work * functional lifecycle tests --- acceptance/go.mod | 11 + acceptance/go.sum | 20 +- .../api-gateway/api_gateway_lifecycle_test.go | 444 ++++++++++++++++++ .../api-gateway/api_gateway_tenancy_test.go | 36 +- control-plane/api-gateway/binding/binder.go | 11 - .../api-gateway/binding/binder_test.go | 45 +- .../api-gateway/binding/route_binding.go | 23 +- control-plane/api-gateway/common/resources.go | 142 +++--- .../controllers/gateway_controller.go | 80 ++-- .../controllers/gateway_controller_test.go | 297 ++++++++++++ .../api-gateway/controllers/index.go | 18 +- 11 files changed, 966 insertions(+), 161 deletions(-) create mode 100644 acceptance/tests/api-gateway/api_gateway_lifecycle_test.go create mode 100644 control-plane/api-gateway/controllers/gateway_controller_test.go diff --git a/acceptance/go.mod b/acceptance/go.mod index 88507a2d54..a63e1187fe 100644 --- a/acceptance/go.mod +++ b/acceptance/go.mod @@ -26,14 +26,18 @@ require ( github.com/aws/aws-sdk-go v1.44.262 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect + github.com/cenkalti/backoff v2.2.1+incompatible // indirect github.com/cenkalti/backoff/v3 v3.0.0 // indirect + github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/deckarep/golang-set v1.7.1 // indirect github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/fatih/color v1.13.0 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-logr/logr v1.2.3 // indirect @@ -42,6 +46,7 @@ require ( github.com/go-openapi/swag v0.22.3 // indirect github.com/go-sql-driver/mysql v1.5.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect @@ -49,12 +54,15 @@ require ( github.com/google/gofuzz v1.1.0 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gruntwork-io/gruntwork-cli v0.7.0 // indirect + github.com/hashicorp/consul-server-connection-manager v0.1.2 // indirect + github.com/hashicorp/consul/proto-public v0.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-bexpr v0.1.11 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v1.2.2 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-netaddrs v0.1.0 // indirect github.com/hashicorp/go-plugin v1.4.5 // indirect github.com/hashicorp/go-retryablehttp v0.6.6 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect @@ -97,6 +105,7 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/objx v0.5.0 // indirect github.com/urfave/cli v1.22.2 // indirect go.uber.org/atomic v1.9.0 // indirect golang.org/x/crypto v0.1.0 // indirect @@ -117,6 +126,8 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/square/go-jose.v2 v2.5.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/apiextensions-apiserver v0.26.3 // indirect + k8s.io/component-base v0.26.3 // indirect k8s.io/klog/v2 v2.100.1 // indirect k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect diff --git a/acceptance/go.sum b/acceptance/go.sum index c0a0a6d870..1c9bd2ad25 100644 --- a/acceptance/go.sum +++ b/acceptance/go.sum @@ -104,8 +104,12 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c= github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= +github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= +github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -143,6 +147,8 @@ github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2 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/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= +github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= @@ -189,6 +195,7 @@ github.com/frankban/quicktest v1.13.0 h1:yNZif1OkDfNoDfb9zZa9aXIpejNR4F23Wely0c+ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -247,6 +254,7 @@ github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -333,8 +341,12 @@ github.com/gruntwork-io/terratest v0.31.2 h1:xvYHA80MUq5kx670dM18HInewOrrQrAN+Xb github.com/gruntwork-io/terratest v0.31.2/go.mod h1:EEgJie28gX/4AD71IFqgMj6e99KP5mi81hEtzmDjxTo= github.com/hashicorp/consul-k8s/control-plane v0.0.0-20230601034256-0c28b9b000cb h1:9GUvDoKVoV3IW78QyfoNY4bRcKxcn26wTGLoBrz92N4= github.com/hashicorp/consul-k8s/control-plane v0.0.0-20230601034256-0c28b9b000cb/go.mod h1:jKzTEgDc/np2gX/KPdfdm1mEUfZLrU8gc71XN3B15VI= +github.com/hashicorp/consul-server-connection-manager v0.1.2 h1:tNVQHUPuMbd+cMdD8kd+qkZUYpmLmrHMAV/49f4L53I= +github.com/hashicorp/consul-server-connection-manager v0.1.2/go.mod h1:NzQoVi1KcxGI2SangsDue8+ZPuXZWs+6BKAKrDNyg+w= github.com/hashicorp/consul/api v1.10.1-0.20230530193107-04a0d0133ae4 h1:6kUTk+YBgA5X5b3gNAoI18WEK4/t75LcWSimEgmpFdg= github.com/hashicorp/consul/api v1.10.1-0.20230530193107-04a0d0133ae4/go.mod h1:tXfrC6o0yFTgAW46xd5Ic8STHc9oIBcRVBcwhX5KNCQ= +github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3usQMXZF6ziK3SzXU= +github.com/hashicorp/consul/proto-public v0.1.0/go.mod h1:vs2KkuWwtjkIgA5ezp4YKPzQp4GitV+q/+PvksrA92k= github.com/hashicorp/consul/sdk v0.13.1 h1:EygWVWWMczTzXGpO93awkHFzfUka6hLYJ0qhETd+6lY= github.com/hashicorp/consul/sdk v0.13.1/go.mod h1:SW/mM4LbKfqmMvcFu8v+eiQQ7oitXEFeiBe9StxERb0= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -358,6 +370,8 @@ github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHh github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-netaddrs v0.1.0 h1:TnlYvODD4C/wO+j7cX1z69kV5gOzI87u3OcUinANaW8= +github.com/hashicorp/go-netaddrs v0.1.0/go.mod h1:33+a/emi5R5dqRspOuZKO0E+Tuz5WV1F84eRWALkedA= github.com/hashicorp/go-plugin v1.4.5 h1:oTE/oQR4eghggRg8VY7PAz3dr++VwDNBGCcOfIvHpBo= github.com/hashicorp/go-plugin v1.4.5/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= @@ -666,6 +680,7 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= @@ -857,6 +872,7 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1088,6 +1104,7 @@ k8s.io/api v0.19.3/go.mod h1:VF+5FT1B74Pw3KxMdKyinLo+zynBaMBiAfGMuldcNDs= k8s.io/api v0.26.3 h1:emf74GIQMTik01Aum9dPP0gAypL8JTLl/lHa4V9RFSU= k8s.io/api v0.26.3/go.mod h1:PXsqwPMXBSBcL1lJ9CYDKy7kIReUydukS5JiRlxC3qE= k8s.io/apiextensions-apiserver v0.26.3 h1:5PGMm3oEzdB1W/FTMgGIDmm100vn7IaUP5er36dB+YE= +k8s.io/apiextensions-apiserver v0.26.3/go.mod h1:jdA5MdjNWGP+njw1EKMZc64xAT5fIhN6VJrElV3sfpQ= k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= k8s.io/apimachinery v0.19.3/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= k8s.io/apimachinery v0.26.3 h1:dQx6PNETJ7nODU3XPtrwkfuubs6w7sX0M8n61zHIV/k= @@ -1100,7 +1117,8 @@ k8s.io/client-go v0.26.3/go.mod h1:ZPNu9lm8/dbRIPAgteN30RSXea6vrCpFvq+MateTUuQ= k8s.io/cloud-provider v0.17.0/go.mod h1:Ze4c3w2C0bRsjkBUoHpFi+qWe3ob1wI2/7cUn+YQIDE= k8s.io/code-generator v0.0.0-20191121015212-c4c8f8345c7e/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= k8s.io/component-base v0.17.0/go.mod h1:rKuRAokNMY2nn2A6LP/MiwpoaMRHpfRnrPaUJJj1Yoc= -k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4= +k8s.io/component-base v0.26.3 h1:oC0WMK/ggcbGDTkdcqefI4wIZRYdK3JySx9/HADpV0g= +k8s.io/component-base v0.26.3/go.mod h1:5kj1kZYwSC6ZstHJN7oHBqcJC6yyn41eR+Sqa/mQc8E= k8s.io/csi-translation-lib v0.17.0/go.mod h1:HEF7MEz7pOLJCnxabi45IPkhSsE/KmxPQksuCrHKWls= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= diff --git a/acceptance/tests/api-gateway/api_gateway_lifecycle_test.go b/acceptance/tests/api-gateway/api_gateway_lifecycle_test.go new file mode 100644 index 0000000000..c07075a709 --- /dev/null +++ b/acceptance/tests/api-gateway/api_gateway_lifecycle_test.go @@ -0,0 +1,444 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package apigateway + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/consul-k8s/acceptance/framework/consul" + "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" + "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" + "github.com/hashicorp/consul-k8s/acceptance/framework/logger" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/sdk/testutil/retry" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +func TestAPIGateway_Lifecycle(t *testing.T) { + ctx := suite.Environment().DefaultContext(t) + cfg := suite.Config() + helmValues := map[string]string{ + "global.logLevel": "trace", + "connectInject.enabled": "true", + } + + releaseName := helpers.RandomName() + consulCluster := consul.NewHelmCluster(t, helmValues, ctx, cfg, releaseName) + + consulCluster.Create(t) + + k8sClient := ctx.ControllerRuntimeClient(t) + consulClient, _ := consulCluster.SetupConsulClient(t, false) + + defaultNamespace := "default" + + // create a service to target + targetName := "static-server" + logger.Log(t, "creating target server") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + + // create a basic GatewayClassConfig + gatewayClassConfigName := "controlled-gateway-class-config" + gatewayClassConfig := &v1alpha1.GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: gatewayClassConfigName, + }, + } + logger.Log(t, "creating gateway class config") + err := k8sClient.Create(context.Background(), gatewayClassConfig) + require.NoError(t, err) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + logger.Log(t, "deleting all gateway class configs") + k8sClient.DeleteAllOf(context.Background(), &v1alpha1.GatewayClassConfig{}) + }) + + gatewayParametersRef := &gwv1beta1.ParametersReference{ + Group: gwv1beta1.Group(v1alpha1.ConsulHashicorpGroup), + Kind: gwv1beta1.Kind(v1alpha1.GatewayClassConfigKind), + Name: gatewayClassConfigName, + } + + // create three gateway classes, two we control, one we don't + controlledGatewayClassOneName := "controlled-gateway-class-one" + logger.Log(t, "creating controlled gateway class one") + createGatewayClass(t, k8sClient, controlledGatewayClassOneName, gatewayClassControllerName, gatewayParametersRef) + + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + logger.Log(t, "deleting all gateway classes") + k8sClient.DeleteAllOf(context.Background(), &gwv1beta1.GatewayClass{}) + }) + + controlledGatewayClassTwoName := "controlled-gateway-class-two" + logger.Log(t, "creating controlled gateway class two") + createGatewayClass(t, k8sClient, controlledGatewayClassTwoName, gatewayClassControllerName, gatewayParametersRef) + + uncontrolledGatewayClassName := "uncontrolled-gateway-class" + logger.Log(t, "creating uncontrolled gateway class") + createGatewayClass(t, k8sClient, uncontrolledGatewayClassName, "example.com/some-controller", nil) + + // Create a certificate to reference in listeners + certificateInfo := generateCertificate(t, nil, "certificate.consul.local") + certificateName := "certificate" + certificate := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: certificateName, + Namespace: defaultNamespace, + Labels: map[string]string{ + "test-certificate": "true", + }, + }, + Type: corev1.SecretTypeTLS, + Data: map[string][]byte{ + corev1.TLSCertKey: certificateInfo.CertPEM, + corev1.TLSPrivateKeyKey: certificateInfo.PrivateKeyPEM, + }, + } + logger.Log(t, "creating certificate") + err = k8sClient.Create(context.Background(), certificate) + require.NoError(t, err) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8sClient.Delete(context.Background(), certificate) + }) + + // Create three gateways with a basic HTTPS listener to correspond to the three classes + controlledGatewayOneName := "controlled-gateway-one" + logger.Log(t, "creating controlled gateway one") + controlledGatewayOne := createGateway(t, k8sClient, controlledGatewayOneName, defaultNamespace, controlledGatewayClassOneName, certificateName) + + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + logger.Log(t, "deleting all gateways") + k8sClient.DeleteAllOf(context.Background(), &gwv1beta1.Gateway{}, client.InNamespace(defaultNamespace)) + }) + + controlledGatewayTwoName := "controlled-gateway-two" + logger.Log(t, "creating controlled gateway two") + controlledGatewayTwo := createGateway(t, k8sClient, controlledGatewayTwoName, defaultNamespace, controlledGatewayClassTwoName, certificateName) + + uncontrolledGatewayName := "uncontrolled-gateway" + logger.Log(t, "creating uncontrolled gateway") + _ = createGateway(t, k8sClient, uncontrolledGatewayName, defaultNamespace, uncontrolledGatewayClassName, certificateName) + + // create two http routes associated with the first controlled gateway + routeOneName := "route-one" + logger.Log(t, "creating route one") + routeOne := createRoute(t, k8sClient, routeOneName, defaultNamespace, controlledGatewayOneName, targetName) + + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + logger.Log(t, "deleting all http routes") + k8sClient.DeleteAllOf(context.Background(), &gwv1beta1.HTTPRoute{}, client.InNamespace(defaultNamespace)) + }) + + routeTwoName := "route-two" + logger.Log(t, "creating route two") + routeTwo := createRoute(t, k8sClient, routeTwoName, defaultNamespace, controlledGatewayTwoName, targetName) + + // Scenario: Swapping a route to another controlled gateway should clean up the old parent statuses and references on Consul resources + + // check that the route is bound properly and objects are reflected in Consul + logger.Log(t, "checking that http route one is bound to gateway one") + checkRouteBound(t, k8sClient, routeOneName, defaultNamespace, controlledGatewayOneName) + + logger.Log(t, "checking that http route one is synchronized to Consul") + checkConsulRouteParent(t, consulClient, routeOneName, controlledGatewayOneName) + + // update the route to point to the other controlled gateway + logger.Log(t, "updating route one to be bound to gateway two") + updateKubernetes(t, k8sClient, routeOne, func(r *gwv1beta1.HTTPRoute) { + r.Spec.ParentRefs[0].Name = gwv1beta1.ObjectName(controlledGatewayTwoName) + }) + + // check that the route is bound properly and objects are reflected in Consul + logger.Log(t, "checking that http route one is bound to gateway two") + checkRouteBound(t, k8sClient, routeOneName, defaultNamespace, controlledGatewayTwoName) + + logger.Log(t, "checking that http route one is synchronized to Consul") + checkConsulRouteParent(t, consulClient, routeOneName, controlledGatewayTwoName) + + // Scenario: Binding a route to a controlled gateway and then associating it with another gateway we don’t control should clean up Consul resources, route statuses, and finalizers + // check that the route is bound properly and objects are reflected in Consul + + // check that our second http route is bound properly + logger.Log(t, "checking that http route two is bound to gateway two") + checkRouteBound(t, k8sClient, routeTwoName, defaultNamespace, controlledGatewayTwoName) + + logger.Log(t, "checking that http route two is synchronized to Consul") + checkConsulRouteParent(t, consulClient, routeTwoName, controlledGatewayTwoName) + + // update the route to point to the uncontrolled gateway + logger.Log(t, "updating route two to be bound to an uncontrolled gateway") + updateKubernetes(t, k8sClient, routeTwo, func(r *gwv1beta1.HTTPRoute) { + r.Spec.ParentRefs[0].Name = gwv1beta1.ObjectName(uncontrolledGatewayName) + }) + + // check that the route is unbound and all Consul objects and Kubernetes statuses are cleaned up + logger.Log(t, "checking that http route two is cleaned up because we no longer control it") + checkEmptyRoute(t, k8sClient, routeTwoName, defaultNamespace) + + logger.Log(t, "checking that http route two is deleted from Consul") + checkConsulNotExists(t, consulClient, api.HTTPRoute, routeTwoName) + + // Scenario: Switching a controlled gateway’s protocol that causes a route to unbind should cause the route to drop the parent ref in Consul and result in proper statuses set in Kubernetes + + // swap the gateway's protocol and see the route unbind + logger.Log(t, "marking gateway two as using TCP") + updateKubernetes(t, k8sClient, controlledGatewayTwo, func(g *gwv1beta1.Gateway) { + g.Spec.Listeners[0].Protocol = gwv1beta1.TCPProtocolType + }) + + // check that the route is unbound and all Consul objects and Kubernetes statuses are cleaned up + logger.Log(t, "checking that http route one is not bound to gateway two") + retryCheck(t, 10, func(r *retry.R) { + var route gwv1beta1.HTTPRoute + err := k8sClient.Get(context.Background(), types.NamespacedName{Name: routeOneName, Namespace: defaultNamespace}, &route) + require.NoError(r, err) + + require.Len(r, route.Status.Parents, 1) + require.EqualValues(r, controlledGatewayTwoName, route.Status.Parents[0].ParentRef.Name) + checkStatusCondition(r, route.Status.Parents[0].Conditions, falseCondition("Accepted", "NotAllowedByListeners")) + }) + + logger.Log(t, "checking that route one is deleted from Consul") + checkConsulNotExists(t, consulClient, api.HTTPRoute, routeOneName) + + // Scenario: Deleting a gateway should result in routes only referencing it to get cleaned up from Consul and their statuses/finalizers cleared, but routes referencing another controlled gateway should still exist in Consul and only have their statuses cleaned up from referencing the gateway we previously controlled. Any referenced certificates should also get cleaned up. + + // delete gateway two + logger.Log(t, "deleting gateway two in Kubernetes") + err = k8sClient.Delete(context.Background(), controlledGatewayTwo) + require.NoError(t, err) + + // check that the gateway is deleted from Consul + logger.Log(t, "checking that gateway two is deleted from Consul") + checkConsulNotExists(t, consulClient, api.APIGateway, controlledGatewayTwoName) + + // check that the Kubernetes route is cleaned up and the entries deleted from Consul + logger.Log(t, "checking that http route one is cleaned up in Kubernetes") + checkEmptyRoute(t, k8sClient, routeOneName, defaultNamespace) + + // Scenario: Changing a gateway class name on a gateway to something we don’t control should have the same affect as deleting it with the addition of cleaning up our finalizer from the gateway. + + // reset route one to point to our first gateway and check that it's bound properly + logger.Log(t, "remarking route one as bound to gateway one") + updateKubernetes(t, k8sClient, routeOne, func(r *gwv1beta1.HTTPRoute) { + r.Spec.ParentRefs[0].Name = gwv1beta1.ObjectName(controlledGatewayOneName) + }) + + logger.Log(t, "checking that http route one is bound to gateway one") + checkRouteBound(t, k8sClient, routeOneName, defaultNamespace, controlledGatewayOneName) + + logger.Log(t, "checking that http route one is synchronized to Consul") + checkConsulRouteParent(t, consulClient, routeOneName, controlledGatewayOneName) + + // make the gateway uncontrolled by pointing to a non-existent gateway class + logger.Log(t, "marking gateway one as not controlled by our controller") + updateKubernetes(t, k8sClient, controlledGatewayOne, func(g *gwv1beta1.Gateway) { + g.Spec.GatewayClassName = "non-existent" + }) + + // check that the Kubernetes gateway is cleaned up + logger.Log(t, "checking that gateway one is cleaned up in Kubernetes") + retryCheck(t, 10, func(r *retry.R) { + var route gwv1beta1.Gateway + err := k8sClient.Get(context.Background(), types.NamespacedName{Name: controlledGatewayOneName, Namespace: defaultNamespace}, &route) + require.NoError(r, err) + + require.Len(r, route.Finalizers, 0) + }) + + // check that the gateway is deleted from Consul + logger.Log(t, "checking that gateway one is deleted from Consul") + checkConsulNotExists(t, consulClient, api.APIGateway, controlledGatewayOneName) + + // check that the Kubernetes route is cleaned up and the entries deleted from Consul + logger.Log(t, "checking that http route one is cleaned up in Kubernetes") + checkEmptyRoute(t, k8sClient, routeOneName, defaultNamespace) + + logger.Log(t, "checking that http route one is deleted from Consul") + checkConsulNotExists(t, consulClient, api.HTTPRoute, routeOneName) + + // Scenario: Deleting a certificate referenced by a gateway’s listener should make the listener invalid and drop it from Consul. + + // reset the gateway + logger.Log(t, "remarking gateway one as controlled by our controller") + updateKubernetes(t, k8sClient, controlledGatewayOne, func(g *gwv1beta1.Gateway) { + g.Spec.GatewayClassName = gwv1beta1.ObjectName(controlledGatewayClassOneName) + }) + + // make sure it exists + logger.Log(t, "checking that gateway one is synchronized to Consul") + checkConsulExists(t, consulClient, api.APIGateway, controlledGatewayOneName) + + // make sure our certificate exists + logger.Log(t, "checking that the certificate is synchronized to Consul") + checkConsulExists(t, consulClient, api.InlineCertificate, certificateName) + + // delete the certificate in Kubernetes + logger.Log(t, "deleting the certificate in Kubernetes") + err = k8sClient.Delete(context.Background(), certificate) + require.NoError(t, err) + + // make sure the certificate no longer exists in Consul + logger.Log(t, "checking that the certificate is deleted from Consul") + checkConsulNotExists(t, consulClient, api.InlineCertificate, certificateName) +} + +func checkConsulNotExists(t *testing.T, client *api.Client, kind, name string, namespace ...string) { + t.Helper() + + opts := &api.QueryOptions{} + if len(namespace) != 0 { + opts.Namespace = namespace[0] + } + + retryCheck(t, 10, func(r *retry.R) { + _, _, err := client.ConfigEntries().Get(kind, name, opts) + require.Error(r, err) + require.EqualError(r, err, fmt.Sprintf("Unexpected response code: 404 (Config entry not found for %q / %q)", kind, name)) + }) +} + +func checkConsulExists(t *testing.T, client *api.Client, kind, name string) { + t.Helper() + + retryCheck(t, 10, func(r *retry.R) { + _, _, err := client.ConfigEntries().Get(kind, name, nil) + require.NoError(r, err) + }) +} + +func checkConsulRouteParent(t *testing.T, client *api.Client, name, parent string) { + t.Helper() + + retryCheck(t, 10, func(r *retry.R) { + entry, _, err := client.ConfigEntries().Get(api.HTTPRoute, name, nil) + require.NoError(r, err) + route := entry.(*api.HTTPRouteConfigEntry) + + require.Len(r, route.Parents, 1) + require.Equal(r, parent, route.Parents[0].Name) + }) +} + +func checkEmptyRoute(t *testing.T, client client.Client, name, namespace string) { + t.Helper() + + retryCheck(t, 10, func(r *retry.R) { + var route gwv1beta1.HTTPRoute + err := client.Get(context.Background(), types.NamespacedName{Name: name, Namespace: namespace}, &route) + require.NoError(r, err) + + require.Len(r, route.Status.Parents, 0) + require.Len(r, route.Finalizers, 0) + }) +} + +func checkRouteBound(t *testing.T, client client.Client, name, namespace, parent string) { + t.Helper() + + retryCheck(t, 10, func(r *retry.R) { + var route gwv1beta1.HTTPRoute + err := client.Get(context.Background(), types.NamespacedName{Name: name, Namespace: namespace}, &route) + require.NoError(r, err) + + require.Len(r, route.Status.Parents, 1) + require.EqualValues(r, gatewayClassControllerName, route.Status.Parents[0].ControllerName) + require.EqualValues(r, parent, route.Status.Parents[0].ParentRef.Name) + checkStatusCondition(r, route.Status.Parents[0].Conditions, trueCondition("Accepted", "Accepted")) + checkStatusCondition(r, route.Status.Parents[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) + checkStatusCondition(r, route.Status.Parents[0].Conditions, trueCondition("Synced", "Synced")) + }) +} + +func updateKubernetes[T client.Object](t *testing.T, k8sClient client.Client, o T, fn func(o T)) { + t.Helper() + + err := k8sClient.Get(context.Background(), client.ObjectKeyFromObject(o), o) + require.NoError(t, err) + fn(o) + err = k8sClient.Update(context.Background(), o) + require.NoError(t, err) +} + +func createRoute(t *testing.T, client client.Client, name, namespace, parent, target string) *gwv1beta1.HTTPRoute { + t.Helper() + + route := &gwv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + {Name: gwv1beta1.ObjectName(parent)}, + }, + }, + Rules: []gwv1beta1.HTTPRouteRule{ + {BackendRefs: []gwv1beta1.HTTPBackendRef{ + {BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{Name: gwv1beta1.ObjectName(target)}, + }}, + }}, + }, + }, + } + + err := client.Create(context.Background(), route) + require.NoError(t, err) + return route +} + +func createGateway(t *testing.T, client client.Client, name, namespace, gatewayClass, certificate string) *gwv1beta1.Gateway { + t.Helper() + + gateway := &gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gwv1beta1.ObjectName(gatewayClass), + Listeners: []gwv1beta1.Listener{{ + Name: gwv1beta1.SectionName("listener"), + Protocol: gwv1beta1.HTTPSProtocolType, + Port: 8443, + TLS: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{{ + Name: gwv1beta1.ObjectName(certificate), + }}, + }, + }}, + }, + } + + err := client.Create(context.Background(), gateway) + require.NoError(t, err) + + return gateway +} + +func createGatewayClass(t *testing.T, client client.Client, name, controllerName string, parameters *gwv1beta1.ParametersReference) { + t.Helper() + + gatewayClass := &gwv1beta1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Spec: gwv1beta1.GatewayClassSpec{ + ControllerName: gwv1beta1.GatewayController(controllerName), + ParametersRef: parameters, + }, + } + + err := client.Create(context.Background(), gatewayClass) + require.NoError(t, err) +} diff --git a/acceptance/tests/api-gateway/api_gateway_tenancy_test.go b/acceptance/tests/api-gateway/api_gateway_tenancy_test.go index 3455b69f58..d5a0845810 100644 --- a/acceptance/tests/api-gateway/api_gateway_tenancy_test.go +++ b/acceptance/tests/api-gateway/api_gateway_tenancy_test.go @@ -148,14 +148,8 @@ func TestAPIGateway_Tenancy(t *testing.T) { checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, falseCondition("ResolvedRefs", "RefNotPermitted")) }) - retryCheck(t, 10, func(r *retry.R) { - // since the sync operation should fail above, check that we don't have the entry in Consul. - _, _, err := consulClient.ConfigEntries().Get(api.APIGateway, "gateway", &api.QueryOptions{ - Namespace: namespaceForConsul(c.namespaceMirroring, gatewayNamespace), - }) - require.Error(r, err) - require.EqualError(r, err, `Unexpected response code: 404 (Config entry not found for "api-gateway" / "gateway")`) - }) + // since the sync operation should fail above, check that we don't have the entry in Consul. + checkConsulNotExists(t, consulClient, api.APIGateway, "gateway", namespaceForConsul(c.namespaceMirroring, gatewayNamespace)) // route failure retryCheck(t, 10, func(r *retry.R) { @@ -170,31 +164,13 @@ func TestAPIGateway_Tenancy(t *testing.T) { require.EqualValues(r, gatewayNamespace, *httproute.Status.Parents[0].ParentRef.Namespace) checkStatusCondition(r, httproute.Status.Parents[0].Conditions, falseCondition("Accepted", "RefNotPermitted")) checkStatusCondition(r, httproute.Status.Parents[0].Conditions, falseCondition("ResolvedRefs", "RefNotPermitted")) - // the route itself actually gets synced to Consul - checkStatusCondition(r, httproute.Status.Parents[0].Conditions, trueCondition("Synced", "Synced")) }) - retryCheck(t, 10, func(r *retry.R) { - // since we're not bound, check to make sure that the route doesn't target the gateway in Consul. - entry, _, err := consulClient.ConfigEntries().Get(api.HTTPRoute, "route", &api.QueryOptions{ - Namespace: namespaceForConsul(c.namespaceMirroring, routeNamespace), - }) - require.NoError(r, err) - route := entry.(*api.HTTPRouteConfigEntry) - - require.EqualValues(r, "route", route.Meta["k8s-name"]) - require.EqualValues(r, routeNamespace, route.Meta["k8s-namespace"]) - require.Len(r, route.Parents, 0) - }) + // since we're not bound to anything, check to make sure that the route doesn't get created in Consul. + checkConsulNotExists(t, consulClient, api.HTTPRoute, "route", namespaceForConsul(c.namespaceMirroring, routeNamespace)) - retryCheck(t, 10, func(r *retry.R) { - // we only sync validly referenced certificates over, so check to make sure it is not created. - _, _, err := consulClient.ConfigEntries().Get(api.InlineCertificate, "certificate", &api.QueryOptions{ - Namespace: namespaceForConsul(c.namespaceMirroring, certificateNamespace), - }) - require.Error(r, err) - require.EqualError(r, err, `Unexpected response code: 404 (Config entry not found for "inline-certificate" / "certificate")`) - }) + // we only sync validly referenced certificates over, so check to make sure it is not created. + checkConsulNotExists(t, consulClient, api.InlineCertificate, "certificate", namespaceForConsul(c.namespaceMirroring, certificateNamespace)) // now create reference grants createReferenceGrant(t, k8sClient, "gateway-certificate", gatewayNamespace, certificateNamespace) diff --git a/control-plane/api-gateway/binding/binder.go b/control-plane/api-gateway/binding/binder.go index 391c083724..96056920c3 100644 --- a/control-plane/api-gateway/binding/binder.go +++ b/control-plane/api-gateway/binding/binder.go @@ -53,19 +53,8 @@ type BinderConfig struct { // Service is the deployed service associated with the Gateway deployment. Service *corev1.Service - // TODO: Do we need to pass in Routes that have references to a Gateway in their statuses - // for cleanup purposes or is the below enough for record keeping? - // ConsulGateway is the config entry we've created in Consul. ConsulGateway *api.APIGatewayConfigEntry - // ConsulHTTPRoutes are a list of HTTPRouteConfigEntry objects that currently reference the - // Gateway we've created in Consul. - ConsulHTTPRoutes []api.HTTPRouteConfigEntry - // ConsulTCPRoutes are a list of TCPRouteConfigEntry objects that currently reference the - // Gateway we've created in Consul. - ConsulTCPRoutes []api.TCPRouteConfigEntry - // ConsulInlineCertificates is a list of certificates that have been created in Consul. - ConsulInlineCertificates []api.InlineCertificateConfigEntry // GatewayServices are the services associated with the Gateway ConsulGatewayServices []api.CatalogService diff --git a/control-plane/api-gateway/binding/binder_test.go b/control-plane/api-gateway/binding/binder_test.go index 484b6d1a60..065290f56a 100644 --- a/control-plane/api-gateway/binding/binder_test.go +++ b/control-plane/api-gateway/binding/binder_test.go @@ -54,15 +54,16 @@ var ( ) type resourceMapResources struct { - grants []gwv1beta1.ReferenceGrant - secrets []corev1.Secret - gateways []gwv1beta1.Gateway - httpRoutes []gwv1beta1.HTTPRoute - tcpRoutes []gwv1alpha2.TCPRoute - meshServices []v1alpha1.MeshService - services []types.NamespacedName - consulHTTPRoutes []api.HTTPRouteConfigEntry - consulTCPRoutes []api.TCPRouteConfigEntry + grants []gwv1beta1.ReferenceGrant + secrets []corev1.Secret + gateways []gwv1beta1.Gateway + httpRoutes []gwv1beta1.HTTPRoute + tcpRoutes []gwv1alpha2.TCPRoute + meshServices []v1alpha1.MeshService + services []types.NamespacedName + consulInlineCertificates []api.InlineCertificateConfigEntry + consulHTTPRoutes []api.HTTPRouteConfigEntry + consulTCPRoutes []api.TCPRouteConfigEntry } func newTestResourceMap(t *testing.T, resources resourceMapResources) *common.ResourceMap { @@ -339,14 +340,16 @@ func TestBinder_Lifecycle(t *testing.T) { }, }, }}, - ConsulHTTPRoutes: []api.HTTPRouteConfigEntry{{ + }), + resources: resourceMapResources{ + consulHTTPRoutes: []api.HTTPRouteConfigEntry{{ Kind: api.HTTPRoute, Name: "route", Parents: []api.ResourceReference{ {Kind: api.APIGateway, Name: "gateway"}, }, }}, - }), + }, expectedUpdates: []client.Object{ &gwv1beta1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{ @@ -464,14 +467,16 @@ func TestBinder_Lifecycle(t *testing.T) { }, }, }}, - ConsulTCPRoutes: []api.TCPRouteConfigEntry{{ + }), + resources: resourceMapResources{ + consulTCPRoutes: []api.TCPRouteConfigEntry{{ Kind: api.TCPRoute, Name: "route", Parents: []api.ResourceReference{ {Kind: api.APIGateway, Name: "gateway"}, }, }}, - }), + }, expectedUpdates: []client.Object{ &gwv1alpha2.TCPRoute{ ObjectMeta: metav1.ObjectMeta{ @@ -573,7 +578,9 @@ func TestBinder_Lifecycle(t *testing.T) { }}, }), }, - ConsulHTTPRoutes: []api.HTTPRouteConfigEntry{ + }), + resources: resourceMapResources{ + consulHTTPRoutes: []api.HTTPRouteConfigEntry{ { Kind: api.HTTPRoute, Name: "http-route-two", Meta: map[string]string{ "k8s-name": "http-route-two", @@ -594,7 +601,7 @@ func TestBinder_Lifecycle(t *testing.T) { }, }, }, - ConsulTCPRoutes: []api.TCPRouteConfigEntry{ + consulTCPRoutes: []api.TCPRouteConfigEntry{ { Kind: api.TCPRoute, Name: "tcp-route-two", Meta: map[string]string{ @@ -617,12 +624,10 @@ func TestBinder_Lifecycle(t *testing.T) { }, }, }, - ConsulInlineCertificates: []api.InlineCertificateConfigEntry{ + consulInlineCertificates: []api.InlineCertificateConfigEntry{ *certificateOne, *certificateTwo, }, - }), - resources: resourceMapResources{ secrets: []corev1.Secret{ secretOne, secretTwo, @@ -755,8 +760,6 @@ func TestBinder_Lifecycle(t *testing.T) { tt.resources.gateways = append(tt.resources.gateways, tt.config.Gateway) tt.resources.httpRoutes = append(tt.resources.httpRoutes, tt.config.HTTPRoutes...) tt.resources.tcpRoutes = append(tt.resources.tcpRoutes, tt.config.TCPRoutes...) - tt.resources.consulHTTPRoutes = append(tt.resources.consulHTTPRoutes, tt.config.ConsulHTTPRoutes...) - tt.resources.consulTCPRoutes = append(tt.resources.consulTCPRoutes, tt.config.ConsulTCPRoutes...) tt.config.Resources = newTestResourceMap(t, tt.resources) tt.config.ControllerName = testControllerName @@ -849,8 +852,6 @@ func TestBinder_Registrations(t *testing.T) { tt.resources.gateways = append(tt.resources.gateways, tt.config.Gateway) tt.resources.httpRoutes = append(tt.resources.httpRoutes, tt.config.HTTPRoutes...) tt.resources.tcpRoutes = append(tt.resources.tcpRoutes, tt.config.TCPRoutes...) - tt.resources.consulHTTPRoutes = append(tt.resources.consulHTTPRoutes, tt.config.ConsulHTTPRoutes...) - tt.resources.consulTCPRoutes = append(tt.resources.consulTCPRoutes, tt.config.ConsulTCPRoutes...) tt.config.Resources = newTestResourceMap(t, tt.resources) tt.config.ControllerName = testControllerName diff --git a/control-plane/api-gateway/binding/route_binding.go b/control-plane/api-gateway/binding/route_binding.go index 187d579852..2a3be4884b 100644 --- a/control-plane/api-gateway/binding/route_binding.go +++ b/control-plane/api-gateway/binding/route_binding.go @@ -20,6 +20,11 @@ func (r *Binder) bindRoute(route client.Object, boundCount map[gwv1beta1.Section // on non-enterprise installations routeConsulKey := r.config.Translator.NonNormalizedConfigEntryReference(entryKind(route), client.ObjectKeyFromObject(route)) filteredParents := filterParentRefs(r.key, route.GetNamespace(), getRouteParents(route)) + filteredParentStatuses := filterParentRefs(r.key, route.GetNamespace(), + common.ConvertSliceFunc(getRouteParentsStatus(route), func(parentStatus gwv1beta1.RouteParentStatus) gwv1beta1.ParentReference { + return parentStatus.ParentRef + }), + ) // flags to mark that some operation needs to occur kubernetesNeedsUpdate := false @@ -58,6 +63,9 @@ func (r *Binder) bindRoute(route client.Object, boundCount map[gwv1beta1.Section } // drop the status conditions + if r.statusSetter.removeRouteReferences(route, filteredParentStatuses) { + kubernetesNeedsStatusUpdate = true + } if r.statusSetter.removeRouteReferences(route, filteredParents) { kubernetesNeedsStatusUpdate = true } @@ -69,8 +77,6 @@ func (r *Binder) bindRoute(route client.Object, boundCount map[gwv1beta1.Section return } - // TODO: scrub route refs from statuses that no longer exist - validation := validateRefs(route, getRouteBackends(route), r.config.Resources) // the spec is dumb and makes you set a parent for any status, even when the // status is not with respect to a parent, as is the case of resolved refs @@ -80,6 +86,14 @@ func (r *Binder) bindRoute(route client.Object, boundCount map[gwv1beta1.Section kubernetesNeedsStatusUpdate = true } } + // if we're orphaned from this gateway we'll + // always need a status update. + if len(filteredParents) == 0 { + // we already checked that these refs existed, so no need to check + // the return value here. + _ = r.statusSetter.removeRouteReferences(route, filteredParentStatuses) + kubernetesNeedsStatusUpdate = true + } namespace := r.config.Namespaces[route.GetNamespace()] groupKind := route.GetObjectKind().GroupVersionKind().GroupKind() @@ -227,6 +241,11 @@ func (r *Binder) dropConsulRouteParent(snapshot *Snapshot, object client.Object, } func (r *Binder) mutateRouteWithBindingResults(snapshot *Snapshot, object client.Object, gatewayConsulKey api.ResourceReference, resources *common.ResourceMap, results parentBindResults) { + if results.boundSections().Cardinality() == 0 { + r.dropConsulRouteParent(snapshot, object, r.nonNormalizedConsulKey, r.config.Resources) + return + } + key := client.ObjectKeyFromObject(object) parents := mapset.NewSet() diff --git a/control-plane/api-gateway/common/resources.go b/control-plane/api-gateway/common/resources.go index 8696dcacb7..4cd3bfc3d2 100644 --- a/control-plane/api-gateway/common/resources.go +++ b/control-plane/api-gateway/common/resources.go @@ -68,7 +68,7 @@ type ReferenceValidator interface { } type certificate struct { - secret corev1.Secret + secret *corev1.Secret gateways mapset.Set } @@ -118,9 +118,8 @@ type ResourceMap struct { gatewayResources map[api.ResourceReference]*resourceSet // consul resources for a gateway - consulTCPRoutes map[api.ResourceReference]*consulTCPRoute - consulHTTPRoutes map[api.ResourceReference]*consulHTTPRoute - consulInlineCertificates map[api.ResourceReference]mapset.Set + consulTCPRoutes map[api.ResourceReference]*consulTCPRoute + consulHTTPRoutes map[api.ResourceReference]*consulHTTPRoute // mutations consulMutations []*ConsulUpdateOperation @@ -128,20 +127,19 @@ type ResourceMap struct { func NewResourceMap(translator ResourceTranslator, validator ReferenceValidator, logger logr.Logger) *ResourceMap { return &ResourceMap{ - translator: translator, - referenceValidator: validator, - logger: logger, - processedCertificates: mapset.NewSet(), - services: make(map[types.NamespacedName]api.ResourceReference), - meshServices: make(map[types.NamespacedName]api.ResourceReference), - certificates: mapset.NewSet(), - consulTCPRoutes: make(map[api.ResourceReference]*consulTCPRoute), - consulHTTPRoutes: make(map[api.ResourceReference]*consulHTTPRoute), - consulInlineCertificates: make(map[api.ResourceReference]mapset.Set), - certificateGateways: make(map[api.ResourceReference]*certificate), - tcpRouteGateways: make(map[api.ResourceReference]*tcpRoute), - httpRouteGateways: make(map[api.ResourceReference]*httpRoute), - gatewayResources: make(map[api.ResourceReference]*resourceSet), + translator: translator, + referenceValidator: validator, + logger: logger, + processedCertificates: mapset.NewSet(), + services: make(map[types.NamespacedName]api.ResourceReference), + meshServices: make(map[types.NamespacedName]api.ResourceReference), + certificates: mapset.NewSet(), + consulTCPRoutes: make(map[api.ResourceReference]*consulTCPRoute), + consulHTTPRoutes: make(map[api.ResourceReference]*consulHTTPRoute), + certificateGateways: make(map[api.ResourceReference]*certificate), + tcpRouteGateways: make(map[api.ResourceReference]*tcpRoute), + httpRouteGateways: make(map[api.ResourceReference]*httpRoute), + gatewayResources: make(map[api.ResourceReference]*resourceSet), } } @@ -190,7 +188,7 @@ func (s *ResourceMap) Certificate(key types.NamespacedName) *corev1.Secret { } consulKey := NormalizeMeta(s.toConsulReference(api.InlineCertificate, key)) if secret, ok := s.certificateGateways[consulKey]; ok { - return &secret.secret + return secret.secret } return nil } @@ -201,7 +199,7 @@ func (s *ResourceMap) ReferenceCountCertificate(secret corev1.Secret) { consulKey := NormalizeMeta(s.toConsulReference(api.InlineCertificate, key)) if _, ok := s.certificateGateways[consulKey]; !ok { s.certificateGateways[consulKey] = &certificate{ - secret: secret, + secret: &secret, gateways: mapset.NewSet(), } } @@ -320,6 +318,21 @@ func (s *ResourceMap) ReferenceCountConsulTCPRoute(route api.TCPRouteConfigEntry s.consulTCPRoutes[NormalizeMeta(key)] = set } +func (s *ResourceMap) ReferenceCountConsulCertificate(cert api.InlineCertificateConfigEntry) { + key := s.objectReference(&cert) + + var referenced *certificate + if existing, ok := s.certificateGateways[NormalizeMeta(key)]; ok { + referenced = existing + } else { + referenced = &certificate{ + gateways: mapset.NewSet(), + } + } + + s.certificateGateways[NormalizeMeta(key)] = referenced +} + func (s *ResourceMap) consulGatewaysForRoute(namespace string, refs []api.ResourceReference) mapset.Set { gateways := mapset.NewSet() @@ -400,21 +413,28 @@ func (s *ResourceMap) TranslateAndMutateHTTPRoute(key types.NamespacedName, onUp consulRoute, ok := s.consulHTTPRoutes[consulKey] if ok { - // remove from the consulHTTPRoutes map since we don't want to - // GC it in the end - delete(s.consulHTTPRoutes, consulKey) mutated := mutateFn(&consulRoute.route, *translated) + if len(mutated.Parents) != 0 { + // if we don't have any parents set, we keep this around to allow the route + // to be GC'd. + delete(s.consulHTTPRoutes, consulKey) + s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ + Entry: &mutated, + OnUpdate: onUpdate, + }) + } + return + } + mutated := mutateFn(nil, *translated) + if len(mutated.Parents) != 0 { + // if we don't have any parents set, we keep this around to allow the route + // to be GC'd. + delete(s.consulHTTPRoutes, consulKey) s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ Entry: &mutated, OnUpdate: onUpdate, }) - return } - mutated := mutateFn(nil, *translated) - s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ - Entry: &mutated, - OnUpdate: onUpdate, - }) } func (s *ResourceMap) MutateHTTPRoute(key types.NamespacedName, onUpdate func(error), mutateFn func(api.HTTPRouteConfigEntry) api.HTTPRouteConfigEntry) { @@ -422,15 +442,16 @@ func (s *ResourceMap) MutateHTTPRoute(key types.NamespacedName, onUpdate func(er consulRoute, ok := s.consulHTTPRoutes[consulKey] if ok { - // remove from the consulHTTPRoutes map since we don't want to - // GC it in the end - delete(s.consulHTTPRoutes, consulKey) mutated := mutateFn(consulRoute.route) - // add it to the mutation set - s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ - Entry: &mutated, - OnUpdate: onUpdate, - }) + if len(mutated.Parents) != 0 { + // if we don't have any parents set, we keep this around to allow the route + // to be GC'd. + delete(s.consulHTTPRoutes, consulKey) + s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ + Entry: &mutated, + OnUpdate: onUpdate, + }) + } } } @@ -454,20 +475,28 @@ func (s *ResourceMap) TranslateAndMutateTCPRoute(key types.NamespacedName, onUpd consulRoute, ok := s.consulTCPRoutes[consulKey] if ok { - // remove from the consulTCPRoutes map since we don't want to - // GC it in the end mutated := mutateFn(&consulRoute.route, *translated) + if len(mutated.Parents) != 0 { + // if we don't have any parents set, we keep this around to allow the route + // to be GC'd. + delete(s.consulTCPRoutes, consulKey) + s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ + Entry: &mutated, + OnUpdate: onUpdate, + }) + } + return + } + mutated := mutateFn(nil, *translated) + if len(mutated.Parents) != 0 { + // if we don't have any parents set, we keep this around to allow the route + // to be GC'd. + delete(s.consulTCPRoutes, consulKey) s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ Entry: &mutated, OnUpdate: onUpdate, }) - return } - mutated := mutateFn(nil, *translated) - s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ - Entry: &mutated, - OnUpdate: onUpdate, - }) } func (s *ResourceMap) MutateTCPRoute(key types.NamespacedName, onUpdate func(error), mutateFn func(api.TCPRouteConfigEntry) api.TCPRouteConfigEntry) { @@ -475,15 +504,16 @@ func (s *ResourceMap) MutateTCPRoute(key types.NamespacedName, onUpdate func(err consulRoute, ok := s.consulTCPRoutes[consulKey] if ok { - // remove from the consulTCPRoutes map since we don't want to - // GC it in the end - delete(s.consulTCPRoutes, consulKey) mutated := mutateFn(consulRoute.route) - // add it to the mutation set - s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ - Entry: &mutated, - OnUpdate: onUpdate, - }) + if len(mutated.Parents) != 0 { + // if we don't have any parents set, we keep this around to allow the route + // to be GC'd. + delete(s.consulTCPRoutes, consulKey) + s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ + Entry: &mutated, + OnUpdate: onUpdate, + }) + } } } @@ -502,7 +532,11 @@ func (s *ResourceMap) TranslateInlineCertificate(key types.NamespacedName) error return nil } - consulCertificate, err := s.translator.ToInlineCertificate(certificate.secret) + if certificate.secret == nil { + return nil + } + + consulCertificate, err := s.translator.ToInlineCertificate(*certificate.secret) if err != nil { return err } diff --git a/control-plane/api-gateway/controllers/gateway_controller.go b/control-plane/api-gateway/controllers/gateway_controller.go index 691c25bf72..8baa27cca1 100644 --- a/control-plane/api-gateway/controllers/gateway_controller.go +++ b/control-plane/api-gateway/controllers/gateway_controller.go @@ -129,6 +129,15 @@ func (r *GatewayController) Reconcile(ctx context.Context, req ctrl.Request) (ct return ctrl.Result{}, err } + // fetch our inline certificates from cache, this needs to happen + // here since the certificates need to be reference counted before + // the gateways. + r.fetchConsulInlineCertificates(resources) + + // add our current gateway even if it's not controlled by us so we + // can garbage collect any resources for it. + resources.ReferenceCountGateway(gateway) + if err := r.fetchControlledGateways(ctx, resources); err != nil { log.Error(err, "unable to fetch controlled gateways") return ctrl.Result{}, err @@ -153,31 +162,27 @@ func (r *GatewayController) Reconcile(ctx context.Context, req ctrl.Request) (ct return ctrl.Result{}, err } - // fetch all consul objects from cache + // fetch the rest of the consul objects from cache consulServices := r.getConsulServices(consulKey) consulGateway := r.getConsulGateway(consulKey) - consulHTTPRoutes := r.getConsulHTTPRoutes(consulKey, resources) - consulTCPRoutes := r.getConsulTCPRoutes(consulKey, resources) - consulInlineCertificates := r.getConsulInlineCertificates() + r.fetchConsulHTTPRoutes(consulKey, resources) + r.fetchConsulTCPRoutes(consulKey, resources) binder := binding.NewBinder(binding.BinderConfig{ - Logger: log, - Translator: r.Translator, - ControllerName: GatewayClassControllerName, - Namespaces: namespaces, - GatewayClassConfig: gatewayClassConfig, - GatewayClass: gatewayClass, - Gateway: gateway, - Pods: pods, - Service: service, - HTTPRoutes: httpRoutes, - TCPRoutes: tcpRoutes, - Resources: resources, - ConsulGateway: consulGateway, - ConsulHTTPRoutes: consulHTTPRoutes, - ConsulTCPRoutes: consulTCPRoutes, - ConsulInlineCertificates: consulInlineCertificates, - ConsulGatewayServices: consulServices, + Logger: log, + Translator: r.Translator, + ControllerName: GatewayClassControllerName, + Namespaces: namespaces, + GatewayClassConfig: gatewayClassConfig, + GatewayClass: gatewayClass, + Gateway: gateway, + Pods: pods, + Service: service, + HTTPRoutes: httpRoutes, + TCPRoutes: tcpRoutes, + Resources: resources, + ConsulGateway: consulGateway, + ConsulGatewayServices: consulServices, }) updates := binder.Snapshot() @@ -417,7 +422,12 @@ func (r *GatewayController) transformGatewayClass(ctx context.Context) func(o cl func (r *GatewayController) transformHTTPRoute(ctx context.Context) func(o client.Object) []reconcile.Request { return func(o client.Object) []reconcile.Request { route := o.(*gwv1beta1.HTTPRoute) - return refsToRequests(common.ParentRefs(common.BetaGroup, common.KindGateway, route.Namespace, route.Spec.ParentRefs)) + + refs := refsToRequests(common.ParentRefs(common.BetaGroup, common.KindGateway, route.Namespace, route.Spec.ParentRefs)) + statusRefs := refsToRequests(common.ParentRefs(common.BetaGroup, common.KindGateway, route.Namespace, common.ConvertSliceFunc(route.Status.Parents, func(parentStatus gwv1beta1.RouteParentStatus) gwv1beta1.ParentReference { + return parentStatus.ParentRef + }))) + return append(refs, statusRefs...) } } @@ -426,7 +436,12 @@ func (r *GatewayController) transformHTTPRoute(ctx context.Context) func(o clien func (r *GatewayController) transformTCPRoute(ctx context.Context) func(o client.Object) []reconcile.Request { return func(o client.Object) []reconcile.Request { route := o.(*gwv1alpha2.TCPRoute) - return refsToRequests(common.ParentRefs(common.BetaGroup, common.KindGateway, route.Namespace, route.Spec.ParentRefs)) + + refs := refsToRequests(common.ParentRefs(common.BetaGroup, common.KindGateway, route.Namespace, route.Spec.ParentRefs)) + statusRefs := refsToRequests(common.ParentRefs(common.BetaGroup, common.KindGateway, route.Namespace, common.ConvertSliceFunc(route.Status.Parents, func(parentStatus gwv1beta1.RouteParentStatus) gwv1beta1.ParentReference { + return parentStatus.ParentRef + }))) + return append(refs, statusRefs...) } } @@ -901,37 +916,26 @@ func (c *GatewayController) getConsulGateway(ref api.ResourceReference) *api.API return nil } -func (c *GatewayController) getConsulHTTPRoutes(ref api.ResourceReference, resources *common.ResourceMap) []api.HTTPRouteConfigEntry { - var filtered []api.HTTPRouteConfigEntry - +func (c *GatewayController) fetchConsulHTTPRoutes(ref api.ResourceReference, resources *common.ResourceMap) { for _, route := range configEntriesTo[*api.HTTPRouteConfigEntry](c.cache.List(api.HTTPRoute)) { if routeReferencesGateway(route.Namespace, ref, route.Parents) { - filtered = append(filtered, *route) resources.ReferenceCountConsulHTTPRoute(*route) } } - return filtered } -func (c *GatewayController) getConsulTCPRoutes(ref api.ResourceReference, resources *common.ResourceMap) []api.TCPRouteConfigEntry { - var filtered []api.TCPRouteConfigEntry - +func (c *GatewayController) fetchConsulTCPRoutes(ref api.ResourceReference, resources *common.ResourceMap) { for _, route := range configEntriesTo[*api.TCPRouteConfigEntry](c.cache.List(api.TCPRoute)) { if routeReferencesGateway(route.Namespace, ref, route.Parents) { - filtered = append(filtered, *route) resources.ReferenceCountConsulTCPRoute(*route) } } - return filtered } -func (c *GatewayController) getConsulInlineCertificates() []api.InlineCertificateConfigEntry { - var filtered []api.InlineCertificateConfigEntry - +func (c *GatewayController) fetchConsulInlineCertificates(resources *common.ResourceMap) { for _, cert := range configEntriesTo[*api.InlineCertificateConfigEntry](c.cache.List(api.InlineCertificate)) { - filtered = append(filtered, *cert) + resources.ReferenceCountConsulCertificate(*cert) } - return filtered } func routeReferencesGateway(namespace string, ref api.ResourceReference, refs []api.ResourceReference) bool { diff --git a/control-plane/api-gateway/controllers/gateway_controller_test.go b/control-plane/api-gateway/controllers/gateway_controller_test.go new file mode 100644 index 0000000000..b36f256121 --- /dev/null +++ b/control-plane/api-gateway/controllers/gateway_controller_test.go @@ -0,0 +1,297 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package controllers + +import ( + "context" + "testing" + + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +func TestTransformHTTPRoute(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + route *gwv1beta1.HTTPRoute + expected []reconcile.Request + }{ + "route with parent empty namespace": { + route: &gwv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + {Name: "gateway"}, + }, + }, + }, + }, + expected: []reconcile.Request{ + {NamespacedName: types.NamespacedName{Name: "gateway", Namespace: "default"}}, + }, + }, + "route with parent with namespace": { + route: &gwv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + {Name: "gateway", Namespace: common.PointerTo(gwv1beta1.Namespace("other"))}, + }, + }, + }, + }, + expected: []reconcile.Request{ + {NamespacedName: types.NamespacedName{Name: "gateway", Namespace: "other"}}, + }, + }, + "route with non gateway parent with namespace": { + route: &gwv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + {Name: "gateway", Group: common.PointerTo(gwv1beta1.Group("group"))}, + }, + }, + }, + }, + expected: []reconcile.Request{}, + }, + "route with parent in status and no namespace": { + route: &gwv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + }, + Status: gwv1beta1.HTTPRouteStatus{ + RouteStatus: gwv1beta1.RouteStatus{ + Parents: []gwv1beta1.RouteParentStatus{ + {ParentRef: gwv1beta1.ParentReference{Name: "gateway"}}, + }, + }, + }, + }, + expected: []reconcile.Request{ + {NamespacedName: types.NamespacedName{Name: "gateway", Namespace: "default"}}, + }, + }, + "route with parent in status and namespace": { + route: &gwv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + }, + Status: gwv1beta1.HTTPRouteStatus{ + RouteStatus: gwv1beta1.RouteStatus{ + Parents: []gwv1beta1.RouteParentStatus{ + {ParentRef: gwv1beta1.ParentReference{Name: "gateway", Namespace: common.PointerTo(gwv1beta1.Namespace("other"))}}, + }, + }, + }, + }, + expected: []reconcile.Request{ + {NamespacedName: types.NamespacedName{Name: "gateway", Namespace: "other"}}, + }, + }, + "route with non gateway parent in status": { + route: &gwv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + }, + Status: gwv1beta1.HTTPRouteStatus{ + RouteStatus: gwv1beta1.RouteStatus{ + Parents: []gwv1beta1.RouteParentStatus{ + {ParentRef: gwv1beta1.ParentReference{Name: "gateway", Group: common.PointerTo(gwv1beta1.Group("group"))}}, + }, + }, + }, + }, + expected: []reconcile.Request{}, + }, + "route parent in spec and in status": { + route: &gwv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + {Name: "gateway-one"}, + }, + }, + }, + Status: gwv1beta1.HTTPRouteStatus{ + RouteStatus: gwv1beta1.RouteStatus{ + Parents: []gwv1beta1.RouteParentStatus{ + {ParentRef: gwv1beta1.ParentReference{Name: "gateway-two"}}, + }, + }, + }, + }, + expected: []reconcile.Request{ + {NamespacedName: types.NamespacedName{Name: "gateway-one", Namespace: "default"}}, + {NamespacedName: types.NamespacedName{Name: "gateway-two", Namespace: "default"}}, + }, + }, + } { + t.Run(name, func(t *testing.T) { + controller := GatewayController{} + + fn := controller.transformHTTPRoute(context.Background()) + require.ElementsMatch(t, tt.expected, fn(tt.route)) + }) + } +} + +func TestTransformTCPRoute(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + route *gwv1alpha2.TCPRoute + expected []reconcile.Request + }{ + "route with parent empty namespace": { + route: &gwv1alpha2.TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + {Name: "gateway"}, + }, + }, + }, + }, + expected: []reconcile.Request{ + {NamespacedName: types.NamespacedName{Name: "gateway", Namespace: "default"}}, + }, + }, + "route with parent with namespace": { + route: &gwv1alpha2.TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + {Name: "gateway", Namespace: common.PointerTo(gwv1beta1.Namespace("other"))}, + }, + }, + }, + }, + expected: []reconcile.Request{ + {NamespacedName: types.NamespacedName{Name: "gateway", Namespace: "other"}}, + }, + }, + "route with non gateway parent with namespace": { + route: &gwv1alpha2.TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + {Name: "gateway", Group: common.PointerTo(gwv1beta1.Group("group"))}, + }, + }, + }, + }, + expected: []reconcile.Request{}, + }, + "route with parent in status and no namespace": { + route: &gwv1alpha2.TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + }, + Status: gwv1alpha2.TCPRouteStatus{ + RouteStatus: gwv1beta1.RouteStatus{ + Parents: []gwv1beta1.RouteParentStatus{ + {ParentRef: gwv1beta1.ParentReference{Name: "gateway"}}, + }, + }, + }, + }, + expected: []reconcile.Request{ + {NamespacedName: types.NamespacedName{Name: "gateway", Namespace: "default"}}, + }, + }, + "route with parent in status and namespace": { + route: &gwv1alpha2.TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + }, + Status: gwv1alpha2.TCPRouteStatus{ + RouteStatus: gwv1beta1.RouteStatus{ + Parents: []gwv1beta1.RouteParentStatus{ + {ParentRef: gwv1beta1.ParentReference{Name: "gateway", Namespace: common.PointerTo(gwv1beta1.Namespace("other"))}}, + }, + }, + }, + }, + expected: []reconcile.Request{ + {NamespacedName: types.NamespacedName{Name: "gateway", Namespace: "other"}}, + }, + }, + "route with non gateway parent in status": { + route: &gwv1alpha2.TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + }, + Status: gwv1alpha2.TCPRouteStatus{ + RouteStatus: gwv1beta1.RouteStatus{ + Parents: []gwv1beta1.RouteParentStatus{ + {ParentRef: gwv1beta1.ParentReference{Name: "gateway", Group: common.PointerTo(gwv1beta1.Group("group"))}}, + }, + }, + }, + }, + expected: []reconcile.Request{}, + }, + "route parent in spec and in status": { + route: &gwv1alpha2.TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + {Name: "gateway-one"}, + }, + }, + }, + Status: gwv1alpha2.TCPRouteStatus{ + RouteStatus: gwv1beta1.RouteStatus{ + Parents: []gwv1beta1.RouteParentStatus{ + {ParentRef: gwv1beta1.ParentReference{Name: "gateway-two"}}, + }, + }, + }, + }, + expected: []reconcile.Request{ + {NamespacedName: types.NamespacedName{Name: "gateway-one", Namespace: "default"}}, + {NamespacedName: types.NamespacedName{Name: "gateway-two", Namespace: "default"}}, + }, + }, + } { + t.Run(name, func(t *testing.T) { + controller := GatewayController{} + + fn := controller.transformTCPRoute(context.Background()) + require.ElementsMatch(t, tt.expected, fn(tt.route)) + }) + } +} diff --git a/control-plane/api-gateway/controllers/index.go b/control-plane/api-gateway/controllers/index.go index cff6dfac12..7fd13de1de 100644 --- a/control-plane/api-gateway/controllers/index.go +++ b/control-plane/api-gateway/controllers/index.go @@ -161,12 +161,18 @@ func gatewayForSecret(o client.Object) []string { func gatewaysForHTTPRoute(o client.Object) []string { route := o.(*gwv1beta1.HTTPRoute) - return gatewaysForRoute(route.Namespace, route.Spec.ParentRefs) + statusRefs := common.ConvertSliceFunc(route.Status.Parents, func(parentStatus gwv1beta1.RouteParentStatus) gwv1beta1.ParentReference { + return parentStatus.ParentRef + }) + return gatewaysForRoute(route.Namespace, route.Spec.ParentRefs, statusRefs) } func gatewaysForTCPRoute(o client.Object) []string { route := o.(*gwv1alpha2.TCPRoute) - return gatewaysForRoute(route.Namespace, route.Spec.ParentRefs) + statusRefs := common.ConvertSliceFunc(route.Status.Parents, func(parentStatus gwv1beta1.RouteParentStatus) gwv1beta1.ParentReference { + return parentStatus.ParentRef + }) + return gatewaysForRoute(route.Namespace, route.Spec.ParentRefs, statusRefs) } func servicesForHTTPRoute(o client.Object) []string { @@ -249,7 +255,7 @@ func meshServicesForTCPRoute(o client.Object) []string { return refs } -func gatewaysForRoute(namespace string, refs []gwv1beta1.ParentReference) []string { +func gatewaysForRoute(namespace string, refs []gwv1beta1.ParentReference, statusRefs []gwv1beta1.ParentReference) []string { var references []string for _, parent := range refs { if common.NilOrEqual(parent.Group, common.BetaGroup) && common.NilOrEqual(parent.Kind, common.KindGateway) { @@ -257,5 +263,11 @@ func gatewaysForRoute(namespace string, refs []gwv1beta1.ParentReference) []stri references = append(references, common.IndexedNamespacedNameWithDefault(parent.Name, parent.Namespace, namespace).String()) } } + for _, parent := range statusRefs { + if common.NilOrEqual(parent.Group, common.BetaGroup) && common.NilOrEqual(parent.Kind, common.KindGateway) { + // If an explicit Gateway namespace is not provided, use the Route namespace to lookup the provided Gateway Namespace. + references = append(references, common.IndexedNamespacedNameWithDefault(parent.Name, parent.Namespace, namespace).String()) + } + } return references } From 05acb5f5b6c8a888c0ef1891b80bcc41866c8a7a Mon Sep 17 00:00:00 2001 From: Mike Morris Date: Fri, 2 Jun 2023 15:43:45 -0400 Subject: [PATCH 193/340] accepance: extend api gateway lifecycle test retryCheck timeouts (#2256) To reduce the likelihood of flakes. --- .../api-gateway/api_gateway_lifecycle_test.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/acceptance/tests/api-gateway/api_gateway_lifecycle_test.go b/acceptance/tests/api-gateway/api_gateway_lifecycle_test.go index c07075a709..08712815cc 100644 --- a/acceptance/tests/api-gateway/api_gateway_lifecycle_test.go +++ b/acceptance/tests/api-gateway/api_gateway_lifecycle_test.go @@ -196,7 +196,7 @@ func TestAPIGateway_Lifecycle(t *testing.T) { // check that the route is unbound and all Consul objects and Kubernetes statuses are cleaned up logger.Log(t, "checking that http route one is not bound to gateway two") - retryCheck(t, 10, func(r *retry.R) { + retryCheck(t, 30, func(r *retry.R) { var route gwv1beta1.HTTPRoute err := k8sClient.Get(context.Background(), types.NamespacedName{Name: routeOneName, Namespace: defaultNamespace}, &route) require.NoError(r, err) @@ -246,7 +246,7 @@ func TestAPIGateway_Lifecycle(t *testing.T) { // check that the Kubernetes gateway is cleaned up logger.Log(t, "checking that gateway one is cleaned up in Kubernetes") - retryCheck(t, 10, func(r *retry.R) { + retryCheck(t, 30, func(r *retry.R) { var route gwv1beta1.Gateway err := k8sClient.Get(context.Background(), types.NamespacedName{Name: controlledGatewayOneName, Namespace: defaultNamespace}, &route) require.NoError(r, err) @@ -299,7 +299,7 @@ func checkConsulNotExists(t *testing.T, client *api.Client, kind, name string, n opts.Namespace = namespace[0] } - retryCheck(t, 10, func(r *retry.R) { + retryCheck(t, 30, func(r *retry.R) { _, _, err := client.ConfigEntries().Get(kind, name, opts) require.Error(r, err) require.EqualError(r, err, fmt.Sprintf("Unexpected response code: 404 (Config entry not found for %q / %q)", kind, name)) @@ -309,7 +309,7 @@ func checkConsulNotExists(t *testing.T, client *api.Client, kind, name string, n func checkConsulExists(t *testing.T, client *api.Client, kind, name string) { t.Helper() - retryCheck(t, 10, func(r *retry.R) { + retryCheck(t, 30, func(r *retry.R) { _, _, err := client.ConfigEntries().Get(kind, name, nil) require.NoError(r, err) }) @@ -318,7 +318,7 @@ func checkConsulExists(t *testing.T, client *api.Client, kind, name string) { func checkConsulRouteParent(t *testing.T, client *api.Client, name, parent string) { t.Helper() - retryCheck(t, 10, func(r *retry.R) { + retryCheck(t, 30, func(r *retry.R) { entry, _, err := client.ConfigEntries().Get(api.HTTPRoute, name, nil) require.NoError(r, err) route := entry.(*api.HTTPRouteConfigEntry) @@ -331,7 +331,7 @@ func checkConsulRouteParent(t *testing.T, client *api.Client, name, parent strin func checkEmptyRoute(t *testing.T, client client.Client, name, namespace string) { t.Helper() - retryCheck(t, 10, func(r *retry.R) { + retryCheck(t, 30, func(r *retry.R) { var route gwv1beta1.HTTPRoute err := client.Get(context.Background(), types.NamespacedName{Name: name, Namespace: namespace}, &route) require.NoError(r, err) @@ -344,7 +344,7 @@ func checkEmptyRoute(t *testing.T, client client.Client, name, namespace string) func checkRouteBound(t *testing.T, client client.Client, name, namespace, parent string) { t.Helper() - retryCheck(t, 10, func(r *retry.R) { + retryCheck(t, 30, func(r *retry.R) { var route gwv1beta1.HTTPRoute err := client.Get(context.Background(), types.NamespacedName{Name: name, Namespace: namespace}, &route) require.NoError(r, err) From 6662c780d57df8aa8dae1b4825cd80a9a81cc823 Mon Sep 17 00:00:00 2001 From: Nathan Coleman Date: Fri, 2 Jun 2023 15:55:57 -0400 Subject: [PATCH 194/340] api-gateway: create RoleBinding attaching Role to ServiceAccount (#2252) * Create RoleBinding attaching Role to ServiceAccount * Update ClusterRole for controller to allow management of RoleBindings * Separate logic for RoleBinding management from logic for Role * Use pointer receiver for all functions on Gatekeeper struct * Use more descriptive name for NamespacedName arg on delete * Clean up missed code in cherrypick * Remove out-of-scope TODO * Make Upsert docstring more robust, explaining dependency ordering * Add RoleBindings to unit tests for Gatekeeper --- .../templates/connect-inject-clusterrole.yaml | 2 +- .../api-gateway/gatekeeper/deployment.go | 4 +- .../api-gateway/gatekeeper/gatekeeper.go | 26 +++-- .../api-gateway/gatekeeper/gatekeeper_test.go | 97 ++++++++++++++++++- control-plane/api-gateway/gatekeeper/role.go | 19 ++-- .../api-gateway/gatekeeper/rolebinding.go | 90 +++++++++++++++++ .../api-gateway/gatekeeper/service.go | 4 +- .../api-gateway/gatekeeper/serviceaccount.go | 4 +- 8 files changed, 215 insertions(+), 31 deletions(-) create mode 100644 control-plane/api-gateway/gatekeeper/rolebinding.go diff --git a/charts/consul/templates/connect-inject-clusterrole.yaml b/charts/consul/templates/connect-inject-clusterrole.yaml index e476949997..730720d460 100644 --- a/charts/consul/templates/connect-inject-clusterrole.yaml +++ b/charts/consul/templates/connect-inject-clusterrole.yaml @@ -73,7 +73,7 @@ rules: - delete - update - apiGroups: [ "rbac.authorization.k8s.io" ] - resources: [ "roles" ] + resources: [ "roles", "rolebindings" ] verbs: - get - list diff --git a/control-plane/api-gateway/gatekeeper/deployment.go b/control-plane/api-gateway/gatekeeper/deployment.go index eecfa31349..cc08e1bbef 100644 --- a/control-plane/api-gateway/gatekeeper/deployment.go +++ b/control-plane/api-gateway/gatekeeper/deployment.go @@ -75,8 +75,8 @@ func (g *Gatekeeper) upsertDeployment(ctx context.Context, gateway gwv1beta1.Gat return nil } -func (g *Gatekeeper) deleteDeployment(ctx context.Context, nsname types.NamespacedName) error { - err := g.Client.Delete(ctx, &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: nsname.Name, Namespace: nsname.Namespace}}) +func (g *Gatekeeper) deleteDeployment(ctx context.Context, gwName types.NamespacedName) error { + err := g.Client.Delete(ctx, &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: gwName.Name, Namespace: gwName.Namespace}}) if k8serrors.IsNotFound(err) { return nil } diff --git a/control-plane/api-gateway/gatekeeper/gatekeeper.go b/control-plane/api-gateway/gatekeeper/gatekeeper.go index 23f96acb3d..46243ff9a1 100644 --- a/control-plane/api-gateway/gatekeeper/gatekeeper.go +++ b/control-plane/api-gateway/gatekeeper/gatekeeper.go @@ -30,6 +30,7 @@ func New(log logr.Logger, client client.Client) *Gatekeeper { } // Upsert creates or updates the resources for handling routing of network traffic. +// This is done in order based on dependencies between resources. func (g *Gatekeeper) Upsert(ctx context.Context, gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config common.HelmConfig) error { g.Log.Info(fmt.Sprintf("Upsert Gateway Deployment %s/%s", gateway.Namespace, gateway.Name)) @@ -41,6 +42,10 @@ func (g *Gatekeeper) Upsert(ctx context.Context, gateway gwv1beta1.Gateway, gcc return err } + if err := g.upsertRoleBinding(ctx, gateway, gcc, config); err != nil { + return err + } + if err := g.upsertService(ctx, gateway, gcc, config); err != nil { return err } @@ -53,20 +58,27 @@ func (g *Gatekeeper) Upsert(ctx context.Context, gateway gwv1beta1.Gateway, gcc } // Delete removes the resources for handling routing of network traffic. -func (g *Gatekeeper) Delete(ctx context.Context, nsname types.NamespacedName) error { - if err := g.deleteRole(ctx, nsname); err != nil { +// This is done in the reverse order of Upsert due to dependencies between resources. +func (g *Gatekeeper) Delete(ctx context.Context, gatewayName types.NamespacedName) error { + g.Log.Info(fmt.Sprintf("Delete Gateway Deployment %s/%s", gatewayName.Namespace, gatewayName.Name)) + + if err := g.deleteDeployment(ctx, gatewayName); err != nil { + return err + } + + if err := g.deleteService(ctx, gatewayName); err != nil { return err } - if err := g.deleteServiceAccount(ctx, nsname); err != nil { + if err := g.deleteRoleBinding(ctx, gatewayName); err != nil { return err } - if err := g.deleteService(ctx, nsname); err != nil { + if err := g.deleteServiceAccount(ctx, gatewayName); err != nil { return err } - if err := g.deleteDeployment(ctx, nsname); err != nil { + if err := g.deleteRole(ctx, gatewayName); err != nil { return err } @@ -76,14 +88,14 @@ func (g *Gatekeeper) Delete(ctx context.Context, nsname types.NamespacedName) er // resourceMutator is passed to create or update functions to mutate Kubernetes resources. type resourceMutator = func() error -func (g Gatekeeper) namespacedName(gateway gwv1beta1.Gateway) types.NamespacedName { +func (g *Gatekeeper) namespacedName(gateway gwv1beta1.Gateway) types.NamespacedName { return types.NamespacedName{ Namespace: gateway.Namespace, Name: gateway.Name, } } -func (g Gatekeeper) serviceAccountName(gateway gwv1beta1.Gateway, config common.HelmConfig) string { +func (g *Gatekeeper) serviceAccountName(gateway gwv1beta1.Gateway, config common.HelmConfig) string { if config.AuthMethod == "" { return "" } diff --git a/control-plane/api-gateway/gatekeeper/gatekeeper_test.go b/control-plane/api-gateway/gatekeeper/gatekeeper_test.go index cc19f68d47..e2da61177f 100644 --- a/control-plane/api-gateway/gatekeeper/gatekeeper_test.go +++ b/control-plane/api-gateway/gatekeeper/gatekeeper_test.go @@ -61,6 +61,7 @@ type testCase struct { type resources struct { deployments []*appsv1.Deployment roles []*rbac.Role + roleBindings []*rbac.RoleBinding services []*corev1.Service serviceAccounts []*corev1.ServiceAccount } @@ -187,6 +188,9 @@ func TestUpsert(t *testing.T) { roles: []*rbac.Role{ configureRole(name, namespace, labels, "1"), }, + roleBindings: []*rbac.RoleBinding{ + configureRoleBinding(name, namespace, labels, "1"), + }, services: []*corev1.Service{ configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ { @@ -240,6 +244,9 @@ func TestUpsert(t *testing.T) { roles: []*rbac.Role{ configureRole(name, namespace, labels, "1"), }, + roleBindings: []*rbac.RoleBinding{ + configureRoleBinding(name, namespace, labels, "1"), + }, services: []*corev1.Service{ configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ { @@ -260,6 +267,9 @@ func TestUpsert(t *testing.T) { roles: []*rbac.Role{ configureRole(name, namespace, labels, "1"), }, + roleBindings: []*rbac.RoleBinding{ + configureRoleBinding(name, namespace, labels, "1"), + }, services: []*corev1.Service{ configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ { @@ -315,6 +325,9 @@ func TestUpsert(t *testing.T) { roles: []*rbac.Role{ configureRole(name, namespace, labels, "1"), }, + roleBindings: []*rbac.RoleBinding{ + configureRoleBinding(name, namespace, labels, "1"), + }, services: []*corev1.Service{ configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ { @@ -340,6 +353,9 @@ func TestUpsert(t *testing.T) { roles: []*rbac.Role{ configureRole(name, namespace, labels, "1"), }, + roleBindings: []*rbac.RoleBinding{ + configureRoleBinding(name, namespace, labels, "1"), + }, services: []*corev1.Service{ configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ { @@ -546,6 +562,9 @@ func TestDelete(t *testing.T) { roles: []*rbac.Role{ configureRole(name, namespace, labels, "1"), }, + roleBindings: []*rbac.RoleBinding{ + configureRoleBinding(name, namespace, labels, "1"), + }, services: []*corev1.Service{ configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ { @@ -609,6 +628,10 @@ func joinResources(resources resources) (objs []client.Object) { objs = append(objs, role) } + for _, roleBinding := range resources.roleBindings { + objs = append(objs, roleBinding) + } + for _, service := range resources.services { objs = append(objs, service) } @@ -664,6 +687,22 @@ func validateResourcesExist(t *testing.T, client client.Client, resources resour require.Equal(t, expected, actual) } + for _, expected := range resources.roleBindings { + actual := &rbac.RoleBinding{} + err := client.Get(context.Background(), types.NamespacedName{ + Name: expected.Name, + Namespace: expected.Namespace, + }, actual) + if err != nil { + return err + } + + // Patch the createdAt label + actual.Labels[createdAtLabelKey] = createdAtLabelValue + + require.Equal(t, expected, actual) + } + for _, expected := range resources.services { actual := &corev1.Service{} err := client.Get(context.Background(), types.NamespacedName{ @@ -700,12 +739,12 @@ func validateResourcesExist(t *testing.T, client client.Client, resources resour return nil } -func validateResourcesAreDeleted(t *testing.T, client client.Client, resources resources) error { +func validateResourcesAreDeleted(t *testing.T, k8sClient client.Client, resources resources) error { t.Helper() for _, expected := range resources.deployments { actual := &appsv1.Deployment{} - err := client.Get(context.Background(), types.NamespacedName{ + err := k8sClient.Get(context.Background(), types.NamespacedName{ Name: expected.Name, Namespace: expected.Namespace, }, actual) @@ -717,7 +756,7 @@ func validateResourcesAreDeleted(t *testing.T, client client.Client, resources r for _, expected := range resources.roles { actual := &rbac.Role{} - err := client.Get(context.Background(), types.NamespacedName{ + err := k8sClient.Get(context.Background(), types.NamespacedName{ Name: expected.Name, Namespace: expected.Namespace, }, actual) @@ -727,9 +766,21 @@ func validateResourcesAreDeleted(t *testing.T, client client.Client, resources r require.Error(t, err) } + for _, expected := range resources.roleBindings { + actual := &rbac.RoleBinding{} + err := k8sClient.Get(context.Background(), types.NamespacedName{ + Name: expected.Name, + Namespace: expected.Namespace, + }, actual) + if !k8serrors.IsNotFound(err) { + return fmt.Errorf("expected rolebinding %s to be deleted", expected.Name) + } + require.Error(t, err) + } + for _, expected := range resources.services { actual := &corev1.Service{} - err := client.Get(context.Background(), types.NamespacedName{ + err := k8sClient.Get(context.Background(), types.NamespacedName{ Name: expected.Name, Namespace: expected.Namespace, }, actual) @@ -741,7 +792,7 @@ func validateResourcesAreDeleted(t *testing.T, client client.Client, resources r for _, expected := range resources.serviceAccounts { actual := &corev1.ServiceAccount{} - err := client.Get(context.Background(), types.NamespacedName{ + err := k8sClient.Get(context.Background(), types.NamespacedName{ Name: expected.Name, Namespace: expected.Namespace, }, actual) @@ -837,6 +888,42 @@ func configureRole(name, namespace string, labels map[string]string, resourceVer } } +func configureRoleBinding(name, namespace string, labels map[string]string, resourceVersion string) *rbac.RoleBinding { + return &rbac.RoleBinding{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "rbac.authorization.k8s.io/v1", + Kind: "RoleBinding", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Labels: labels, + ResourceVersion: resourceVersion, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: "gateway.networking.k8s.io/v1beta1", + Kind: "Gateway", + Name: name, + Controller: common.PointerTo(true), + BlockOwnerDeletion: common.PointerTo(true), + }, + }, + }, + RoleRef: rbac.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "Role", + Name: name, + }, + Subjects: []rbac.Subject{ + { + Kind: "ServiceAccount", + Name: name, + Namespace: namespace, + }, + }, + } +} + func configureService(name, namespace string, labels, annotations map[string]string, serviceType corev1.ServiceType, ports []corev1.ServicePort, resourceVersion string) *corev1.Service { return &corev1.Service{ TypeMeta: metav1.TypeMeta{ diff --git a/control-plane/api-gateway/gatekeeper/role.go b/control-plane/api-gateway/gatekeeper/role.go index 688c425f5f..eb8431075a 100644 --- a/control-plane/api-gateway/gatekeeper/role.go +++ b/control-plane/api-gateway/gatekeeper/role.go @@ -24,19 +24,12 @@ func (g *Gatekeeper) upsertRole(ctx context.Context, gateway gwv1beta1.Gateway, } role := &rbac.Role{} - exists := false - // Get ServiceAccount + // If the Role already exists, ensure that we own the Role err := g.Client.Get(ctx, g.namespacedName(gateway), role) if err != nil && !k8serrors.IsNotFound(err) { return err - } else if k8serrors.IsNotFound(err) { - exists = false - } else { - exists = true - } - - if exists { + } else if !k8serrors.IsNotFound(err) { // Ensure we own the Role. for _, ref := range role.GetOwnerReferences() { if ref.UID == gateway.GetUID() && ref.Name == gateway.GetName() { @@ -44,7 +37,7 @@ func (g *Gatekeeper) upsertRole(ctx context.Context, gateway gwv1beta1.Gateway, return nil } } - return errors.New("Role not owned by controller") + return errors.New("role not owned by controller") } role = g.role(gateway, gcc) @@ -58,8 +51,8 @@ func (g *Gatekeeper) upsertRole(ctx context.Context, gateway gwv1beta1.Gateway, return nil } -func (g *Gatekeeper) deleteRole(ctx context.Context, nsname types.NamespacedName) error { - if err := g.Client.Delete(ctx, &rbac.Role{ObjectMeta: metav1.ObjectMeta{Name: nsname.Name, Namespace: nsname.Namespace}}); err != nil { +func (g *Gatekeeper) deleteRole(ctx context.Context, gwName types.NamespacedName) error { + if err := g.Client.Delete(ctx, &rbac.Role{ObjectMeta: metav1.ObjectMeta{Name: gwName.Name, Namespace: gwName.Namespace}}); err != nil { if k8serrors.IsNotFound(err) { return nil } @@ -78,6 +71,7 @@ func (g *Gatekeeper) role(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassCo }, Rules: []rbac.PolicyRule{}, } + if gcc.Spec.PodSecurityPolicy != "" { role.Rules = append(role.Rules, rbac.PolicyRule{ APIGroups: []string{"policy"}, @@ -86,5 +80,6 @@ func (g *Gatekeeper) role(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassCo Verbs: []string{"use"}, }) } + return role } diff --git a/control-plane/api-gateway/gatekeeper/rolebinding.go b/control-plane/api-gateway/gatekeeper/rolebinding.go new file mode 100644 index 0000000000..8891a754e6 --- /dev/null +++ b/control-plane/api-gateway/gatekeeper/rolebinding.go @@ -0,0 +1,90 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package gatekeeper + +import ( + "context" + "errors" + + "k8s.io/apimachinery/pkg/types" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + rbac "k8s.io/api/rbac/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrl "sigs.k8s.io/controller-runtime" +) + +func (g *Gatekeeper) upsertRoleBinding(ctx context.Context, gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config common.HelmConfig) error { + if config.AuthMethod == "" { + return g.deleteRole(ctx, types.NamespacedName{Namespace: gateway.Namespace, Name: gateway.Name}) + } + + roleBinding := &rbac.RoleBinding{} + + // If the RoleBinding already exists, ensure that we own the RoleBinding + err := g.Client.Get(ctx, g.namespacedName(gateway), roleBinding) + if err != nil && !k8serrors.IsNotFound(err) { + return err + } else if !k8serrors.IsNotFound(err) { + // Ensure we own the Role. + for _, ref := range roleBinding.GetOwnerReferences() { + if ref.UID == gateway.GetUID() && ref.Name == gateway.GetName() { + // We found ourselves! + return nil + } + } + return errors.New("role not owned by controller") + } + + // Create or update the RoleBinding + roleBinding = g.roleBinding(gateway, gcc, config) + if err := ctrl.SetControllerReference(&gateway, roleBinding, g.Client.Scheme()); err != nil { + return err + } + if err := g.Client.Create(ctx, roleBinding); err != nil { + return err + } + + return nil +} + +func (g *Gatekeeper) deleteRoleBinding(ctx context.Context, gwName types.NamespacedName) error { + if err := g.Client.Delete(ctx, &rbac.RoleBinding{ObjectMeta: metav1.ObjectMeta{Name: gwName.Name, Namespace: gwName.Namespace}}); err != nil { + if k8serrors.IsNotFound(err) { + return nil + } + return err + } + + return nil +} + +func (g *Gatekeeper) roleBinding(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config common.HelmConfig) *rbac.RoleBinding { + // Create resources for reference. This avoids bugs if naming patterns change. + serviceAccount := g.serviceAccount(gateway) + role := g.role(gateway, gcc) + + return &rbac.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: gateway.Name, + Namespace: gateway.Namespace, + Labels: common.LabelsForGateway(&gateway), + }, + RoleRef: rbac.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "Role", + Name: role.Name, + }, + Subjects: []rbac.Subject{ + { + Kind: "ServiceAccount", + Name: serviceAccount.Name, + Namespace: serviceAccount.Namespace, + }, + }, + } +} diff --git a/control-plane/api-gateway/gatekeeper/service.go b/control-plane/api-gateway/gatekeeper/service.go index 149dc067e3..80272b7495 100644 --- a/control-plane/api-gateway/gatekeeper/service.go +++ b/control-plane/api-gateway/gatekeeper/service.go @@ -53,8 +53,8 @@ func (g *Gatekeeper) upsertService(ctx context.Context, gateway gwv1beta1.Gatewa return nil } -func (g *Gatekeeper) deleteService(ctx context.Context, nsname types.NamespacedName) error { - if err := g.Client.Delete(ctx, &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: nsname.Name, Namespace: nsname.Namespace}}); err != nil { +func (g *Gatekeeper) deleteService(ctx context.Context, gwName types.NamespacedName) error { + if err := g.Client.Delete(ctx, &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: gwName.Name, Namespace: gwName.Namespace}}); err != nil { if k8serrors.IsNotFound(err) { return nil } diff --git a/control-plane/api-gateway/gatekeeper/serviceaccount.go b/control-plane/api-gateway/gatekeeper/serviceaccount.go index 864047fb1b..47336867aa 100644 --- a/control-plane/api-gateway/gatekeeper/serviceaccount.go +++ b/control-plane/api-gateway/gatekeeper/serviceaccount.go @@ -58,8 +58,8 @@ func (g *Gatekeeper) upsertServiceAccount(ctx context.Context, gateway gwv1beta1 return nil } -func (g *Gatekeeper) deleteServiceAccount(ctx context.Context, nsname types.NamespacedName) error { - if err := g.Client.Delete(ctx, &corev1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: nsname.Name, Namespace: nsname.Namespace}}); err != nil { +func (g *Gatekeeper) deleteServiceAccount(ctx context.Context, gwName types.NamespacedName) error { + if err := g.Client.Delete(ctx, &corev1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: gwName.Name, Namespace: gwName.Namespace}}); err != nil { if k8serrors.IsNotFound(err) { return nil } From 3f346768b03c0180e49801c86310d04e024889e8 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Fri, 2 Jun 2023 19:09:05 -0400 Subject: [PATCH 195/340] Add missing resources to kustomization.yaml (#2255) * Add missing JWT provider resource to kustomization.yaml - Add missing assertions for JWT provider too. * Add OSS tests for exported-services --- .../config_entries_namespaces_test.go | 91 +++++++++++++------ .../config-entries/config_entries_test.go | 57 +++++++++++- ...viceexports.yaml => exportedservices.yaml} | 8 +- .../fixtures/bases/crds-oss/jwtprovider.yaml | 28 +++--- .../bases/crds-oss/kustomization.yaml | 20 ++-- .../cases/crds-ent/exportedservices.yaml | 4 +- .../cases/crds-ent/kustomization.yaml | 5 +- 7 files changed, 150 insertions(+), 63 deletions(-) rename acceptance/tests/fixtures/bases/crds-oss/{serviceexports.yaml => exportedservices.yaml} (65%) diff --git a/acceptance/tests/config-entries/config_entries_namespaces_test.go b/acceptance/tests/config-entries/config_entries_namespaces_test.go index c15277a6ee..507b1a07af 100644 --- a/acceptance/tests/config-entries/config_entries_namespaces_test.go +++ b/acceptance/tests/config-entries/config_entries_namespaces_test.go @@ -159,13 +159,6 @@ func TestControllerNamespaces(t *testing.T) { require.True(r, ok, "could not cast to ProxyConfigEntry") require.Equal(r, api.MeshGatewayModeLocal, proxyDefaultEntry.MeshGateway.Mode) - // exported-services - entry, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", defaultOpts) - require.NoError(r, err) - exportedServicesEntry, ok := entry.(*api.ExportedServicesConfigEntry) - require.True(r, ok, "could not cast to ExportedServicesConfigEntry") - require.Equal(r, "frontend", exportedServicesEntry.Services[0].Name) - // mesh entry, _, err = consulClient.ConfigEntries().Get(api.MeshConfig, "mesh", defaultOpts) require.NoError(r, err) @@ -216,6 +209,33 @@ func TestControllerNamespaces(t *testing.T) { require.Equal(r, "certFile", terminatingGatewayEntry.Services[0].CertFile) require.Equal(r, "keyFile", terminatingGatewayEntry.Services[0].KeyFile) require.Equal(r, "sni", terminatingGatewayEntry.Services[0].SNI) + + // jwt-provider + entry, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "jwt-provider", nil) + require.NoError(r, err) + jwtProviderConfigEntry, ok := entry.(*api.JWTProviderConfigEntry) + require.True(r, ok, "could not cast to JWTProviderConfigEntry") + require.Equal(r, "jwks.txt", jwtProviderConfigEntry.JSONWebKeySet.Local.Filename) + require.Equal(r, "test-issuer", jwtProviderConfigEntry.Issuer) + require.ElementsMatch(r, []string{"aud1", "aud2"}, jwtProviderConfigEntry.Audiences) + require.Equal(r, "x-jwt-header", jwtProviderConfigEntry.Locations[0].Header.Name) + require.Equal(r, "x-query-param", jwtProviderConfigEntry.Locations[1].QueryParam.Name) + require.Equal(r, "session-id", jwtProviderConfigEntry.Locations[2].Cookie.Name) + require.Equal(r, "x-forwarded-jwt", jwtProviderConfigEntry.Forwarding.HeaderName) + require.True(r, jwtProviderConfigEntry.Forwarding.PadForwardPayloadHeader) + require.Equal(r, 45, jwtProviderConfigEntry.ClockSkewSeconds) + require.Equal(r, 15, jwtProviderConfigEntry.CacheConfig.Size) + + // exported-services + entry, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", nil) + require.NoError(r, err) + exportedServicesConfigEntry, ok := entry.(*api.ExportedServicesConfigEntry) + require.True(r, ok, "could not cast to ExportedServicesConfigEntry") + require.Equal(r, "frontend", exportedServicesConfigEntry.Services[0].Name) + require.Equal(r, "frontend", exportedServicesConfigEntry.Services[0].Namespace) + require.Equal(r, "partitionName", exportedServicesConfigEntry.Services[0].Consumers[0].Partition) + require.Equal(r, "peerName", exportedServicesConfigEntry.Services[0].Consumers[1].Peer) + require.Equal(r, "groupName", exportedServicesConfigEntry.Services[0].Consumers[2].SamenessGroup) }) } @@ -233,10 +253,6 @@ func TestControllerNamespaces(t *testing.T) { patchMeshGatewayMode := "remote" k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "-n", KubeNS, "proxydefaults", "global", "-p", fmt.Sprintf(`{"spec":{"meshGateway":{"mode": "%s"}}}`, patchMeshGatewayMode), "--type=merge") - logger.Log(t, "patching partition-exports custom resource") - patchServiceName := "backend" - k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "-n", KubeNS, "exportedservices", "default", "-p", fmt.Sprintf(`{"spec":{"services":[{"name": "%s", "namespace": "front", "consumers":[{"partition": "foo"}]}]}}`, patchServiceName), "--type=merge") - logger.Log(t, "patching mesh custom resource") k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "-n", KubeNS, "mesh", "mesh", "-p", fmt.Sprintf(`{"spec":{"transparentProxy":{"meshDestinationsOnly": %t}}}`, false), "--type=merge") @@ -258,6 +274,14 @@ func TestControllerNamespaces(t *testing.T) { patchSNI := "patch-sni" k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "-n", KubeNS, "terminatinggateway", "terminating-gateway", "-p", fmt.Sprintf(`{"spec": {"services": [{"name":"name","caFile":"caFile","certFile":"certFile","keyFile":"keyFile","sni":"%s"}]}}`, patchSNI), "--type=merge") + logger.Log(t, "patching jwt-provider custom resource") + patchIssuer := "other-issuer" + k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "-n", KubeNS, "jwtprovider", "jwt-provider", "-p", fmt.Sprintf(`{"spec": {"issuer": "%s"}}`, patchIssuer), "--type=merge") + + logger.Log(t, "patching exported-services custom resource") + patchPartition := "destination" + k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "-n", KubeNS, "exportedservices", "default", "-p", fmt.Sprintf(`{"spec": {"services": [{"name": "frontend", "namespace": "frontend", "consumers": [{"partition": "%s"}, {"peer": "peerName"}, {"samenessGroup": "groupName"}]}]}}`, patchPartition), "--type=merge") + counter := &retry.Counter{Count: 20, Wait: 2 * time.Second} retry.RunWith(counter, t, func(r *retry.R) { // service-defaults @@ -281,13 +305,6 @@ func TestControllerNamespaces(t *testing.T) { require.True(r, ok, "could not cast to ProxyConfigEntry") require.Equal(r, api.MeshGatewayModeRemote, proxyDefaultsEntry.MeshGateway.Mode) - // partition-exports - entry, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", defaultOpts) - require.NoError(r, err) - exportedServicesEntry, ok := entry.(*api.ExportedServicesConfigEntry) - require.True(r, ok, "could not cast to ExportedServicesConfigEntry") - require.Equal(r, "backend", exportedServicesEntry.Services[0].Name) - // mesh entry, _, err = consulClient.ConfigEntries().Get(api.MeshConfig, "mesh", defaultOpts) require.NoError(r, err) @@ -331,6 +348,20 @@ func TestControllerNamespaces(t *testing.T) { terminatingGatewayEntry, ok := entry.(*api.TerminatingGatewayConfigEntry) require.True(r, ok, "could not cast to TerminatingGatewayConfigEntry") require.Equal(r, patchSNI, terminatingGatewayEntry.Services[0].SNI) + + // jwt-Provider + entry, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "jwt-provider", nil) + require.NoError(r, err) + jwtProviderConfigEntry, ok := entry.(*api.JWTProviderConfigEntry) + require.True(r, ok, "could not cast to JWTProviderConfigEntry") + require.Equal(r, patchIssuer, jwtProviderConfigEntry.Issuer) + + // exported-services + entry, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", nil) + require.NoError(r, err) + exportedServicesConfigEntry, ok := entry.(*api.ExportedServicesConfigEntry) + require.True(r, ok, "could not cast to ExportedServicesConfigEntry") + require.Equal(r, patchPartition, exportedServicesConfigEntry.Services[0].Consumers[0].Partition) }) } @@ -345,9 +376,6 @@ func TestControllerNamespaces(t *testing.T) { logger.Log(t, "deleting proxy-defaults custom resource") k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "-n", KubeNS, "proxydefaults", "global") - logger.Log(t, "deleting partition-exports custom resource") - k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "-n", KubeNS, "exportedservices", "default") - logger.Log(t, "deleting mesh custom resource") k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "-n", KubeNS, "mesh", "mesh") @@ -366,6 +394,12 @@ func TestControllerNamespaces(t *testing.T) { logger.Log(t, "deleting terminating-gateway custom resource") k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "-n", KubeNS, "terminatinggateway", "terminating-gateway") + logger.Log(t, "deleting jwt-provider custom resource") + k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "-n", KubeNS, "jwtprovider", "jwt-provider") + + logger.Log(t, "deleting exported-services custom resource") + k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "-n", KubeNS, "exportedservices", "default") + counter := &retry.Counter{Count: 20, Wait: 2 * time.Second} retry.RunWith(counter, t, func(r *retry.R) { // service-defaults @@ -383,11 +417,6 @@ func TestControllerNamespaces(t *testing.T) { require.Error(r, err) require.Contains(r, err.Error(), "404 (Config entry not found") - // partition-exports - _, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", defaultOpts) - require.Error(r, err) - require.Contains(r, err.Error(), "404 (Config entry not found") - // mesh _, _, err = consulClient.ConfigEntries().Get(api.MeshConfig, "mesh", defaultOpts) require.Error(r, err) @@ -417,6 +446,16 @@ func TestControllerNamespaces(t *testing.T) { _, _, err = consulClient.ConfigEntries().Get(api.IngressGateway, "terminating-gateway", queryOpts) require.Error(r, err) require.Contains(r, err.Error(), "404 (Config entry not found") + + // jwt-provider + _, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "jwt-provider", nil) + require.Error(r, err) + require.Contains(r, err.Error(), "404 (Config entry not found") + + // exported-services + _, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", nil) + require.Error(r, err) + require.Contains(r, err.Error(), "404 (Config entry not found") }) } }) diff --git a/acceptance/tests/config-entries/config_entries_test.go b/acceptance/tests/config-entries/config_entries_test.go index a31f4762fd..5f88594468 100644 --- a/acceptance/tests/config-entries/config_entries_test.go +++ b/acceptance/tests/config-entries/config_entries_test.go @@ -95,7 +95,7 @@ func TestController(t *testing.T) { // On startup, the controller can take upwards of 1m to perform // leader election so we may need to wait a long time for - // the reconcile loop to run (hence the 1m timeout here). + // the reconcile loop to run (hence the 2m timeout here). counter := &retry.Counter{Count: 60, Wait: 2 * time.Second} retry.RunWith(counter, t, func(r *retry.R) { // service-defaults @@ -180,20 +180,29 @@ func TestController(t *testing.T) { require.Equal(r, "sni", terminatingGatewayEntry.Services[0].SNI) // jwt-provider - entry, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "test-jwt-provider", nil) + entry, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "jwt-provider", nil) require.NoError(r, err) jwtProviderConfigEntry, ok := entry.(*api.JWTProviderConfigEntry) require.True(r, ok, "could not cast to JWTProviderConfigEntry") require.Equal(r, "jwks.txt", jwtProviderConfigEntry.JSONWebKeySet.Local.Filename) require.Equal(r, "test-issuer", jwtProviderConfigEntry.Issuer) - require.Equal(r, []string{"aud1", "aud2"}, jwtProviderConfigEntry.Issuer) + require.ElementsMatch(r, []string{"aud1", "aud2"}, jwtProviderConfigEntry.Audiences) require.Equal(r, "x-jwt-header", jwtProviderConfigEntry.Locations[0].Header.Name) require.Equal(r, "x-query-param", jwtProviderConfigEntry.Locations[1].QueryParam.Name) require.Equal(r, "session-id", jwtProviderConfigEntry.Locations[2].Cookie.Name) require.Equal(r, "x-forwarded-jwt", jwtProviderConfigEntry.Forwarding.HeaderName) require.True(r, jwtProviderConfigEntry.Forwarding.PadForwardPayloadHeader) require.Equal(r, 45, jwtProviderConfigEntry.ClockSkewSeconds) - require.Equal(r, 15, jwtProviderConfigEntry.CacheConfig) + require.Equal(r, 15, jwtProviderConfigEntry.CacheConfig.Size) + + // exported-services + entry, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", nil) + require.NoError(r, err) + exportedServicesConfigEntry, ok := entry.(*api.ExportedServicesConfigEntry) + require.True(r, ok, "could not cast to ExportedServicesConfigEntry") + require.Equal(r, "frontend", exportedServicesConfigEntry.Services[0].Name) + require.Equal(r, "peerName", exportedServicesConfigEntry.Services[0].Consumers[0].Peer) + require.Equal(r, "groupName", exportedServicesConfigEntry.Services[0].Consumers[1].SamenessGroup) }) } @@ -232,6 +241,14 @@ func TestController(t *testing.T) { patchSNI := "patch-sni" k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "terminatinggateway", "terminating-gateway", "-p", fmt.Sprintf(`{"spec": {"services": [{"name":"name","caFile":"caFile","certFile":"certFile","keyFile":"keyFile","sni":"%s"}]}}`, patchSNI), "--type=merge") + logger.Log(t, "patching JWTProvider custom resource") + patchIssuer := "other-issuer" + k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "jwtprovider", "jwt-provider", "-p", fmt.Sprintf(`{"spec": {"issuer": "%s"}}`, patchIssuer), "--type=merge") + + logger.Log(t, "patching ExportedServices custom resource") + patchPeer := "destination" + k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "exportedservices", "default", "-p", fmt.Sprintf(`{"spec": {"services": [{"name": "frontend", "consumers": [{"peer": "%s"}, {"samenessGroup": "groupName"}]}]}}`, patchPeer), "--type=merge") + counter := &retry.Counter{Count: 10, Wait: 500 * time.Millisecond} retry.RunWith(counter, t, func(r *retry.R) { // service-defaults @@ -299,6 +316,20 @@ func TestController(t *testing.T) { terminatingGatewayEntry, ok := entry.(*api.TerminatingGatewayConfigEntry) require.True(r, ok, "could not cast to TerminatingGatewayConfigEntry") require.Equal(r, patchSNI, terminatingGatewayEntry.Services[0].SNI) + + // jwt-provider + entry, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "jwt-provider", nil) + require.NoError(r, err) + jwtProviderConfigEntry, ok := entry.(*api.JWTProviderConfigEntry) + require.True(r, ok, "could not cast to JWTProviderConfigEntry") + require.Equal(r, patchIssuer, jwtProviderConfigEntry.Issuer) + + // exported-services + entry, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", nil) + require.NoError(r, err) + exportedServicesConfigEntry, ok := entry.(*api.ExportedServicesConfigEntry) + require.True(r, ok, "could not cast to ExportedServicesConfigEntry") + require.Equal(r, patchPeer, exportedServicesConfigEntry.Services[0].Consumers[0].Peer) }) } @@ -331,6 +362,12 @@ func TestController(t *testing.T) { logger.Log(t, "deleting terminating-gateway custom resource") k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "terminatinggateway", "terminating-gateway") + logger.Log(t, "deleting jwt-provider custom resource") + k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "jwtprovider", "jwt-provider") + + logger.Log(t, "deleting exported-services custom resource") + k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "exportedservices", "default") + counter := &retry.Counter{Count: 10, Wait: 500 * time.Millisecond} retry.RunWith(counter, t, func(r *retry.R) { // service-defaults @@ -374,7 +411,17 @@ func TestController(t *testing.T) { require.Contains(r, err.Error(), "404 (Config entry not found") // terminating-gateway - _, _, err = consulClient.ConfigEntries().Get(api.IngressGateway, "terminating-gateway", nil) + _, _, err = consulClient.ConfigEntries().Get(api.TerminatingGateway, "terminating-gateway", nil) + require.Error(r, err) + require.Contains(r, err.Error(), "404 (Config entry not found") + + // jwt-provider + _, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "jwt-provider", nil) + require.Error(r, err) + require.Contains(r, err.Error(), "404 (Config entry not found") + + // exported-services + _, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", nil) require.Error(r, err) require.Contains(r, err.Error(), "404 (Config entry not found") }) diff --git a/acceptance/tests/fixtures/bases/crds-oss/serviceexports.yaml b/acceptance/tests/fixtures/bases/crds-oss/exportedservices.yaml similarity index 65% rename from acceptance/tests/fixtures/bases/crds-oss/serviceexports.yaml rename to acceptance/tests/fixtures/bases/crds-oss/exportedservices.yaml index 704ea9ee19..51d69ae709 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/serviceexports.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/exportedservices.yaml @@ -2,12 +2,12 @@ # SPDX-License-Identifier: MPL-2.0 apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceExports +kind: ExportedServices metadata: - name: exports + name: default spec: services: - name: frontend - namespace: frontend consumers: - - partition: other \ No newline at end of file + - peer: peerName + - samenessGroup: groupName \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/crds-oss/jwtprovider.yaml b/acceptance/tests/fixtures/bases/crds-oss/jwtprovider.yaml index d28139dc40..d35e532bf2 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/jwtprovider.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/jwtprovider.yaml @@ -4,31 +4,27 @@ apiVersion: consul.hashicorp.com/v1alpha1 kind: JWTProvider metadata: - name: test-jwt-provider + name: jwt-provider spec: jsonWebKeySet: local: filename: "jwks.txt" issuer: "test-issuer" audiences: - - "aud1" - - "aud2" + - "aud1" + - "aud2" locations: - - header: - name: "x-jwt-header" - valuePrefix: "bearer" - forward: true - - queryParam: - name: "x-query-param" - - cookie: - name: "session-id" + - header: + name: "x-jwt-header" + valuePrefix: "bearer" + forward: true + - queryParam: + name: "x-query-param" + - cookie: + name: "session-id" forwarding: headerName: "x-forwarded-jwt" padForwardPayloadHeader: true clockSkewSeconds: 45 cacheConfig: - size: 15 - - - - + size: 15 \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/crds-oss/kustomization.yaml b/acceptance/tests/fixtures/bases/crds-oss/kustomization.yaml index 1217d857c8..de02de61e9 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/kustomization.yaml @@ -2,12 +2,14 @@ # SPDX-License-Identifier: MPL-2.0 resources: - - ingressgateway.yaml - - mesh.yaml - - proxydefaults.yaml - - servicedefaults.yaml - - serviceintentions.yaml - - serviceresolver.yaml - - servicerouter.yaml - - servicesplitter.yaml - - terminatinggateway.yaml +- ingressgateway.yaml +- mesh.yaml +- proxydefaults.yaml +- servicedefaults.yaml +- serviceintentions.yaml +- serviceresolver.yaml +- servicerouter.yaml +- servicesplitter.yaml +- terminatinggateway.yaml +- jwtprovider.yaml +- exportedservices.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/crds-ent/exportedservices.yaml b/acceptance/tests/fixtures/cases/crds-ent/exportedservices.yaml index f9f8aad4bf..dd39ab3626 100644 --- a/acceptance/tests/fixtures/cases/crds-ent/exportedservices.yaml +++ b/acceptance/tests/fixtures/cases/crds-ent/exportedservices.yaml @@ -10,4 +10,6 @@ spec: - name: frontend namespace: frontend consumers: - - partition: other \ No newline at end of file + - partition: partitionName + - peer: peerName + - samenessGroup: groupName \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/crds-ent/kustomization.yaml b/acceptance/tests/fixtures/cases/crds-ent/kustomization.yaml index 14f6c765d8..69d972b417 100644 --- a/acceptance/tests/fixtures/cases/crds-ent/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crds-ent/kustomization.yaml @@ -2,5 +2,6 @@ # SPDX-License-Identifier: MPL-2.0 resources: - - ../../bases/crds-oss - - exportedservices.yaml +- ../../bases/crds-oss +patchesStrategicMerge: +- exportedservices.yaml From 89666515f5f0aa7d426eca95b9df6dbc3a4d240f Mon Sep 17 00:00:00 2001 From: Andrew Stucki Date: Fri, 2 Jun 2023 22:39:35 -0400 Subject: [PATCH 196/340] Fix Gateway trigger for when secret is modified (#2261) * Fix Gateway trigger for when secret is modified * Add some simple unit tests * up some testing timeouts for acceptance tests --- .../api-gateway/api_gateway_tenancy_test.go | 12 +-- .../controllers/gateway_controller.go | 2 +- .../controllers/gateway_controller_test.go | 91 +++++++++++++++++++ 3 files changed, 98 insertions(+), 7 deletions(-) diff --git a/acceptance/tests/api-gateway/api_gateway_tenancy_test.go b/acceptance/tests/api-gateway/api_gateway_tenancy_test.go index d5a0845810..f2e6899094 100644 --- a/acceptance/tests/api-gateway/api_gateway_tenancy_test.go +++ b/acceptance/tests/api-gateway/api_gateway_tenancy_test.go @@ -152,7 +152,7 @@ func TestAPIGateway_Tenancy(t *testing.T) { checkConsulNotExists(t, consulClient, api.APIGateway, "gateway", namespaceForConsul(c.namespaceMirroring, gatewayNamespace)) // route failure - retryCheck(t, 10, func(r *retry.R) { + retryCheck(t, 30, func(r *retry.R) { var httproute gwv1beta1.HTTPRoute err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "route", Namespace: routeNamespace}, &httproute) require.NoError(r, err) @@ -178,7 +178,7 @@ func TestAPIGateway_Tenancy(t *testing.T) { createReferenceGrant(t, k8sClient, "route-service", routeNamespace, serviceNamespace) // gateway updated with references allowed - retryCheck(t, 10, func(r *retry.R) { + retryCheck(t, 30, func(r *retry.R) { var gateway gwv1beta1.Gateway err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway", Namespace: gatewayNamespace}, &gateway) require.NoError(r, err) @@ -195,7 +195,7 @@ func TestAPIGateway_Tenancy(t *testing.T) { }) // check the Consul gateway is updated, with the listener. - retryCheck(t, 10, func(r *retry.R) { + retryCheck(t, 30, func(r *retry.R) { entry, _, err := consulClient.ConfigEntries().Get(api.APIGateway, "gateway", &api.QueryOptions{ Namespace: namespaceForConsul(c.namespaceMirroring, gatewayNamespace), }) @@ -210,7 +210,7 @@ func TestAPIGateway_Tenancy(t *testing.T) { }) // route updated with gateway and services allowed - retryCheck(t, 10, func(r *retry.R) { + retryCheck(t, 30, func(r *retry.R) { var httproute gwv1beta1.HTTPRoute err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "route", Namespace: routeNamespace}, &httproute) require.NoError(r, err) @@ -225,7 +225,7 @@ func TestAPIGateway_Tenancy(t *testing.T) { }) // now check to make sure that the route is updated and valid - retryCheck(t, 10, func(r *retry.R) { + retryCheck(t, 30, func(r *retry.R) { // since we're not bound, check to make sure that the route doesn't target the gateway in Consul. entry, _, err := consulClient.ConfigEntries().Get(api.HTTPRoute, "route", &api.QueryOptions{ Namespace: namespaceForConsul(c.namespaceMirroring, routeNamespace), @@ -239,7 +239,7 @@ func TestAPIGateway_Tenancy(t *testing.T) { }) // and check to make sure that the certificate exists - retryCheck(t, 10, func(r *retry.R) { + retryCheck(t, 30, func(r *retry.R) { entry, _, err := consulClient.ConfigEntries().Get(api.InlineCertificate, "certificate", &api.QueryOptions{ Namespace: namespaceForConsul(c.namespaceMirroring, certificateNamespace), }) diff --git a/control-plane/api-gateway/controllers/gateway_controller.go b/control-plane/api-gateway/controllers/gateway_controller.go index 8baa27cca1..cc90ad6f17 100644 --- a/control-plane/api-gateway/controllers/gateway_controller.go +++ b/control-plane/api-gateway/controllers/gateway_controller.go @@ -452,7 +452,7 @@ func (r *GatewayController) transformSecret(ctx context.Context) func(o client.O secret := o.(*corev1.Secret) gatewayList := &gwv1beta1.GatewayList{} if err := r.Client.List(ctx, gatewayList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(Secret_GatewayIndex, secret.Name), + FieldSelector: fields.OneTermEqualSelector(Secret_GatewayIndex, client.ObjectKeyFromObject(secret).String()), }); err != nil { return nil } diff --git a/control-plane/api-gateway/controllers/gateway_controller_test.go b/control-plane/api-gateway/controllers/gateway_controller_test.go index b36f256121..d1028eb945 100644 --- a/control-plane/api-gateway/controllers/gateway_controller_test.go +++ b/control-plane/api-gateway/controllers/gateway_controller_test.go @@ -8,9 +8,14 @@ import ( "testing" "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/reconcile" gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" @@ -295,3 +300,89 @@ func TestTransformTCPRoute(t *testing.T) { }) } } + +func TestTransformSecret(t *testing.T) { + t.Parallel() + + gateway := &gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway", + Namespace: "test", + }, + Spec: gwv1beta1.GatewaySpec{ + Listeners: []gwv1beta1.Listener{ + {Name: "terminate", TLS: &gwv1beta1.GatewayTLSConfig{ + Mode: common.PointerTo(gwv1beta1.TLSModeTerminate), + CertificateRefs: []gwv1beta1.SecretObjectReference{ + {Name: "secret-no-namespace"}, + {Name: "secret-namespace", Namespace: common.PointerTo(gwv1beta1.Namespace("other"))}, + }, + }}, + {Name: "passthrough", TLS: &gwv1beta1.GatewayTLSConfig{ + Mode: common.PointerTo(gwv1beta1.TLSModePassthrough), + CertificateRefs: []gwv1beta1.SecretObjectReference{ + {Name: "passthrough", Namespace: common.PointerTo(gwv1beta1.Namespace("other"))}, + }, + }}, + }, + }, + } + + for name, tt := range map[string]struct { + secret *corev1.Secret + expected []reconcile.Request + }{ + "explicit namespace from parent": { + secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "secret-namespace", Namespace: "other"}, + }, + expected: []reconcile.Request{ + {NamespacedName: types.NamespacedName{Name: "gateway", Namespace: "test"}}, + }, + }, + "implicit namespace from parent": { + secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "secret-no-namespace", Namespace: "test"}, + }, + expected: []reconcile.Request{ + {NamespacedName: types.NamespacedName{Name: "gateway", Namespace: "test"}}, + }, + }, + "mismatched namespace": { + secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "secret-no-namespace", Namespace: "other"}, + }, + }, + "mismatched names": { + secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "something", Namespace: "test"}, + }, + }, + "passthrough ignored": { + secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "passthrough", Namespace: "other"}, + }, + }, + } { + t.Run(name, func(t *testing.T) { + tt := tt + + t.Parallel() + + s := runtime.NewScheme() + require.NoError(t, clientgoscheme.AddToScheme(s)) + require.NoError(t, gwv1alpha2.Install(s)) + require.NoError(t, gwv1beta1.Install(s)) + require.NoError(t, v1alpha1.AddToScheme(s)) + + fakeClient := registerFieldIndexersForTest(fake.NewClientBuilder().WithScheme(s)).WithRuntimeObjects(gateway).Build() + + controller := GatewayController{ + Client: fakeClient, + } + + fn := controller.transformSecret(context.Background()) + require.ElementsMatch(t, tt.expected, fn(tt.secret)) + }) + } +} From 3cecd2e5d82c5b22e0fa741dc3df313609228cfe Mon Sep 17 00:00:00 2001 From: Dan Bond Date: Sun, 4 Jun 2023 17:36:35 -0700 Subject: [PATCH 197/340] Add CRD for ControlPlane RequestLimits (#2166) --- .changelog/2166.txt | 3 + Makefile | 2 +- .../config_entries_namespaces_test.go | 65 +- .../config-entries/config_entries_test.go | 54 ++ .../crds-oss/controlplanerequestlimit.yaml | 50 ++ .../bases/crds-oss/kustomization.yaml | 3 +- ...ewayclassconfigs.consul.hashicorp.com.yaml | 3 +- .../templates/connect-inject-clusterrole.yaml | 2 + ...t-inject-mutatingwebhookconfiguration.yaml | 21 + .../crd-controlplanerequestlimits.yaml | 196 ++++++ .../templates/crd-exportedservices.yaml | 5 +- .../consul/templates/crd-ingressgateways.yaml | 5 +- charts/consul/templates/crd-jwtproviders.yaml | 5 +- charts/consul/templates/crd-meshes.yaml | 5 +- charts/consul/templates/crd-meshservices.yaml | 5 +- .../templates/crd-peeringacceptors.yaml | 5 +- .../consul/templates/crd-peeringdialers.yaml | 5 +- .../consul/templates/crd-proxydefaults.yaml | 5 +- .../consul/templates/crd-samenessgroups.yaml | 5 +- .../consul/templates/crd-servicedefaults.yaml | 5 +- .../templates/crd-serviceintentions.yaml | 5 +- .../templates/crd-serviceresolvers.yaml | 5 +- .../consul/templates/crd-servicerouters.yaml | 5 +- .../templates/crd-servicesplitters.yaml | 5 +- .../templates/crd-terminatinggateways.yaml | 5 +- ...t-inject-mutatingwebhookconfiguration.bats | 4 +- .../unit/crd-controlplanerequestlimits.bats | 26 + control-plane/PROJECT | 9 + control-plane/api/common/common.go | 23 +- .../controlplanerequestlimit_types.go | 268 +++++++++ .../controlplanerequestlimit_types_test.go | 566 ++++++++++++++++++ .../controlplanerequestlimit_webhook.go | 83 +++ .../controlplanerequestlimit_webhook_test.go | 145 +++++ .../api/v1alpha1/zz_generated.deepcopy.go | 153 +++++ ...shicorp.com_controlplanerequestlimits.yaml | 191 ++++++ ...consul.hashicorp.com_exportedservices.yaml | 3 +- ...sul.hashicorp.com_gatewayclassconfigs.yaml | 3 +- .../consul.hashicorp.com_ingressgateways.yaml | 3 +- .../consul.hashicorp.com_jwtproviders.yaml | 3 +- .../bases/consul.hashicorp.com_meshes.yaml | 3 +- .../consul.hashicorp.com_meshservices.yaml | 3 +- ...consul.hashicorp.com_peeringacceptors.yaml | 3 +- .../consul.hashicorp.com_peeringdialers.yaml | 3 +- .../consul.hashicorp.com_proxydefaults.yaml | 3 +- .../consul.hashicorp.com_samenessgroups.yaml | 3 +- .../consul.hashicorp.com_servicedefaults.yaml | 3 +- ...onsul.hashicorp.com_serviceintentions.yaml | 3 +- ...consul.hashicorp.com_serviceresolvers.yaml | 3 +- .../consul.hashicorp.com_servicerouters.yaml | 3 +- ...consul.hashicorp.com_servicesplitters.yaml | 3 +- ...sul.hashicorp.com_terminatinggateways.yaml | 3 +- control-plane/config/crd/kustomization.yaml | 21 + control-plane/config/crd/kustomizeconfig.yaml | 17 + control-plane/config/rbac/role.yaml | 21 +- control-plane/config/webhook/manifests.yaml | 22 +- .../configentry_controller_test.go | 314 +++++++++- .../controlplanerequestlimit_controller.go | 40 ++ .../subcommand/inject-connect/command.go | 15 + 58 files changed, 2337 insertions(+), 103 deletions(-) create mode 100644 .changelog/2166.txt create mode 100644 acceptance/tests/fixtures/bases/crds-oss/controlplanerequestlimit.yaml create mode 100644 charts/consul/templates/crd-controlplanerequestlimits.yaml create mode 100644 charts/consul/test/unit/crd-controlplanerequestlimits.bats create mode 100644 control-plane/api/v1alpha1/controlplanerequestlimit_types.go create mode 100644 control-plane/api/v1alpha1/controlplanerequestlimit_types_test.go create mode 100644 control-plane/api/v1alpha1/controlplanerequestlimit_webhook.go create mode 100644 control-plane/api/v1alpha1/controlplanerequestlimit_webhook_test.go create mode 100644 control-plane/config/crd/bases/consul.hashicorp.com_controlplanerequestlimits.yaml create mode 100644 control-plane/config/crd/kustomization.yaml create mode 100644 control-plane/config/crd/kustomizeconfig.yaml create mode 100644 control-plane/controllers/controlplanerequestlimit_controller.go diff --git a/.changelog/2166.txt b/.changelog/2166.txt new file mode 100644 index 0000000000..b2392bd7d5 --- /dev/null +++ b/.changelog/2166.txt @@ -0,0 +1,3 @@ +```release-note:feature +Add support for configuring Consul server-side rate limiting +``` diff --git a/Makefile b/Makefile index 151f7868de..437357898a 100644 --- a/Makefile +++ b/Makefile @@ -139,7 +139,7 @@ ifeq (, $(shell which controller-gen)) CONTROLLER_GEN_TMP_DIR=$$(mktemp -d) ;\ cd $$CONTROLLER_GEN_TMP_DIR ;\ go mod init tmp ;\ - go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.10.0 ;\ + go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.12.0 ;\ rm -rf $$CONTROLLER_GEN_TMP_DIR ;\ } CONTROLLER_GEN=$(shell go env GOPATH)/bin/controller-gen diff --git a/acceptance/tests/config-entries/config_entries_namespaces_test.go b/acceptance/tests/config-entries/config_entries_namespaces_test.go index 507b1a07af..ced7cc8236 100644 --- a/acceptance/tests/config-entries/config_entries_namespaces_test.go +++ b/acceptance/tests/config-entries/config_entries_namespaces_test.go @@ -211,7 +211,7 @@ func TestControllerNamespaces(t *testing.T) { require.Equal(r, "sni", terminatingGatewayEntry.Services[0].SNI) // jwt-provider - entry, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "jwt-provider", nil) + entry, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "jwt-provider", defaultOpts) require.NoError(r, err) jwtProviderConfigEntry, ok := entry.(*api.JWTProviderConfigEntry) require.True(r, ok, "could not cast to JWTProviderConfigEntry") @@ -227,7 +227,7 @@ func TestControllerNamespaces(t *testing.T) { require.Equal(r, 15, jwtProviderConfigEntry.CacheConfig.Size) // exported-services - entry, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", nil) + entry, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", defaultOpts) require.NoError(r, err) exportedServicesConfigEntry, ok := entry.(*api.ExportedServicesConfigEntry) require.True(r, ok, "could not cast to ExportedServicesConfigEntry") @@ -236,6 +236,41 @@ func TestControllerNamespaces(t *testing.T) { require.Equal(r, "partitionName", exportedServicesConfigEntry.Services[0].Consumers[0].Partition) require.Equal(r, "peerName", exportedServicesConfigEntry.Services[0].Consumers[1].Peer) require.Equal(r, "groupName", exportedServicesConfigEntry.Services[0].Consumers[2].SamenessGroup) + + // control-plane-request-limit + entry, _, err = consulClient.ConfigEntries().Get(api.RateLimitIPConfig, "controlplanerequestlimit", defaultOpts) + require.NoError(r, err) + rateLimitIPConfigEntry, ok := entry.(*api.RateLimitIPConfigEntry) + require.True(r, ok, "could not cast to RateLimitIPConfigEntry") + require.Equal(t, "permissive", rateLimitIPConfigEntry.Mode) + require.Equal(t, 100.0, rateLimitIPConfigEntry.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.WriteRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.ACL.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.ACL.WriteRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Catalog.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Catalog.WriteRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.ConfigEntry.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.ConfigEntry.WriteRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.ConnectCA.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.ConnectCA.WriteRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Coordinate.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Coordinate.WriteRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.DiscoveryChain.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.DiscoveryChain.WriteRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Health.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Health.WriteRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Intention.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Intention.WriteRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.KV.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.KV.WriteRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Tenancy.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Tenancy.WriteRate) + //require.Equal(t, 100.0, rateLimitIPConfigEntry.PreparedQuery.ReadRate) + //require.Equal(t, 100.0, rateLimitIPConfigEntry.PreparedQuery.WriteRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Session.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Session.WriteRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Txn.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Txn.WriteRate) }) } @@ -282,6 +317,9 @@ func TestControllerNamespaces(t *testing.T) { patchPartition := "destination" k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "-n", KubeNS, "exportedservices", "default", "-p", fmt.Sprintf(`{"spec": {"services": [{"name": "frontend", "namespace": "frontend", "consumers": [{"partition": "%s"}, {"peer": "peerName"}, {"samenessGroup": "groupName"}]}]}}`, patchPartition), "--type=merge") + logger.Log(t, "patching control-plane-request-limit custom resource") + k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "-n", KubeNS, "controlplanerequestlimit", "controlplanerequestlimit", "-p", `{"spec": {"mode": "disabled"}}`, "--type=merge") + counter := &retry.Counter{Count: 20, Wait: 2 * time.Second} retry.RunWith(counter, t, func(r *retry.R) { // service-defaults @@ -350,18 +388,25 @@ func TestControllerNamespaces(t *testing.T) { require.Equal(r, patchSNI, terminatingGatewayEntry.Services[0].SNI) // jwt-Provider - entry, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "jwt-provider", nil) + entry, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "jwt-provider", defaultOpts) require.NoError(r, err) jwtProviderConfigEntry, ok := entry.(*api.JWTProviderConfigEntry) require.True(r, ok, "could not cast to JWTProviderConfigEntry") require.Equal(r, patchIssuer, jwtProviderConfigEntry.Issuer) // exported-services - entry, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", nil) + entry, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", defaultOpts) require.NoError(r, err) exportedServicesConfigEntry, ok := entry.(*api.ExportedServicesConfigEntry) require.True(r, ok, "could not cast to ExportedServicesConfigEntry") require.Equal(r, patchPartition, exportedServicesConfigEntry.Services[0].Consumers[0].Partition) + + // control-plane-request-limit + entry, _, err = consulClient.ConfigEntries().Get(api.RateLimitIPConfig, "controlplanerequestlimit", defaultOpts) + require.NoError(r, err) + rateLimitIPConfigEntry, ok := entry.(*api.RateLimitIPConfigEntry) + require.True(r, ok, "could not cast to RateLimitIPConfigEntry") + require.Equal(r, rateLimitIPConfigEntry.Mode, "disabled") }) } @@ -400,6 +445,9 @@ func TestControllerNamespaces(t *testing.T) { logger.Log(t, "deleting exported-services custom resource") k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "-n", KubeNS, "exportedservices", "default") + logger.Log(t, "deleting control-plane-request-limit custom resource") + k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "-n", KubeNS, "controlplanerequestlimit", "controlplanerequestlimit") + counter := &retry.Counter{Count: 20, Wait: 2 * time.Second} retry.RunWith(counter, t, func(r *retry.R) { // service-defaults @@ -448,12 +496,17 @@ func TestControllerNamespaces(t *testing.T) { require.Contains(r, err.Error(), "404 (Config entry not found") // jwt-provider - _, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "jwt-provider", nil) + _, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "jwt-provider", defaultOpts) require.Error(r, err) require.Contains(r, err.Error(), "404 (Config entry not found") // exported-services - _, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", nil) + _, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", defaultOpts) + require.Error(r, err) + require.Contains(r, err.Error(), "404 (Config entry not found") + + // control-plane-request-limit + _, _, err = consulClient.ConfigEntries().Get(api.RateLimitIPConfig, "controlplanerequestlimit", defaultOpts) require.Error(r, err) require.Contains(r, err.Error(), "404 (Config entry not found") }) diff --git a/acceptance/tests/config-entries/config_entries_test.go b/acceptance/tests/config-entries/config_entries_test.go index 5f88594468..089f96767f 100644 --- a/acceptance/tests/config-entries/config_entries_test.go +++ b/acceptance/tests/config-entries/config_entries_test.go @@ -203,6 +203,42 @@ func TestController(t *testing.T) { require.Equal(r, "frontend", exportedServicesConfigEntry.Services[0].Name) require.Equal(r, "peerName", exportedServicesConfigEntry.Services[0].Consumers[0].Peer) require.Equal(r, "groupName", exportedServicesConfigEntry.Services[0].Consumers[1].SamenessGroup) + + // control-plane-request-limit + entry, _, err = consulClient.ConfigEntries().Get(api.RateLimitIPConfig, "controlplanerequestlimit", nil) + require.NoError(r, err) + rateLimitIPConfigEntry, ok := entry.(*api.RateLimitIPConfigEntry) + require.True(r, ok, "could not cast to RateLimitIPConfigEntry") + require.Equal(t, "permissive", rateLimitIPConfigEntry.Mode) + require.Equal(t, 100.0, rateLimitIPConfigEntry.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.WriteRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.ACL.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.ACL.WriteRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Catalog.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Catalog.WriteRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.ConfigEntry.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.ConfigEntry.WriteRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.ConnectCA.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.ConnectCA.WriteRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Coordinate.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Coordinate.WriteRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.DiscoveryChain.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.DiscoveryChain.WriteRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Health.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Health.WriteRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Intention.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Intention.WriteRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.KV.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.KV.WriteRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Tenancy.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Tenancy.WriteRate) + //require.Equal(t, 100.0, rateLimitIPConfigEntry.PreparedQuery.ReadRate) + //require.Equal(t, 100.0, rateLimitIPConfigEntry.PreparedQuery.WriteRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Session.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Session.WriteRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Txn.ReadRate) + require.Equal(t, 100.0, rateLimitIPConfigEntry.Txn.WriteRate, 100.0) + }) } @@ -249,6 +285,9 @@ func TestController(t *testing.T) { patchPeer := "destination" k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "exportedservices", "default", "-p", fmt.Sprintf(`{"spec": {"services": [{"name": "frontend", "consumers": [{"peer": "%s"}, {"samenessGroup": "groupName"}]}]}}`, patchPeer), "--type=merge") + logger.Log(t, "patching control-plane-request-limit custom resource") + k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "controlplanerequestlimit", "controlplanerequestlimit", "-p", `{"spec": {"mode": "disabled"}}`, "--type=merge") + counter := &retry.Counter{Count: 10, Wait: 500 * time.Millisecond} retry.RunWith(counter, t, func(r *retry.R) { // service-defaults @@ -330,6 +369,13 @@ func TestController(t *testing.T) { exportedServicesConfigEntry, ok := entry.(*api.ExportedServicesConfigEntry) require.True(r, ok, "could not cast to ExportedServicesConfigEntry") require.Equal(r, patchPeer, exportedServicesConfigEntry.Services[0].Consumers[0].Peer) + + // control-plane-request-limit + entry, _, err = consulClient.ConfigEntries().Get(api.RateLimitIPConfig, "controlplanerequestlimit", nil) + require.NoError(r, err) + rateLimitIPConfigEntry, ok := entry.(*api.RateLimitIPConfigEntry) + require.True(r, ok, "could not cast to RateLimitIPConfigEntry") + require.Equal(r, rateLimitIPConfigEntry.Mode, "disabled") }) } @@ -368,6 +414,9 @@ func TestController(t *testing.T) { logger.Log(t, "deleting exported-services custom resource") k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "exportedservices", "default") + logger.Log(t, "deleting control-plane-request-limit custom resource") + k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "controlplanerequestlimit", "controlplanerequestlimit") + counter := &retry.Counter{Count: 10, Wait: 500 * time.Millisecond} retry.RunWith(counter, t, func(r *retry.R) { // service-defaults @@ -424,6 +473,11 @@ func TestController(t *testing.T) { _, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", nil) require.Error(r, err) require.Contains(r, err.Error(), "404 (Config entry not found") + + // control-plane-request-limit + _, _, err = consulClient.ConfigEntries().Get(api.RateLimitIPConfig, "controlplanerequestlimit", nil) + require.Error(r, err) + require.Contains(r, err.Error(), "404 (Config entry not found") }) } }) diff --git a/acceptance/tests/fixtures/bases/crds-oss/controlplanerequestlimit.yaml b/acceptance/tests/fixtures/bases/crds-oss/controlplanerequestlimit.yaml new file mode 100644 index 0000000000..5e8e32dbb5 --- /dev/null +++ b/acceptance/tests/fixtures/bases/crds-oss/controlplanerequestlimit.yaml @@ -0,0 +1,50 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ControlPlaneRequestLimit +metadata: + name: controlplanerequestlimit +spec: + mode: "permissive" + readRate: 100.0 + writeRate: 100.0 + acl: + readRate: 100.0 + writeRate: 100.0 + catalog: + readRate: 100.0 + writeRate: 100.0 + configEntry: + readRate: 100.0 + writeRate: 100.0 + connectCA: + readRate: 100.0 + writeRate: 100.0 + coordinate: + readRate: 100.0 + writeRate: 100.0 + discoveryChain: + readRate: 100.0 + writeRate: 100.0 + health: + readRate: 100.0 + writeRate: 100.0 + intention: + readRate: 100.0 + writeRate: 100.0 + kv: + readRate: 100.0 + writeRate: 100.0 + tenancy: + readRate: 100.0 + writeRate: 100.0 +# preparedQuery: +# readRate: 100.0 +# writeRate: 100.0 + session: + readRate: 100.0 + writeRate: 100.0 + txn: + readRate: 100.0 + writeRate: 100.0 diff --git a/acceptance/tests/fixtures/bases/crds-oss/kustomization.yaml b/acceptance/tests/fixtures/bases/crds-oss/kustomization.yaml index de02de61e9..77afbc9522 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/kustomization.yaml @@ -12,4 +12,5 @@ resources: - servicesplitter.yaml - terminatinggateway.yaml - jwtprovider.yaml -- exportedservices.yaml \ No newline at end of file +- exportedservices.yaml +- controlplanerequestlimit.yaml \ No newline at end of file diff --git a/charts/consul/crds/gatewayclassconfigs.consul.hashicorp.com.yaml b/charts/consul/crds/gatewayclassconfigs.consul.hashicorp.com.yaml index a8393cd8fd..44eff52492 100644 --- a/charts/consul/crds/gatewayclassconfigs.consul.hashicorp.com.yaml +++ b/charts/consul/crds/gatewayclassconfigs.consul.hashicorp.com.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: gatewayclassconfigs.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/charts/consul/templates/connect-inject-clusterrole.yaml b/charts/consul/templates/connect-inject-clusterrole.yaml index 730720d460..8c0bbe9bf7 100644 --- a/charts/consul/templates/connect-inject-clusterrole.yaml +++ b/charts/consul/templates/connect-inject-clusterrole.yaml @@ -27,6 +27,7 @@ rules: - gatewayclassconfigs - meshservices - samenessgroups + - controlplanerequestlimits {{- if .Values.global.peering.enabled }} - peeringacceptors - peeringdialers @@ -54,6 +55,7 @@ rules: - ingressgateways/status - terminatinggateways/status - samenessgroups/status + - controlplanerequestlimits/status {{- if .Values.global.peering.enabled }} - peeringacceptors/status - peeringdialers/status diff --git a/charts/consul/templates/connect-inject-mutatingwebhookconfiguration.yaml b/charts/consul/templates/connect-inject-mutatingwebhookconfiguration.yaml index 5f26807e0a..e4fe79f621 100644 --- a/charts/consul/templates/connect-inject-mutatingwebhookconfiguration.yaml +++ b/charts/consul/templates/connect-inject-mutatingwebhookconfiguration.yaml @@ -222,6 +222,27 @@ webhooks: resources: - exportedservices sideEffects: None +- clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-controlplanerequestlimits + failurePolicy: Fail + admissionReviewVersions: + - "v1beta1" + - "v1" + name: mutate-controlplanerequestlimit.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - controlplanerequestlimits + sideEffects: None - name: {{ template "consul.fullname" . }}-connect-injector.consul.hashicorp.com # The webhook will fail scheduling all pods that are not part of consul if all replicas of the webhook are unhealthy. objectSelector: diff --git a/charts/consul/templates/crd-controlplanerequestlimits.yaml b/charts/consul/templates/crd-controlplanerequestlimits.yaml new file mode 100644 index 0000000000..67ff258eb8 --- /dev/null +++ b/charts/consul/templates/crd-controlplanerequestlimits.yaml @@ -0,0 +1,196 @@ +{{- if .Values.connectInject.enabled }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.12.0 + name: controlplanerequestlimits.consul.hashicorp.com +spec: + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + group: consul.hashicorp.com + names: + kind: ControlPlaneRequestLimit + listKind: ControlPlaneRequestLimitList + plural: controlplanerequestlimits + singular: controlplanerequestlimit + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ControlPlaneRequestLimit is the Schema for the controlplanerequestlimits + API. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ControlPlaneRequestLimitSpec defines the desired state of + ControlPlaneRequestLimit. + properties: + acl: + properties: + readRate: + type: number + writeRate: + type: number + type: object + catalog: + properties: + readRate: + type: number + writeRate: + type: number + type: object + configEntry: + properties: + readRate: + type: number + writeRate: + type: number + type: object + connectCA: + properties: + readRate: + type: number + writeRate: + type: number + type: object + coordinate: + properties: + readRate: + type: number + writeRate: + type: number + type: object + discoveryChain: + properties: + readRate: + type: number + writeRate: + type: number + type: object + health: + properties: + readRate: + type: number + writeRate: + type: number + type: object + intention: + properties: + readRate: + type: number + writeRate: + type: number + type: object + kv: + properties: + readRate: + type: number + writeRate: + type: number + type: object + mode: + type: string + perparedQuery: + properties: + readRate: + type: number + writeRate: + type: number + type: object + readRate: + type: number + session: + properties: + readRate: + type: number + writeRate: + type: number + type: object + tenancy: + properties: + readRate: + type: number + writeRate: + type: number + type: object + txn: + properties: + readRate: + type: number + writeRate: + type: number + type: object + writeRate: + type: number + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/consul/templates/crd-exportedservices.yaml b/charts/consul/templates/crd-exportedservices.yaml index 7ffddf7537..8581ac4e88 100644 --- a/charts/consul/templates/crd-exportedservices.yaml +++ b/charts/consul/templates/crd-exportedservices.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: exportedservices.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: ExportedServices diff --git a/charts/consul/templates/crd-ingressgateways.yaml b/charts/consul/templates/crd-ingressgateways.yaml index ef33890461..eff7ef61a9 100644 --- a/charts/consul/templates/crd-ingressgateways.yaml +++ b/charts/consul/templates/crd-ingressgateways.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: ingressgateways.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: IngressGateway diff --git a/charts/consul/templates/crd-jwtproviders.yaml b/charts/consul/templates/crd-jwtproviders.yaml index c7d20883e8..fa87f37489 100644 --- a/charts/consul/templates/crd-jwtproviders.yaml +++ b/charts/consul/templates/crd-jwtproviders.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: jwtproviders.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: JWTProvider diff --git a/charts/consul/templates/crd-meshes.yaml b/charts/consul/templates/crd-meshes.yaml index cdc11b6ed9..f2549b5111 100644 --- a/charts/consul/templates/crd-meshes.yaml +++ b/charts/consul/templates/crd-meshes.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: meshes.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: Mesh diff --git a/charts/consul/templates/crd-meshservices.yaml b/charts/consul/templates/crd-meshservices.yaml index 859c8683ee..aa808113a2 100644 --- a/charts/consul/templates/crd-meshservices.yaml +++ b/charts/consul/templates/crd-meshservices.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: meshservices.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: MeshService diff --git a/charts/consul/templates/crd-peeringacceptors.yaml b/charts/consul/templates/crd-peeringacceptors.yaml index 3822f3bdfe..40f7f1d4d6 100644 --- a/charts/consul/templates/crd-peeringacceptors.yaml +++ b/charts/consul/templates/crd-peeringacceptors.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: peeringacceptors.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: PeeringAcceptor diff --git a/charts/consul/templates/crd-peeringdialers.yaml b/charts/consul/templates/crd-peeringdialers.yaml index 405361c486..bfe4778d0c 100644 --- a/charts/consul/templates/crd-peeringdialers.yaml +++ b/charts/consul/templates/crd-peeringdialers.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: peeringdialers.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: PeeringDialer diff --git a/charts/consul/templates/crd-proxydefaults.yaml b/charts/consul/templates/crd-proxydefaults.yaml index 30dd25f674..a224effc12 100644 --- a/charts/consul/templates/crd-proxydefaults.yaml +++ b/charts/consul/templates/crd-proxydefaults.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: proxydefaults.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: ProxyDefaults diff --git a/charts/consul/templates/crd-samenessgroups.yaml b/charts/consul/templates/crd-samenessgroups.yaml index c1d1c85a8e..7cc3b71ae1 100644 --- a/charts/consul/templates/crd-samenessgroups.yaml +++ b/charts/consul/templates/crd-samenessgroups.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: samenessgroups.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: SamenessGroup diff --git a/charts/consul/templates/crd-servicedefaults.yaml b/charts/consul/templates/crd-servicedefaults.yaml index 2562c53320..8f284782e9 100644 --- a/charts/consul/templates/crd-servicedefaults.yaml +++ b/charts/consul/templates/crd-servicedefaults.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: servicedefaults.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: ServiceDefaults diff --git a/charts/consul/templates/crd-serviceintentions.yaml b/charts/consul/templates/crd-serviceintentions.yaml index 335d2eff7a..5f849f65ba 100644 --- a/charts/consul/templates/crd-serviceintentions.yaml +++ b/charts/consul/templates/crd-serviceintentions.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: serviceintentions.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: ServiceIntentions diff --git a/charts/consul/templates/crd-serviceresolvers.yaml b/charts/consul/templates/crd-serviceresolvers.yaml index ed95c15846..ef380f77b5 100644 --- a/charts/consul/templates/crd-serviceresolvers.yaml +++ b/charts/consul/templates/crd-serviceresolvers.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: serviceresolvers.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: ServiceResolver diff --git a/charts/consul/templates/crd-servicerouters.yaml b/charts/consul/templates/crd-servicerouters.yaml index 0157f646b4..c5ba99466c 100644 --- a/charts/consul/templates/crd-servicerouters.yaml +++ b/charts/consul/templates/crd-servicerouters.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: servicerouters.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: ServiceRouter diff --git a/charts/consul/templates/crd-servicesplitters.yaml b/charts/consul/templates/crd-servicesplitters.yaml index 18fb10341e..abe3ac85cc 100644 --- a/charts/consul/templates/crd-servicesplitters.yaml +++ b/charts/consul/templates/crd-servicesplitters.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: servicesplitters.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: ServiceSplitter diff --git a/charts/consul/templates/crd-terminatinggateways.yaml b/charts/consul/templates/crd-terminatinggateways.yaml index 955496aeee..cd58d1679c 100644 --- a/charts/consul/templates/crd-terminatinggateways.yaml +++ b/charts/consul/templates/crd-terminatinggateways.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: terminatinggateways.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: TerminatingGateway diff --git a/charts/consul/test/unit/connect-inject-mutatingwebhookconfiguration.bats b/charts/consul/test/unit/connect-inject-mutatingwebhookconfiguration.bats index 81eda87875..bc0876586c 100755 --- a/charts/consul/test/unit/connect-inject-mutatingwebhookconfiguration.bats +++ b/charts/consul/test/unit/connect-inject-mutatingwebhookconfiguration.bats @@ -60,7 +60,7 @@ load _helpers --set 'meshGateway.enabled=true' \ --set 'global.peering.enabled=true' \ . | tee /dev/stderr | - yq '.webhooks[11].name | contains("peeringacceptors.consul.hashicorp.com")' | tee /dev/stderr) + yq '.webhooks[12].name | contains("peeringacceptors.consul.hashicorp.com")' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(helm template \ -s templates/connect-inject-mutatingwebhookconfiguration.yaml \ @@ -69,6 +69,6 @@ load _helpers --set 'meshGateway.enabled=true' \ --set 'global.peering.enabled=true' \ . | tee /dev/stderr | - yq '.webhooks[12].name | contains("peeringdialers.consul.hashicorp.com")' | tee /dev/stderr) + yq '.webhooks[13].name | contains("peeringdialers.consul.hashicorp.com")' | tee /dev/stderr) [ "${actual}" = "true" ] } diff --git a/charts/consul/test/unit/crd-controlplanerequestlimits.bats b/charts/consul/test/unit/crd-controlplanerequestlimits.bats new file mode 100644 index 0000000000..ed98fc539f --- /dev/null +++ b/charts/consul/test/unit/crd-controlplanerequestlimits.bats @@ -0,0 +1,26 @@ +#!/usr/bin/env bats + +load _helpers + +@test "controlPlaneRequestLimit/CustomResourceDefinition: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/crd-controlplanerequestlimits.yaml \ + . | tee /dev/stderr | + yq -s 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "controlPlaneRequestLimit/CustomResourceDefinition: enabled with connectInject.enabled=true" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/crd-controlplanerequestlimits.yaml \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + # The generated CRDs have "---" at the top which results in two objects + # being detected by yq, the first of which is null. We must therefore use + # yq -s so that length operates on both objects at once rather than + # individually, which would output false\ntrue and fail the test. + yq -s 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} diff --git a/control-plane/PROJECT b/control-plane/PROJECT index beec0e6504..5338cff047 100644 --- a/control-plane/PROJECT +++ b/control-plane/PROJECT @@ -99,4 +99,13 @@ resources: kind: JWTProvider path: github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1beta1 + namespaced: true + controller: true + domain: hashicorp.com + group: consul + kind: ControlPlaneRequestLimit + path: github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1 + version: v1alpha1 version: "3" diff --git a/control-plane/api/common/common.go b/control-plane/api/common/common.go index 779ece9218..a4063d6147 100644 --- a/control-plane/api/common/common.go +++ b/control-plane/api/common/common.go @@ -5,17 +5,18 @@ package common const ( - ServiceDefaults string = "servicedefaults" - ProxyDefaults string = "proxydefaults" - ServiceResolver string = "serviceresolver" - ServiceRouter string = "servicerouter" - ServiceSplitter string = "servicesplitter" - ServiceIntentions string = "serviceintentions" - ExportedServices string = "exportedservices" - IngressGateway string = "ingressgateway" - TerminatingGateway string = "terminatinggateway" - SamenessGroup string = "samenessgroup" - JWTProvider string = "jwtprovider" + ServiceDefaults string = "servicedefaults" + ProxyDefaults string = "proxydefaults" + ServiceResolver string = "serviceresolver" + ServiceRouter string = "servicerouter" + ServiceSplitter string = "servicesplitter" + ServiceIntentions string = "serviceintentions" + ExportedServices string = "exportedservices" + IngressGateway string = "ingressgateway" + TerminatingGateway string = "terminatinggateway" + SamenessGroup string = "samenessgroup" + JWTProvider string = "jwtprovider" + ControlPlaneRequestLimit string = "controlplanerequestlimit" Global string = "global" Mesh string = "mesh" diff --git a/control-plane/api/v1alpha1/controlplanerequestlimit_types.go b/control-plane/api/v1alpha1/controlplanerequestlimit_types.go new file mode 100644 index 0000000000..6b469696d1 --- /dev/null +++ b/control-plane/api/v1alpha1/controlplanerequestlimit_types.go @@ -0,0 +1,268 @@ +package v1alpha1 + +import ( + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + consul "github.com/hashicorp/consul/api" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + + "github.com/hashicorp/consul-k8s/control-plane/api/common" +) + +const ( + ControlPlaneRequestLimitKubeKind = "controlplanerequestlimit" +) + +func init() { + SchemeBuilder.Register(&ControlPlaneRequestLimit{}, &ControlPlaneRequestLimitList{}) +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// ControlPlaneRequestLimit is the Schema for the controlplanerequestlimits API. +// +kubebuilder:printcolumn:name="Synced",type="string",JSONPath=".status.conditions[?(@.type==\"Synced\")].status",description="The sync status of the resource with Consul" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="The age of the resource" +type ControlPlaneRequestLimit struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ControlPlaneRequestLimitSpec `json:"spec,omitempty"` + Status `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// ControlPlaneRequestLimitList contains a list of ControlPlaneRequestLimit. +type ControlPlaneRequestLimitList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ControlPlaneRequestLimit `json:"items"` +} + +type ReadWriteRatesConfig struct { + ReadRate float64 `json:"readRate,omitempty"` + WriteRate float64 `json:"writeRate,omitempty"` +} + +func (c *ReadWriteRatesConfig) toConsul() *consul.ReadWriteRatesConfig { + if c == nil { + return nil + } + return &consul.ReadWriteRatesConfig{ + ReadRate: c.ReadRate, + WriteRate: c.WriteRate, + } +} + +func (c *ReadWriteRatesConfig) validate(path *field.Path) field.ErrorList { + if c == nil { + return nil + } + + var errs field.ErrorList + + if c.ReadRate < 0 { + errs = append(errs, field.Invalid(path.Child("readRate"), c.ReadRate, "readRate must be >= 0")) + } + + if c.WriteRate <= 0 { + errs = append(errs, field.Invalid(path.Child("writeRate"), c.WriteRate, "writeRate must be > 0")) + } + return errs +} + +// ControlPlaneRequestLimitSpec defines the desired state of ControlPlaneRequestLimit. +type ControlPlaneRequestLimitSpec struct { + Mode string `json:"mode,omitempty"` + ReadWriteRatesConfig `json:",inline"` + ACL *ReadWriteRatesConfig `json:"acl,omitempty"` + Catalog *ReadWriteRatesConfig `json:"catalog,omitempty"` + ConfigEntry *ReadWriteRatesConfig `json:"configEntry,omitempty"` + ConnectCA *ReadWriteRatesConfig `json:"connectCA,omitempty"` + Coordinate *ReadWriteRatesConfig `json:"coordinate,omitempty"` + DiscoveryChain *ReadWriteRatesConfig `json:"discoveryChain,omitempty"` + Health *ReadWriteRatesConfig `json:"health,omitempty"` + Intention *ReadWriteRatesConfig `json:"intention,omitempty"` + KV *ReadWriteRatesConfig `json:"kv,omitempty"` + Tenancy *ReadWriteRatesConfig `json:"tenancy,omitempty"` + PreparedQuery *ReadWriteRatesConfig `json:"perparedQuery,omitempty"` + Session *ReadWriteRatesConfig `json:"session,omitempty"` + Txn *ReadWriteRatesConfig `json:"txn,omitempty"` +} + +// GetObjectMeta returns object meta. +func (c *ControlPlaneRequestLimit) GetObjectMeta() metav1.ObjectMeta { + return c.ObjectMeta +} + +// AddFinalizer adds a finalizer to the list of finalizers. +func (c *ControlPlaneRequestLimit) AddFinalizer(name string) { + c.ObjectMeta.Finalizers = append(c.ObjectMeta.Finalizers, name) +} + +// RemoveFinalizer removes this finalizer from the list. +func (c *ControlPlaneRequestLimit) RemoveFinalizer(name string) { + for i, n := range c.ObjectMeta.Finalizers { + if n == name { + c.ObjectMeta.Finalizers = append(c.ObjectMeta.Finalizers[:i], c.ObjectMeta.Finalizers[i+1:]...) + return + } + } +} + +// Finalizers returns the list of finalizers for this object. +func (c *ControlPlaneRequestLimit) Finalizers() []string { + return c.ObjectMeta.Finalizers +} + +// ConsulKind returns the Consul config entry kind, i.e. service-defaults, not +// servicedefaults. +func (c *ControlPlaneRequestLimit) ConsulKind() string { + return consul.RateLimitIPConfig +} + +// ConsulGlobalResource returns if the resource exists in the default +// Consul namespace only. +func (c *ControlPlaneRequestLimit) ConsulGlobalResource() bool { + return true +} + +// ConsulMirroringNS returns the Consul namespace that the config entry should +// be created in if namespaces and mirroring are enabled. +func (c *ControlPlaneRequestLimit) ConsulMirroringNS() string { + return common.DefaultConsulNamespace +} + +// KubeKind returns the Kube config entry kind, i.e. servicedefaults, not +// service-defaults. +func (c *ControlPlaneRequestLimit) KubeKind() string { + return ControlPlaneRequestLimitKubeKind +} + +// ConsulName returns the name of the config entry as saved in Consul. +// This may be different than KubernetesName() in the case of a ServiceIntentions +// config entry. +func (c *ControlPlaneRequestLimit) ConsulName() string { + return c.ObjectMeta.Name +} + +// KubernetesName returns the name of the Kubernetes resource. +func (c *ControlPlaneRequestLimit) KubernetesName() string { + return c.ObjectMeta.Name +} + +// SetSyncedCondition updates the synced condition. +func (c *ControlPlaneRequestLimit) SetSyncedCondition(status corev1.ConditionStatus, reason, message string) { + c.Status.Conditions = Conditions{ + { + Type: ConditionSynced, + Status: status, + LastTransitionTime: metav1.Now(), + Reason: reason, + Message: message, + }, + } +} + +// SetLastSyncedTime updates the last synced time. +func (c *ControlPlaneRequestLimit) SetLastSyncedTime(time *metav1.Time) { + c.Status.LastSyncedTime = time +} + +// SyncedCondition gets the synced condition. +func (c *ControlPlaneRequestLimit) SyncedCondition() (status corev1.ConditionStatus, reason, message string) { + cond := c.Status.GetCondition(ConditionSynced) + if cond == nil { + return corev1.ConditionUnknown, "", "" + } + return cond.Status, cond.Reason, cond.Message +} + +// SyncedConditionStatus returns the status of the synced condition. +func (c *ControlPlaneRequestLimit) SyncedConditionStatus() corev1.ConditionStatus { + condition := c.Status.GetCondition(ConditionSynced) + if condition == nil { + return corev1.ConditionUnknown + } + return condition.Status +} + +// ToConsul converts the resource to the corresponding Consul API definition. +// Its return type is the generic ConfigEntry but a specific config entry +// type should be constructed e.g. ServiceConfigEntry. +func (c *ControlPlaneRequestLimit) ToConsul(datacenter string) consul.ConfigEntry { + return &consul.RateLimitIPConfigEntry{ + Kind: c.ConsulKind(), + Name: c.ConsulName(), + Mode: c.Spec.Mode, + ReadRate: c.Spec.ReadRate, + WriteRate: c.Spec.WriteRate, + Meta: meta(datacenter), + ACL: c.Spec.ACL.toConsul(), + Catalog: c.Spec.Catalog.toConsul(), + ConfigEntry: c.Spec.ConfigEntry.toConsul(), + ConnectCA: c.Spec.ConnectCA.toConsul(), + Coordinate: c.Spec.Coordinate.toConsul(), + DiscoveryChain: c.Spec.DiscoveryChain.toConsul(), + Health: c.Spec.Health.toConsul(), + Intention: c.Spec.Intention.toConsul(), + KV: c.Spec.KV.toConsul(), + Tenancy: c.Spec.Tenancy.toConsul(), + PreparedQuery: c.Spec.PreparedQuery.toConsul(), + Session: c.Spec.Session.toConsul(), + Txn: c.Spec.Txn.toConsul(), + } +} + +// MatchesConsul returns true if the resource has the same fields as the Consul +// config entry. +func (c *ControlPlaneRequestLimit) MatchesConsul(candidate consul.ConfigEntry) bool { + configEntry, ok := candidate.(*consul.RateLimitIPConfigEntry) + if !ok { + return false + } + // No datacenter is passed to ToConsul as we ignore the Meta field when checking for equality. + return cmp.Equal(c.ToConsul(""), configEntry, cmpopts.IgnoreFields(consul.RateLimitIPConfigEntry{}, "Partition", "Namespace", "Meta", "ModifyIndex", "CreateIndex"), cmpopts.IgnoreUnexported(), cmpopts.EquateEmpty()) +} + +// Validate returns an error if the resource is invalid. +func (c *ControlPlaneRequestLimit) Validate(consulMeta common.ConsulMeta) error { + var errs field.ErrorList + path := field.NewPath("spec") + + if c.Spec.Mode != "permissive" && c.Spec.Mode != "enforcing" && c.Spec.Mode != "disabled" { + errs = append(errs, field.Invalid(path.Child("mode"), c.Spec.Mode, "mode must be one of: permissive, enforcing, disabled")) + } + + errs = append(errs, c.Spec.ReadWriteRatesConfig.validate(path)...) + errs = append(errs, c.Spec.ACL.validate(path.Child("acl"))...) + errs = append(errs, c.Spec.Catalog.validate(path.Child("catalog"))...) + errs = append(errs, c.Spec.ConfigEntry.validate(path.Child("configEntry"))...) + errs = append(errs, c.Spec.ConnectCA.validate(path.Child("connectCA"))...) + errs = append(errs, c.Spec.Coordinate.validate(path.Child("coordinate"))...) + errs = append(errs, c.Spec.DiscoveryChain.validate(path.Child("discoveryChain"))...) + errs = append(errs, c.Spec.Health.validate(path.Child("health"))...) + errs = append(errs, c.Spec.Intention.validate(path.Child("intention"))...) + errs = append(errs, c.Spec.KV.validate(path.Child("kv"))...) + errs = append(errs, c.Spec.Tenancy.validate(path.Child("tenancy"))...) + errs = append(errs, c.Spec.PreparedQuery.validate(path.Child("preparedQuery"))...) + errs = append(errs, c.Spec.Session.validate(path.Child("session"))...) + errs = append(errs, c.Spec.Txn.validate(path.Child("txn"))...) + + if len(errs) > 0 { + return apierrors.NewInvalid( + schema.GroupKind{Group: ConsulHashicorpGroup, Kind: ControlPlaneRequestLimitKubeKind}, + c.KubernetesName(), errs) + } + + return nil +} + +// DefaultNamespaceFields has no behaviour here as control-plane-request-limit have no namespace specific fields. +func (s *ControlPlaneRequestLimit) DefaultNamespaceFields(_ common.ConsulMeta) { +} diff --git a/control-plane/api/v1alpha1/controlplanerequestlimit_types_test.go b/control-plane/api/v1alpha1/controlplanerequestlimit_types_test.go new file mode 100644 index 0000000000..5b8c4b4352 --- /dev/null +++ b/control-plane/api/v1alpha1/controlplanerequestlimit_types_test.go @@ -0,0 +1,566 @@ +package v1alpha1 + +import ( + "testing" + "time" + + consul "github.com/hashicorp/consul/api" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/hashicorp/consul-k8s/control-plane/api/common" +) + +func TestControlPlaneRequestLimit_ToConsul(t *testing.T) { + cases := map[string]struct { + input *ControlPlaneRequestLimit + expected *consul.RateLimitIPConfigEntry + }{ + "empty fields": { + &ControlPlaneRequestLimit{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: ControlPlaneRequestLimitSpec{ + Mode: "disabled", + ReadWriteRatesConfig: ReadWriteRatesConfig{ + ReadRate: 0, + WriteRate: 0, + }, + }, + }, + &consul.RateLimitIPConfigEntry{ + Name: "foo", + Kind: consul.RateLimitIPConfig, + Mode: "disabled", + Meta: map[string]string{ + common.DatacenterKey: "datacenter", + common.SourceKey: common.SourceValue, + }, + ReadRate: 0, + WriteRate: 0, + }, + }, + "every field set": { + &ControlPlaneRequestLimit{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: ControlPlaneRequestLimitSpec{ + Mode: "permissive", + ReadWriteRatesConfig: ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ACL: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Catalog: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ConfigEntry: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ConnectCA: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Coordinate: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + DiscoveryChain: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Health: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Intention: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + KV: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Tenancy: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + PreparedQuery: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Session: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Txn: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + }, + }, + &consul.RateLimitIPConfigEntry{ + Kind: consul.RateLimitIPConfig, + Name: "foo", + Mode: "permissive", + ReadRate: 100.0, + WriteRate: 100.0, + Meta: map[string]string{ + common.DatacenterKey: "datacenter", + common.SourceKey: common.SourceValue, + }, + ACL: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Catalog: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ConfigEntry: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ConnectCA: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Coordinate: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + DiscoveryChain: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Health: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Intention: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + KV: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Tenancy: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + PreparedQuery: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Session: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Txn: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + }, + }, + } + + for name, testCase := range cases { + t.Run(name, func(t *testing.T) { + output := testCase.input.ToConsul("datacenter") + require.Equal(t, testCase.expected, output) + }) + } +} + +func TestControlPlaneRequestLimit_MatchesConsul(t *testing.T) { + cases := map[string]struct { + internal *ControlPlaneRequestLimit + consul consul.ConfigEntry + matches bool + }{ + "empty fields matches": { + &ControlPlaneRequestLimit{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + }, + Spec: ControlPlaneRequestLimitSpec{}, + }, + &consul.RateLimitIPConfigEntry{ + Kind: consul.RateLimitIPConfig, + Name: "my-test-service", + Namespace: "namespace", + CreateIndex: 1, + ModifyIndex: 2, + Meta: map[string]string{ + common.SourceKey: common.SourceValue, + common.DatacenterKey: "datacenter", + }, + }, + true, + }, + "all fields populated matches": { + &ControlPlaneRequestLimit{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + }, + Spec: ControlPlaneRequestLimitSpec{ + Mode: "permissive", + ReadWriteRatesConfig: ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ACL: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Catalog: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ConfigEntry: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ConnectCA: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Coordinate: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + DiscoveryChain: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Health: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Intention: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + KV: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Tenancy: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + PreparedQuery: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Session: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Txn: &ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + }, + }, + &consul.RateLimitIPConfigEntry{ + Kind: consul.RateLimitIPConfig, + Name: "my-test-service", + Mode: "permissive", + ReadRate: 100.0, + WriteRate: 100.0, + Meta: map[string]string{ + common.DatacenterKey: "datacenter", + common.SourceKey: common.SourceValue, + }, + ACL: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Catalog: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ConfigEntry: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ConnectCA: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Coordinate: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + DiscoveryChain: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Health: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Intention: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + KV: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Tenancy: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + PreparedQuery: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Session: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Txn: &consul.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + }, + true, + }, + "mismatched types does not match": { + &ControlPlaneRequestLimit{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-test-service", + }, + Spec: ControlPlaneRequestLimitSpec{}, + }, + &consul.ProxyConfigEntry{ + Kind: consul.RateLimitIPConfig, + Name: "my-test-service", + Namespace: "namespace", + CreateIndex: 1, + ModifyIndex: 2, + }, + false, + }, + } + + for name, testCase := range cases { + t.Run(name, func(t *testing.T) { + require.Equal(t, testCase.matches, testCase.internal.MatchesConsul(testCase.consul)) + }) + } +} + +func TestControlPlaneRequestLimit_Validate(t *testing.T) { + invalidReadWriteRatesConfig := &ReadWriteRatesConfig{ + ReadRate: -1, + WriteRate: 0, + } + + validReadWriteRatesConfig := &ReadWriteRatesConfig{ + ReadRate: 100, + WriteRate: 100, + } + + cases := map[string]struct { + input *ControlPlaneRequestLimit + expectedErrMsgs []string + }{ + "invalid": { + input: &ControlPlaneRequestLimit{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.ControlPlaneRequestLimit, + }, + Spec: ControlPlaneRequestLimitSpec{ + Mode: "invalid", + ACL: invalidReadWriteRatesConfig, + Catalog: invalidReadWriteRatesConfig, + ConfigEntry: invalidReadWriteRatesConfig, + ConnectCA: invalidReadWriteRatesConfig, + Coordinate: invalidReadWriteRatesConfig, + DiscoveryChain: invalidReadWriteRatesConfig, + Health: invalidReadWriteRatesConfig, + Intention: invalidReadWriteRatesConfig, + KV: invalidReadWriteRatesConfig, + Tenancy: invalidReadWriteRatesConfig, + PreparedQuery: invalidReadWriteRatesConfig, + Session: invalidReadWriteRatesConfig, + Txn: invalidReadWriteRatesConfig, + }, + }, + expectedErrMsgs: []string{ + `spec.mode: Invalid value: "invalid": mode must be one of: permissive, enforcing, disabled`, + `spec.acl.readRate: Invalid value: -1: readRate must be >= 0, spec.acl.writeRate: Invalid value: 0: writeRate must be > 0`, + `spec.catalog.readRate: Invalid value: -1: readRate must be >= 0, spec.catalog.writeRate: Invalid value: 0: writeRate must be > 0`, + `spec.configEntry.readRate: Invalid value: -1: readRate must be >= 0, spec.configEntry.writeRate: Invalid value: 0: writeRate must be > 0`, + `spec.connectCA.readRate: Invalid value: -1: readRate must be >= 0, spec.connectCA.writeRate: Invalid value: 0: writeRate must be > 0`, + `spec.coordinate.readRate: Invalid value: -1: readRate must be >= 0, spec.coordinate.writeRate: Invalid value: 0: writeRate must be > 0`, + `spec.discoveryChain.readRate: Invalid value: -1: readRate must be >= 0, spec.discoveryChain.writeRate: Invalid value: 0: writeRate must be > 0`, + `spec.health.readRate: Invalid value: -1: readRate must be >= 0, spec.health.writeRate: Invalid value: 0: writeRate must be > 0`, + `spec.intention.readRate: Invalid value: -1: readRate must be >= 0, spec.intention.writeRate: Invalid value: 0: writeRate must be > 0`, + `spec.kv.readRate: Invalid value: -1: readRate must be >= 0, spec.kv.writeRate: Invalid value: 0: writeRate must be > 0`, + `spec.tenancy.readRate: Invalid value: -1: readRate must be >= 0, spec.tenancy.writeRate: Invalid value: 0: writeRate must be > 0`, + `spec.preparedQuery.readRate: Invalid value: -1: readRate must be >= 0, spec.preparedQuery.writeRate: Invalid value: 0: writeRate must be > 0`, + `spec.session.readRate: Invalid value: -1: readRate must be >= 0, spec.session.writeRate: Invalid value: 0: writeRate must be > 0`, + `spec.txn.readRate: Invalid value: -1: readRate must be >= 0, spec.txn.writeRate: Invalid value: 0: writeRate must be > 0`, + }, + }, + "valid": { + input: &ControlPlaneRequestLimit{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.ControlPlaneRequestLimit, + }, + Spec: ControlPlaneRequestLimitSpec{ + Mode: "permissive", + ReadWriteRatesConfig: *validReadWriteRatesConfig, + ACL: validReadWriteRatesConfig, + Catalog: validReadWriteRatesConfig, + ConfigEntry: validReadWriteRatesConfig, + ConnectCA: validReadWriteRatesConfig, + Coordinate: validReadWriteRatesConfig, + DiscoveryChain: validReadWriteRatesConfig, + Health: validReadWriteRatesConfig, + Intention: validReadWriteRatesConfig, + KV: validReadWriteRatesConfig, + Tenancy: validReadWriteRatesConfig, + PreparedQuery: validReadWriteRatesConfig, + Session: validReadWriteRatesConfig, + Txn: validReadWriteRatesConfig, + }, + }, + expectedErrMsgs: []string{}, + }, + } + + for name, testCase := range cases { + t.Run(name, func(t *testing.T) { + err := testCase.input.Validate(common.ConsulMeta{}) + if len(testCase.expectedErrMsgs) != 0 { + require.Error(t, err) + for _, s := range testCase.expectedErrMsgs { + require.Contains(t, err.Error(), s) + } + } else { + require.NoError(t, err) + } + }) + } +} + +func TestControlPlaneRequestLimit_AddFinalizer(t *testing.T) { + controlPlaneRequestLimit := &ControlPlaneRequestLimit{} + controlPlaneRequestLimit.AddFinalizer("finalizer") + require.Equal(t, []string{"finalizer"}, controlPlaneRequestLimit.ObjectMeta.Finalizers) +} + +func TestControlPlaneRequestLimit_RemoveFinalizer(t *testing.T) { + controlPlaneRequestLimit := &ControlPlaneRequestLimit{ + ObjectMeta: metav1.ObjectMeta{ + Finalizers: []string{"f1", "f2"}, + }, + } + controlPlaneRequestLimit.RemoveFinalizer("f1") + require.Equal(t, []string{"f2"}, controlPlaneRequestLimit.ObjectMeta.Finalizers) +} + +func TestControlPlaneRequestLimit_SetSyncedCondition(t *testing.T) { + controlPlaneRequestLimit := &ControlPlaneRequestLimit{} + controlPlaneRequestLimit.SetSyncedCondition(corev1.ConditionTrue, "reason", "message") + + require.Equal(t, corev1.ConditionTrue, controlPlaneRequestLimit.Status.Conditions[0].Status) + require.Equal(t, "reason", controlPlaneRequestLimit.Status.Conditions[0].Reason) + require.Equal(t, "message", controlPlaneRequestLimit.Status.Conditions[0].Message) + now := metav1.Now() + require.True(t, controlPlaneRequestLimit.Status.Conditions[0].LastTransitionTime.Before(&now)) +} + +func TestControlPlaneRequestLimit_SetLastSyncedTime(t *testing.T) { + controlPlaneRequestLimit := &ControlPlaneRequestLimit{} + syncedTime := metav1.NewTime(time.Now()) + controlPlaneRequestLimit.SetLastSyncedTime(&syncedTime) + + require.Equal(t, &syncedTime, controlPlaneRequestLimit.Status.LastSyncedTime) +} + +func TestControlPlaneRequestLimit_GetSyncedConditionStatus(t *testing.T) { + cases := []corev1.ConditionStatus{ + corev1.ConditionUnknown, + corev1.ConditionFalse, + corev1.ConditionTrue, + } + for _, status := range cases { + t.Run(string(status), func(t *testing.T) { + controlPlaneRequestLimit := &ControlPlaneRequestLimit{ + Status: Status{ + Conditions: []Condition{{ + Type: ConditionSynced, + Status: status, + }}, + }, + } + + require.Equal(t, status, controlPlaneRequestLimit.SyncedConditionStatus()) + }) + } +} + +func TestControlPlaneRequestLimit_GetConditionWhenStatusNil(t *testing.T) { + require.Nil(t, (&ControlPlaneRequestLimit{}).GetCondition(ConditionSynced)) +} + +func TestControlPlaneRequestLimit_SyncedConditionStatusWhenStatusNil(t *testing.T) { + require.Equal(t, corev1.ConditionUnknown, (&ControlPlaneRequestLimit{}).SyncedConditionStatus()) +} + +func TestControlPlaneRequestLimit_SyncedConditionWhenStatusNil(t *testing.T) { + status, reason, message := (&ControlPlaneRequestLimit{}).SyncedCondition() + require.Equal(t, corev1.ConditionUnknown, status) + require.Equal(t, "", reason) + require.Equal(t, "", message) +} + +func TestControlPlaneRequestLimit_ConsulKind(t *testing.T) { + require.Equal(t, consul.RateLimitIPConfig, (&ControlPlaneRequestLimit{}).ConsulKind()) +} + +func TestControlPlaneRequestLimit_KubeKind(t *testing.T) { + require.Equal(t, "controlplanerequestlimit", (&ControlPlaneRequestLimit{}).KubeKind()) +} + +func TestControlPlaneRequestLimit_ConsulName(t *testing.T) { + require.Equal(t, "foo", (&ControlPlaneRequestLimit{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}).ConsulName()) +} + +func TestControlPlaneRequestLimit_KubernetesName(t *testing.T) { + require.Equal(t, "foo", (&ControlPlaneRequestLimit{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}).KubernetesName()) +} + +func TestControlPlaneRequestLimit_ConsulNamespace(t *testing.T) { + require.Equal(t, "default", (&ControlPlaneRequestLimit{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}}).ConsulMirroringNS()) +} + +func TestControlPlaneRequestLimit_ConsulGlobalResource(t *testing.T) { + require.True(t, (&ControlPlaneRequestLimit{}).ConsulGlobalResource()) +} + +func TestControlPlaneRequestLimit_ObjectMeta(t *testing.T) { + meta := metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + } + controlPlaneRequestLimit := &ControlPlaneRequestLimit{ + ObjectMeta: meta, + } + require.Equal(t, meta, controlPlaneRequestLimit.GetObjectMeta()) +} diff --git a/control-plane/api/v1alpha1/controlplanerequestlimit_webhook.go b/control-plane/api/v1alpha1/controlplanerequestlimit_webhook.go new file mode 100644 index 0000000000..d99d9143f7 --- /dev/null +++ b/control-plane/api/v1alpha1/controlplanerequestlimit_webhook.go @@ -0,0 +1,83 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package v1alpha1 + +import ( + "context" + "fmt" + "net/http" + + "github.com/go-logr/logr" + admissionv1 "k8s.io/api/admission/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + "github.com/hashicorp/consul-k8s/control-plane/api/common" +) + +// +kubebuilder:object:generate=false + +type ControlPlaneRequestLimitWebhook struct { + client.Client + Logger logr.Logger + decoder *admission.Decoder + ConsulMeta common.ConsulMeta +} + +// NOTE: The path value in the below line is the path to the webhook. +// If it is updated, run code-gen, update subcommand/controller/command.go +// and the consul-helm value for the path to the webhook. +// +// NOTE: The below line cannot be combined with any other comment. If it is +// it will break the code generation. +// +// +kubebuilder:webhook:verbs=create;update,path=/mutate-v1alpha1-controlplanerequestlimits,mutating=true,failurePolicy=fail,groups=consul.hashicorp.com,resources=controlplanerequestlimits,versions=v1alpha1,name=mutate-controlplanerequestlimits.consul.hashicorp.com,sideEffects=None,admissionReviewVersions=v1beta1;v1 + +func (v *ControlPlaneRequestLimitWebhook) Handle(ctx context.Context, req admission.Request) admission.Response { + var limit ControlPlaneRequestLimit + var limitList ControlPlaneRequestLimitList + err := v.decoder.Decode(req, &limit) + if err != nil { + return admission.Errored(http.StatusBadRequest, err) + } + + if req.Operation == admissionv1.Create { + v.Logger.Info("validate create", "name", limit.KubernetesName()) + + if limit.KubernetesName() != common.ControlPlaneRequestLimit { + return admission.Errored(http.StatusBadRequest, + fmt.Errorf(`%s resource name must be "%s"`, + limit.KubeKind(), common.ControlPlaneRequestLimit)) + } + + if err := v.Client.List(ctx, &limitList); err != nil { + return admission.Errored(http.StatusInternalServerError, err) + } + + if len(limitList.Items) > 0 { + return admission.Errored(http.StatusBadRequest, + fmt.Errorf("%s resource already defined - only one control plane request limit entry is supported", + limit.KubeKind())) + } + } + + return common.ValidateConfigEntry(ctx, req, v.Logger, v, &limit, v.ConsulMeta) +} + +func (v *ControlPlaneRequestLimitWebhook) List(ctx context.Context) ([]common.ConfigEntryResource, error) { + var limitList ControlPlaneRequestLimitList + if err := v.Client.List(ctx, &limitList); err != nil { + return nil, err + } + var entries []common.ConfigEntryResource + for _, item := range limitList.Items { + entries = append(entries, common.ConfigEntryResource(&item)) + } + return entries, nil +} + +func (v *ControlPlaneRequestLimitWebhook) InjectDecoder(d *admission.Decoder) error { + v.decoder = d + return nil +} diff --git a/control-plane/api/v1alpha1/controlplanerequestlimit_webhook_test.go b/control-plane/api/v1alpha1/controlplanerequestlimit_webhook_test.go new file mode 100644 index 0000000000..c1ab7cc6af --- /dev/null +++ b/control-plane/api/v1alpha1/controlplanerequestlimit_webhook_test.go @@ -0,0 +1,145 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package v1alpha1 + +import ( + "context" + "encoding/json" + "testing" + + logrtest "github.com/go-logr/logr/testr" + "github.com/stretchr/testify/require" + admissionv1 "k8s.io/api/admission/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + "github.com/hashicorp/consul-k8s/control-plane/api/common" +) + +func TestValidateControlPlaneRequestLimit(t *testing.T) { + otherNS := "other" + + cases := map[string]struct { + existingResources []runtime.Object + newResource *ControlPlaneRequestLimit + expAllow bool + expErrMessage string + }{ + "no duplicates, valid": { + existingResources: nil, + newResource: &ControlPlaneRequestLimit{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.ControlPlaneRequestLimit, + }, + Spec: ControlPlaneRequestLimitSpec{ + Mode: "permissive", + ReadWriteRatesConfig: ReadWriteRatesConfig{ + ReadRate: 100, + WriteRate: 100, + }, + }, + }, + expAllow: true, + }, + "invalid resource name": { + existingResources: nil, + newResource: &ControlPlaneRequestLimit{ + ObjectMeta: metav1.ObjectMeta{ + Name: "invalid", + }, + Spec: ControlPlaneRequestLimitSpec{ + Mode: "permissive", + ReadWriteRatesConfig: ReadWriteRatesConfig{ + ReadRate: 100, + WriteRate: 100, + }, + }, + }, + expAllow: false, + expErrMessage: `controlplanerequestlimit resource name must be "controlplanerequestlimit"`, + }, + "resource already exists": { + existingResources: []runtime.Object{ + &ControlPlaneRequestLimit{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.ControlPlaneRequestLimit, + }, + Spec: ControlPlaneRequestLimitSpec{ + Mode: "permissive", + ReadWriteRatesConfig: ReadWriteRatesConfig{ + ReadRate: 100, + WriteRate: 100, + }, + }, + }, + }, + newResource: &ControlPlaneRequestLimit{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.ControlPlaneRequestLimit, + }, + Spec: ControlPlaneRequestLimitSpec{ + Mode: "permissive", + ReadWriteRatesConfig: ReadWriteRatesConfig{ + ReadRate: 100, + WriteRate: 100, + }, + }, + }, + expAllow: false, + expErrMessage: `controlplanerequestlimit resource already defined - only one control plane request limit entry is supported`, + }, + "invalid spec": { + existingResources: nil, + newResource: &ControlPlaneRequestLimit{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.ControlPlaneRequestLimit, + }, + Spec: ControlPlaneRequestLimitSpec{ + Mode: "invalid", + ReadWriteRatesConfig: ReadWriteRatesConfig{ + ReadRate: 100, + WriteRate: 100, + }, + }, + }, + expAllow: false, + expErrMessage: `controlplanerequestlimit.consul.hashicorp.com "controlplanerequestlimit" is invalid: spec.mode: Invalid value: "invalid": mode must be one of: permissive, enforcing, disabled`, + }, + } + for name, c := range cases { + t.Run(name, func(t *testing.T) { + ctx := context.Background() + marshalledRequestObject, err := json.Marshal(c.newResource) + require.NoError(t, err) + s := runtime.NewScheme() + s.AddKnownTypes(GroupVersion, &ControlPlaneRequestLimit{}, &ControlPlaneRequestLimitList{}) + client := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(c.existingResources...).Build() + decoder, err := admission.NewDecoder(s) + require.NoError(t, err) + + validator := &ControlPlaneRequestLimitWebhook{ + Client: client, + Logger: logrtest.New(t), + decoder: decoder, + } + response := validator.Handle(ctx, admission.Request{ + AdmissionRequest: admissionv1.AdmissionRequest{ + Name: c.newResource.KubernetesName(), + Namespace: otherNS, + Operation: admissionv1.Create, + Object: runtime.RawExtension{ + Raw: marshalledRequestObject, + }, + }, + }) + + require.Equal(t, c.expAllow, response.Allowed) + if c.expErrMessage != "" { + require.Equal(t, c.expErrMessage, response.AdmissionResponse.Result.Message) + } + }) + } +} diff --git a/control-plane/api/v1alpha1/zz_generated.deepcopy.go b/control-plane/api/v1alpha1/zz_generated.deepcopy.go index e8bff89986..0787f24097 100644 --- a/control-plane/api/v1alpha1/zz_generated.deepcopy.go +++ b/control-plane/api/v1alpha1/zz_generated.deepcopy.go @@ -64,6 +64,145 @@ func (in Conditions) DeepCopy() Conditions { return *out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ControlPlaneRequestLimit) DeepCopyInto(out *ControlPlaneRequestLimit) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControlPlaneRequestLimit. +func (in *ControlPlaneRequestLimit) DeepCopy() *ControlPlaneRequestLimit { + if in == nil { + return nil + } + out := new(ControlPlaneRequestLimit) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ControlPlaneRequestLimit) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ControlPlaneRequestLimitList) DeepCopyInto(out *ControlPlaneRequestLimitList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ControlPlaneRequestLimit, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControlPlaneRequestLimitList. +func (in *ControlPlaneRequestLimitList) DeepCopy() *ControlPlaneRequestLimitList { + if in == nil { + return nil + } + out := new(ControlPlaneRequestLimitList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ControlPlaneRequestLimitList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ControlPlaneRequestLimitSpec) DeepCopyInto(out *ControlPlaneRequestLimitSpec) { + *out = *in + if in.ACL != nil { + in, out := &in.ACL, &out.ACL + *out = new(ReadWriteRatesConfig) + **out = **in + } + if in.Catalog != nil { + in, out := &in.Catalog, &out.Catalog + *out = new(ReadWriteRatesConfig) + **out = **in + } + if in.ConfigEntry != nil { + in, out := &in.ConfigEntry, &out.ConfigEntry + *out = new(ReadWriteRatesConfig) + **out = **in + } + if in.ConnectCA != nil { + in, out := &in.ConnectCA, &out.ConnectCA + *out = new(ReadWriteRatesConfig) + **out = **in + } + if in.Coordinate != nil { + in, out := &in.Coordinate, &out.Coordinate + *out = new(ReadWriteRatesConfig) + **out = **in + } + if in.DiscoveryChain != nil { + in, out := &in.DiscoveryChain, &out.DiscoveryChain + *out = new(ReadWriteRatesConfig) + **out = **in + } + if in.Health != nil { + in, out := &in.Health, &out.Health + *out = new(ReadWriteRatesConfig) + **out = **in + } + if in.Intention != nil { + in, out := &in.Intention, &out.Intention + *out = new(ReadWriteRatesConfig) + **out = **in + } + if in.KV != nil { + in, out := &in.KV, &out.KV + *out = new(ReadWriteRatesConfig) + **out = **in + } + if in.Tenancy != nil { + in, out := &in.Tenancy, &out.Tenancy + *out = new(ReadWriteRatesConfig) + **out = **in + } + if in.PreparedQuery != nil { + in, out := &in.PreparedQuery, &out.PreparedQuery + *out = new(ReadWriteRatesConfig) + **out = **in + } + if in.Session != nil { + in, out := &in.Session, &out.Session + *out = new(ReadWriteRatesConfig) + **out = **in + } + if in.Txn != nil { + in, out := &in.Txn, &out.Txn + *out = new(ReadWriteRatesConfig) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControlPlaneRequestLimitSpec. +func (in *ControlPlaneRequestLimitSpec) DeepCopy() *ControlPlaneRequestLimitSpec { + if in == nil { + return nil + } + out := new(ControlPlaneRequestLimitSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CookieConfig) DeepCopyInto(out *CookieConfig) { *out = *in @@ -1848,6 +1987,20 @@ func (in *RetryPolicyBackOff) DeepCopy() *RetryPolicyBackOff { return out } +func (in *ReadWriteRatesConfig) DeepCopyInto(out *ReadWriteRatesConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReadWriteRatesConfig. +func (in *ReadWriteRatesConfig) DeepCopy() *ReadWriteRatesConfig { + if in == nil { + return nil + } + out := new(ReadWriteRatesConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RingHashConfig) DeepCopyInto(out *RingHashConfig) { *out = *in diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_controlplanerequestlimits.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_controlplanerequestlimits.yaml new file mode 100644 index 0000000000..4d1d808428 --- /dev/null +++ b/control-plane/config/crd/bases/consul.hashicorp.com_controlplanerequestlimits.yaml @@ -0,0 +1,191 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.12.0 + name: controlplanerequestlimits.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: ControlPlaneRequestLimit + listKind: ControlPlaneRequestLimitList + plural: controlplanerequestlimits + singular: controlplanerequestlimit + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ControlPlaneRequestLimit is the Schema for the controlplanerequestlimits + API. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ControlPlaneRequestLimitSpec defines the desired state of + ControlPlaneRequestLimit. + properties: + acl: + properties: + readRate: + type: number + writeRate: + type: number + type: object + catalog: + properties: + readRate: + type: number + writeRate: + type: number + type: object + configEntry: + properties: + readRate: + type: number + writeRate: + type: number + type: object + connectCA: + properties: + readRate: + type: number + writeRate: + type: number + type: object + coordinate: + properties: + readRate: + type: number + writeRate: + type: number + type: object + discoveryChain: + properties: + readRate: + type: number + writeRate: + type: number + type: object + health: + properties: + readRate: + type: number + writeRate: + type: number + type: object + intention: + properties: + readRate: + type: number + writeRate: + type: number + type: object + kv: + properties: + readRate: + type: number + writeRate: + type: number + type: object + mode: + type: string + perparedQuery: + properties: + readRate: + type: number + writeRate: + type: number + type: object + readRate: + type: number + session: + properties: + readRate: + type: number + writeRate: + type: number + type: object + tenancy: + properties: + readRate: + type: number + writeRate: + type: number + type: object + txn: + properties: + readRate: + type: number + writeRate: + type: number + type: object + writeRate: + type: number + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml index f066c90612..dac72f3646 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: exportedservices.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml index a8393cd8fd..44eff52492 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: gatewayclassconfigs.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml index f7ccf205d9..e9cf081721 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: ingressgateways.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml index 8ca1ec0748..7506cc57dc 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: jwtproviders.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml index bc46b6ab37..16dd398f99 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: meshes.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml index 0871fc32e5..125883bdc5 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: meshservices.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml index f6f9eda72b..894228a218 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: peeringacceptors.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml index 7e0927c169..51c3e38319 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: peeringdialers.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml index 7396816f7e..1be3b37703 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: proxydefaults.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml index 23de092485..259ca7b910 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: samenessgroups.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml index 0890c6323b..c86138cf3d 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: servicedefaults.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml index cd28173ba8..9553c73450 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: serviceintentions.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml index 3cd3b37324..a8a8107439 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: serviceresolvers.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml index 5919e23005..04590cc007 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: servicerouters.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml index d5848ed6ec..3a47472ba7 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: servicesplitters.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml index 4910e42829..acf61cde4c 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: terminatinggateways.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/kustomization.yaml b/control-plane/config/crd/kustomization.yaml new file mode 100644 index 0000000000..2b1d90d6d0 --- /dev/null +++ b/control-plane/config/crd/kustomization.yaml @@ -0,0 +1,21 @@ +# This kustomization.yaml is not intended to be run by itself, +# since it depends on service name and namespace that are out of this kustomize package. +# It should be run by config/default +resources: +- bases/consul.hashicorp.com_controlplanerequestlimits.yaml +#+kubebuilder:scaffold:crdkustomizeresource + +patchesStrategicMerge: +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. +# patches here are for enabling the conversion webhook for each CRD +#- patches/webhook_in_controlplanerequestlimits.yaml +#+kubebuilder:scaffold:crdkustomizewebhookpatch + +# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. +# patches here are for enabling the CA injection for each CRD +#- patches/cainjection_in_controlplanerequestlimits.yaml +#+kubebuilder:scaffold:crdkustomizecainjectionpatch + +# the following config is for teaching kustomize how to do kustomization for CRDs. +configurations: +- kustomizeconfig.yaml diff --git a/control-plane/config/crd/kustomizeconfig.yaml b/control-plane/config/crd/kustomizeconfig.yaml new file mode 100644 index 0000000000..6f83d9a94b --- /dev/null +++ b/control-plane/config/crd/kustomizeconfig.yaml @@ -0,0 +1,17 @@ +# This file is for teaching kustomize how to substitute name and namespace reference in CRD +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: CustomResourceDefinition + group: apiextensions.k8s.io + path: spec/conversion/webhookClientConfig/service/name + +namespace: +- kind: CustomResourceDefinition + group: apiextensions.k8s.io + path: spec/conversion/webhookClientConfig/service/namespace + create: false + +varReference: +- path: metadata/annotations diff --git a/control-plane/config/rbac/role.yaml b/control-plane/config/rbac/role.yaml index 28ddd913b5..4fac4ac9b8 100644 --- a/control-plane/config/rbac/role.yaml +++ b/control-plane/config/rbac/role.yaml @@ -5,7 +5,6 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - creationTimestamp: null name: manager-role rules: - apiGroups: @@ -26,6 +25,26 @@ rules: - secrets/status verbs: - get +- apiGroups: + - consul.hashicorp.com + resources: + - controlplanerequestlimits + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - consul.hashicorp.com + resources: + - controlplanerequestlimits/status + verbs: + - get + - patch + - update - apiGroups: - consul.hashicorp.com resources: diff --git a/control-plane/config/webhook/manifests.yaml b/control-plane/config/webhook/manifests.yaml index 3a65e10d1b..6d748ed06c 100644 --- a/control-plane/config/webhook/manifests.yaml +++ b/control-plane/config/webhook/manifests.yaml @@ -5,9 +5,29 @@ apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: - creationTimestamp: null name: mutating-webhook-configuration webhooks: +- admissionReviewVersions: + - v1beta1 + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-v1alpha1-controlplanerequestlimits + failurePolicy: Fail + name: mutate-controlplanerequestlimits.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - controlplanerequestlimits + sideEffects: None - admissionReviewVersions: - v1beta1 - v1 diff --git a/control-plane/controllers/configentry_controller_test.go b/control-plane/controllers/configentry_controller_test.go index 38a02732d0..0d5f8af5bf 100644 --- a/control-plane/controllers/configentry_controller_test.go +++ b/control-plane/controllers/configentry_controller_test.go @@ -476,6 +476,119 @@ func TestConfigEntryControllers_createsConfigEntry(t *testing.T) { require.Equal(t, "test-issuer", jwt.Issuer) }, }, + { + kubeKind: "ControlPlaneRequestLimit", + consulKind: capi.RateLimitIPConfig, + configEntryResource: &v1alpha1.ControlPlaneRequestLimit{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: kubeNS, + }, + Spec: v1alpha1.ControlPlaneRequestLimitSpec{ + Mode: "permissive", + ReadWriteRatesConfig: v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ACL: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Catalog: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ConfigEntry: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ConnectCA: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Coordinate: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + DiscoveryChain: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Health: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Intention: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + KV: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Tenancy: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + PreparedQuery: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Session: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Txn: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + }, + }, + reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) testReconciler { + return &ControlPlaneRequestLimitController{ + Client: client, + Log: logger, + ConfigEntryController: &ConfigEntryController{ + ConsulClientConfig: cfg, + ConsulServerConnMgr: watcher, + DatacenterName: datacenterName, + }, + } + }, + compare: func(t *testing.T, consulEntry capi.ConfigEntry) { + resource, ok := consulEntry.(*capi.RateLimitIPConfigEntry) + require.True(t, ok, "cast error") + require.Equal(t, "permissive", resource.Mode) + require.Equal(t, 100.0, resource.ReadRate) + require.Equal(t, 100.0, resource.WriteRate) + require.Equal(t, 100.0, resource.ACL.ReadRate) + require.Equal(t, 100.0, resource.ACL.WriteRate) + require.Equal(t, 100.0, resource.Catalog.ReadRate) + require.Equal(t, 100.0, resource.Catalog.WriteRate) + require.Equal(t, 100.0, resource.ConfigEntry.ReadRate) + require.Equal(t, 100.0, resource.ConfigEntry.WriteRate) + require.Equal(t, 100.0, resource.ConnectCA.ReadRate) + require.Equal(t, 100.0, resource.ConnectCA.WriteRate) + require.Equal(t, 100.0, resource.Coordinate.ReadRate) + require.Equal(t, 100.0, resource.Coordinate.WriteRate) + require.Equal(t, 100.0, resource.DiscoveryChain.ReadRate) + require.Equal(t, 100.0, resource.DiscoveryChain.WriteRate) + require.Equal(t, 100.0, resource.Health.ReadRate) + require.Equal(t, 100.0, resource.Health.WriteRate) + require.Equal(t, 100.0, resource.Intention.ReadRate) + require.Equal(t, 100.0, resource.Intention.WriteRate) + require.Equal(t, 100.0, resource.KV.ReadRate) + require.Equal(t, 100.0, resource.KV.WriteRate) + require.Equal(t, 100.0, resource.Tenancy.ReadRate) + require.Equal(t, 100.0, resource.Tenancy.WriteRate) + require.Equal(t, 100.0, resource.PreparedQuery.ReadRate) + require.Equal(t, 100.0, resource.PreparedQuery.WriteRate) + require.Equal(t, 100.0, resource.Session.ReadRate) + require.Equal(t, 100.0, resource.Session.WriteRate) + require.Equal(t, 100.0, resource.Txn.ReadRate) + require.Equal(t, 100.0, resource.Txn.WriteRate, 100.0) + }, + }, } for _, c := range cases { @@ -953,7 +1066,6 @@ func TestConfigEntryControllers_updatesConfigEntry(t *testing.T) { require.Equal(t, "new-sni", resource.Services[0].SNI) }, }, - { kubeKind: "JWTProvider", consulKind: capi.JWTProvider, @@ -1004,6 +1116,123 @@ func TestConfigEntryControllers_updatesConfigEntry(t *testing.T) { require.Equal(t, []string{"aud1"}, jwt.Audiences) }, }, + { + kubeKind: "ControlPlaneRequestLimit", + consulKind: capi.RateLimitIPConfig, + configEntryResource: &v1alpha1.ControlPlaneRequestLimit{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: kubeNS, + }, + Spec: v1alpha1.ControlPlaneRequestLimitSpec{ + Mode: "permissive", + ReadWriteRatesConfig: v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ACL: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Catalog: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ConfigEntry: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ConnectCA: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Coordinate: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + DiscoveryChain: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Health: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Intention: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + KV: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Tenancy: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + PreparedQuery: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Session: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Txn: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + }, + }, + reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) testReconciler { + return &ControlPlaneRequestLimitController{ + Client: client, + Log: logger, + ConfigEntryController: &ConfigEntryController{ + ConsulClientConfig: cfg, + ConsulServerConnMgr: watcher, + DatacenterName: datacenterName, + }, + } + }, + updateF: func(resource common.ConfigEntryResource) { + ipRateLimit := resource.(*v1alpha1.ControlPlaneRequestLimit) + ipRateLimit.Spec.Mode = "enforcing" + }, + compare: func(t *testing.T, consulEntry capi.ConfigEntry) { + resource, ok := consulEntry.(*capi.RateLimitIPConfigEntry) + require.True(t, ok, "cast error") + require.Equal(t, "enforcing", resource.Mode) + require.Equal(t, 100.0, resource.ReadRate) + require.Equal(t, 100.0, resource.WriteRate) + require.Equal(t, 100.0, resource.ACL.ReadRate) + require.Equal(t, 100.0, resource.ACL.WriteRate) + require.Equal(t, 100.0, resource.Catalog.ReadRate) + require.Equal(t, 100.0, resource.Catalog.WriteRate) + require.Equal(t, 100.0, resource.ConfigEntry.ReadRate) + require.Equal(t, 100.0, resource.ConfigEntry.WriteRate) + require.Equal(t, 100.0, resource.ConnectCA.ReadRate) + require.Equal(t, 100.0, resource.ConnectCA.WriteRate) + require.Equal(t, 100.0, resource.Coordinate.ReadRate) + require.Equal(t, 100.0, resource.Coordinate.WriteRate) + require.Equal(t, 100.0, resource.DiscoveryChain.ReadRate) + require.Equal(t, 100.0, resource.DiscoveryChain.WriteRate) + require.Equal(t, 100.0, resource.Health.ReadRate) + require.Equal(t, 100.0, resource.Health.WriteRate) + require.Equal(t, 100.0, resource.Intention.ReadRate) + require.Equal(t, 100.0, resource.Intention.WriteRate) + require.Equal(t, 100.0, resource.KV.ReadRate) + require.Equal(t, 100.0, resource.KV.WriteRate) + require.Equal(t, 100.0, resource.Tenancy.ReadRate) + require.Equal(t, 100.0, resource.Tenancy.WriteRate) + require.Equal(t, 100.0, resource.PreparedQuery.ReadRate) + require.Equal(t, 100.0, resource.PreparedQuery.WriteRate) + require.Equal(t, 100.0, resource.Session.ReadRate) + require.Equal(t, 100.0, resource.Session.WriteRate) + require.Equal(t, 100.0, resource.Txn.ReadRate) + require.Equal(t, 100.0, resource.Txn.WriteRate) + }, + }, } for _, c := range cases { @@ -1436,6 +1665,89 @@ func TestConfigEntryControllers_deletesConfigEntry(t *testing.T) { } }, }, + { + + kubeKind: "ControlPlaneRequestLimit", + consulKind: capi.RateLimitIPConfig, + configEntryResourceWithDeletion: &v1alpha1.ControlPlaneRequestLimit{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: kubeNS, + DeletionTimestamp: &metav1.Time{Time: time.Now()}, + Finalizers: []string{FinalizerName}, + }, + Spec: v1alpha1.ControlPlaneRequestLimitSpec{ + Mode: "permissive", + ReadWriteRatesConfig: v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ACL: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Catalog: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ConfigEntry: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ConnectCA: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Coordinate: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + DiscoveryChain: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Health: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Intention: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + KV: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Tenancy: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + PreparedQuery: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Session: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Txn: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + }, + }, + reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) testReconciler { + return &ControlPlaneRequestLimitController{ + Client: client, + Log: logger, + ConfigEntryController: &ConfigEntryController{ + ConsulClientConfig: cfg, + ConsulServerConnMgr: watcher, + DatacenterName: datacenterName, + }, + } + }, + }, } for _, c := range cases { diff --git a/control-plane/controllers/controlplanerequestlimit_controller.go b/control-plane/controllers/controlplanerequestlimit_controller.go new file mode 100644 index 0000000000..0441f1ed14 --- /dev/null +++ b/control-plane/controllers/controlplanerequestlimit_controller.go @@ -0,0 +1,40 @@ +package controllers + +import ( + "context" + + "github.com/go-logr/logr" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + consulv1alpha1 "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" +) + +// ControlPlaneRequestLimitController reconciles a ControlPlaneRequestLimit object. +type ControlPlaneRequestLimitController struct { + client.Client + Log logr.Logger + Scheme *runtime.Scheme + ConfigEntryController *ConfigEntryController +} + +//+kubebuilder:rbac:groups=consul.hashicorp.com,resources=controlplanerequestlimits,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=consul.hashicorp.com,resources=controlplanerequestlimits/status,verbs=get;update;patch + +func (r *ControlPlaneRequestLimitController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + return r.ConfigEntryController.ReconcileEntry(ctx, r, req, &consulv1alpha1.ControlPlaneRequestLimit{}) +} + +func (r *ControlPlaneRequestLimitController) Logger(name types.NamespacedName) logr.Logger { + return r.Log.WithValues("request", name) +} + +func (r *ControlPlaneRequestLimitController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { + return r.Status().Update(ctx, obj, opts...) +} + +func (r *ControlPlaneRequestLimitController) SetupWithManager(mgr ctrl.Manager) error { + return setupWithManager(mgr, &consulv1alpha1.ControlPlaneRequestLimit{}, r) +} diff --git a/control-plane/subcommand/inject-connect/command.go b/control-plane/subcommand/inject-connect/command.go index c969b3058b..9e99bd1a03 100644 --- a/control-plane/subcommand/inject-connect/command.go +++ b/control-plane/subcommand/inject-connect/command.go @@ -646,6 +646,15 @@ func (c *Command) Run(args []string) int { setupLog.Error(err, "unable to create controller", "controller", apicommon.JWTProvider) return 1 } + if err = (&controllers.ControlPlaneRequestLimitController{ + ConfigEntryController: configEntryReconciler, + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controller").WithName(apicommon.ControlPlaneRequestLimit), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", apicommon.ControlPlaneRequestLimit) + return 1 + } if err = mgr.AddReadyzCheck("ready", webhook.ReadinessCheck{CertDir: c.flagCertDir}.Ready); err != nil { setupLog.Error(err, "unable to create readiness check", "controller", endpoints.Controller{}) @@ -817,6 +826,12 @@ func (c *Command) Run(args []string) int { Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.JWTProvider), ConsulMeta: consulMeta, }}) + mgr.GetWebhookServer().Register("/mutate-v1alpha1-controlplanerequestlimits", + &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.ControlPlaneRequestLimitWebhook{ + Client: mgr.GetClient(), + Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.ControlPlaneRequestLimit), + ConsulMeta: consulMeta, + }}) if c.flagEnableWebhookCAUpdate { err = c.updateWebhookCABundle(ctx) From 48f97c8a55c9f7d4df1955615f85ad5c103906a7 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Mon, 5 Jun 2023 11:51:11 -0400 Subject: [PATCH 198/340] Update casing of json tag for ServiceDefault field (#2266) --- .changelog/2266.txt | 3 +++ acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml | 2 +- charts/consul/templates/crd-servicedefaults.yaml | 4 ++-- control-plane/api/v1alpha1/servicedefaults_types.go | 2 +- .../crd/bases/consul.hashicorp.com_servicedefaults.yaml | 4 ++-- 5 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 .changelog/2266.txt diff --git a/.changelog/2266.txt b/.changelog/2266.txt new file mode 100644 index 0000000000..e156f95f8c --- /dev/null +++ b/.changelog/2266.txt @@ -0,0 +1,3 @@ +```release-note:bug +control-plane: Fix casing of the Enforce Consecutive 5xx field on Service Defaults and acceptance test fixtures. +``` \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml b/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml index 1a6818f7fe..cd9c35fa39 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml @@ -21,7 +21,7 @@ spec: passiveHealthCheck: interval: 1s maxFailures: 10 - enforcing_consecutive_5xx: 60 + enforcingConsecutive5xx: 60 maxEjectionPercent: 100 baseEjectionTime: 20s - name: "bar" diff --git a/charts/consul/templates/crd-servicedefaults.yaml b/charts/consul/templates/crd-servicedefaults.yaml index 8f284782e9..e295732bfa 100644 --- a/charts/consul/templates/crd-servicedefaults.yaml +++ b/charts/consul/templates/crd-servicedefaults.yaml @@ -292,7 +292,7 @@ spec: capped by max_ejection_time (Default 300s). Defaults to 30000ms or 30s. type: string - enforcing_consecutive_5xx: + enforcingConsecutive5xx: description: EnforcingConsecutive5xx is the % chance that a host will be actually ejected when an outlier status is detected through consecutive 5xx. This setting can @@ -409,7 +409,7 @@ spec: is capped by max_ejection_time (Default 300s). Defaults to 30000ms or 30s. type: string - enforcing_consecutive_5xx: + enforcingConsecutive5xx: description: EnforcingConsecutive5xx is the % chance that a host will be actually ejected when an outlier status is detected through consecutive 5xx. This setting diff --git a/control-plane/api/v1alpha1/servicedefaults_types.go b/control-plane/api/v1alpha1/servicedefaults_types.go index 2f3b95a297..54044cb3a8 100644 --- a/control-plane/api/v1alpha1/servicedefaults_types.go +++ b/control-plane/api/v1alpha1/servicedefaults_types.go @@ -195,7 +195,7 @@ type PassiveHealthCheck struct { // EnforcingConsecutive5xx is the % chance that a host will be actually ejected // when an outlier status is detected through consecutive 5xx. // This setting can be used to disable ejection or to ramp it up slowly. - EnforcingConsecutive5xx *uint32 `json:"enforcing_consecutive_5xx,omitempty"` + EnforcingConsecutive5xx *uint32 `json:"enforcingConsecutive5xx,omitempty"` // The maximum % of an upstream cluster that can be ejected due to outlier detection. // Defaults to 10% but will eject at least one host regardless of the value. MaxEjectionPercent *uint32 `json:"maxEjectionPercent,omitempty"` diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml index c86138cf3d..83503f11f3 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml @@ -288,7 +288,7 @@ spec: capped by max_ejection_time (Default 300s). Defaults to 30000ms or 30s. type: string - enforcing_consecutive_5xx: + enforcingConsecutive5xx: description: EnforcingConsecutive5xx is the % chance that a host will be actually ejected when an outlier status is detected through consecutive 5xx. This setting can @@ -405,7 +405,7 @@ spec: is capped by max_ejection_time (Default 300s). Defaults to 30000ms or 30s. type: string - enforcing_consecutive_5xx: + enforcingConsecutive5xx: description: EnforcingConsecutive5xx is the % chance that a host will be actually ejected when an outlier status is detected through consecutive 5xx. This setting From 2ddd05a218d8d22ce807fd8b9d1af1f303f4ee39 Mon Sep 17 00:00:00 2001 From: Andrew Stucki Date: Mon, 5 Jun 2023 15:06:37 -0400 Subject: [PATCH 199/340] Add the endpoint ignoring logic for triggering gateway reconciliation (#2227) --- .../controllers/gateway_controller.go | 13 +- .../controllers/gateway_controller_test.go | 254 ++++++++++++++++++ 2 files changed, 261 insertions(+), 6 deletions(-) diff --git a/control-plane/api-gateway/controllers/gateway_controller.go b/control-plane/api-gateway/controllers/gateway_controller.go index cc90ad6f17..80c8e83977 100644 --- a/control-plane/api-gateway/controllers/gateway_controller.go +++ b/control-plane/api-gateway/controllers/gateway_controller.go @@ -368,10 +368,6 @@ func SetupGatewayControllerWithManager(ctx context.Context, mgr ctrl.Manager, co source.NewKindWithCache(&corev1.Secret{}, mgr.GetCache()), handler.EnqueueRequestsFromMapFunc(r.transformSecret(ctx)), ). - Watches( - source.NewKindWithCache(&gwv1beta1.ReferenceGrant{}, mgr.GetCache()), - handler.EnqueueRequestsFromMapFunc(r.transformReferenceGrant(ctx)), - ). Watches( source.NewKindWithCache(&v1alpha1.MeshService{}, mgr.GetCache()), handler.EnqueueRequestsFromMapFunc(r.transformMeshService(ctx)), @@ -573,9 +569,14 @@ func gatewayReferencesCertificate(certificateKey api.ResourceReference, gateway // by a TCPRoute or HTTPRoute that references the service. func (r *GatewayController) transformEndpoints(ctx context.Context) func(o client.Object) []reconcile.Request { return func(o client.Object) []reconcile.Request { - key := client.ObjectKeyFromObject(o).String() + key := client.ObjectKeyFromObject(o) + endpoints := o.(*corev1.Endpoints) + + if shouldIgnore(key.Namespace, r.denyK8sNamespacesSet, r.allowK8sNamespacesSet) || isLabeledIgnore(endpoints.Labels) { + return nil + } - return r.gatewaysForRoutesReferencing(ctx, TCPRoute_ServiceIndex, HTTPRoute_ServiceIndex, key) + return r.gatewaysForRoutesReferencing(ctx, TCPRoute_ServiceIndex, HTTPRoute_ServiceIndex, key.String()) } } diff --git a/control-plane/api-gateway/controllers/gateway_controller_test.go b/control-plane/api-gateway/controllers/gateway_controller_test.go index d1028eb945..7b21d01ff8 100644 --- a/control-plane/api-gateway/controllers/gateway_controller_test.go +++ b/control-plane/api-gateway/controllers/gateway_controller_test.go @@ -7,8 +7,11 @@ import ( "context" "testing" + mapset "github.com/deckarep/golang-set" "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" + "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -21,6 +24,257 @@ import ( gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) +func TestTransformEndpoints(t *testing.T) { + t.Parallel() + + httpRoute := &gwv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "http", + Namespace: "test", + }, + Spec: gwv1beta1.HTTPRouteSpec{ + Rules: []gwv1beta1.HTTPRouteRule{ + {BackendRefs: []gwv1beta1.HTTPBackendRef{ + {BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{Name: "http-test-namespace"}, + }}, + {BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{Name: "http-other-namespace", Namespace: common.PointerTo(gwv1beta1.Namespace("other"))}, + }}, + {BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{Name: "http-system-namespace", Namespace: common.PointerTo(gwv1beta1.Namespace("system"))}, + }}, + {BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{Name: "http-public-namespace", Namespace: common.PointerTo(gwv1beta1.Namespace("public"))}, + }}, + {BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{Name: "http-local-path-storage-namespace", Namespace: common.PointerTo(gwv1beta1.Namespace("local-path-storage"))}, + }}}, + }, + }, + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + {Name: "http-gateway"}, + {Name: "general-gateway"}, + }, + }, + }, + } + + tcpRoute := &gwv1alpha2.TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tcp", + Namespace: "test", + }, + Spec: gwv1alpha2.TCPRouteSpec{ + Rules: []gwv1alpha2.TCPRouteRule{ + {BackendRefs: []gwv1beta1.BackendRef{ + {BackendObjectReference: gwv1beta1.BackendObjectReference{Name: "tcp-test-namespace"}}, + {BackendObjectReference: gwv1beta1.BackendObjectReference{Name: "tcp-other-namespace", Namespace: common.PointerTo(gwv1beta1.Namespace("other"))}}, + {BackendObjectReference: gwv1beta1.BackendObjectReference{Name: "tcp-system-namespace", Namespace: common.PointerTo(gwv1beta1.Namespace("system"))}}, + {BackendObjectReference: gwv1beta1.BackendObjectReference{Name: "tcp-public-namespace", Namespace: common.PointerTo(gwv1beta1.Namespace("public"))}}, + {BackendObjectReference: gwv1beta1.BackendObjectReference{Name: "tcp-local-path-storage-namespace", Namespace: common.PointerTo(gwv1beta1.Namespace("local-path-storage"))}}, + }}, + }, + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + {Name: "tcp-gateway"}, + {Name: "general-gateway"}, + }, + }, + }, + } + + for name, tt := range map[string]struct { + endpoints *corev1.Endpoints + expected []reconcile.Request + allowedNamespaces []string + denyNamespaces []string + }{ + "ignore system namespace": { + endpoints: &corev1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: "http-system-namespace", + Namespace: metav1.NamespaceSystem, + }, + }, + allowedNamespaces: []string{"*"}, + }, + "ignore public namespace": { + endpoints: &corev1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: "http-public-namespace", + Namespace: metav1.NamespacePublic, + }, + }, + allowedNamespaces: []string{"*"}, + }, + "ignore local-path-storage namespace": { + endpoints: &corev1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: "http-local-path-storage-namespace", + Namespace: "local-path-storage", + }, + }, + allowedNamespaces: []string{"*"}, + }, + "explicit deny namespace": { + endpoints: &corev1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: "http-test-namespace", + Namespace: "test", + }, + }, + allowedNamespaces: []string{"*"}, + denyNamespaces: []string{"test"}, + }, + "ignore labels": { + endpoints: &corev1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: "http-test-namespace", + Namespace: "test", + Labels: map[string]string{ + constants.LabelServiceIgnore: "true", + }, + }, + }, + allowedNamespaces: []string{"test"}, + }, + "http same namespace wildcard allow": { + endpoints: &corev1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: "http-test-namespace", + Namespace: "test", + }, + }, + allowedNamespaces: []string{"*"}, + expected: []reconcile.Request{ + {NamespacedName: types.NamespacedName{Name: "http-gateway", Namespace: "test"}}, + {NamespacedName: types.NamespacedName{Name: "general-gateway", Namespace: "test"}}, + }, + }, + "http same namespace explicit allow": { + endpoints: &corev1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: "http-test-namespace", + Namespace: "test", + }, + }, + allowedNamespaces: []string{"test"}, + expected: []reconcile.Request{ + {NamespacedName: types.NamespacedName{Name: "http-gateway", Namespace: "test"}}, + {NamespacedName: types.NamespacedName{Name: "general-gateway", Namespace: "test"}}, + }, + }, + "http other namespace wildcard allow": { + endpoints: &corev1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: "http-other-namespace", + Namespace: "other", + }, + }, + allowedNamespaces: []string{"*"}, + expected: []reconcile.Request{ + {NamespacedName: types.NamespacedName{Name: "http-gateway", Namespace: "test"}}, + {NamespacedName: types.NamespacedName{Name: "general-gateway", Namespace: "test"}}, + }, + }, + "http other namespace explicit allow": { + endpoints: &corev1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: "http-other-namespace", + Namespace: "other", + }, + }, + allowedNamespaces: []string{"other"}, + expected: []reconcile.Request{ + {NamespacedName: types.NamespacedName{Name: "http-gateway", Namespace: "test"}}, + {NamespacedName: types.NamespacedName{Name: "general-gateway", Namespace: "test"}}, + }, + }, + "tcp same namespace wildcard allow": { + endpoints: &corev1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tcp-test-namespace", + Namespace: "test", + }, + }, + allowedNamespaces: []string{"*"}, + expected: []reconcile.Request{ + {NamespacedName: types.NamespacedName{Name: "tcp-gateway", Namespace: "test"}}, + {NamespacedName: types.NamespacedName{Name: "general-gateway", Namespace: "test"}}, + }, + }, + "tcp same namespace explicit allow": { + endpoints: &corev1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tcp-test-namespace", + Namespace: "test", + }, + }, + allowedNamespaces: []string{"test"}, + expected: []reconcile.Request{ + {NamespacedName: types.NamespacedName{Name: "tcp-gateway", Namespace: "test"}}, + {NamespacedName: types.NamespacedName{Name: "general-gateway", Namespace: "test"}}, + }, + }, + "tcp other namespace wildcard allow": { + endpoints: &corev1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tcp-other-namespace", + Namespace: "other", + }, + }, + allowedNamespaces: []string{"*"}, + expected: []reconcile.Request{ + {NamespacedName: types.NamespacedName{Name: "tcp-gateway", Namespace: "test"}}, + {NamespacedName: types.NamespacedName{Name: "general-gateway", Namespace: "test"}}, + }, + }, + "tcp other namespace explicit allow": { + endpoints: &corev1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tcp-other-namespace", + Namespace: "other", + }, + }, + allowedNamespaces: []string{"other"}, + expected: []reconcile.Request{ + {NamespacedName: types.NamespacedName{Name: "tcp-gateway", Namespace: "test"}}, + {NamespacedName: types.NamespacedName{Name: "general-gateway", Namespace: "test"}}, + }, + }, + } { + t.Run(name, func(t *testing.T) { + s := runtime.NewScheme() + require.NoError(t, clientgoscheme.AddToScheme(s)) + require.NoError(t, gwv1alpha2.Install(s)) + require.NoError(t, gwv1beta1.Install(s)) + require.NoError(t, v1alpha1.AddToScheme(s)) + + denySet := mapset.NewSet() + for _, v := range tt.denyNamespaces { + denySet.Add(v) + } + allowSet := mapset.NewSet() + for _, v := range tt.allowedNamespaces { + allowSet.Add(v) + } + + fakeClient := registerFieldIndexersForTest(fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(httpRoute, tcpRoute)).Build() + + controller := GatewayController{ + Client: fakeClient, + denyK8sNamespacesSet: denySet, + allowK8sNamespacesSet: allowSet, + } + + fn := controller.transformEndpoints(context.Background()) + require.ElementsMatch(t, tt.expected, fn(tt.endpoints)) + }) + } +} + func TestTransformHTTPRoute(t *testing.T) { t.Parallel() From ff021cc1cc9366ed4a9f6c5fd3d54bdcf789f895 Mon Sep 17 00:00:00 2001 From: "hashicorp-copywrite[bot]" <110428419+hashicorp-copywrite[bot]@users.noreply.github.com> Date: Mon, 5 Jun 2023 19:05:20 -0400 Subject: [PATCH 200/340] [COMPLIANCE] Add Copyright and License Headers (#2271) Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com> --- control-plane/api-gateway/cache/gateway.go | 3 +++ control-plane/api/v1alpha1/controlplanerequestlimit_types.go | 3 +++ .../api/v1alpha1/controlplanerequestlimit_types_test.go | 3 +++ control-plane/config/crd/kustomization.yaml | 3 +++ control-plane/config/crd/kustomizeconfig.yaml | 3 +++ .../controllers/controlplanerequestlimit_controller.go | 3 +++ 6 files changed, 18 insertions(+) diff --git a/control-plane/api-gateway/cache/gateway.go b/control-plane/api-gateway/cache/gateway.go index d6d0c27ed2..846131d11e 100644 --- a/control-plane/api-gateway/cache/gateway.go +++ b/control-plane/api-gateway/cache/gateway.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package cache import ( diff --git a/control-plane/api/v1alpha1/controlplanerequestlimit_types.go b/control-plane/api/v1alpha1/controlplanerequestlimit_types.go index 6b469696d1..ac2c05ded0 100644 --- a/control-plane/api/v1alpha1/controlplanerequestlimit_types.go +++ b/control-plane/api/v1alpha1/controlplanerequestlimit_types.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/controlplanerequestlimit_types_test.go b/control-plane/api/v1alpha1/controlplanerequestlimit_types_test.go index 5b8c4b4352..12633250ab 100644 --- a/control-plane/api/v1alpha1/controlplanerequestlimit_types_test.go +++ b/control-plane/api/v1alpha1/controlplanerequestlimit_types_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( diff --git a/control-plane/config/crd/kustomization.yaml b/control-plane/config/crd/kustomization.yaml index 2b1d90d6d0..2c8358a48b 100644 --- a/control-plane/config/crd/kustomization.yaml +++ b/control-plane/config/crd/kustomization.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + # This kustomization.yaml is not intended to be run by itself, # since it depends on service name and namespace that are out of this kustomize package. # It should be run by config/default diff --git a/control-plane/config/crd/kustomizeconfig.yaml b/control-plane/config/crd/kustomizeconfig.yaml index 6f83d9a94b..5d1332c4bf 100644 --- a/control-plane/config/crd/kustomizeconfig.yaml +++ b/control-plane/config/crd/kustomizeconfig.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + # This file is for teaching kustomize how to substitute name and namespace reference in CRD nameReference: - kind: Service diff --git a/control-plane/controllers/controlplanerequestlimit_controller.go b/control-plane/controllers/controlplanerequestlimit_controller.go index 0441f1ed14..f5afbdcc48 100644 --- a/control-plane/controllers/controlplanerequestlimit_controller.go +++ b/control-plane/controllers/controlplanerequestlimit_controller.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package controllers import ( From fe2c481f38747d5bf06b5ac402c167a985b038a7 Mon Sep 17 00:00:00 2001 From: Andrew Stucki Date: Tue, 6 Jun 2023 10:08:00 -0400 Subject: [PATCH 201/340] Add additional helm hook for resource management (#2259) * Add additional helm hook for resource management * Move GatewayClassConfig CRD to templates * Add CRDs to templates * Add value to values.yaml * Remove GatewayClass and GatewayClassConfig bats * Fix CRD ExportedServices * Change -release to -release-name on gateway-resources subcommand * switch to pointer to avoid lock copy for linter * Move forcible test cleanup to before helm delete since it will now drop CRDs * adjust cleanup logic since it looks like the testing framework sometimes uninstalls the helm chart early * Fix cli unit test and drop CRD reading data since it's no longer embedded in the CLI * Add BATs for Gateway CRDs * Add BATs for Gateway Resources * Update Contributing --------- Co-authored-by: Thomas Eckert --- CONTRIBUTING.md | 8 +- Makefile | 2 +- acceptance/framework/consul/helm_cluster.go | 82 ++-- .../crd-controlplanerequestlimits.yaml | 5 +- .../templates/crd-exportedservices.yaml | 5 +- .../crd-gatewayclassconfigs.yaml} | 14 +- .../crd-gatewayclasses.yaml} | 11 +- .../crd-gateways.yaml} | 11 +- .../crd-grpcroutes.yaml} | 11 +- .../crd-httproutes.yaml} | 11 +- .../consul/templates/crd-ingressgateways.yaml | 5 +- charts/consul/templates/crd-jwtproviders.yaml | 5 +- charts/consul/templates/crd-meshes.yaml | 5 +- charts/consul/templates/crd-meshservices.yaml | 5 +- .../templates/crd-peeringacceptors.yaml | 5 +- .../consul/templates/crd-peeringdialers.yaml | 5 +- .../consul/templates/crd-proxydefaults.yaml | 5 +- .../crd-referencegrants.yaml} | 11 +- .../consul/templates/crd-samenessgroups.yaml | 5 +- .../consul/templates/crd-servicedefaults.yaml | 5 +- .../templates/crd-serviceintentions.yaml | 5 +- .../templates/crd-serviceresolvers.yaml | 5 +- .../consul/templates/crd-servicerouters.yaml | 5 +- .../templates/crd-servicesplitters.yaml | 5 +- .../crd-tcproutes.yaml} | 11 +- .../templates/crd-terminatinggateways.yaml | 5 +- .../crd-tlsroutes.yaml} | 11 +- .../crd-udproutes.yaml} | 11 +- .../gateway-cleanup-podsecuritypolicy.yaml | 4 +- .../templates/gateway-gatewayclass.yaml | 18 - .../templates/gateway-gatewayclassconfig.yaml | 74 ---- .../gateway-resources-clusterrole.yaml | 37 ++ .../gateway-resources-clusterrolebinding.yaml | 20 + .../templates/gateway-resources-job.yaml | 112 ++++++ .../gateway-resources-podsecuritypolicy.yaml | 32 ++ .../gateway-resources-serviceaccount.yaml | 13 + .../test/unit/crd-exportedservices.bats | 2 +- .../test/unit/crd-gatewayclassconfigs.bats | 20 + .../consul/test/unit/crd-gatewayclasses.bats | 28 ++ charts/consul/test/unit/crd-gateways.bats | 28 ++ charts/consul/test/unit/crd-grpcroutes.bats | 28 ++ charts/consul/test/unit/crd-httproutes.bats | 28 ++ charts/consul/test/unit/crd-meshservices.bats | 21 ++ charts/consul/test/unit/crd-tcproutes.bats | 28 ++ charts/consul/test/unit/crd-tlsroutes.bats | 28 ++ charts/consul/test/unit/crd-udproutes.bats | 28 ++ .../unit/gateway-cleanup-clusterrole.bats | 33 ++ .../gateway-cleanup-clusterrolebinding.bats | 23 ++ .../consul/test/unit/gateway-cleanup-job.bats | 23 ++ .../gateway-cleanup-podsecuritypolicy.bats | 41 ++ .../unit/gateway-cleanup-serviceaccount.bats | 23 ++ .../test/unit/gateway-gatewayclass.bats | 21 -- .../test/unit/gateway-gatewayclassconfig.bats | 117 ------ .../unit/gateway-resources-clusterrole.bats | 33 ++ .../gateway-resources-clusterrolebinding.bats | 23 ++ .../test/unit/gateway-resources-job.bats | 118 ++++++ .../gateway-resources-podsecuritypolicy.bats | 41 ++ .../gateway-resources-serviceaccount.bats | 23 ++ charts/consul/todo.txt | 3 + charts/consul/values.yaml | 6 + charts/demo/crds/blank | 4 - charts/embed_chart.go | 4 +- cli/helm/chart.go | 20 - cli/helm/chart_test.go | 1 - cli/helm/test_fixtures/consul/crds/foo.yaml | 4 - control-plane/commands.go | 5 + ...shicorp.com_controlplanerequestlimits.yaml | 3 +- ...consul.hashicorp.com_exportedservices.yaml | 3 +- ...sul.hashicorp.com_gatewayclassconfigs.yaml | 3 +- .../consul.hashicorp.com_ingressgateways.yaml | 3 +- .../consul.hashicorp.com_jwtproviders.yaml | 3 +- .../bases/consul.hashicorp.com_meshes.yaml | 3 +- .../consul.hashicorp.com_meshservices.yaml | 3 +- ...consul.hashicorp.com_peeringacceptors.yaml | 3 +- .../consul.hashicorp.com_peeringdialers.yaml | 3 +- .../consul.hashicorp.com_proxydefaults.yaml | 3 +- .../consul.hashicorp.com_samenessgroups.yaml | 3 +- .../consul.hashicorp.com_servicedefaults.yaml | 3 +- ...onsul.hashicorp.com_serviceintentions.yaml | 3 +- ...consul.hashicorp.com_serviceresolvers.yaml | 3 +- .../consul.hashicorp.com_servicerouters.yaml | 3 +- ...consul.hashicorp.com_servicesplitters.yaml | 3 +- ...sul.hashicorp.com_terminatinggateways.yaml | 3 +- ...ewayclasses.gateway.networking.k8s.io.yaml | 0 .../gateways.gateway.networking.k8s.io.yaml | 0 .../grpcroutes.gateway.networking.k8s.io.yaml | 0 .../httproutes.gateway.networking.k8s.io.yaml | 0 .../config/crd/external}/kustomization.yaml | 0 ...rencegrants.gateway.networking.k8s.io.yaml | 0 .../tcproutes.gateway.networking.k8s.io.yaml | 0 .../tlsroutes.gateway.networking.k8s.io.yaml | 0 .../udproutes.gateway.networking.k8s.io.yaml | 0 control-plane/config/rbac/role.yaml | 1 + control-plane/config/webhook/manifests.yaml | 1 + control-plane/go.mod | 2 +- .../subcommand/gateway-cleanup/command.go | 2 +- .../subcommand/gateway-resources/command.go | 352 ++++++++++++++++++ .../gateway-resources/command_test.go | 253 +++++++++++++ hack/copy-crds-to-chart/main.go | 94 ++--- 99 files changed, 1702 insertions(+), 443 deletions(-) rename charts/consul/{crds/gatewayclassconfigs.consul.hashicorp.com.yaml => templates/crd-gatewayclassconfigs.yaml} (95%) rename charts/consul/{crds/gatewayclasses.gateway.networking.k8s.io.yaml.yml => templates/crd-gatewayclasses.yaml} (98%) rename charts/consul/{crds/gateways.gateway.networking.k8s.io.yaml.yml => templates/crd-gateways.yaml} (99%) rename charts/consul/{crds/grpcroutes.gateway.networking.k8s.io.yaml.yml => templates/crd-grpcroutes.yaml} (99%) rename charts/consul/{crds/httproutes.gateway.networking.k8s.io.yaml.yml => templates/crd-httproutes.yaml} (99%) rename charts/consul/{crds/referencegrants.gateway.networking.k8s.io.yaml.yml => templates/crd-referencegrants.yaml} (97%) rename charts/consul/{crds/tcproutes.gateway.networking.k8s.io.yaml.yml => templates/crd-tcproutes.yaml} (98%) rename charts/consul/{crds/tlsroutes.gateway.networking.k8s.io.yaml.yml => templates/crd-tlsroutes.yaml} (99%) rename charts/consul/{crds/udproutes.gateway.networking.k8s.io.yaml.yml => templates/crd-udproutes.yaml} (98%) delete mode 100644 charts/consul/templates/gateway-gatewayclass.yaml delete mode 100644 charts/consul/templates/gateway-gatewayclassconfig.yaml create mode 100644 charts/consul/templates/gateway-resources-clusterrole.yaml create mode 100644 charts/consul/templates/gateway-resources-clusterrolebinding.yaml create mode 100644 charts/consul/templates/gateway-resources-job.yaml create mode 100644 charts/consul/templates/gateway-resources-podsecuritypolicy.yaml create mode 100644 charts/consul/templates/gateway-resources-serviceaccount.yaml create mode 100644 charts/consul/test/unit/crd-gatewayclassconfigs.bats create mode 100644 charts/consul/test/unit/crd-gatewayclasses.bats create mode 100644 charts/consul/test/unit/crd-gateways.bats create mode 100644 charts/consul/test/unit/crd-grpcroutes.bats create mode 100644 charts/consul/test/unit/crd-httproutes.bats create mode 100644 charts/consul/test/unit/crd-meshservices.bats create mode 100644 charts/consul/test/unit/crd-tcproutes.bats create mode 100644 charts/consul/test/unit/crd-tlsroutes.bats create mode 100644 charts/consul/test/unit/crd-udproutes.bats create mode 100644 charts/consul/test/unit/gateway-cleanup-clusterrole.bats create mode 100644 charts/consul/test/unit/gateway-cleanup-clusterrolebinding.bats create mode 100644 charts/consul/test/unit/gateway-cleanup-job.bats create mode 100644 charts/consul/test/unit/gateway-cleanup-podsecuritypolicy.bats create mode 100644 charts/consul/test/unit/gateway-cleanup-serviceaccount.bats delete mode 100755 charts/consul/test/unit/gateway-gatewayclass.bats delete mode 100644 charts/consul/test/unit/gateway-gatewayclassconfig.bats create mode 100644 charts/consul/test/unit/gateway-resources-clusterrole.bats create mode 100644 charts/consul/test/unit/gateway-resources-clusterrolebinding.bats create mode 100644 charts/consul/test/unit/gateway-resources-job.bats create mode 100644 charts/consul/test/unit/gateway-resources-podsecuritypolicy.bats create mode 100644 charts/consul/test/unit/gateway-resources-serviceaccount.bats create mode 100644 charts/consul/todo.txt delete mode 100644 charts/demo/crds/blank delete mode 100644 cli/helm/test_fixtures/consul/crds/foo.yaml rename {charts/consul/crds => control-plane/config/crd/external}/gatewayclasses.gateway.networking.k8s.io.yaml (100%) rename {charts/consul/crds => control-plane/config/crd/external}/gateways.gateway.networking.k8s.io.yaml (100%) rename {charts/consul/crds => control-plane/config/crd/external}/grpcroutes.gateway.networking.k8s.io.yaml (100%) rename {charts/consul/crds => control-plane/config/crd/external}/httproutes.gateway.networking.k8s.io.yaml (100%) rename {charts/consul/crds => control-plane/config/crd/external}/kustomization.yaml (100%) rename {charts/consul/crds => control-plane/config/crd/external}/referencegrants.gateway.networking.k8s.io.yaml (100%) rename {charts/consul/crds => control-plane/config/crd/external}/tcproutes.gateway.networking.k8s.io.yaml (100%) rename {charts/consul/crds => control-plane/config/crd/external}/tlsroutes.gateway.networking.k8s.io.yaml (100%) rename {charts/consul/crds => control-plane/config/crd/external}/udproutes.gateway.networking.k8s.io.yaml (100%) create mode 100644 control-plane/subcommand/gateway-resources/command.go create mode 100644 control-plane/subcommand/gateway-resources/command_test.go diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 63ae3b2323..f0deb97ce9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1224,17 +1224,17 @@ Networking. To pull external CRDs into our Helm chart and make sure they get installed, we generate their configuration using [Kustomize](https://kustomize.io/) which can pull in Kubernetes config from external sources. We split these -generated CRDs into individual files and store them in the -[Helm `/crds` directory](https://helm.sh/docs/chart_best_practices/custom_resource_definitions/). +generated CRDs into individual files and store them in the `charts/consul/templates` directory. If you need to update the external CRDs we depend on, or add to them, you can do this by editing the -[crds/kustomization.yaml](/charts/consul/crds/kustomization.yaml) file. Once modified, running +[control-plane/config/crd/external/kustomization.yaml](/control-plane/config/crd/external/kustomization.yaml) file. +Once modified, running ```bash make generate-external-crds ``` -will update the CRDs in the `/crds` directory. +will update the CRDs in the `/templates` directory. ## Adding a Changelog Entry diff --git a/Makefile b/Makefile index 437357898a..5adfb55657 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ copy-crds-to-chart: ## Copy generated CRD YAML into charts/consul. Usage: make c @cd hack/copy-crds-to-chart; go run ./... generate-external-crds: ## Generate CRDs for externally defined CRDs and copy them to charts/consul. Usage: make generate-external-crds - @cd ./charts/consul/crds/; \ + @cd ./control-plane/config/crd/external; \ kustomize build | yq --split-exp '.metadata.name + ".yaml"' --no-doc bats-tests: ## Run Helm chart bats tests. diff --git a/acceptance/framework/consul/helm_cluster.go b/acceptance/framework/consul/helm_cluster.go index 613b69da91..6c61a605f6 100644 --- a/acceptance/framework/consul/helm_cluster.go +++ b/acceptance/framework/consul/helm_cluster.go @@ -153,6 +153,39 @@ func (h *HelmCluster) Destroy(t *testing.T) { "--wait": nil, } + // Clean up any stuck gateway resources, note that we swallow all errors from + // here down since the terratest helm installation may actually already be + // deleted at this point, in which case these operations will fail on non-existent + // CRD cleanups. + requirement, err := labels.NewRequirement("release", selection.Equals, []string{h.releaseName}) + require.NoError(t, err) + + // Forcibly delete all gateway classes and remove their finalizers. + _ = h.runtimeClient.DeleteAllOf(context.Background(), &gwv1beta1.GatewayClass{}, client.HasLabels{"release=" + h.releaseName}) + + var gatewayClassList gwv1beta1.GatewayClassList + if h.runtimeClient.List(context.Background(), &gatewayClassList, &client.ListOptions{ + LabelSelector: labels.NewSelector().Add(*requirement), + }) == nil { + for _, item := range gatewayClassList.Items { + item.SetFinalizers([]string{}) + _ = h.runtimeClient.Update(context.Background(), &item) + } + } + + // Forcibly delete all gateway class configs and remove their finalizers. + _ = h.runtimeClient.DeleteAllOf(context.Background(), &v1alpha1.GatewayClassConfig{}, client.HasLabels{"release=" + h.releaseName}) + + var gatewayClassConfigList v1alpha1.GatewayClassConfigList + if h.runtimeClient.List(context.Background(), &gatewayClassConfigList, &client.ListOptions{ + LabelSelector: labels.NewSelector().Add(*requirement), + }) == nil { + for _, item := range gatewayClassConfigList.Items { + item.SetFinalizers([]string{}) + _ = h.runtimeClient.Update(context.Background(), &item) + } + } + retry.RunWith(&retry.Counter{Wait: 2 * time.Second, Count: 30}, t, func(r *retry.R) { err := helm.DeleteE(t, h.helmOptions, h.releaseName, false) require.NoError(r, err) @@ -161,9 +194,6 @@ func (h *HelmCluster) Destroy(t *testing.T) { // Retry because sometimes certain resources (like PVC) take time to delete // in cloud providers. retry.RunWith(&retry.Counter{Wait: 2 * time.Second, Count: 600}, t, func(r *retry.R) { - requirement, err := labels.NewRequirement("release", selection.Equals, []string{h.releaseName}) - require.NoError(r, err) - // Force delete any pods that have h.releaseName in their name because sometimes // graceful termination takes a long time and since this is an uninstall // we don't care that they're stopped gracefully. @@ -243,34 +273,6 @@ func (h *HelmCluster) Destroy(t *testing.T) { } } - // Forcibly delete all gateway classes and remove their finalizers. - err = h.runtimeClient.DeleteAllOf(context.Background(), &gwv1beta1.GatewayClass{}, client.HasLabels{"release=" + h.releaseName}) - require.NoError(r, err) - - var gatewayClassList gwv1beta1.GatewayClassList - err = h.runtimeClient.List(context.Background(), &gatewayClassList, &client.ListOptions{ - LabelSelector: labels.NewSelector().Add(*requirement), - }) - require.NoError(r, err) - for _, item := range gatewayClassList.Items { - item.SetFinalizers([]string{}) - require.NoError(r, h.runtimeClient.Update(context.Background(), &item)) - } - - // Forcibly delete all gateway class configs and remove their finalizers. - err = h.runtimeClient.DeleteAllOf(context.Background(), &v1alpha1.GatewayClassConfig{}, client.HasLabels{"release=" + h.releaseName}) - require.NoError(r, err) - - var gatewayClassConfigList v1alpha1.GatewayClassConfigList - err = h.runtimeClient.List(context.Background(), &gatewayClassConfigList, &client.ListOptions{ - LabelSelector: labels.NewSelector().Add(*requirement), - }) - require.NoError(r, err) - for _, item := range gatewayClassConfigList.Items { - item.SetFinalizers([]string{}) - require.NoError(r, h.runtimeClient.Update(context.Background(), &item)) - } - // Verify all Consul Pods are deleted. pods, err = h.kubernetesClient.CoreV1().Pods(h.helmOptions.KubectlOptions.Namespace).List(context.Background(), metav1.ListOptions{LabelSelector: "release=" + h.releaseName}) require.NoError(r, err) @@ -329,24 +331,6 @@ func (h *HelmCluster) Destroy(t *testing.T) { r.Errorf("Found job which should have been deleted: %s", job.Name) } } - - // Verify all Gateway Classes are deleted. - err = h.runtimeClient.List(context.Background(), &gatewayClassList, &client.ListOptions{ - LabelSelector: labels.NewSelector().Add(*requirement), - }) - require.NoError(r, err) - for _, gatewayClass := range gatewayClassList.Items { - r.Errorf("Found gateway class which should have been deleted: %s", gatewayClass.Name) - } - - // Verify all Gateway Class Configs are deleted. - err = h.runtimeClient.List(context.Background(), &gatewayClassConfigList, &client.ListOptions{ - LabelSelector: labels.NewSelector().Add(*requirement), - }) - require.NoError(r, err) - for _, gatewayClassConfig := range gatewayClassConfigList.Items { - r.Errorf("Found gateway class config which should have been deleted: %s", gatewayClassConfig.Name) - } }) } diff --git a/charts/consul/templates/crd-controlplanerequestlimits.yaml b/charts/consul/templates/crd-controlplanerequestlimits.yaml index 67ff258eb8..bd1d6118b9 100644 --- a/charts/consul/templates/crd-controlplanerequestlimits.yaml +++ b/charts/consul/templates/crd-controlplanerequestlimits.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: controlplanerequestlimits.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: ControlPlaneRequestLimit diff --git a/charts/consul/templates/crd-exportedservices.yaml b/charts/consul/templates/crd-exportedservices.yaml index 8581ac4e88..7ffddf7537 100644 --- a/charts/consul/templates/crd-exportedservices.yaml +++ b/charts/consul/templates/crd-exportedservices.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: exportedservices.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: ExportedServices diff --git a/charts/consul/crds/gatewayclassconfigs.consul.hashicorp.com.yaml b/charts/consul/templates/crd-gatewayclassconfigs.yaml similarity index 95% rename from charts/consul/crds/gatewayclassconfigs.consul.hashicorp.com.yaml rename to charts/consul/templates/crd-gatewayclassconfigs.yaml index 44eff52492..65d425edc4 100644 --- a/charts/consul/crds/gatewayclassconfigs.consul.hashicorp.com.yaml +++ b/charts/consul/templates/crd-gatewayclassconfigs.yaml @@ -1,13 +1,18 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - +{{- if .Values.connectInject.enabled }} --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: gatewayclassconfigs.consul.hashicorp.com + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd spec: group: consul.hashicorp.com names: @@ -137,3 +142,4 @@ spec: type: object served: true storage: true +{{- end }} diff --git a/charts/consul/crds/gatewayclasses.gateway.networking.k8s.io.yaml.yml b/charts/consul/templates/crd-gatewayclasses.yaml similarity index 98% rename from charts/consul/crds/gatewayclasses.gateway.networking.k8s.io.yaml.yml rename to charts/consul/templates/crd-gatewayclasses.yaml index 044c7af939..93435b7fce 100644 --- a/charts/consul/crds/gatewayclasses.gateway.networking.k8s.io.yaml.yml +++ b/charts/consul/templates/crd-gatewayclasses.yaml @@ -1,6 +1,4 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - +{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: @@ -9,6 +7,12 @@ metadata: gateway.networking.k8s.io/bundle-version: v0.6.2 gateway.networking.k8s.io/channel: experimental creationTimestamp: null + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd name: gatewayclasses.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io @@ -321,3 +325,4 @@ status: plural: "" conditions: [] storedVersions: [] +{{- end }} diff --git a/charts/consul/crds/gateways.gateway.networking.k8s.io.yaml.yml b/charts/consul/templates/crd-gateways.yaml similarity index 99% rename from charts/consul/crds/gateways.gateway.networking.k8s.io.yaml.yml rename to charts/consul/templates/crd-gateways.yaml index b7a7c8a7d1..41df34942a 100644 --- a/charts/consul/crds/gateways.gateway.networking.k8s.io.yaml.yml +++ b/charts/consul/templates/crd-gateways.yaml @@ -1,6 +1,4 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - +{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: @@ -9,6 +7,12 @@ metadata: gateway.networking.k8s.io/bundle-version: v0.6.2 gateway.networking.k8s.io/channel: experimental creationTimestamp: null + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd name: gateways.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io @@ -875,3 +879,4 @@ status: plural: "" conditions: [] storedVersions: [] +{{- end }} diff --git a/charts/consul/crds/grpcroutes.gateway.networking.k8s.io.yaml.yml b/charts/consul/templates/crd-grpcroutes.yaml similarity index 99% rename from charts/consul/crds/grpcroutes.gateway.networking.k8s.io.yaml.yml rename to charts/consul/templates/crd-grpcroutes.yaml index 8f3ab6d385..739ed2c659 100644 --- a/charts/consul/crds/grpcroutes.gateway.networking.k8s.io.yaml.yml +++ b/charts/consul/templates/crd-grpcroutes.yaml @@ -1,6 +1,4 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - +{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: @@ -9,6 +7,12 @@ metadata: gateway.networking.k8s.io/bundle-version: v0.6.2 gateway.networking.k8s.io/channel: experimental creationTimestamp: null + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd name: grpcroutes.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io @@ -759,3 +763,4 @@ status: plural: "" conditions: [] storedVersions: [] +{{- end }} diff --git a/charts/consul/crds/httproutes.gateway.networking.k8s.io.yaml.yml b/charts/consul/templates/crd-httproutes.yaml similarity index 99% rename from charts/consul/crds/httproutes.gateway.networking.k8s.io.yaml.yml rename to charts/consul/templates/crd-httproutes.yaml index b455d788de..bba3672d16 100644 --- a/charts/consul/crds/httproutes.gateway.networking.k8s.io.yaml.yml +++ b/charts/consul/templates/crd-httproutes.yaml @@ -1,6 +1,4 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - +{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: @@ -9,6 +7,12 @@ metadata: gateway.networking.k8s.io/bundle-version: v0.6.2 gateway.networking.k8s.io/channel: experimental creationTimestamp: null + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd name: httproutes.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io @@ -1907,3 +1911,4 @@ status: plural: "" conditions: [] storedVersions: [] +{{- end }} diff --git a/charts/consul/templates/crd-ingressgateways.yaml b/charts/consul/templates/crd-ingressgateways.yaml index eff7ef61a9..ef33890461 100644 --- a/charts/consul/templates/crd-ingressgateways.yaml +++ b/charts/consul/templates/crd-ingressgateways.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: ingressgateways.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: IngressGateway diff --git a/charts/consul/templates/crd-jwtproviders.yaml b/charts/consul/templates/crd-jwtproviders.yaml index fa87f37489..c7d20883e8 100644 --- a/charts/consul/templates/crd-jwtproviders.yaml +++ b/charts/consul/templates/crd-jwtproviders.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: jwtproviders.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: JWTProvider diff --git a/charts/consul/templates/crd-meshes.yaml b/charts/consul/templates/crd-meshes.yaml index f2549b5111..cdc11b6ed9 100644 --- a/charts/consul/templates/crd-meshes.yaml +++ b/charts/consul/templates/crd-meshes.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: meshes.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: Mesh diff --git a/charts/consul/templates/crd-meshservices.yaml b/charts/consul/templates/crd-meshservices.yaml index aa808113a2..859c8683ee 100644 --- a/charts/consul/templates/crd-meshservices.yaml +++ b/charts/consul/templates/crd-meshservices.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: meshservices.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: MeshService diff --git a/charts/consul/templates/crd-peeringacceptors.yaml b/charts/consul/templates/crd-peeringacceptors.yaml index 40f7f1d4d6..3822f3bdfe 100644 --- a/charts/consul/templates/crd-peeringacceptors.yaml +++ b/charts/consul/templates/crd-peeringacceptors.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: peeringacceptors.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: PeeringAcceptor diff --git a/charts/consul/templates/crd-peeringdialers.yaml b/charts/consul/templates/crd-peeringdialers.yaml index bfe4778d0c..405361c486 100644 --- a/charts/consul/templates/crd-peeringdialers.yaml +++ b/charts/consul/templates/crd-peeringdialers.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: peeringdialers.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: PeeringDialer diff --git a/charts/consul/templates/crd-proxydefaults.yaml b/charts/consul/templates/crd-proxydefaults.yaml index a224effc12..30dd25f674 100644 --- a/charts/consul/templates/crd-proxydefaults.yaml +++ b/charts/consul/templates/crd-proxydefaults.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: proxydefaults.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: ProxyDefaults diff --git a/charts/consul/crds/referencegrants.gateway.networking.k8s.io.yaml.yml b/charts/consul/templates/crd-referencegrants.yaml similarity index 97% rename from charts/consul/crds/referencegrants.gateway.networking.k8s.io.yaml.yml rename to charts/consul/templates/crd-referencegrants.yaml index cd39b9c12a..db9cf12027 100644 --- a/charts/consul/crds/referencegrants.gateway.networking.k8s.io.yaml.yml +++ b/charts/consul/templates/crd-referencegrants.yaml @@ -1,6 +1,4 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - +{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: @@ -9,6 +7,12 @@ metadata: gateway.networking.k8s.io/bundle-version: v0.6.2 gateway.networking.k8s.io/channel: experimental creationTimestamp: null + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd name: referencegrants.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io @@ -201,3 +205,4 @@ status: plural: "" conditions: [] storedVersions: [] +{{- end }} diff --git a/charts/consul/templates/crd-samenessgroups.yaml b/charts/consul/templates/crd-samenessgroups.yaml index 7cc3b71ae1..c1d1c85a8e 100644 --- a/charts/consul/templates/crd-samenessgroups.yaml +++ b/charts/consul/templates/crd-samenessgroups.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: samenessgroups.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: SamenessGroup diff --git a/charts/consul/templates/crd-servicedefaults.yaml b/charts/consul/templates/crd-servicedefaults.yaml index e295732bfa..c926ece62a 100644 --- a/charts/consul/templates/crd-servicedefaults.yaml +++ b/charts/consul/templates/crd-servicedefaults.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: servicedefaults.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: ServiceDefaults diff --git a/charts/consul/templates/crd-serviceintentions.yaml b/charts/consul/templates/crd-serviceintentions.yaml index 5f849f65ba..335d2eff7a 100644 --- a/charts/consul/templates/crd-serviceintentions.yaml +++ b/charts/consul/templates/crd-serviceintentions.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: serviceintentions.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: ServiceIntentions diff --git a/charts/consul/templates/crd-serviceresolvers.yaml b/charts/consul/templates/crd-serviceresolvers.yaml index ef380f77b5..ed95c15846 100644 --- a/charts/consul/templates/crd-serviceresolvers.yaml +++ b/charts/consul/templates/crd-serviceresolvers.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: serviceresolvers.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: ServiceResolver diff --git a/charts/consul/templates/crd-servicerouters.yaml b/charts/consul/templates/crd-servicerouters.yaml index c5ba99466c..0157f646b4 100644 --- a/charts/consul/templates/crd-servicerouters.yaml +++ b/charts/consul/templates/crd-servicerouters.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: servicerouters.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: ServiceRouter diff --git a/charts/consul/templates/crd-servicesplitters.yaml b/charts/consul/templates/crd-servicesplitters.yaml index abe3ac85cc..18fb10341e 100644 --- a/charts/consul/templates/crd-servicesplitters.yaml +++ b/charts/consul/templates/crd-servicesplitters.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: servicesplitters.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: ServiceSplitter diff --git a/charts/consul/crds/tcproutes.gateway.networking.k8s.io.yaml.yml b/charts/consul/templates/crd-tcproutes.yaml similarity index 98% rename from charts/consul/crds/tcproutes.gateway.networking.k8s.io.yaml.yml rename to charts/consul/templates/crd-tcproutes.yaml index 906b442d31..b5bc7be13c 100644 --- a/charts/consul/crds/tcproutes.gateway.networking.k8s.io.yaml.yml +++ b/charts/consul/templates/crd-tcproutes.yaml @@ -1,6 +1,4 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - +{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: @@ -9,6 +7,12 @@ metadata: gateway.networking.k8s.io/bundle-version: v0.6.2 gateway.networking.k8s.io/channel: experimental creationTimestamp: null + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd name: tcproutes.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io @@ -274,3 +278,4 @@ status: plural: "" conditions: [] storedVersions: [] +{{- end }} diff --git a/charts/consul/templates/crd-terminatinggateways.yaml b/charts/consul/templates/crd-terminatinggateways.yaml index cd58d1679c..955496aeee 100644 --- a/charts/consul/templates/crd-terminatinggateways.yaml +++ b/charts/consul/templates/crd-terminatinggateways.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: terminatinggateways.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: TerminatingGateway diff --git a/charts/consul/crds/tlsroutes.gateway.networking.k8s.io.yaml.yml b/charts/consul/templates/crd-tlsroutes.yaml similarity index 99% rename from charts/consul/crds/tlsroutes.gateway.networking.k8s.io.yaml.yml rename to charts/consul/templates/crd-tlsroutes.yaml index 2e22b04ef0..1acd1b973a 100644 --- a/charts/consul/crds/tlsroutes.gateway.networking.k8s.io.yaml.yml +++ b/charts/consul/templates/crd-tlsroutes.yaml @@ -1,6 +1,4 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - +{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: @@ -9,6 +7,12 @@ metadata: gateway.networking.k8s.io/bundle-version: v0.6.2 gateway.networking.k8s.io/channel: experimental creationTimestamp: null + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd name: tlsroutes.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io @@ -284,3 +288,4 @@ status: plural: "" conditions: [] storedVersions: [] +{{- end }} diff --git a/charts/consul/crds/udproutes.gateway.networking.k8s.io.yaml.yml b/charts/consul/templates/crd-udproutes.yaml similarity index 98% rename from charts/consul/crds/udproutes.gateway.networking.k8s.io.yaml.yml rename to charts/consul/templates/crd-udproutes.yaml index 19b03dcd0b..0661b24c1a 100644 --- a/charts/consul/crds/udproutes.gateway.networking.k8s.io.yaml.yml +++ b/charts/consul/templates/crd-udproutes.yaml @@ -1,6 +1,4 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - +{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: @@ -9,6 +7,12 @@ metadata: gateway.networking.k8s.io/bundle-version: v0.6.2 gateway.networking.k8s.io/channel: experimental creationTimestamp: null + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd name: udproutes.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io @@ -274,3 +278,4 @@ status: plural: "" conditions: [] storedVersions: [] +{{- end }} diff --git a/charts/consul/templates/gateway-cleanup-podsecuritypolicy.yaml b/charts/consul/templates/gateway-cleanup-podsecuritypolicy.yaml index c4d4e8acca..ffbad130cc 100644 --- a/charts/consul/templates/gateway-cleanup-podsecuritypolicy.yaml +++ b/charts/consul/templates/gateway-cleanup-podsecuritypolicy.yaml @@ -1,5 +1,4 @@ -{{- if .Values.global.enablePodSecurityPolicies }} -{{- if .Values.connectInject.enabled }} +{{- if (and .Values.connectInject.enabled .Values.global.enablePodSecurityPolicies)}} apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: @@ -31,4 +30,3 @@ spec: rule: 'RunAsAny' readOnlyRootFilesystem: false {{- end }} -{{- end }} diff --git a/charts/consul/templates/gateway-gatewayclass.yaml b/charts/consul/templates/gateway-gatewayclass.yaml deleted file mode 100644 index a6856620d2..0000000000 --- a/charts/consul/templates/gateway-gatewayclass.yaml +++ /dev/null @@ -1,18 +0,0 @@ -{{- if .Values.connectInject.enabled }} -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: GatewayClass -metadata: - name: consul-api-gateway - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: api-gateway-controller -spec: - controllerName: "hashicorp.com/consul-api-gateway-controller" - parametersRef: - group: consul.hashicorp.com - kind: GatewayClassConfig - name: consul-api-gateway -{{- end }} diff --git a/charts/consul/templates/gateway-gatewayclassconfig.yaml b/charts/consul/templates/gateway-gatewayclassconfig.yaml deleted file mode 100644 index b812ebd814..0000000000 --- a/charts/consul/templates/gateway-gatewayclassconfig.yaml +++ /dev/null @@ -1,74 +0,0 @@ -{{- if .Values.connectInject.enabled }} ---- -apiVersion: consul.hashicorp.com/v1alpha1 -kind: GatewayClassConfig -metadata: - name: consul-api-gateway - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: api-gateway -spec: - {{- if .Values.apiGateway.enabled }} # Overide values from the old stanza. To be removed in 1.17 (t-eckert 2023-05-19) - - {{- if .Values.apiGateway.managedGatewayClass.deployment }} - deployment: - {{- if .Values.apiGateway.managedGatewayClass.deployment.defaultInstances }} - defaultInstances: {{ .Values.apiGateway.managedGatewayClass.deployment.defaultInstances }} - {{- end}} - {{- if .Values.apiGateway.managedGatewayClass.deployment.maxInstances }} - maxInstances: {{ .Values.apiGateway.managedGatewayClass.deployment.maxInstances }} - {{- end}} - {{- if .Values.apiGateway.managedGatewayClass.deployment.minInstances }} - minInstances: {{ .Values.apiGateway.managedGatewayClass.deployment.minInstances }} - {{- end}} - {{- end}} - {{- if .Values.apiGateway.managedGatewayClass.nodeSelector }} - nodeSelector: - {{ tpl .Values.apiGateway.managedGatewayClass.nodeSelector . | indent 4 | trim }} - {{- end }} - {{- if .Values.apiGateway.managedGatewayClass.tolerations }} - tolerations: - {{ tpl .Values.apiGateway.managedGatewayClass.tolerations . | indent 4 | trim }} - {{- end }} - {{- if .Values.apiGateway.managedGatewayClass.copyAnnotations.service }} - copyAnnotations: - service: - {{ tpl .Values.apiGateway.managedGatewayClass.copyAnnotations.service.annotations . | nindent 6 | trim }} - {{- end }} - serviceType: {{ .Values.apiGateway.managedGatewayClass.serviceType }} - - {{- else }} - - {{- if .Values.connectInject.apiGateway.managedGatewayClass.deployment }} - deployment: - {{- if .Values.connectInject.apiGateway.managedGatewayClass.deployment.defaultInstances }} - defaultInstances: {{ .Values.connectInject.apiGateway.managedGatewayClass.deployment.defaultInstances }} - {{- end}} - {{- if .Values.connectInject.apiGateway.managedGatewayClass.deployment.maxInstances }} - maxInstances: {{ .Values.connectInject.apiGateway.managedGatewayClass.deployment.maxInstances }} - {{- end}} - {{- if .Values.connectInject.apiGateway.managedGatewayClass.deployment.minInstances }} - minInstances: {{ .Values.connectInject.apiGateway.managedGatewayClass.deployment.minInstances }} - {{- end}} - {{- end}} - {{- if .Values.connectInject.apiGateway.managedGatewayClass.nodeSelector }} - nodeSelector: - {{ tpl .Values.connectInject.apiGateway.managedGatewayClass.nodeSelector . | indent 4 | trim }} - {{- end }} - {{- if .Values.connectInject.apiGateway.managedGatewayClass.tolerations }} - tolerations: - {{ tpl .Values.connectInject.apiGateway.managedGatewayClass.tolerations . | indent 4 | trim }} - {{- end }} - {{- if .Values.connectInject.apiGateway.managedGatewayClass.copyAnnotations.service }} - copyAnnotations: - service: - {{ tpl .Values.connectInject.apiGateway.managedGatewayClass.copyAnnotations.service.annotations . | nindent 6 | trim }} - {{- end }} - serviceType: {{ .Values.connectInject.apiGateway.managedGatewayClass.serviceType }} - - {{- end }} - -{{- end }} diff --git a/charts/consul/templates/gateway-resources-clusterrole.yaml b/charts/consul/templates/gateway-resources-clusterrole.yaml new file mode 100644 index 0000000000..c3bdfeb4a3 --- /dev/null +++ b/charts/consul/templates/gateway-resources-clusterrole.yaml @@ -0,0 +1,37 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "consul.fullname" . }}-gateway-resources + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gateway-resources +rules: + - apiGroups: + - consul.hashicorp.com + resources: + - gatewayclassconfigs + verbs: + - get + - update + - create + - apiGroups: + - gateway.networking.k8s.io + resources: + - gatewayclasses + verbs: + - get + - update + - create +{{- if .Values.global.enablePodSecurityPolicies }} + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" . }}-gateway-resources + verbs: + - use +{{- end }} +{{- end }} diff --git a/charts/consul/templates/gateway-resources-clusterrolebinding.yaml b/charts/consul/templates/gateway-resources-clusterrolebinding.yaml new file mode 100644 index 0000000000..921df23239 --- /dev/null +++ b/charts/consul/templates/gateway-resources-clusterrolebinding.yaml @@ -0,0 +1,20 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "consul.fullname" . }}-gateway-resources + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gateway-resources +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "consul.fullname" . }}-gateway-resources +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" . }}-gateway-resources + namespace: {{ .Release.Namespace }} +{{- end }} \ No newline at end of file diff --git a/charts/consul/templates/gateway-resources-job.yaml b/charts/consul/templates/gateway-resources-job.yaml new file mode 100644 index 0000000000..f8f92f799d --- /dev/null +++ b/charts/consul/templates/gateway-resources-job.yaml @@ -0,0 +1,112 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "consul.fullname" . }}-gateway-resources + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gateway-resources + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-weight": "0" + "helm.sh/hook-delete-policy": hook-succeeded +spec: + template: + metadata: + name: {{ template "consul.fullname" . }}-gateway-resources + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: gateway-resources + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} + annotations: + "consul.hashicorp.com/connect-inject": "false" + spec: + restartPolicy: Never + serviceAccountName: {{ template "consul.fullname" . }}-gateway-resources + containers: + - name: gateway-resources + image: {{ .Values.global.imageK8S }} + command: + - consul-k8s-control-plane + args: + - gateway-resources + - -gateway-class-name=consul-api-gateway + - -gateway-class-config-name=consul-api-gateway + - -controller-name=hashicorp.com/consul-api-gateway-controller + - -app={{template "consul.name" .}} + - -chart={{template "consul.chart" .}} + - -heritage={{ .Release.Service }} + - -release-name={{ .Release.Name }} + - -component=api-gateway + {{- if .Values.apiGateway.enabled }} # Overide values from the old stanza. To be removed in 1.17 (t-eckert 2023-05-19) + {{- if .Values.apiGateway.managedGatewayClass.deployment }} + {{- if .Values.apiGateway.managedGatewayClass.deployment.defaultInstances }} + - -deployment-default-instances={{ .Values.apiGateway.managedGatewayClass.deployment.defaultInstances }} + {{- end}} + {{- if .Values.apiGateway.managedGatewayClass.deployment.maxInstances }} + - -deployment-max-instances={{ .Values.apiGateway.managedGatewayClass.deployment.maxInstances }} + {{- end}} + {{- if .Values.apiGateway.managedGatewayClass.deployment.minInstances }} + - -deployment-min-instances={{ .Values.apiGateway.managedGatewayClass.deployment.minInstances }} + {{- end}} + {{- end}} + {{- if .Values.apiGateway.managedGatewayClass.nodeSelector }} + - -node-selector={{ .Values.apiGateway.managedGatewayClass.nodeSelector }} + {{- end }} + {{- if .Values.apiGateway.managedGatewayClass.tolerations }} + - -tolerations={{ .Values.apiGateway.managedGatewayClass.tolerations }} + {{- end }} + {{- if .Values.apiGateway.managedGatewayClass.copyAnnotations.service }} + - -service-annotations={{ .Values.apiGateway.managedGatewayClass.copyAnnotations.service.annotations }} + {{- end }} + - -service-type={{ .Values.apiGateway.managedGatewayClass.serviceType }} + {{- else }} # the new stanza + {{- if .Values.connectInject.apiGateway.managedGatewayClass.deployment }} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.deployment.defaultInstances }} + - -deployment-default-instances={{ .Values.connectInject.apiGateway.managedGatewayClass.deployment.defaultInstances }} + {{- end}} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.deployment.maxInstances }} + - -deployment-max-instances={{ .Values.connectInject.apiGateway.managedGatewayClass.deployment.maxInstances }} + {{- end}} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.deployment.minInstances }} + - -deployment-min-instances={{ .Values.connectInject.apiGateway.managedGatewayClass.deployment.minInstances }} + {{- end}} + {{- end}} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.nodeSelector }} + - -node-selector={{ .Values.connectInject.apiGateway.managedGatewayClass.nodeSelector }} + {{- end }} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.tolerations }} + - -tolerations={{ .Values.connectInject.apiGateway.managedGatewayClass.tolerations }} + {{- end }} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.copyAnnotations.service }} + - -service-annotations={{ .Values.connectInject.apiGateway.managedGatewayClass.copyAnnotations.service.annotations }} + {{- end }} + - -service-type={{ .Values.connectInject.apiGateway.managedGatewayClass.serviceType }} + {{- end}} + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" + {{- if .Values.global.acls.tolerations }} + tolerations: + {{ tpl .Values.global.acls.tolerations . | indent 8 | trim }} + {{- end }} + {{- if .Values.global.acls.nodeSelector }} + nodeSelector: + {{ tpl .Values.global.acls.nodeSelector . | indent 8 | trim }} + {{- end }} +{{- end }} diff --git a/charts/consul/templates/gateway-resources-podsecuritypolicy.yaml b/charts/consul/templates/gateway-resources-podsecuritypolicy.yaml new file mode 100644 index 0000000000..da5299194c --- /dev/null +++ b/charts/consul/templates/gateway-resources-podsecuritypolicy.yaml @@ -0,0 +1,32 @@ +{{- if (and .Values.global.enablePodSecurityPolicies .Values.connectInject.enabled)}} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-gateway-resources + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gateway-resources +spec: + privileged: false + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/consul/templates/gateway-resources-serviceaccount.yaml b/charts/consul/templates/gateway-resources-serviceaccount.yaml new file mode 100644 index 0000000000..4611dc38e1 --- /dev/null +++ b/charts/consul/templates/gateway-resources-serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-gateway-resources + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gateway-resources +{{- end }} diff --git a/charts/consul/test/unit/crd-exportedservices.bats b/charts/consul/test/unit/crd-exportedservices.bats index 1b8f4430b5..235fe6bd24 100644 --- a/charts/consul/test/unit/crd-exportedservices.bats +++ b/charts/consul/test/unit/crd-exportedservices.bats @@ -7,7 +7,7 @@ load _helpers local actual=$(helm template \ -s templates/crd-exportedservices.yaml \ . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) + yq -s 'length > 0' | tee /dev/stderr) [ "${actual}" = "true" ] } diff --git a/charts/consul/test/unit/crd-gatewayclassconfigs.bats b/charts/consul/test/unit/crd-gatewayclassconfigs.bats new file mode 100644 index 0000000000..0228110b6b --- /dev/null +++ b/charts/consul/test/unit/crd-gatewayclassconfigs.bats @@ -0,0 +1,20 @@ +#!/usr/bin/env bats + +load _helpers + +@test "gatewayclassconfigs/CustomResourceDefinition: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/crd-gatewayclassconfigs.yaml \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "$actual" = "true" ] +} + +@test "gatewayclassconfigs/CustomResourceDefinition: disabled with connectInject.enabled=false" { + cd `chart_dir` + assert_empty helm template \ + -s templates/crd-gatewayclassconfigs.yaml \ + --set 'connectInject.enabled=false' \ + . +} diff --git a/charts/consul/test/unit/crd-gatewayclasses.bats b/charts/consul/test/unit/crd-gatewayclasses.bats new file mode 100644 index 0000000000..8400590606 --- /dev/null +++ b/charts/consul/test/unit/crd-gatewayclasses.bats @@ -0,0 +1,28 @@ +#!/usr/bin/env bats + +load _helpers + +@test "gatewayclasses/CustomResourceDefinition: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/crd-gatewayclasses.yaml \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "$actual" = "true" ] +} + +@test "gatewayclasses/CustomResourceDefinition: disabled with connectInject.enabled=false" { + cd `chart_dir` + assert_empty helm template \ + -s templates/crd-gatewayclasses.yaml \ + --set 'connectInject.enabled=false' \ + . +} + +@test "gatewayclasses/CustomResourceDefinition: disabled with connectInject.apiGateway.manageExternalCRDs=false" { + cd `chart_dir` + assert_empty helm template \ + -s templates/crd-gatewayclasses.yaml \ + --set 'connectInject.apiGateway.manageExternalCRDs=false' \ + . +} diff --git a/charts/consul/test/unit/crd-gateways.bats b/charts/consul/test/unit/crd-gateways.bats new file mode 100644 index 0000000000..8a7f0284e2 --- /dev/null +++ b/charts/consul/test/unit/crd-gateways.bats @@ -0,0 +1,28 @@ +#!/usr/bin/env bats + +load _helpers + +@test "gateways/CustomResourceDefinition: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/crd-gateways.yaml \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "$actual" = "true" ] +} + +@test "gateways/CustomResourceDefinition: disabled with connectInject.enabled=false" { + cd `chart_dir` + assert_empty helm template \ + -s templates/crd-gateways.yaml \ + --set 'connectInject.enabled=false' \ + . +} + +@test "gateways/CustomResourceDefinition: disabled with connectInject.apiGateway.manageExternalCRDs=false" { + cd `chart_dir` + assert_empty helm template \ + -s templates/crd-gateways.yaml \ + --set 'connectInject.apiGateway.manageExternalCRDs=false' \ + . +} diff --git a/charts/consul/test/unit/crd-grpcroutes.bats b/charts/consul/test/unit/crd-grpcroutes.bats new file mode 100644 index 0000000000..d5e3e298d7 --- /dev/null +++ b/charts/consul/test/unit/crd-grpcroutes.bats @@ -0,0 +1,28 @@ +#!/usr/bin/env bats + +load _helpers + +@test "grpcroutes/CustomResourceDefinition: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/crd-grpcroutes.yaml \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "$actual" = "true" ] +} + +@test "grpcroutes/CustomResourceDefinition: disabled with connectInject.enabled=false" { + cd `chart_dir` + assert_empty helm template \ + -s templates/crd-grpcroutes.yaml \ + --set 'connectInject.enabled=false' \ + . +} + +@test "grpcroutes/CustomResourceDefinition: disabled with connectInject.apiGateway.manageExternalCRDs=false" { + cd `chart_dir` + assert_empty helm template \ + -s templates/crd-grpcroutes.yaml \ + --set 'connectInject.apiGateway.manageExternalCRDs=false' \ + . +} diff --git a/charts/consul/test/unit/crd-httproutes.bats b/charts/consul/test/unit/crd-httproutes.bats new file mode 100644 index 0000000000..99c58e0492 --- /dev/null +++ b/charts/consul/test/unit/crd-httproutes.bats @@ -0,0 +1,28 @@ +#!/usr/bin/env bats + +load _helpers + +@test "httproutes/CustomResourceDefinition: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/crd-httproutes.yaml \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "$actual" = "true" ] +} + +@test "httproutes/CustomResourceDefinition: disabled with connectInject.enabled=false" { + cd `chart_dir` + assert_empty helm template \ + -s templates/crd-httproutes.yaml \ + --set 'connectInject.enabled=false' \ + . +} + +@test "httproutes/CustomResourceDefinition: disabled with connectInject.apiGateway.manageExternalCRDs=false" { + cd `chart_dir` + assert_empty helm template \ + -s templates/crd-httproutes.yaml \ + --set 'connectInject.apiGateway.manageExternalCRDs=false' \ + . +} diff --git a/charts/consul/test/unit/crd-meshservices.bats b/charts/consul/test/unit/crd-meshservices.bats new file mode 100644 index 0000000000..c1ee806ad4 --- /dev/null +++ b/charts/consul/test/unit/crd-meshservices.bats @@ -0,0 +1,21 @@ +#!/usr/bin/env bats + +load _helpers + +@test "meshservices/CustomResourceDefinition: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/crd-meshservices.yaml \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "$actual" = "true" ] +} + +@test "meshservices/CustomResourceDefinition: disabled with connectInject.enabled=false" { + cd `chart_dir` + assert_empty helm template \ + -s templates/crd-meshservices.yaml \ + --set 'connectInject.enabled=false' \ + . +} + diff --git a/charts/consul/test/unit/crd-tcproutes.bats b/charts/consul/test/unit/crd-tcproutes.bats new file mode 100644 index 0000000000..6916efdc6f --- /dev/null +++ b/charts/consul/test/unit/crd-tcproutes.bats @@ -0,0 +1,28 @@ +#!/usr/bin/env bats + +load _helpers + +@test "tcproutes/CustomResourceDefinition: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/crd-tcproutes.yaml \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "$actual" = "true" ] +} + +@test "tcproutes/CustomResourceDefinition: disabled with connectInject.enabled=false" { + cd `chart_dir` + assert_empty helm template \ + -s templates/crd-tcproutes.yaml \ + --set 'connectInject.enabled=false' \ + . +} + +@test "tcproutes/CustomResourceDefinition: disabled with connectInject.apiGateway.manageExternalCRDs=false" { + cd `chart_dir` + assert_empty helm template \ + -s templates/crd-tcproutes.yaml \ + --set 'connectInject.apiGateway.manageExternalCRDs=false' \ + . +} diff --git a/charts/consul/test/unit/crd-tlsroutes.bats b/charts/consul/test/unit/crd-tlsroutes.bats new file mode 100644 index 0000000000..7e1d5c471f --- /dev/null +++ b/charts/consul/test/unit/crd-tlsroutes.bats @@ -0,0 +1,28 @@ +#!/usr/bin/env bats + +load _helpers + +@test "tlsroutes/CustomResourceDefinition: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/crd-tlsroutes.yaml \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "$actual" = "true" ] +} + +@test "tlsroutes/CustomResourceDefinition: disabled with connectInject.enabled=false" { + cd `chart_dir` + assert_empty helm template \ + -s templates/crd-tlsroutes.yaml \ + --set 'connectInject.enabled=false' \ + . +} + +@test "tlsroutes/CustomResourceDefinition: disabled with connectInject.apiGateway.manageExternalCRDs=false" { + cd `chart_dir` + assert_empty helm template \ + -s templates/crd-tlsroutes.yaml \ + --set 'connectInject.apiGateway.manageExternalCRDs=false' \ + . +} diff --git a/charts/consul/test/unit/crd-udproutes.bats b/charts/consul/test/unit/crd-udproutes.bats new file mode 100644 index 0000000000..407592a770 --- /dev/null +++ b/charts/consul/test/unit/crd-udproutes.bats @@ -0,0 +1,28 @@ +#!/usr/bin/env bats + +load _helpers + +@test "udproutes/CustomResourceDefinition: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/crd-udproutes.yaml \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "$actual" = "true" ] +} + +@test "udproutes/CustomResourceDefinition: disabled with connectInject.enabled=false" { + cd `chart_dir` + assert_empty helm template \ + -s templates/crd-udproutes.yaml \ + --set 'connectInject.enabled=false' \ + . +} + +@test "udproutes/CustomResourceDefinition: disabled with connectInject.apiGateway.manageExternalCRDs=false" { + cd `chart_dir` + assert_empty helm template \ + -s templates/crd-udproutes.yaml \ + --set 'connectInject.apiGateway.manageExternalCRDs=false' \ + . +} diff --git a/charts/consul/test/unit/gateway-cleanup-clusterrole.bats b/charts/consul/test/unit/gateway-cleanup-clusterrole.bats new file mode 100644 index 0000000000..c672ac5593 --- /dev/null +++ b/charts/consul/test/unit/gateway-cleanup-clusterrole.bats @@ -0,0 +1,33 @@ +#!/usr/bin/env bats + +load _helpers + +target=templates/gateway-cleanup-clusterrole.yaml + +@test "gatewaycleanup/ClusterRole: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + -s $target \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "$actual" = "true" ] +} + +@test "gatewaycleanup/ClusterRole: disabled with connectInject.enabled=false" { + cd `chart_dir` + assert_empty helm template \ + -s $target \ + --set 'connectInject.enabled=false' \ + . +} + +@test "gatewaycleanup/ClusterRole: can use podsecuritypolicies with global.enablePodSecurityPolicy=true" { + cd `chart_dir` + local actual=$(helm template \ + -s $target \ + --set "global.enablePodSecurityPolicies=true" \ + . | tee /dev/stderr | + yq '.rules[] | select((.resources[0] == "podsecuritypolicies") and (.verbs[0] == "use")) | length > 0' | tee /dev/stderr) + [ "$actual" = "true" ] +} + diff --git a/charts/consul/test/unit/gateway-cleanup-clusterrolebinding.bats b/charts/consul/test/unit/gateway-cleanup-clusterrolebinding.bats new file mode 100644 index 0000000000..a6e4af5d2c --- /dev/null +++ b/charts/consul/test/unit/gateway-cleanup-clusterrolebinding.bats @@ -0,0 +1,23 @@ +#!/usr/bin/env bats + +load _helpers + +target=templates/gateway-cleanup-clusterrolebinding.yaml + +@test "gatewaycleanup/ClusterRoleBinding: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + -s $target \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "$actual" = "true" ] +} + +@test "gatewaycleanup/ClusterRoleBinding: disabled with connectInject.enabled=false" { + cd `chart_dir` + assert_empty helm template \ + -s $target \ + --set 'connectInject.enabled=false' \ + . +} + diff --git a/charts/consul/test/unit/gateway-cleanup-job.bats b/charts/consul/test/unit/gateway-cleanup-job.bats new file mode 100644 index 0000000000..ff59768c75 --- /dev/null +++ b/charts/consul/test/unit/gateway-cleanup-job.bats @@ -0,0 +1,23 @@ +#!/usr/bin/env bats + +load _helpers + +target=templates/gateway-cleanup-job.yaml + +@test "gatewaycleanup/Job: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + -s $target \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "$actual" = "true" ] +} + +@test "gatewaycleanup/Job: disabled with connectInject.enabled=false" { + cd `chart_dir` + assert_empty helm template \ + -s $target \ + --set 'connectInject.enabled=false' \ + . +} + diff --git a/charts/consul/test/unit/gateway-cleanup-podsecuritypolicy.bats b/charts/consul/test/unit/gateway-cleanup-podsecuritypolicy.bats new file mode 100644 index 0000000000..66974da2fd --- /dev/null +++ b/charts/consul/test/unit/gateway-cleanup-podsecuritypolicy.bats @@ -0,0 +1,41 @@ +#!/usr/bin/env bats + +load _helpers + +target=templates/gateway-cleanup-podsecuritypolicy.yaml + +@test "gatewaycleanup/PodSecurityPolicy: disabled by default" { + cd `chart_dir` + assert_empty helm template \ + -s $target \ + --set 'connectInject.enabled=false' \ + . +} + +@test "gatewaycleanup/PodSecurityPolicy: disabled with connectInject.enabled=false" { + cd `chart_dir` + assert_empty helm template \ + -s $target \ + --set 'connectInject.enabled=false' \ + . +} + +@test "gatewaycleanup/PodSecurityPolicy: disabled with global.enablePodSecurityPolicies=false" { + cd `chart_dir` + assert_empty helm template \ + -s $target \ + --set 'global.enablePodSecurityPolicies=false' \ + . +} + + +@test "gatewaycleanup/PodSecurityPolicy: enabled with connectInject.enabled=true and global.enablePodSecurityPolicies=true" { + cd `chart_dir` + local actual=$(helm template \ + -s $target \ + --set 'connectInject.enabled=true' \ + --set 'global.enablePodSecurityPolicies=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "$actual" = "true" ] +} diff --git a/charts/consul/test/unit/gateway-cleanup-serviceaccount.bats b/charts/consul/test/unit/gateway-cleanup-serviceaccount.bats new file mode 100644 index 0000000000..50d01b99e9 --- /dev/null +++ b/charts/consul/test/unit/gateway-cleanup-serviceaccount.bats @@ -0,0 +1,23 @@ +#!/usr/bin/env bats + +load _helpers + +target=templates/gateway-cleanup-serviceaccount.yaml + +@test "gatewaycleanup/ServiceAccount: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + -s $target \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "$actual" = "true" ] +} + +@test "gatewaycleanup/ServiceAccount: disabled with connectInject.enabled=false" { + cd `chart_dir` + assert_empty helm template \ + -s $target \ + --set 'connectInject.enabled=false' \ + . +} + diff --git a/charts/consul/test/unit/gateway-gatewayclass.bats b/charts/consul/test/unit/gateway-gatewayclass.bats deleted file mode 100755 index ac8a53aed9..0000000000 --- a/charts/consul/test/unit/gateway-gatewayclass.bats +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "apiGateway/GatewayClass: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/gateway-gatewayclass.yaml \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "apiGateway/GatewayClass: disabled with connectInject.enabled false" { - cd `chart_dir` - assert_empty helm template \ - -s templates/gateway-gatewayclass.yaml \ - --set 'connectInject.enabled=false' \ - . -} - diff --git a/charts/consul/test/unit/gateway-gatewayclassconfig.bats b/charts/consul/test/unit/gateway-gatewayclassconfig.bats deleted file mode 100644 index 30aa972cbe..0000000000 --- a/charts/consul/test/unit/gateway-gatewayclassconfig.bats +++ /dev/null @@ -1,117 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "apiGateway/GatewayClassConfig: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/gateway-gatewayclassconfig.yaml \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "apiGateway/GatewayClassConfig: disabled with connectInject.enabled" { - cd `chart_dir` - assert_empty helm template \ - -s templates/gateway-gatewayclassconfig.yaml \ - --set 'connectInject.enabled=false' \ - . -} - -#-------------------------------------------------------------------- -# fallback configuration -# to be removed in 1.17 (t-eckert 2023-05-23) - -@test "apiGateway/GatewayClassConfig: fallback configuration is used when apiGateway.enabled is true" { - cd `chart_dir` - local spec=$(helm template \ - -s templates/gateway-gatewayclassconfig.yaml \ - --set 'apiGateway.enabled=true' \ - --set 'apiGateway.image=testing' \ - --set 'apiGateway.managedGatewayClass.nodeSelector=foo: bar' \ - --set 'apiGateway.managedGatewayClass.tolerations=- key: bar' \ - --set 'apiGateway.managedGatewayClass.copyAnnotations.service.annotations=- bingo' \ - --set 'apiGateway.managedGatewayClass.serviceType=LoadBalancer' \ - . | tee /dev/stderr | - yq '.spec' | tee /dev/stderr) - - local actual=$(echo "$spec" | - jq -r '.nodeSelector.foo') - [ "${actual}" = "bar" ] - - local actual=$(echo "$spec" | - jq -r '.tolerations[0].key') - [ "${actual}" = "bar" ] - - local actual=$(echo "$spec" | - jq -r '.copyAnnotations.service[0]') - [ "${actual}" = "bingo" ] - - local actual=$(echo "$spec" | - jq -r '.serviceType') - [ "${actual}" = "LoadBalancer" ] -} - -#-------------------------------------------------------------------- -# configuration - -@test "apiGateway/GatewayClassConfig: default configuration" { - cd `chart_dir` - local spec=$(helm template \ - -s templates/gateway-gatewayclassconfig.yaml \ - . | tee /dev/stderr | - yq '.spec' | tee /dev/stderr) - - local actual=$(echo "$spec" | - jq -r '.deployment.defaultInstances') - [ "${actual}" = 1 ] - - local actual=$(echo "$spec" | - jq -r '.deployment.maxInstances') - [ "${actual}" = 1 ] - - local actual=$(echo "$spec" | - jq -r '.deployment.minInstances') - [ "${actual}" = 1 ] -} - -@test "apigateway/gatewayclassconfig: custom configuration" { - cd `chart_dir` - local spec=$(helm template \ - -s templates/gateway-gatewayclassconfig.yaml \ - --set 'connectInject.apiGateway.managedGatewayClass.nodeSelector=foo: bar' \ - --set 'connectInject.apiGateway.managedGatewayClass.tolerations=- key: bar' \ - --set 'connectInject.apiGateway.managedGatewayClass.copyAnnotations.service.annotations=- bingo' \ - --set 'connectInject.apiGateway.managedGatewayClass.serviceType=LoadBalancer' \ - . | tee /dev/stderr | - yq '.spec' | tee /dev/stderr) - - local actual=$(echo "$spec" | - jq -r '.deployment.defaultInstances') - [ "${actual}" = "1" ] - - local actual=$(echo "$spec" | - jq -r '.deployment.maxInstances') - [ "${actual}" = "1" ] - - local actual=$(echo "$spec" | - jq -r '.deployment.minInstances') - [ "${actual}" = "1" ] - - local actual=$(echo "$spec" | - jq -r '.nodeSelector.foo') - [ "${actual}" = "bar" ] - - local actual=$(echo "$spec" | - jq -r '.tolerations[0].key') - [ "${actual}" = "bar" ] - - local actual=$(echo "$spec" | - jq -r '.copyAnnotations.service[0]') - [ "${actual}" = "bingo" ] - - local actual=$(echo "$spec" | - jq -r '.serviceType') - [ "${actual}" = "LoadBalancer" ] -} diff --git a/charts/consul/test/unit/gateway-resources-clusterrole.bats b/charts/consul/test/unit/gateway-resources-clusterrole.bats new file mode 100644 index 0000000000..152209a1b5 --- /dev/null +++ b/charts/consul/test/unit/gateway-resources-clusterrole.bats @@ -0,0 +1,33 @@ +#!/usr/bin/env bats + +load _helpers + +target=templates/gateway-resources-clusterrole.yaml + +@test "gatewayresources/ClusterRole: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + -s $target \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "$actual" = "true" ] +} + +@test "gatewayresources/ClusterRole: disabled with connectInject.enabled=false" { + cd `chart_dir` + assert_empty helm template \ + -s $target \ + --set 'connectInject.enabled=false' \ + . +} + +@test "gatewayresources/ClusterRole: can use podsecuritypolicies with global.enablePodSecurityPolicy=true" { + cd `chart_dir` + local actual=$(helm template \ + -s $target \ + --set "global.enablePodSecurityPolicies=true" \ + . | tee /dev/stderr | + yq '.rules[] | select((.resources[0] == "podsecuritypolicies") and (.verbs[0] == "use")) | length > 0' | tee /dev/stderr) + [ "$actual" = "true" ] +} + diff --git a/charts/consul/test/unit/gateway-resources-clusterrolebinding.bats b/charts/consul/test/unit/gateway-resources-clusterrolebinding.bats new file mode 100644 index 0000000000..efc1429e20 --- /dev/null +++ b/charts/consul/test/unit/gateway-resources-clusterrolebinding.bats @@ -0,0 +1,23 @@ +#!/usr/bin/env bats + +load _helpers + +target=templates/gateway-resources-clusterrolebinding.yaml + +@test "gatewayresources/ClusterRoleBinding: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + -s $target \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "$actual" = "true" ] +} + +@test "gatewayresources/ClusterRoleBinding: disabled with connectInject.enabled=false" { + cd `chart_dir` + assert_empty helm template \ + -s $target \ + --set 'connectInject.enabled=false' \ + . +} + diff --git a/charts/consul/test/unit/gateway-resources-job.bats b/charts/consul/test/unit/gateway-resources-job.bats new file mode 100644 index 0000000000..0d3bfa2e4d --- /dev/null +++ b/charts/consul/test/unit/gateway-resources-job.bats @@ -0,0 +1,118 @@ +#!/usr/bin/env bats + +load _helpers + +target=templates/gateway-resources-job.yaml + +@test "gatewayresources/Job: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + -s $target \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "$actual" = "true" ] +} + +@test "gatewayresources/Job: disabled with connectInject.enabled=false" { + cd `chart_dir` + assert_empty helm template \ + -s $target \ + --set 'connectInject.enabled=false' \ + . +} + +@test "gatewayresources/Job: imageK8S set properly" { + cd `chart_dir` + local actual=$(helm template \ + -s $target \ + --set 'global.imageK8S=foo' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].image == "foo"' | tee /dev/stderr) + [ "$actual" = "true" ] +} + +#-------------------------------------------------------------------- +# fallback configuration +# to be removed in 1.17 (t-eckert 2023-05-23) + +@test "gatewayresources/Job: fallback configuration is used when apiGateway.enabled is true" { + cd `chart_dir` + local spec=$(helm template \ + -s $target \ + --set 'apiGateway.enabled=true' \ + --set 'apiGateway.image=testing' \ + --set 'apiGateway.managedGatewayClass.nodeSelector=foo: bar' \ + --set 'apiGateway.managedGatewayClass.tolerations=- key: bar' \ + --set 'apiGateway.managedGatewayClass.copyAnnotations.service.annotations=- bingo' \ + --set 'apiGateway.managedGatewayClass.serviceType=LoadBalancer' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].args' | tee /dev/stderr) + + local actual=$(echo "$spec" | jq '.[9] | ."-node-selector=foo"') + [ "${actual}" = "\"bar\"" ] + + local actual=$(echo "$spec" | jq '.[10] | ."-tolerations=- key"') + [ "${actual}" = "\"bar\"" ] + + local actual=$(echo "$spec" | jq '.[11]') + [ "${actual}" = "\"-service-annotations=- bingo\"" ] +} + +#-------------------------------------------------------------------- +# configuration + +@test "gatewayresources/Job: default configuration" { + cd `chart_dir` + local spec=$(helm template \ + -s $target \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].args' | tee /dev/stderr) + + local actual=$(echo "$spec" | jq 'any(index("-deployment-default-instances=1"))') + [ "${actual}" = "true" ] + + local actual=$(echo "$spec" | jq 'any(index("-deployment-max-instances=1"))') + [ "${actual}" = "true" ] + + local actual=$(echo "$spec" | jq 'any(index("-deployment-min-instances=1"))') + [ "${actual}" = "true" ] + + local actual=$(echo "$spec" | jq 'any(index("-service-type=LoadBalancer"))') + [ "${actual}" = "true" ] +} + +@test "apiGateway/GatewayClassConfig: custom configuration" { + cd `chart_dir` + local spec=$(helm template \ + -s $target \ + --set 'connectInject.apiGateway.managedGatewayClass.deployment.defaultInstances=2' \ + --set 'connectInject.apiGateway.managedGatewayClass.deployment.minInstances=1' \ + --set 'connectInject.apiGateway.managedGatewayClass.deployment.maxInstances=3' \ + --set 'connectInject.apiGateway.managedGatewayClass.nodeSelector=foo: bar' \ + --set 'connectInject.apiGateway.managedGatewayClass.tolerations=- key: bar' \ + --set 'connectInject.apiGateway.managedGatewayClass.copyAnnotations.service.annotations=- bingo' \ + --set 'connectInject.apiGateway.managedGatewayClass.serviceType=Foo' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].args' | tee /dev/stderr) + + local actual=$(echo "$spec" | jq 'any(index("-deployment-default-instances=2"))') + [ "${actual}" = "true" ] + + local actual=$(echo "$spec" | jq 'any(index("-deployment-max-instances=3"))') + [ "${actual}" = "true" ] + + local actual=$(echo "$spec" | jq 'any(index("-deployment-min-instances=1"))') + [ "${actual}" = "true" ] + + local actual=$(echo "$spec" | jq 'any(index("-service-type=Foo"))') + [ "${actual}" = "true" ] + + local actual=$(echo "$spec" | jq '.[12] | ."-node-selector=foo"') + [ "${actual}" = "\"bar\"" ] + + local actual=$(echo "$spec" | jq '.[13] | ."-tolerations=- key"') + [ "${actual}" = "\"bar\"" ] + + local actual=$(echo "$spec" | jq '.[14]') + [ "${actual}" = "\"-service-annotations=- bingo\"" ] +} diff --git a/charts/consul/test/unit/gateway-resources-podsecuritypolicy.bats b/charts/consul/test/unit/gateway-resources-podsecuritypolicy.bats new file mode 100644 index 0000000000..81818c525a --- /dev/null +++ b/charts/consul/test/unit/gateway-resources-podsecuritypolicy.bats @@ -0,0 +1,41 @@ +#!/usr/bin/env bats + +load _helpers + +target=templates/gateway-resources-podsecuritypolicy.yaml + +@test "gatewayresources/PodSecurityPolicy: disabled by default" { + cd `chart_dir` + assert_empty helm template \ + -s $target \ + --set 'connectInject.enabled=false' \ + . +} + +@test "gatewayresources/PodSecurityPolicy: disabled with connectInject.enabled=false" { + cd `chart_dir` + assert_empty helm template \ + -s $target \ + --set 'connectInject.enabled=false' \ + . +} + +@test "gatewayresources/PodSecurityPolicy: disabled with global.enablePodSecurityPolicies=false" { + cd `chart_dir` + assert_empty helm template \ + -s $target \ + --set 'global.enablePodSecurityPolicies=false' \ + . +} + + +@test "gatewayresources/PodSecurityPolicy: enabled with connectInject.enabled=true and global.enablePodSecurityPolicies=true" { + cd `chart_dir` + local actual=$(helm template \ + -s $target \ + --set 'connectInject.enabled=true' \ + --set 'global.enablePodSecurityPolicies=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "$actual" = "true" ] +} diff --git a/charts/consul/test/unit/gateway-resources-serviceaccount.bats b/charts/consul/test/unit/gateway-resources-serviceaccount.bats new file mode 100644 index 0000000000..90011e226b --- /dev/null +++ b/charts/consul/test/unit/gateway-resources-serviceaccount.bats @@ -0,0 +1,23 @@ +#!/usr/bin/env bats + +load _helpers + +target=templates/gateway-resources-serviceaccount.yaml + +@test "gatewayresources/ServiceAccount: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + -s $target \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "$actual" = "true" ] +} + +@test "gatewayresources/ServiceAccount: disabled with connectInject.enabled=false" { + cd `chart_dir` + assert_empty helm template \ + -s $target \ + --set 'connectInject.enabled=false' \ + . +} + diff --git a/charts/consul/todo.txt b/charts/consul/todo.txt new file mode 100644 index 0000000000..c79bef389b --- /dev/null +++ b/charts/consul/todo.txt @@ -0,0 +1,3 @@ + +- [x] Remove gatewayclass gatewayclassconfig bats +- [ ] Add test for each of the CRDs diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 9f004cab30..c6c225b3d0 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -2006,6 +2006,12 @@ connectInject: # Configuration settings for the Consul API Gateway integration. apiGateway: + # Enables Consul on Kubernetes to manage the CRDs used for Gateway API. + # Setting this to true will install the CRDs used for the Gateway API when Consul on Kubernetes is installed. + # These CRDs can clash with existing Gateway API CRDs if they are already installed in your cluster. + # If this setting is false, you will need to install the Gateway API CRDs manually. + manageExternalCRDs: true + # Configuration settings for the GatewayClass installed by Consul on Kubernetes. managedGatewayClass: # This value defines [`nodeSelector`](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) diff --git a/charts/demo/crds/blank b/charts/demo/crds/blank deleted file mode 100644 index a6560dc716..0000000000 --- a/charts/demo/crds/blank +++ /dev/null @@ -1,4 +0,0 @@ -This is here purely so we can embed it so the HelmDeployment that references does not fail. Otherwise, at the time of writing, we get: - -==> Installing Consul demo application - ! open demo/crds: file does not exist \ No newline at end of file diff --git a/charts/embed_chart.go b/charts/embed_chart.go index 72650e8819..8e36abba23 100644 --- a/charts/embed_chart.go +++ b/charts/embed_chart.go @@ -16,8 +16,8 @@ import "embed" // The embed directive does not include files with underscores unless explicitly listed, which is why _helpers.tpl is // explicitly embedded. -//go:embed consul/Chart.yaml consul/values.yaml consul/crds consul/templates consul/templates/_helpers.tpl +//go:embed consul/Chart.yaml consul/values.yaml consul/templates consul/templates/_helpers.tpl var ConsulHelmChart embed.FS -//go:embed demo/Chart.yaml demo/values.yaml demo/crds/blank demo/templates +//go:embed demo/Chart.yaml demo/values.yaml demo/templates var DemoHelmChart embed.FS diff --git a/cli/helm/chart.go b/cli/helm/chart.go index 3bb9484487..c57d9220bc 100644 --- a/cli/helm/chart.go +++ b/cli/helm/chart.go @@ -18,7 +18,6 @@ const ( chartFileName = "Chart.yaml" valuesFileName = "values.yaml" templatesDirName = "templates" - crdDirName = "crds" ) // LoadChart will attempt to load a Helm chart from the embedded file system. @@ -94,25 +93,6 @@ func readChartFiles(chart embed.FS, chartDirName string) ([]*loader.BufferedFile chartFiles = append(chartFiles, file) } - // Load everything under crds/. - dirs, err = chart.ReadDir(path.Join(chartDirName, crdDirName)) - if err != nil { - return nil, err - } - - for _, f := range dirs { - if f.IsDir() || f.Name() == "kustomization.yaml" { - // We only need to include files in the crds directory. - continue - } - - file, err := readFile(chart, path.Join(chartDirName, crdDirName, f.Name()), chartDirName) - if err != nil { - return nil, err - } - chartFiles = append(chartFiles, file) - } - return chartFiles, nil } diff --git a/cli/helm/chart_test.go b/cli/helm/chart_test.go index 7fe6cf50a2..f0e33e092b 100644 --- a/cli/helm/chart_test.go +++ b/cli/helm/chart_test.go @@ -42,7 +42,6 @@ func TestReadChartFiles(t *testing.T) { "values.yaml": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\n# This is a mock Helm values.yaml file used for testing.\nkey: value", "templates/_helpers.tpl": "helpers", "templates/foo.yaml": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\nfoo: bar\n", - "crds/foo.yaml": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\nfoo: bar\n", } files, err := readChartFiles(testChartFiles, directory) diff --git a/cli/helm/test_fixtures/consul/crds/foo.yaml b/cli/helm/test_fixtures/consul/crds/foo.yaml deleted file mode 100644 index b17972509f..0000000000 --- a/cli/helm/test_fixtures/consul/crds/foo.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -foo: bar diff --git a/control-plane/commands.go b/control-plane/commands.go index 4e76abf00d..e2bcb0f693 100644 --- a/control-plane/commands.go +++ b/control-plane/commands.go @@ -13,6 +13,7 @@ import ( cmdDeleteCompletedJob "github.com/hashicorp/consul-k8s/control-plane/subcommand/delete-completed-job" cmdFetchServerRegion "github.com/hashicorp/consul-k8s/control-plane/subcommand/fetch-server-region" cmdGatewayCleanup "github.com/hashicorp/consul-k8s/control-plane/subcommand/gateway-cleanup" + cmdGatewayResources "github.com/hashicorp/consul-k8s/control-plane/subcommand/gateway-resources" cmdGetConsulClientCA "github.com/hashicorp/consul-k8s/control-plane/subcommand/get-consul-client-ca" cmdGossipEncryptionAutogenerate "github.com/hashicorp/consul-k8s/control-plane/subcommand/gossip-encryption-autogenerate" cmdInjectConnect "github.com/hashicorp/consul-k8s/control-plane/subcommand/inject-connect" @@ -54,6 +55,10 @@ func init() { return &cmdGatewayCleanup.Command{UI: ui}, nil }, + "gateway-resources": func() (cli.Command, error) { + return &cmdGatewayResources.Command{UI: ui}, nil + }, + "server-acl-init": func() (cli.Command, error) { return &cmdServerACLInit.Command{UI: ui}, nil }, diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_controlplanerequestlimits.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_controlplanerequestlimits.yaml index 4d1d808428..2eef465ada 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_controlplanerequestlimits.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_controlplanerequestlimits.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: controlplanerequestlimits.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml index dac72f3646..f066c90612 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: exportedservices.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml index 44eff52492..a8393cd8fd 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: gatewayclassconfigs.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml index e9cf081721..f7ccf205d9 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: ingressgateways.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml index 7506cc57dc..8ca1ec0748 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: jwtproviders.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml index 16dd398f99..bc46b6ab37 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: meshes.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml index 125883bdc5..0871fc32e5 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: meshservices.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml index 894228a218..f6f9eda72b 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: peeringacceptors.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml index 51c3e38319..7e0927c169 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: peeringdialers.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml index 1be3b37703..7396816f7e 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: proxydefaults.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml index 259ca7b910..23de092485 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: samenessgroups.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml index 83503f11f3..a5501a98d2 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: servicedefaults.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml index 9553c73450..cd28173ba8 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: serviceintentions.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml index a8a8107439..3cd3b37324 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: serviceresolvers.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml index 04590cc007..5919e23005 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: servicerouters.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml index 3a47472ba7..d5848ed6ec 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: servicesplitters.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml index acf61cde4c..4910e42829 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null name: terminatinggateways.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/charts/consul/crds/gatewayclasses.gateway.networking.k8s.io.yaml b/control-plane/config/crd/external/gatewayclasses.gateway.networking.k8s.io.yaml similarity index 100% rename from charts/consul/crds/gatewayclasses.gateway.networking.k8s.io.yaml rename to control-plane/config/crd/external/gatewayclasses.gateway.networking.k8s.io.yaml diff --git a/charts/consul/crds/gateways.gateway.networking.k8s.io.yaml b/control-plane/config/crd/external/gateways.gateway.networking.k8s.io.yaml similarity index 100% rename from charts/consul/crds/gateways.gateway.networking.k8s.io.yaml rename to control-plane/config/crd/external/gateways.gateway.networking.k8s.io.yaml diff --git a/charts/consul/crds/grpcroutes.gateway.networking.k8s.io.yaml b/control-plane/config/crd/external/grpcroutes.gateway.networking.k8s.io.yaml similarity index 100% rename from charts/consul/crds/grpcroutes.gateway.networking.k8s.io.yaml rename to control-plane/config/crd/external/grpcroutes.gateway.networking.k8s.io.yaml diff --git a/charts/consul/crds/httproutes.gateway.networking.k8s.io.yaml b/control-plane/config/crd/external/httproutes.gateway.networking.k8s.io.yaml similarity index 100% rename from charts/consul/crds/httproutes.gateway.networking.k8s.io.yaml rename to control-plane/config/crd/external/httproutes.gateway.networking.k8s.io.yaml diff --git a/charts/consul/crds/kustomization.yaml b/control-plane/config/crd/external/kustomization.yaml similarity index 100% rename from charts/consul/crds/kustomization.yaml rename to control-plane/config/crd/external/kustomization.yaml diff --git a/charts/consul/crds/referencegrants.gateway.networking.k8s.io.yaml b/control-plane/config/crd/external/referencegrants.gateway.networking.k8s.io.yaml similarity index 100% rename from charts/consul/crds/referencegrants.gateway.networking.k8s.io.yaml rename to control-plane/config/crd/external/referencegrants.gateway.networking.k8s.io.yaml diff --git a/charts/consul/crds/tcproutes.gateway.networking.k8s.io.yaml b/control-plane/config/crd/external/tcproutes.gateway.networking.k8s.io.yaml similarity index 100% rename from charts/consul/crds/tcproutes.gateway.networking.k8s.io.yaml rename to control-plane/config/crd/external/tcproutes.gateway.networking.k8s.io.yaml diff --git a/charts/consul/crds/tlsroutes.gateway.networking.k8s.io.yaml b/control-plane/config/crd/external/tlsroutes.gateway.networking.k8s.io.yaml similarity index 100% rename from charts/consul/crds/tlsroutes.gateway.networking.k8s.io.yaml rename to control-plane/config/crd/external/tlsroutes.gateway.networking.k8s.io.yaml diff --git a/charts/consul/crds/udproutes.gateway.networking.k8s.io.yaml b/control-plane/config/crd/external/udproutes.gateway.networking.k8s.io.yaml similarity index 100% rename from charts/consul/crds/udproutes.gateway.networking.k8s.io.yaml rename to control-plane/config/crd/external/udproutes.gateway.networking.k8s.io.yaml diff --git a/control-plane/config/rbac/role.yaml b/control-plane/config/rbac/role.yaml index 4fac4ac9b8..7f90780e02 100644 --- a/control-plane/config/rbac/role.yaml +++ b/control-plane/config/rbac/role.yaml @@ -5,6 +5,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + creationTimestamp: null name: manager-role rules: - apiGroups: diff --git a/control-plane/config/webhook/manifests.yaml b/control-plane/config/webhook/manifests.yaml index 6d748ed06c..0861f9253a 100644 --- a/control-plane/config/webhook/manifests.yaml +++ b/control-plane/config/webhook/manifests.yaml @@ -5,6 +5,7 @@ apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: + creationTimestamp: null name: mutating-webhook-configuration webhooks: - admissionReviewVersions: diff --git a/control-plane/go.mod b/control-plane/go.mod index 96f1d91fed..c916acb745 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -32,6 +32,7 @@ require ( golang.org/x/text v0.9.0 golang.org/x/time v0.3.0 gomodules.xyz/jsonpatch/v2 v2.3.0 + gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.26.1 k8s.io/apimachinery v0.26.1 k8s.io/client-go v0.26.1 @@ -158,7 +159,6 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/resty.v1 v1.12.0 // indirect gopkg.in/square/go-jose.v2 v2.5.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.26.1 // indirect k8s.io/component-base v0.26.1 // indirect diff --git a/control-plane/subcommand/gateway-cleanup/command.go b/control-plane/subcommand/gateway-cleanup/command.go index cecb225290..04aa8f60a9 100644 --- a/control-plane/subcommand/gateway-cleanup/command.go +++ b/control-plane/subcommand/gateway-cleanup/command.go @@ -173,7 +173,7 @@ func (c *Command) Help() string { const synopsis = "Clean up global gateway resources prior to uninstall." const help = ` -Usage: consul-k8s-control-plane gateay-cleanup [options] +Usage: consul-k8s-control-plane gateway-cleanup [options] Deletes installed gateway class and gateway class config objects prior to helm uninstallation. This is required due to finalizers diff --git a/control-plane/subcommand/gateway-resources/command.go b/control-plane/subcommand/gateway-resources/command.go new file mode 100644 index 0000000000..2da2abccb1 --- /dev/null +++ b/control-plane/subcommand/gateway-resources/command.go @@ -0,0 +1,352 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package gatewayresources + +import ( + "context" + "errors" + "flag" + "fmt" + "sync" + "time" + + "github.com/cenkalti/backoff" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul-k8s/control-plane/subcommand" + "github.com/hashicorp/consul-k8s/control-plane/subcommand/flags" + "github.com/mitchellh/cli" + yaml "gopkg.in/yaml.v2" + corev1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +// this dupes the Kubernetes tolerations +// struct with yaml tags for validation. +type toleration struct { + Key string `yaml:"key"` + Operator string `yaml:"operator"` + Value string `yaml:"value"` + Effect string `yaml:"effect"` + TolerationSeconds *int64 `yaml:"tolerationSeconds"` +} + +func tolerationToKubernetes(t toleration) corev1.Toleration { + return corev1.Toleration{ + Key: t.Key, + Operator: corev1.TolerationOperator(t.Operator), + Value: t.Value, + Effect: corev1.TaintEffect(t.Effect), + TolerationSeconds: t.TolerationSeconds, + } +} + +type Command struct { + UI cli.Ui + + flags *flag.FlagSet + k8s *flags.K8SFlags + + flagHeritage string + flagChart string + flagApp string + flagRelease string + flagComponent string + flagControllerName string + flagGatewayClassName string + flagGatewayClassConfigName string + + flagServiceType string + flagDeploymentDefaultInstances int + flagDeploymentMaxInstances int + flagDeploymentMinInstances int + + flagNodeSelector string // this is a yaml multiline string map + flagTolerations string // this is a multiline yaml string matching the tolerations array + flagServiceAnnotations string // this is a multiline yaml string array of annotations to allow + + k8sClient client.Client + + once sync.Once + help string + + nodeSelector map[string]string + tolerations []corev1.Toleration + serviceAnnotations []string + + ctx context.Context +} + +func (c *Command) init() { + c.flags = flag.NewFlagSet("", flag.ContinueOnError) + + c.flags.StringVar(&c.flagGatewayClassName, "gateway-class-name", "", + "Name of Kubernetes GatewayClass to ensure is created.") + c.flags.StringVar(&c.flagGatewayClassConfigName, "gateway-class-config-name", "", + "Name of Kubernetes GatewayClassConfig to ensure is created.") + c.flags.StringVar(&c.flagHeritage, "heritage", "", + "Helm chart heritage for created objects.") + c.flags.StringVar(&c.flagChart, "chart", "", + "Helm chart name for created objects.") + c.flags.StringVar(&c.flagApp, "app", "", + "Helm chart app for created objects.") + c.flags.StringVar(&c.flagRelease, "release-name", "", + "Helm chart release for created objects.") + c.flags.StringVar(&c.flagComponent, "component", "", + "Helm chart component for created objects.") + c.flags.StringVar(&c.flagControllerName, "controller-name", "", + "The controller name value to use in the GatewayClass.") + c.flags.StringVar(&c.flagServiceType, "service-type", "", + "The service type to use for a gateway deployment.", + ) + c.flags.IntVar(&c.flagDeploymentDefaultInstances, "deployment-default-instances", 0, + "The number of instances to deploy for each gateway by default.", + ) + c.flags.IntVar(&c.flagDeploymentMaxInstances, "deployment-max-instances", 0, + "The maximum number of instances to deploy for each gateway.", + ) + c.flags.IntVar(&c.flagDeploymentMinInstances, "deployment-min-instances", 0, + "The minimum number of instances to deploy for each gateway.", + ) + c.flags.StringVar(&c.flagNodeSelector, "node-selector", "", + "The node selector to use in scheduling a gateway.", + ) + c.flags.StringVar(&c.flagTolerations, "tolerations", "", + "The tolerations to use in a deployed gateway.", + ) + c.flags.StringVar(&c.flagServiceAnnotations, "service-annotations", "", + "The annotations to copy over from a gateway to its service.", + ) + + c.k8s = &flags.K8SFlags{} + flags.Merge(c.flags, c.k8s.Flags()) + c.help = flags.Usage(help, c.flags) +} + +func (c *Command) Run(args []string) int { + var err error + c.once.Do(c.init) + if err = c.flags.Parse(args); err != nil { + return 1 + } + // Validate flags + if err := c.validateFlags(); err != nil { + c.UI.Error(err.Error()) + return 1 + } + + if c.ctx == nil { + c.ctx = context.Background() + } + + // Create the Kubernetes client + if c.k8sClient == nil { + config, err := subcommand.K8SConfig(c.k8s.KubeConfig()) + if err != nil { + c.UI.Error(fmt.Sprintf("Error retrieving Kubernetes auth: %s", err)) + return 1 + } + + s := runtime.NewScheme() + if err := clientgoscheme.AddToScheme(s); err != nil { + c.UI.Error(fmt.Sprintf("Could not add client-go schema: %s", err)) + return 1 + } + if err := gwv1beta1.Install(s); err != nil { + c.UI.Error(fmt.Sprintf("Could not add api-gateway schema: %s", err)) + return 1 + } + if err := v1alpha1.AddToScheme(s); err != nil { + c.UI.Error(fmt.Sprintf("Could not add consul-k8s schema: %s", err)) + return 1 + } + + c.k8sClient, err = client.New(config, client.Options{Scheme: s}) + if err != nil { + c.UI.Error(fmt.Sprintf("Error initializing Kubernetes client: %s", err)) + return 1 + } + } + + // do the creation + + labels := map[string]string{ + "app": c.flagApp, + "chart": c.flagChart, + "heritage": c.flagHeritage, + "release": c.flagRelease, + "component": c.flagComponent, + } + classConfig := &v1alpha1.GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{Name: c.flagGatewayClassConfigName, Labels: labels}, + Spec: v1alpha1.GatewayClassConfigSpec{ + ServiceType: serviceTypeIfSet(c.flagServiceType), + NodeSelector: c.nodeSelector, + CopyAnnotations: v1alpha1.CopyAnnotationsSpec{ + Service: c.serviceAnnotations, + }, + Tolerations: c.tolerations, + DeploymentSpec: v1alpha1.DeploymentSpec{ + DefaultInstances: nonZeroOrNil(c.flagDeploymentDefaultInstances), + MaxInstances: nonZeroOrNil(c.flagDeploymentMaxInstances), + MinInstances: nonZeroOrNil(c.flagDeploymentMinInstances), + }, + }, + } + + class := &gwv1beta1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{Name: c.flagGatewayClassName, Labels: labels}, + Spec: gwv1beta1.GatewayClassSpec{ + ControllerName: gwv1beta1.GatewayController(c.flagControllerName), + ParametersRef: &gwv1beta1.ParametersReference{ + Group: gwv1beta1.Group(v1alpha1.ConsulHashicorpGroup), + Kind: gwv1beta1.Kind(v1alpha1.GatewayClassConfigKind), + Name: c.flagGatewayClassConfigName, + }, + }, + } + + if err := forceClassConfig(context.Background(), c.k8sClient, classConfig); err != nil { + c.UI.Error(err.Error()) + return 1 + } + if err := forceClass(context.Background(), c.k8sClient, class); err != nil { + c.UI.Error(err.Error()) + return 1 + } + + return 0 +} + +func (c *Command) validateFlags() error { + if c.flagGatewayClassConfigName == "" { + return errors.New("-gateway-class-config-name must be set") + } + if c.flagGatewayClassName == "" { + return errors.New("-gateway-class-name must be set") + } + if c.flagHeritage == "" { + return errors.New("-heritage must be set") + } + if c.flagChart == "" { + return errors.New("-chart must be set") + } + if c.flagApp == "" { + return errors.New("-app must be set") + } + if c.flagRelease == "" { + return errors.New("-release-name must be set") + } + if c.flagComponent == "" { + return errors.New("-component must be set") + } + if c.flagControllerName == "" { + return errors.New("-controller-name must be set") + } + if c.flagTolerations != "" { + var tolerations []toleration + if err := yaml.Unmarshal([]byte(c.flagTolerations), &tolerations); err != nil { + return fmt.Errorf("error decoding tolerations: %w", err) + } + c.tolerations = common.ConvertSliceFunc(tolerations, tolerationToKubernetes) + } + if c.flagNodeSelector != "" { + if err := yaml.Unmarshal([]byte(c.flagNodeSelector), &c.nodeSelector); err != nil { + return fmt.Errorf("error decoding node selector: %w", err) + } + } + if c.flagNodeSelector != "" { + if err := yaml.Unmarshal([]byte(c.flagNodeSelector), &c.nodeSelector); err != nil { + return fmt.Errorf("error decoding node selector: %w", err) + } + } + if c.flagServiceAnnotations != "" { + if err := yaml.Unmarshal([]byte(c.flagServiceAnnotations), &c.serviceAnnotations); err != nil { + return fmt.Errorf("error decoding service annotations: %w", err) + } + } + + return nil +} + +func (c *Command) Synopsis() string { return synopsis } +func (c *Command) Help() string { + c.once.Do(c.init) + return c.help +} + +const synopsis = "Create managed gateway resources after installation/upgrade." +const help = ` +Usage: consul-k8s-control-plane gateway-resources [options] + + Installs managed gateway class and configuration resources + after a helm installation or upgrade in order to avoid the + dependencies of CRDs being in-place prior to resource creation. + +` + +func forceClassConfig(ctx context.Context, k8sClient client.Client, o *v1alpha1.GatewayClassConfig) error { + return backoff.Retry(func() error { + var existing v1alpha1.GatewayClassConfig + err := k8sClient.Get(ctx, client.ObjectKeyFromObject(o), &existing) + if err != nil && !k8serrors.IsNotFound(err) { + return err + } + + if k8serrors.IsNotFound(err) { + return k8sClient.Create(ctx, o) + } + + existing.Spec = o.Spec + existing.Labels = o.Labels + + return k8sClient.Update(ctx, &existing) + }, exponentialBackoffWithMaxIntervalAndTime()) +} + +func forceClass(ctx context.Context, k8sClient client.Client, o *gwv1beta1.GatewayClass) error { + return backoff.Retry(func() error { + var existing gwv1beta1.GatewayClass + err := k8sClient.Get(ctx, client.ObjectKeyFromObject(o), &existing) + if err != nil && !k8serrors.IsNotFound(err) { + return err + } + + if k8serrors.IsNotFound(err) { + return k8sClient.Create(ctx, o) + } + + existing.Spec = o.Spec + existing.Labels = o.Labels + + return k8sClient.Update(ctx, &existing) + }, exponentialBackoffWithMaxIntervalAndTime()) +} + +func exponentialBackoffWithMaxIntervalAndTime() *backoff.ExponentialBackOff { + backoff := backoff.NewExponentialBackOff() + backoff.MaxElapsedTime = 10 * time.Second + backoff.MaxInterval = 1 * time.Second + backoff.Reset() + return backoff +} + +func nonZeroOrNil(v int) *int32 { + if v == 0 { + return nil + } + return common.PointerTo(int32(v)) +} + +func serviceTypeIfSet(v string) *corev1.ServiceType { + if v == "" { + return nil + } + return common.PointerTo(corev1.ServiceType(v)) +} diff --git a/control-plane/subcommand/gateway-resources/command_test.go b/control-plane/subcommand/gateway-resources/command_test.go new file mode 100644 index 0000000000..0c40e67244 --- /dev/null +++ b/control-plane/subcommand/gateway-resources/command_test.go @@ -0,0 +1,253 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package gatewayresources + +import ( + "testing" + + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/mitchellh/cli" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +func TestRun_flagValidation(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + cmd *Command + expectedErr string + }{ + "required gateway class config name": { + cmd: &Command{}, + expectedErr: "-gateway-class-config-name must be set", + }, + "required gateway class name": { + cmd: &Command{ + flagGatewayClassConfigName: "test", + }, + expectedErr: "-gateway-class-name must be set", + }, + "required heritage": { + cmd: &Command{ + flagGatewayClassConfigName: "test", + flagGatewayClassName: "test", + }, + expectedErr: "-heritage must be set", + }, + "required chart": { + cmd: &Command{ + flagGatewayClassConfigName: "test", + flagGatewayClassName: "test", + flagHeritage: "test", + }, + expectedErr: "-chart must be set", + }, + "required app": { + cmd: &Command{ + flagGatewayClassConfigName: "test", + flagGatewayClassName: "test", + flagHeritage: "test", + flagChart: "test", + }, + expectedErr: "-app must be set", + }, + "required release": { + cmd: &Command{ + flagGatewayClassConfigName: "test", + flagGatewayClassName: "test", + flagHeritage: "test", + flagChart: "test", + flagApp: "test", + }, + expectedErr: "-release-name must be set", + }, + "required component": { + cmd: &Command{ + flagGatewayClassConfigName: "test", + flagGatewayClassName: "test", + flagHeritage: "test", + flagChart: "test", + flagApp: "test", + flagRelease: "test", + }, + expectedErr: "-component must be set", + }, + "required controller name": { + cmd: &Command{ + flagGatewayClassConfigName: "test", + flagGatewayClassName: "test", + flagHeritage: "test", + flagChart: "test", + flagApp: "test", + flagRelease: "test", + flagComponent: "test", + }, + expectedErr: "-controller-name must be set", + }, + "required valid tolerations": { + cmd: &Command{ + flagGatewayClassConfigName: "test", + flagGatewayClassName: "test", + flagHeritage: "test", + flagChart: "test", + flagApp: "test", + flagRelease: "test", + flagComponent: "test", + flagControllerName: "test", + flagTolerations: "foo", + }, + expectedErr: "error decoding tolerations: yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `foo` into []gatewayresources.toleration", + }, + "required valid nodeSelector": { + cmd: &Command{ + flagGatewayClassConfigName: "test", + flagGatewayClassName: "test", + flagHeritage: "test", + flagChart: "test", + flagApp: "test", + flagRelease: "test", + flagComponent: "test", + flagControllerName: "test", + flagNodeSelector: "foo", + }, + expectedErr: "error decoding node selector: yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `foo` into map[string]string", + }, + "required valid service annotations": { + cmd: &Command{ + flagGatewayClassConfigName: "test", + flagGatewayClassName: "test", + flagHeritage: "test", + flagChart: "test", + flagApp: "test", + flagRelease: "test", + flagComponent: "test", + flagControllerName: "test", + flagServiceAnnotations: "foo", + }, + expectedErr: "error decoding service annotations: yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `foo` into []string", + }, + "valid without optional flags": { + cmd: &Command{ + flagGatewayClassConfigName: "test", + flagGatewayClassName: "test", + flagHeritage: "test", + flagChart: "test", + flagApp: "test", + flagRelease: "test", + flagComponent: "test", + flagControllerName: "test", + }, + }, + "valid with optional flags": { + cmd: &Command{ + flagGatewayClassConfigName: "test", + flagGatewayClassName: "test", + flagHeritage: "test", + flagChart: "test", + flagApp: "test", + flagRelease: "test", + flagComponent: "test", + flagControllerName: "test", + flagNodeSelector: ` +foo: 1 +bar: 2`, + flagTolerations: ` +- value: foo +- value: bar`, + flagServiceAnnotations: ` +- foo +- bar`, + }, + }, + } { + t.Run(name, func(t *testing.T) { + tt := tt + + t.Parallel() + + err := tt.cmd.validateFlags() + if tt.expectedErr == "" && err != nil { + t.Errorf("unexpected error occured: %v", err) + } + if tt.expectedErr != "" && err == nil { + t.Error("expected error but got none") + } + if tt.expectedErr != "" { + require.EqualError(t, err, tt.expectedErr) + } + }) + } +} + +func TestRun(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + existingGatewayClass bool + existingGatewayClassConfig bool + }{ + "both exist": { + existingGatewayClass: true, + existingGatewayClassConfig: true, + }, + "gateway class config doesn't exist": { + existingGatewayClass: true, + }, + "gateway class doesn't exist": { + existingGatewayClassConfig: true, + }, + "neither exist": {}, + } { + t.Run(name, func(t *testing.T) { + tt := tt + + t.Parallel() + + existingGatewayClassConfig := &v1alpha1.GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, + } + existingGatewayClass := &gwv1beta1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, + } + + s := runtime.NewScheme() + require.NoError(t, gwv1beta1.Install(s)) + require.NoError(t, v1alpha1.AddToScheme(s)) + + objs := []client.Object{} + if tt.existingGatewayClass { + objs = append(objs, existingGatewayClass) + } + if tt.existingGatewayClassConfig { + objs = append(objs, existingGatewayClassConfig) + } + + client := fake.NewClientBuilder().WithScheme(s).WithObjects(objs...).Build() + + ui := cli.NewMockUi() + cmd := Command{ + UI: ui, + k8sClient: client, + } + + code := cmd.Run([]string{ + "-gateway-class-config-name", "test", + "-gateway-class-name", "test", + "-heritage", "test", + "-chart", "test", + "-app", "test", + "-release-name", "test", + "-component", "test", + "-controller-name", "test", + }) + + require.Equal(t, 0, code) + }) + } +} diff --git a/hack/copy-crds-to-chart/main.go b/hack/copy-crds-to-chart/main.go index b5eef3e1aa..149126b392 100644 --- a/hack/copy-crds-to-chart/main.go +++ b/hack/copy-crds-to-chart/main.go @@ -18,12 +18,6 @@ var ( "consul.hashicorp.com_peeringacceptors.yaml": {}, "consul.hashicorp.com_peeringdialers.yaml": {}, } - // HACK IT (again)! These CRDs need to go in the Helm chart's crds directory which means they - // cannot have any templating in them. They need to be in the CRD directory because we install - // resources that reference them in the main installation sequence. - toCRDDir = map[string]struct{}{ - "consul.hashicorp.com_gatewayclassconfigs.yaml": {}, - } ) func main() { @@ -40,37 +34,40 @@ func main() { } func realMain(helmPath string) error { - return filepath.Walk("../../control-plane/config/crd/bases", func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } + root := "../../control-plane/config/crd/" + dirs := []string{"bases", "external"} - if info.IsDir() || filepath.Ext(path) != ".yaml" { - return nil - } + for _, dir := range dirs { + err := filepath.Walk(root+dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } - printf("processing %s", filepath.Base(path)) + if info.IsDir() || filepath.Ext(path) != ".yaml" || filepath.Base(path) == "kustomization.yaml" { + return nil + } - contentBytes, err := os.ReadFile(path) - if err != nil { - return err - } - contents := string(contentBytes) - - // Strip leading newline. - contents = strings.TrimPrefix(contents, "\n") - - if _, ok := requiresPeering[info.Name()]; ok { - // Add {{- if and .Values.connectInject.enabled .Values.global.peering.enabled }} {{- end }} wrapper. - contents = fmt.Sprintf("{{- if and .Values.connectInject.enabled .Values.global.peering.enabled }}\n%s{{- end }}\n", contents) - } else if _, ok := toCRDDir[info.Name()]; ok { - // No-op (we don't want templating). - } else { - // Add {{- if .Values.connectInject.enabled }} {{- end }} wrapper. - contents = fmt.Sprintf("{{- if .Values.connectInject.enabled }}\n%s{{- end }}\n", contents) - } + printf("processing %s", filepath.Base(path)) + + contentBytes, err := os.ReadFile(path) + if err != nil { + return err + } + contents := string(contentBytes) + + // Strip leading newline. + contents = strings.TrimPrefix(contents, "\n") + + if _, ok := requiresPeering[info.Name()]; ok { + // Add {{- if and .Values.connectInject.enabled .Values.global.peering.enabled }} {{- end }} wrapper. + contents = fmt.Sprintf("{{- if and .Values.connectInject.enabled .Values.global.peering.enabled }}\n%s{{- end }}\n", contents) + } else if dir == "external" { + contents = fmt.Sprintf("{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }}\n%s{{- end }}\n", contents) + } else { + // Add {{- if .Values.connectInject.enabled }} {{- end }} wrapper. + contents = fmt.Sprintf("{{- if .Values.connectInject.enabled }}\n%s{{- end }}\n", contents) + } - if _, ok := toCRDDir[info.Name()]; !ok { // Add labels, this is hacky because we're relying on the line number // but it means we don't need to regex or yaml parse. splitOnNewlines := strings.Split(contents, "\n") @@ -84,20 +81,27 @@ func realMain(helmPath string) error { } withLabels := append(splitOnNewlines[0:9], append(labelLines, splitOnNewlines[9:]...)...) contents = strings.Join(withLabels, "\n") - } - // Construct the destination filename. - filenameSplit := strings.Split(info.Name(), "_") - crdName := filenameSplit[1] - destinationPath := filepath.Join(helmPath, "templates", fmt.Sprintf("crd-%s", crdName)) - if _, ok := toCRDDir[info.Name()]; ok { - destinationPath = filepath.Join(helmPath, "crds", formatCRDName(info.Name())) - } + var crdName string + if dir == "bases" { + // Construct the destination filename. + filenameSplit := strings.Split(info.Name(), "_") + crdName = filenameSplit[1] + } else if dir == "external" { + filenameSplit := strings.Split(info.Name(), ".") + crdName = filenameSplit[0] + ".yaml" + } - // Write it. - printf("writing to %s", destinationPath) - return os.WriteFile(destinationPath, []byte(contents), 0644) - }) + destinationPath := filepath.Join(helmPath, "templates", fmt.Sprintf("crd-%s", crdName)) + // Write it. + printf("writing to %s", destinationPath) + return os.WriteFile(destinationPath, []byte(contents), 0644) + }) + if err != nil { + return err + } + } + return nil } func printf(format string, args ...interface{}) { From 8d014c0c4f3c652c11dcd289106b10fdd05333f3 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Tue, 6 Jun 2023 10:17:21 -0400 Subject: [PATCH 202/340] Add missing entries to main CHANGELOG (#2275) --- CHANGELOG.md | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0df700efa..1cf1d2084f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,115 @@ +## 1.1.2 (June 5, 2023) + +SECURITY: + +* Bump Dockerfile base image for RedHat UBI `consul-k8s-control-plane` image to `ubi-minimal:9.2`. [[GH-2204](https://github.com/hashicorp/consul-k8s/issues/2204)] +* Bump `controller-runtime` to address CVEs in dependencies. [[GH-2226](https://github.com/hashicorp/consul-k8s/issues/2226)] +* Upgrade to use Go 1.20.4. +This resolves vulnerabilities [CVE-2023-24537](https://github.com/advisories/GHSA-9f7g-gqwh-jpf5)(`go/scanner`), +[CVE-2023-24538](https://github.com/advisories/GHSA-v4m2-x4rp-hv22)(`html/template`), +[CVE-2023-24534](https://github.com/advisories/GHSA-8v5j-pwr7-w5f8)(`net/textproto`) and +[CVE-2023-24536](https://github.com/advisories/GHSA-9f7g-gqwh-jpf5)(`mime/multipart`). +Also, `golang.org/x/net` has been updated to v0.7.0 to resolve CVEs [CVE-2022-41721 +](https://github.com/advisories/GHSA-fxg5-wq6x-vr4w +), [CVE-2022-27664](https://github.com/advisories/GHSA-69cg-p879-7622) and [CVE-2022-41723 +](https://github.com/advisories/GHSA-vvpx-j8f3-3w6h +.) [[GH-2104](https://github.com/hashicorp/consul-k8s/issues/2104)] + +FEATURES: + +* Add support for consul-telemetry-collector to forward envoy metrics to an otelhttp compatible receiver or HCP [[GH-2134](https://github.com/hashicorp/consul-k8s/issues/2134)] +* consul-telemetry-collector: Configure envoy proxy config during registration when consul-telemetry-collector is enabled. [[GH-2143](https://github.com/hashicorp/consul-k8s/issues/2143)] +* sync-catalog: add ability to sync hostname from a Kubernetes Ingress resource to the Consul Catalog during service registration. [[GH-2098](https://github.com/hashicorp/consul-k8s/issues/2098)] + +IMPROVEMENTS: + +* cli: Add `consul-k8s config read` command that returns the helm configuration in yaml format. [[GH-2078](https://github.com/hashicorp/consul-k8s/issues/2078)] +* cli: add consul-telemetry-gateway allow-all intention for -demo [[GH-2262](https://github.com/hashicorp/consul-k8s/issues/2262)] +* cli: update cloud preset to enable telemetry collector [[GH-2205](https://github.com/hashicorp/consul-k8s/issues/2205)] +* consul-telemetry-collector: add acceptance tests for consul telemetry collector component [[GH-2195](https://github.com/hashicorp/consul-k8s/issues/2195)] + +BUG FIXES: + +* crd: fix bug on service intentions CRD causing some updates to be ignored. [[GH-2194](https://github.com/hashicorp/consul-k8s/issues/2083)] +* api-gateway: fix issue where the API Gateway controller is unable to start up successfully when Vault is configured as the secrets backend [[GH-2083](https://github.com/hashicorp/consul-k8s/issues/2083)] +* control-plane: add support for idleTimeout in the Service Router config [[GH-2156](https://github.com/hashicorp/consul-k8s/issues/2156)] +* control-plane: fix issue with json tags of service defaults fields EnforcingConsecutive5xx, MaxEjectionPercent and BaseEjectionTime. [[GH-2160](https://github.com/hashicorp/consul-k8s/issues/2160)] +* control-plane: fix issue with multiport pods crashlooping due to dataplane port conflicts by ensuring dns redirection is disabled for non-tproxy pods [[GH-2176](https://github.com/hashicorp/consul-k8s/issues/2176)] +* helm: add missing `$HOST_IP` environment variable to to mesh gateway deployments. [[GH-1808](https://github.com/hashicorp/consul-k8s/issues/1808)] +* sync-catalog: fix issue where the sync-catalog ACL token were set with an incorrect ENV VAR. [[GH-2068](https://github.com/hashicorp/consul-k8s/issues/2068)] + +## 1.0.7 (May 17, 2023) + +SECURITY: + +* Upgrade to use Go 1.19.9. +This resolves vulnerabilities [CVE-2023-24537](https://github.com/advisories/GHSA-9f7g-gqwh-jpf5)(`go/scanner`), +[CVE-2023-24538](https://github.com/advisories/GHSA-v4m2-x4rp-hv22)(`html/template`), +[CVE-2023-24534](https://github.com/advisories/GHSA-8v5j-pwr7-w5f8)(`net/textproto`) and +[CVE-2023-24536](https://github.com/advisories/GHSA-9f7g-gqwh-jpf5)(`mime/multipart`). +Also, `golang.org/x/net` has been updated to v0.7.0 to resolve CVEs [CVE-2022-41721 +](https://github.com/advisories/GHSA-fxg5-wq6x-vr4w +), [CVE-2022-27664](https://github.com/advisories/GHSA-69cg-p879-7622) and [CVE-2022-41723 +](https://github.com/advisories/GHSA-vvpx-j8f3-3w6h +.) [[GH-2108](https://github.com/hashicorp/consul-k8s/issues/2108)] + +FEATURES: + +* sync-catalog: add ability to sync hostname from a Kubernetes Ingress resource to the Consul Catalog during service registration. [[GH-2098](https://github.com/hashicorp/consul-k8s/issues/2098)] + +IMPROVEMENTS: + +* cli: Add `consul-k8s config read` command that returns the helm configuration in yaml format. [[GH-2078](https://github.com/hashicorp/consul-k8s/issues/2078)] +* helm: update `imageConsulDataplane` value to `hashicorp/consul-dataplane:1.0.2`, `image` value to `hashicorp/consul:1.14.7`, +and `imageEnvoy` to `envoyproxy/envoy:v1.24.7`. [[GH-2140](https://github.com/hashicorp/consul-k8s/issues/2140)] + +BUG FIXES: + +* api-gateway: fix issue where the API Gateway controller is unable to start up successfully when Vault is configured as the secrets backend [[GH-2083](https://github.com/hashicorp/consul-k8s/issues/2083)] +* helm: add missing `$HOST_IP` environment variable to to mesh gateway deployments. [[GH-1808](https://github.com/hashicorp/consul-k8s/issues/1808)] +* sync-catalog: fix issue where the sync-catalog ACL token were set with an incorrect ENV VAR. [[GH-2068](https://github.com/hashicorp/consul-k8s/issues/2068)] + +## 0.49.6 (May 17, 2023) + +SECURITY: + +* Upgrade to use Go 1.19.9. +This resolves vulnerabilities [CVE-2023-24537](https://github.com/advisories/GHSA-9f7g-gqwh-jpf5)(`go/scanner`), +[CVE-2023-24538](https://github.com/advisories/GHSA-v4m2-x4rp-hv22)(`html/template`), +[CVE-2023-24534](https://github.com/advisories/GHSA-8v5j-pwr7-w5f8)(`net/textproto`) and +[CVE-2023-24536](https://github.com/advisories/GHSA-9f7g-gqwh-jpf5)(`mime/multipart`). +Also, `golang.org/x/net` has been updated to v0.7.0 to resolve CVEs [CVE-2022-41721 +](https://github.com/advisories/GHSA-fxg5-wq6x-vr4w +), [CVE-2022-27664](https://github.com/advisories/GHSA-69cg-p879-7622) and [CVE-2022-41723 +](https://github.com/advisories/GHSA-vvpx-j8f3-3w6h +.) [[GH-2110](https://github.com/hashicorp/consul-k8s/issues/2110)] + +IMPROVEMENTS: + +* helm: Set default `limits.cpu` resource setting to `null` for `consul-connect-inject-init` container to speed up registration times when onboarding services onto the mesh during the init container lifecycle. [[GH-2008](https://github.com/hashicorp/consul-k8s/issues/2008)] + +## 1.1.1 (March 31, 2023) + +IMPROVEMENTS: + +* helm: Set default `limits.cpu` resource setting to `null` for `consul-connect-inject-init` container to speed up registration times when onboarding services onto the mesh during the init container lifecycle. [[GH-2008](https://github.com/hashicorp/consul-k8s/issues/2008)] +* helm: When the `global.acls.bootstrapToken` field is set and the content of the secret is empty, the bootstrap ACL token is written to that secret after bootstrapping ACLs. This applies to both the Vault and Consul secrets backends. [[GH-1920](https://github.com/hashicorp/consul-k8s/issues/1920)] + +BUG FIXES: + +* api-gateway: fix ACL issue where when adminPartitions and ACLs are enabled, API Gateway Controller is unable to create a new namespace in Consul [[GH-2029](https://github.com/hashicorp/consul-k8s/issues/2029)] +* api-gateway: fix issue where specifying an external server SNI name while using client nodes resulted in a TLS verification error. [[GH-2013](https://github.com/hashicorp/consul-k8s/issues/2013)] + +## 1.0.6 (March 20, 2023) + +IMPROVEMENTS: + +* helm: Set default `limits.cpu` resource setting to `null` for `consul-connect-inject-init` container to speed up registration times when onboarding services onto the mesh during the init container lifecycle. [[GH-2008](https://github.com/hashicorp/consul-k8s/issues/2008)] + +BUG FIXES: + +* api-gateway: fix issue where specifying an external server SNI name while using client nodes resulted in a TLS verification error. [[GH-2013](https://github.com/hashicorp/consul-k8s/issues/2013)] + ## 1.0.5 (March 9, 2023) SECURITY: From 38cd4d724ed201c7bbea83ba3f0a46e5a0e3e92a Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Tue, 6 Jun 2023 10:20:50 -0400 Subject: [PATCH 203/340] Fixing changelog for 2195 (#2277) --- .changelog/2195.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changelog/2195.txt b/.changelog/2195.txt index 1b450eb40d..e7475f88e0 100644 --- a/.changelog/2195.txt +++ b/.changelog/2195.txt @@ -1,3 +1,3 @@ -```release-note:imrpovement +```release-note:improvement consul-telemetry-collector: add acceptance tests for consul telemetry collector component. -``` \ No newline at end of file +``` From 0f893beacd8616798ffb2ddf31b6616405dc1c26 Mon Sep 17 00:00:00 2001 From: Andrew Stucki Date: Tue, 6 Jun 2023 11:23:13 -0400 Subject: [PATCH 204/340] [API Gateway] Add external consul servers test (#2270) * [API Gateway] Add external consul servers test * Fix up releaseName usage on CLI-based tests to mirror helm-based tests --- acceptance/framework/consul/cli_cluster.go | 15 +- acceptance/framework/consul/cluster.go | 2 +- acceptance/framework/consul/helm_cluster.go | 21 ++- .../api_gateway_external_servers_test.go | 133 ++++++++++++++++++ 4 files changed, 159 insertions(+), 12 deletions(-) create mode 100644 acceptance/tests/api-gateway/api_gateway_external_servers_test.go diff --git a/acceptance/framework/consul/cli_cluster.go b/acceptance/framework/consul/cli_cluster.go index a9b1dbe74f..ba4cfc93ab 100644 --- a/acceptance/framework/consul/cli_cluster.go +++ b/acceptance/framework/consul/cli_cluster.go @@ -200,9 +200,14 @@ func (c *CLICluster) Destroy(t *testing.T) { require.NoError(t, err) } -func (c *CLICluster) SetupConsulClient(t *testing.T, secure bool) (*api.Client, string) { +func (c *CLICluster) SetupConsulClient(t *testing.T, secure bool, release ...string) (*api.Client, string) { t.Helper() + releaseName := c.releaseName + if len(release) > 0 { + releaseName = release[0] + } + namespace := c.kubectlOptions.Namespace config := api.DefaultConfig() localPort := terratestk8s.GetAvailablePort(t) @@ -222,13 +227,13 @@ func (c *CLICluster) SetupConsulClient(t *testing.T, secure bool) (*api.Client, // In secondary servers, we don't create a bootstrap token since ACLs are only bootstrapped in the primary. // Instead, we provide a replication token that serves the role of the bootstrap token. - aclSecretName := fmt.Sprintf("%s-consul-bootstrap-acl-token", c.releaseName) + aclSecretName := fmt.Sprintf("%s-consul-bootstrap-acl-token", releaseName) if c.releaseName == CLIReleaseName { aclSecretName = "consul-bootstrap-acl-token" } aclSecret, err := c.kubernetesClient.CoreV1().Secrets(namespace).Get(context.Background(), aclSecretName, metav1.GetOptions{}) if err != nil && errors.IsNotFound(err) { - federationSecret := fmt.Sprintf("%s-consul-federation", c.releaseName) + federationSecret := fmt.Sprintf("%s-consul-federation", releaseName) if c.releaseName == CLIReleaseName { federationSecret = "consul-federation" } @@ -242,8 +247,8 @@ func (c *CLICluster) SetupConsulClient(t *testing.T, secure bool) (*api.Client, } } - serverPod := fmt.Sprintf("%s-consul-server-0", c.releaseName) - if c.releaseName == CLIReleaseName { + serverPod := fmt.Sprintf("%s-consul-server-0", releaseName) + if releaseName == CLIReleaseName { serverPod = "consul-server-0" } tunnel := terratestk8s.NewTunnelWithLogger( diff --git a/acceptance/framework/consul/cluster.go b/acceptance/framework/consul/cluster.go index 734f07f36e..1b9a543245 100644 --- a/acceptance/framework/consul/cluster.go +++ b/acceptance/framework/consul/cluster.go @@ -12,7 +12,7 @@ import ( // Cluster represents a consul cluster object. type Cluster interface { // SetupConsulClient returns a new Consul client. - SetupConsulClient(t *testing.T, secure bool) (*api.Client, string) + SetupConsulClient(t *testing.T, secure bool, release ...string) (*api.Client, string) // Create creates a new Consul Cluster. Create(t *testing.T) diff --git a/acceptance/framework/consul/helm_cluster.go b/acceptance/framework/consul/helm_cluster.go index 6c61a605f6..aa6fdef7d8 100644 --- a/acceptance/framework/consul/helm_cluster.go +++ b/acceptance/framework/consul/helm_cluster.go @@ -346,14 +346,23 @@ func (h *HelmCluster) Upgrade(t *testing.T, helmValues map[string]string) { k8s.WaitForAllPodsToBeReady(t, h.kubernetesClient, h.helmOptions.KubectlOptions.Namespace, fmt.Sprintf("release=%s", h.releaseName)) } -func (h *HelmCluster) CreatePortForwardTunnel(t *testing.T, remotePort int) string { - serverPod := fmt.Sprintf("%s-consul-server-0", h.releaseName) +func (h *HelmCluster) CreatePortForwardTunnel(t *testing.T, remotePort int, release ...string) string { + releaseName := h.releaseName + if len(release) > 0 { + releaseName = release[0] + } + serverPod := fmt.Sprintf("%s-consul-server-0", releaseName) return portforward.CreateTunnelToResourcePort(t, serverPod, remotePort, h.helmOptions.KubectlOptions, h.logger) } -func (h *HelmCluster) SetupConsulClient(t *testing.T, secure bool) (client *api.Client, configAddress string) { +func (h *HelmCluster) SetupConsulClient(t *testing.T, secure bool, release ...string) (client *api.Client, configAddress string) { t.Helper() + releaseName := h.releaseName + if len(release) > 0 { + releaseName = release[0] + } + namespace := h.helmOptions.KubectlOptions.Namespace config := api.DefaultConfig() remotePort := 8500 // use non-secure by default @@ -376,9 +385,9 @@ func (h *HelmCluster) SetupConsulClient(t *testing.T, secure bool) (client *api. // and will try to read the replication token from the federation secret. // In secondary servers, we don't create a bootstrap token since ACLs are only bootstrapped in the primary. // Instead, we provide a replication token that serves the role of the bootstrap token. - aclSecret, err := h.kubernetesClient.CoreV1().Secrets(namespace).Get(context.Background(), h.releaseName+"-consul-bootstrap-acl-token", metav1.GetOptions{}) + aclSecret, err := h.kubernetesClient.CoreV1().Secrets(namespace).Get(context.Background(), releaseName+"-consul-bootstrap-acl-token", metav1.GetOptions{}) if err != nil && errors.IsNotFound(err) { - federationSecret := fmt.Sprintf("%s-consul-federation", h.releaseName) + federationSecret := fmt.Sprintf("%s-consul-federation", releaseName) aclSecret, err = h.kubernetesClient.CoreV1().Secrets(namespace).Get(context.Background(), federationSecret, metav1.GetOptions{}) require.NoError(r, err) config.Token = string(aclSecret.Data["replicationToken"]) @@ -392,7 +401,7 @@ func (h *HelmCluster) SetupConsulClient(t *testing.T, secure bool) (client *api. } } - config.Address = h.CreatePortForwardTunnel(t, remotePort) + config.Address = h.CreatePortForwardTunnel(t, remotePort, release...) consulClient, err := api.NewClient(config) require.NoError(t, err) diff --git a/acceptance/tests/api-gateway/api_gateway_external_servers_test.go b/acceptance/tests/api-gateway/api_gateway_external_servers_test.go new file mode 100644 index 0000000000..c0fa8bbcca --- /dev/null +++ b/acceptance/tests/api-gateway/api_gateway_external_servers_test.go @@ -0,0 +1,133 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package apigateway + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/consul-k8s/acceptance/framework/consul" + "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" + "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" + "github.com/hashicorp/consul-k8s/acceptance/framework/logger" + "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/sdk/testutil/retry" + "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/types" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +// TestAPIGateway_ExternalServers tests that connect works when using external servers. +// It sets up an external Consul server in the same cluster but a different Helm installation +// and then treats this server as external. +func TestAPIGateway_ExternalServers(t *testing.T) { + cfg := suite.Config() + ctx := suite.Environment().DefaultContext(t) + + serverHelmValues := map[string]string{ + "global.acls.manageSystemACLs": "true", + "global.tls.enabled": "true", + + // Don't install injector, controller and cni on this cluster so that it's not installed twice. + "connectInject.enabled": "false", + "connectInject.cni.enabled": "false", + } + serverReleaseName := helpers.RandomName() + consulServerCluster := consul.NewHelmCluster(t, serverHelmValues, ctx, cfg, serverReleaseName) + + consulServerCluster.Create(t) + + helmValues := map[string]string{ + "server.enabled": "false", + "global.acls.manageSystemACLs": "true", + "global.tls.enabled": "true", + "connectInject.enabled": "true", + "externalServers.enabled": "true", + "externalServers.hosts[0]": fmt.Sprintf("%s-consul-server", serverReleaseName), + "externalServers.httpsPort": "8501", + "global.tls.caCert.secretName": fmt.Sprintf("%s-consul-ca-cert", serverReleaseName), + "global.tls.caCert.secretKey": "tls.crt", + "global.acls.bootstrapToken.secretName": fmt.Sprintf("%s-consul-bootstrap-acl-token", serverReleaseName), + "global.acls.bootstrapToken.secretKey": "token", + } + + releaseName := helpers.RandomName() + consulCluster := consul.NewHelmCluster(t, helmValues, ctx, cfg, releaseName) + consulCluster.SkipCheckForPreviousInstallations = true + + consulCluster.Create(t) + + logger.Log(t, "creating static-server and static-client deployments") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + + // Override the default proxy config settings for this test + consulClient, _ := consulCluster.SetupConsulClient(t, true, serverReleaseName) + logger.Log(t, "have consul client") + _, _, err := consulClient.ConfigEntries().Set(&api.ProxyConfigEntry{ + Kind: api.ProxyDefaults, + Name: api.ProxyConfigGlobal, + Config: map[string]interface{}{ + "protocol": "http", + }, + }, nil) + require.NoError(t, err) + logger.Log(t, "set consul config entry") + + logger.Log(t, "creating api-gateway resources") + out, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "apply", "-k", "../fixtures/bases/api-gateway") + require.NoError(t, err, out) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + // Ignore errors here because if the test ran as expected + // the custom resources will have been deleted. + k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-k", "../fixtures/bases/api-gateway") + }) + + logger.Log(t, "patching route to target server") + k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "httproute", "http-route", "-p", `{"spec":{"rules":[{"backendRefs":[{"name":"static-server","port":80}]}]}}`, "--type=merge") + + // Grab a kubernetes client so that we can verify binding + // behavior prior to issuing requests through the gateway. + k8sClient := ctx.ControllerRuntimeClient(t) + + // On startup, the controller can take upwards of 1m to perform + // leader election so we may need to wait a long time for + // the reconcile loop to run (hence a ~1m timeout here). + var gatewayAddress string + retryCheck(t, 30, func(r *retry.R) { + var gateway gwv1beta1.Gateway + err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway", Namespace: "default"}, &gateway) + require.NoError(r, err) + + // check that we have an address to use + require.Len(r, gateway.Status.Addresses, 1) + // now we know we have an address, set it so we can use it + gatewayAddress = gateway.Status.Addresses[0].Value + }) + + k8sOptions := ctx.KubectlOptions(t) + targetAddress := fmt.Sprintf("http://%s/", gatewayAddress) + + // check that intentions keep our connection from happening + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, targetAddress) + + // Now we create the allow intention. + _, _, err = consulClient.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{ + Kind: api.ServiceIntentions, + Name: "static-server", + Sources: []*api.SourceIntention{ + { + Name: "gateway", + Action: api.IntentionActionAllow, + }, + }, + }, nil) + require.NoError(t, err) + + // Test that we can make a call to the api gateway + // via the static-client pod. It should route to the static-server pod. + logger.Log(t, "trying calls to api gateway") + k8s.CheckStaticServerConnectionSuccessful(t, k8sOptions, StaticClientName, targetAddress) +} From b5b0b27d8222bb59f6698ac869a9f6c05d84a18f Mon Sep 17 00:00:00 2001 From: John Maguire Date: Tue, 6 Jun 2023 11:55:44 -0400 Subject: [PATCH 205/340] Add check for timeout error (#2280) --- control-plane/api-gateway/cache/consul.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/control-plane/api-gateway/cache/consul.go b/control-plane/api-gateway/cache/consul.go index 5c4125a040..9acfb2b1ee 100644 --- a/control-plane/api-gateway/cache/consul.go +++ b/control-plane/api-gateway/cache/consul.go @@ -160,7 +160,11 @@ func (c *Cache) subscribeToConsul(ctx context.Context, kind string) { entries, meta, err := client.ConfigEntries().List(kind, opts.WithContext(ctx)) if err != nil { - c.logger.Error(err, fmt.Sprintf("error fetching config entries for kind: %s", kind)) + // if we timeout we don't care about the error message because it's expected to happen on long polls + // any other error we want to alert on + if !strings.Contains(strings.ToLower(err.Error()), "timeout") { + c.logger.Error(err, fmt.Sprintf("error fetching config entries for kind: %s", kind)) + } continue } From 9dd605e238dc81a493134793138a2f248c923644 Mon Sep 17 00:00:00 2001 From: Andrew Stucki Date: Tue, 6 Jun 2023 15:21:08 -0400 Subject: [PATCH 206/340] Add Consul status to routes and gateways (#2281) --- .../tests/api-gateway/api_gateway_test.go | 2 + control-plane/api-gateway/binding/binder.go | 8 ++-- .../api-gateway/binding/route_binding.go | 38 ++++++++++++++-- control-plane/api-gateway/common/resources.go | 45 ++++++++++++------- 4 files changed, 70 insertions(+), 23 deletions(-) diff --git a/acceptance/tests/api-gateway/api_gateway_test.go b/acceptance/tests/api-gateway/api_gateway_test.go index 94a91e79c2..2291587bcc 100644 --- a/acceptance/tests/api-gateway/api_gateway_test.go +++ b/acceptance/tests/api-gateway/api_gateway_test.go @@ -110,6 +110,7 @@ func TestAPIGateway_Basic(t *testing.T) { // check our statuses checkStatusCondition(r, gateway.Status.Conditions, trueCondition("Accepted", "Accepted")) + checkStatusCondition(r, gateway.Status.Conditions, trueCondition("ConsulAccepted", "Accepted")) require.Len(r, gateway.Status.Listeners, 3) require.EqualValues(r, 1, gateway.Status.Listeners[0].AttachedRoutes) checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, trueCondition("Accepted", "Accepted")) @@ -157,6 +158,7 @@ func TestAPIGateway_Basic(t *testing.T) { require.EqualValues(t, "gateway", httproute.Status.Parents[0].ParentRef.Name) checkStatusCondition(t, httproute.Status.Parents[0].Conditions, trueCondition("Accepted", "Accepted")) checkStatusCondition(t, httproute.Status.Parents[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) + checkStatusCondition(t, httproute.Status.Parents[0].Conditions, trueCondition("ConsulAccepted", "Accepted")) // check that the Consul entries were created entry, _, err := consulClient.ConfigEntries().Get(api.APIGateway, "gateway", nil) diff --git a/control-plane/api-gateway/binding/binder.go b/control-plane/api-gateway/binding/binder.go index 96056920c3..b677d69253 100644 --- a/control-plane/api-gateway/binding/binder.go +++ b/control-plane/api-gateway/binding/binder.go @@ -152,7 +152,6 @@ func (b *Binder) Snapshot() *Snapshot { // process secrets gatewaySecrets := secretsForGateway(b.config.Gateway, b.config.Resources) - certs := mapset.NewSet() if !isGatewayDeleted { // we only do this if the gateway isn't going to be deleted so that the // resources can get GC'd @@ -163,7 +162,6 @@ func (b *Binder) Snapshot() *Snapshot { b.config.Logger.Error(err, "error parsing referenced secret, ignoring") continue } - certs.Add(secret) } } @@ -180,10 +178,14 @@ func (b *Binder) Snapshot() *Snapshot { snapshot.GatewayClassConfig = gatewayClassConfig snapshot.UpsertGatewayDeployment = true + var consulStatus api.ConfigEntryStatus + if b.config.ConsulGateway != nil { + consulStatus = b.config.ConsulGateway.Status + } entry := b.config.Translator.ToAPIGateway(b.config.Gateway, b.config.Resources) snapshot.Consul.Updates = append(snapshot.Consul.Updates, &common.ConsulUpdateOperation{ Entry: entry, - OnUpdate: b.handleGatewaySyncStatus(snapshot, &b.config.Gateway), + OnUpdate: b.handleGatewaySyncStatus(snapshot, &b.config.Gateway, consulStatus), }) registrations := registrationsForPods(entry.Namespace, b.config.Gateway, registrationPods) diff --git a/control-plane/api-gateway/binding/route_binding.go b/control-plane/api-gateway/binding/route_binding.go index 2a3be4884b..cc33a00e07 100644 --- a/control-plane/api-gateway/binding/route_binding.go +++ b/control-plane/api-gateway/binding/route_binding.go @@ -280,6 +280,7 @@ func (r *Binder) mutateRouteWithBindingResults(snapshot *Snapshot, object client // set the old parent states new.Parents = old.Parents + new.Status = old.Status } // and now add what is left for parent := range parents.Iter() { @@ -299,6 +300,7 @@ func (r *Binder) mutateRouteWithBindingResults(snapshot *Snapshot, object client // set the old parent states new.Parents = old.Parents + new.Status = old.Status } // and now add what is left for parent := range parents.Iter() { @@ -402,8 +404,8 @@ func canReferenceBackend(object client.Object, ref gwv1beta1.BackendRef, resourc return false } -func (r *Binder) handleRouteSyncStatus(snapshot *Snapshot, object client.Object) func(error) { - return func(err error) { +func (r *Binder) handleRouteSyncStatus(snapshot *Snapshot, object client.Object) func(error, api.ConfigEntryStatus) { + return func(err error, status api.ConfigEntryStatus) { condition := metav1.Condition{ Type: "Synced", Status: metav1.ConditionTrue, @@ -425,10 +427,15 @@ func (r *Binder) handleRouteSyncStatus(snapshot *Snapshot, object client.Object) if r.statusSetter.setRouteConditionOnAllRefs(object, condition) { snapshot.Kubernetes.StatusUpdates.Add(object) } + if consulCondition := consulCondition(object.GetGeneration(), status); consulCondition != nil { + if r.statusSetter.setRouteConditionOnAllRefs(object, *consulCondition) { + snapshot.Kubernetes.StatusUpdates.Add(object) + } + } } } -func (r *Binder) handleGatewaySyncStatus(snapshot *Snapshot, gateway *gwv1beta1.Gateway) func(error) { +func (r *Binder) handleGatewaySyncStatus(snapshot *Snapshot, gateway *gwv1beta1.Gateway, status api.ConfigEntryStatus) func(error) { return func(err error) { condition := metav1.Condition{ Type: "Synced", @@ -453,5 +460,30 @@ func (r *Binder) handleGatewaySyncStatus(snapshot *Snapshot, gateway *gwv1beta1. gateway.Status.Conditions = conditions snapshot.Kubernetes.StatusUpdates.Add(gateway) } + + if consulCondition := consulCondition(gateway.Generation, status); consulCondition != nil { + if conditions, updated := setCondition(gateway.Status.Conditions, *consulCondition); updated { + gateway.Status.Conditions = conditions + snapshot.Kubernetes.StatusUpdates.Add(gateway) + } + } } } + +func consulCondition(generation int64, status api.ConfigEntryStatus) *metav1.Condition { + for _, c := range status.Conditions { + // we only care about the top-level status that isn't in reference + // to a resource. + if c.Type == "Accepted" && c.Resource.Name == "" { + return &metav1.Condition{ + Type: "ConsulAccepted", + Reason: c.Reason, + Status: metav1.ConditionStatus(c.Status), + Message: c.Message, + ObservedGeneration: generation, + LastTransitionTime: timeFunc(), + } + } + } + return nil +} diff --git a/control-plane/api-gateway/common/resources.go b/control-plane/api-gateway/common/resources.go index 4cd3bfc3d2..a8f36502d3 100644 --- a/control-plane/api-gateway/common/resources.go +++ b/control-plane/api-gateway/common/resources.go @@ -401,7 +401,7 @@ func (s *ResourceMap) gatewaysForRoute(namespace string, refs []gwv1beta1.Parent return gateways } -func (s *ResourceMap) TranslateAndMutateHTTPRoute(key types.NamespacedName, onUpdate func(error), mutateFn func(old *api.HTTPRouteConfigEntry, new api.HTTPRouteConfigEntry) api.HTTPRouteConfigEntry) { +func (s *ResourceMap) TranslateAndMutateHTTPRoute(key types.NamespacedName, onUpdate func(error, api.ConfigEntryStatus), mutateFn func(old *api.HTTPRouteConfigEntry, new api.HTTPRouteConfigEntry) api.HTTPRouteConfigEntry) { consulKey := NormalizeMeta(s.toConsulReference(api.HTTPRoute, key)) route, ok := s.httpRouteGateways[consulKey] @@ -419,8 +419,10 @@ func (s *ResourceMap) TranslateAndMutateHTTPRoute(key types.NamespacedName, onUp // to be GC'd. delete(s.consulHTTPRoutes, consulKey) s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ - Entry: &mutated, - OnUpdate: onUpdate, + Entry: &mutated, + OnUpdate: func(err error) { + onUpdate(err, mutated.Status) + }, }) } return @@ -431,13 +433,15 @@ func (s *ResourceMap) TranslateAndMutateHTTPRoute(key types.NamespacedName, onUp // to be GC'd. delete(s.consulHTTPRoutes, consulKey) s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ - Entry: &mutated, - OnUpdate: onUpdate, + Entry: &mutated, + OnUpdate: func(err error) { + onUpdate(err, mutated.Status) + }, }) } } -func (s *ResourceMap) MutateHTTPRoute(key types.NamespacedName, onUpdate func(error), mutateFn func(api.HTTPRouteConfigEntry) api.HTTPRouteConfigEntry) { +func (s *ResourceMap) MutateHTTPRoute(key types.NamespacedName, onUpdate func(error, api.ConfigEntryStatus), mutateFn func(api.HTTPRouteConfigEntry) api.HTTPRouteConfigEntry) { consulKey := NormalizeMeta(s.toConsulReference(api.HTTPRoute, key)) consulRoute, ok := s.consulHTTPRoutes[consulKey] @@ -448,8 +452,10 @@ func (s *ResourceMap) MutateHTTPRoute(key types.NamespacedName, onUpdate func(er // to be GC'd. delete(s.consulHTTPRoutes, consulKey) s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ - Entry: &mutated, - OnUpdate: onUpdate, + Entry: &mutated, + OnUpdate: func(err error) { + onUpdate(err, mutated.Status) + }, }) } } @@ -462,12 +468,11 @@ func (s *ResourceMap) CanGCHTTPRouteOnUnbind(id api.ResourceReference) bool { return true } -func (s *ResourceMap) TranslateAndMutateTCPRoute(key types.NamespacedName, onUpdate func(error), mutateFn func(*api.TCPRouteConfigEntry, api.TCPRouteConfigEntry) api.TCPRouteConfigEntry) { +func (s *ResourceMap) TranslateAndMutateTCPRoute(key types.NamespacedName, onUpdate func(error, api.ConfigEntryStatus), mutateFn func(*api.TCPRouteConfigEntry, api.TCPRouteConfigEntry) api.TCPRouteConfigEntry) { consulKey := NormalizeMeta(s.toConsulReference(api.TCPRoute, key)) route, ok := s.tcpRouteGateways[consulKey] if !ok { - return } @@ -481,8 +486,10 @@ func (s *ResourceMap) TranslateAndMutateTCPRoute(key types.NamespacedName, onUpd // to be GC'd. delete(s.consulTCPRoutes, consulKey) s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ - Entry: &mutated, - OnUpdate: onUpdate, + Entry: &mutated, + OnUpdate: func(err error) { + onUpdate(err, mutated.Status) + }, }) } return @@ -493,13 +500,15 @@ func (s *ResourceMap) TranslateAndMutateTCPRoute(key types.NamespacedName, onUpd // to be GC'd. delete(s.consulTCPRoutes, consulKey) s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ - Entry: &mutated, - OnUpdate: onUpdate, + Entry: &mutated, + OnUpdate: func(err error) { + onUpdate(err, mutated.Status) + }, }) } } -func (s *ResourceMap) MutateTCPRoute(key types.NamespacedName, onUpdate func(error), mutateFn func(api.TCPRouteConfigEntry) api.TCPRouteConfigEntry) { +func (s *ResourceMap) MutateTCPRoute(key types.NamespacedName, onUpdate func(error, api.ConfigEntryStatus), mutateFn func(api.TCPRouteConfigEntry) api.TCPRouteConfigEntry) { consulKey := NormalizeMeta(s.toConsulReference(api.TCPRoute, key)) consulRoute, ok := s.consulTCPRoutes[consulKey] @@ -510,8 +519,10 @@ func (s *ResourceMap) MutateTCPRoute(key types.NamespacedName, onUpdate func(err // to be GC'd. delete(s.consulTCPRoutes, consulKey) s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ - Entry: &mutated, - OnUpdate: onUpdate, + Entry: &mutated, + OnUpdate: func(err error) { + onUpdate(err, mutated.Status) + }, }) } } From ee256e98a756bbd98f864f2259cc9fd1f009a4c2 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Tue, 6 Jun 2023 16:03:20 -0400 Subject: [PATCH 207/340] Update alpine to 3.18 to fix CVE-2023-2650 (#2284) * Update alpine to 3.18 --- .changelog/2284.txt | 3 +++ control-plane/Dockerfile | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 .changelog/2284.txt diff --git a/.changelog/2284.txt b/.changelog/2284.txt new file mode 100644 index 0000000000..27c51dff07 --- /dev/null +++ b/.changelog/2284.txt @@ -0,0 +1,3 @@ +```release-note:security +Bump Dockerfile base image to `alpine:3.18`. Resolves [CVE-2023-2650](https://github.com/advisories/GHSA-gqxg-9vfr-p9cg) vulnerability in openssl@3.0.8-r4 +``` diff --git a/control-plane/Dockerfile b/control-plane/Dockerfile index a870e20e2b..de0c1cf1ff 100644 --- a/control-plane/Dockerfile +++ b/control-plane/Dockerfile @@ -22,7 +22,7 @@ RUN CGO_ENABLED=0 go install github.com/hashicorp/go-discover/cmd/discover@49f60 # dev copies the binary from a local build # ----------------------------------- # BIN_NAME is a requirement in the hashicorp docker github action -FROM alpine:3.17 AS dev +FROM alpine:3.18 AS dev # NAME and VERSION are the name of the software in releases.hashicorp.com # and the version to download. Example: NAME=consul VERSION=1.2.3. @@ -74,7 +74,7 @@ CMD /bin/${BIN_NAME} # We don't rebuild the software because we want the exact checksums and # binary signatures to match the software and our builds aren't fully # reproducible currently. -FROM alpine:3.17 AS release-default +FROM alpine:3.18 AS release-default ARG BIN_NAME=consul-k8s-control-plane ARG CNI_BIN_NAME=consul-cni From 49c5219255ee28142693e9ed7498999e8aeb8508 Mon Sep 17 00:00:00 2001 From: John Maguire Date: Tue, 6 Jun 2023 17:28:00 -0400 Subject: [PATCH 208/340] Remove check for reference grant for route to gateway (#2283) * Remove check for reference grant for route to gateway * Fix tenancy tests * Final cleaning up of acceptance test --- .../api-gateway/api_gateway_tenancy_test.go | 19 +-- .../api-gateway/binding/binder_test.go | 24 +-- .../api-gateway/binding/reference_grant.go | 31 +--- .../binding/reference_grant_test.go | 158 ------------------ control-plane/api-gateway/binding/result.go | 15 +- .../api-gateway/binding/route_binding.go | 23 +-- control-plane/api-gateway/common/resources.go | 15 +- .../api-gateway/common/translation_test.go | 9 +- 8 files changed, 37 insertions(+), 257 deletions(-) diff --git a/acceptance/tests/api-gateway/api_gateway_tenancy_test.go b/acceptance/tests/api-gateway/api_gateway_tenancy_test.go index f2e6899094..2f0005da80 100644 --- a/acceptance/tests/api-gateway/api_gateway_tenancy_test.go +++ b/acceptance/tests/api-gateway/api_gateway_tenancy_test.go @@ -19,6 +19,12 @@ import ( "time" terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + "github.com/hashicorp/consul-k8s/acceptance/framework/config" "github.com/hashicorp/consul-k8s/acceptance/framework/consul" "github.com/hashicorp/consul-k8s/acceptance/framework/environment" @@ -28,11 +34,6 @@ import ( "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "github.com/hashicorp/consul/api" "github.com/hashicorp/consul/sdk/testutil/retry" - "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) var ( @@ -142,7 +143,7 @@ func TestAPIGateway_Tenancy(t *testing.T) { checkStatusCondition(r, gateway.Status.Conditions, falseCondition("Synced", "SyncError")) require.Len(r, gateway.Status.Listeners, 1) - require.EqualValues(r, 0, gateway.Status.Listeners[0].AttachedRoutes) + require.EqualValues(r, 1, gateway.Status.Listeners[0].AttachedRoutes) checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, trueCondition("Accepted", "Accepted")) checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, falseCondition("Conflicted", "NoConflicts")) checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, falseCondition("ResolvedRefs", "RefNotPermitted")) @@ -162,19 +163,15 @@ func TestAPIGateway_Tenancy(t *testing.T) { require.EqualValues(r, "gateway", httproute.Status.Parents[0].ParentRef.Name) require.NotNil(r, httproute.Status.Parents[0].ParentRef.Namespace) require.EqualValues(r, gatewayNamespace, *httproute.Status.Parents[0].ParentRef.Namespace) - checkStatusCondition(r, httproute.Status.Parents[0].Conditions, falseCondition("Accepted", "RefNotPermitted")) + checkStatusCondition(r, httproute.Status.Parents[0].Conditions, trueCondition("Accepted", "Accepted")) checkStatusCondition(r, httproute.Status.Parents[0].Conditions, falseCondition("ResolvedRefs", "RefNotPermitted")) }) - // since we're not bound to anything, check to make sure that the route doesn't get created in Consul. - checkConsulNotExists(t, consulClient, api.HTTPRoute, "route", namespaceForConsul(c.namespaceMirroring, routeNamespace)) - // we only sync validly referenced certificates over, so check to make sure it is not created. checkConsulNotExists(t, consulClient, api.InlineCertificate, "certificate", namespaceForConsul(c.namespaceMirroring, certificateNamespace)) // now create reference grants createReferenceGrant(t, k8sClient, "gateway-certificate", gatewayNamespace, certificateNamespace) - createReferenceGrant(t, k8sClient, "route-gateway", routeNamespace, gatewayNamespace) createReferenceGrant(t, k8sClient, "route-service", routeNamespace, serviceNamespace) // gateway updated with references allowed diff --git a/control-plane/api-gateway/binding/binder_test.go b/control-plane/api-gateway/binding/binder_test.go index 065290f56a..65cca94419 100644 --- a/control-plane/api-gateway/binding/binder_test.go +++ b/control-plane/api-gateway/binding/binder_test.go @@ -15,9 +15,6 @@ import ( logrtest "github.com/go-logr/logr/testing" "github.com/google/go-cmp/cmp" - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul/api" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -25,6 +22,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul/api" ) func init() { @@ -34,9 +35,8 @@ func init() { } const ( - testGatewayClassName = "gateway-class" - testControllerName = "test-controller" - routeListenerReferenceGrantErrorMessage = `http-listener-allowed-selector: reference not permitted due to lack of ReferenceGrant; http-listener-default-same: reference not permitted due to lack of ReferenceGrant; http-listener-explicit-all-allowed: reference not permitted due to lack of ReferenceGrant; http-listener-explicit-allowed-same: reference not permitted due to lack of ReferenceGrant; http-listener-hostname: reference not permitted due to lack of ReferenceGrant; http-listener-mismatched-kind-allowed: reference not permitted due to lack of ReferenceGrant; http-listener-tls: reference not permitted due to lack of ReferenceGrant; tcp-listener-allowed-selector: reference not permitted due to lack of ReferenceGrant; tcp-listener-default-same: reference not permitted due to lack of ReferenceGrant; tcp-listener-explicit-all-allowed: reference not permitted due to lack of ReferenceGrant; tcp-listener-explicit-allowed-same: reference not permitted due to lack of ReferenceGrant; tcp-listener-mismatched-kind-allowed: reference not permitted due to lack of ReferenceGrant; tcp-listener-tls: reference not permitted due to lack of ReferenceGrant` + testGatewayClassName = "gateway-class" + testControllerName = "test-controller" ) var ( @@ -1113,9 +1113,9 @@ func TestBinder_BindingRulesKitchenSink(t *testing.T) { Message: "resolved backend references", }, { Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: routeListenerReferenceGrantErrorMessage, + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", }, }}, }), @@ -1628,9 +1628,9 @@ func TestBinder_BindingRulesKitchenSink(t *testing.T) { Message: "resolved backend references", }, { Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: routeListenerReferenceGrantErrorMessage, + Status: metav1.ConditionTrue, + Reason: "Accepted", + Message: "route accepted", }, }}, }), diff --git a/control-plane/api-gateway/binding/reference_grant.go b/control-plane/api-gateway/binding/reference_grant.go index 12c0f3b048..c2cc421a30 100644 --- a/control-plane/api-gateway/binding/reference_grant.go +++ b/control-plane/api-gateway/binding/reference_grant.go @@ -4,12 +4,13 @@ package binding import ( - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" ) type referenceValidator struct { @@ -45,20 +46,6 @@ func (rv *referenceValidator) GatewayCanReferenceSecret(gateway gwv1beta1.Gatewa return rv.referenceAllowed(fromGK, fromNS, toGK, toNS, string(secretRef.Name)) } -func (rv *referenceValidator) HTTPRouteCanReferenceGateway(httproute gwv1beta1.HTTPRoute, parentRef gwv1beta1.ParentReference) bool { - fromNS := httproute.GetNamespace() - fromGK := metav1.GroupKind{ - Group: httproute.GroupVersionKind().Group, - Kind: httproute.GroupVersionKind().Kind, - } - - // Kind should default to Gateway if not set - // https://github.com/kubernetes-sigs/gateway-api/blob/v0.6.2/apis/v1beta1/shared_types.go#L48 - toNS, toGK := createValuesFromRef(parentRef.Namespace, parentRef.Group, parentRef.Kind, common.BetaGroup, common.KindGateway) - - return rv.referenceAllowed(fromGK, fromNS, toGK, toNS, string(parentRef.Name)) -} - func (rv *referenceValidator) HTTPRouteCanReferenceBackend(httproute gwv1beta1.HTTPRoute, backendRef gwv1beta1.BackendRef) bool { fromNS := httproute.GetNamespace() fromGK := metav1.GroupKind{ @@ -73,20 +60,6 @@ func (rv *referenceValidator) HTTPRouteCanReferenceBackend(httproute gwv1beta1.H return rv.referenceAllowed(fromGK, fromNS, toGK, toNS, string(backendRef.Name)) } -func (rv *referenceValidator) TCPRouteCanReferenceGateway(tcpRoute gwv1alpha2.TCPRoute, parentRef gwv1beta1.ParentReference) bool { - fromNS := tcpRoute.GetNamespace() - fromGK := metav1.GroupKind{ - Group: tcpRoute.GroupVersionKind().Group, - Kind: tcpRoute.GroupVersionKind().Kind, - } - - // Kind should default to Gateway if not set - // https://github.com/kubernetes-sigs/gateway-api/blob/v0.6.2/apis/v1beta1/shared_types.go#L48 - toNS, toGK := createValuesFromRef(parentRef.Namespace, parentRef.Group, parentRef.Kind, common.BetaGroup, common.KindGateway) - - return rv.referenceAllowed(fromGK, fromNS, toGK, toNS, string(parentRef.Name)) -} - func (rv *referenceValidator) TCPRouteCanReferenceBackend(tcpRoute gwv1alpha2.TCPRoute, backendRef gwv1beta1.BackendRef) bool { fromNS := tcpRoute.GetNamespace() fromGK := metav1.GroupKind{ diff --git a/control-plane/api-gateway/binding/reference_grant_test.go b/control-plane/api-gateway/binding/reference_grant_test.go index a325a2e927..12f01478fc 100644 --- a/control-plane/api-gateway/binding/reference_grant_test.go +++ b/control-plane/api-gateway/binding/reference_grant_test.go @@ -105,85 +105,6 @@ func TestGatewayCanReferenceSecret(t *testing.T) { } } -func TestHTTPRouteCanReferenceGateway(t *testing.T) { - t.Parallel() - - objName := gwv1beta1.ObjectName("mygateway") - - basicValidReferenceGrant := gwv1beta1.ReferenceGrant{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: ToNamespace, - }, - Spec: gwv1beta1.ReferenceGrantSpec{ - From: []gwv1beta1.ReferenceGrantFrom{ - { - Group: Group, - Kind: HTTPRouteKind, - Namespace: FromNamespace, - }, - }, - To: []gwv1beta1.ReferenceGrantTo{ - { - Group: Group, - Kind: GatewayKind, - Name: &objName, - }, - }, - }, - } - - gatewayRefGroup := gwv1beta1.Group(Group) - gatewayRefKind := gwv1beta1.Kind(GatewayKind) - gatewayRefNamespace := gwv1beta1.Namespace(ToNamespace) - - cases := map[string]struct { - canReference bool - err error - ctx context.Context - httpRoute gwv1beta1.HTTPRoute - gatewayRef gwv1beta1.ParentReference - k8sReferenceGrants []gwv1beta1.ReferenceGrant - }{ - "httproute allowed to gateway": { - canReference: true, - err: nil, - ctx: context.TODO(), - httpRoute: gwv1beta1.HTTPRoute{ - TypeMeta: metav1.TypeMeta{ - Kind: HTTPRouteKind, - APIVersion: Group + V1Beta1, - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: FromNamespace, - }, - Spec: gwv1beta1.HTTPRouteSpec{}, - Status: gwv1beta1.HTTPRouteStatus{}, - }, - gatewayRef: gwv1beta1.ParentReference{ - Group: &gatewayRefGroup, - Kind: &gatewayRefKind, - Namespace: &gatewayRefNamespace, - Name: objName, - SectionName: nil, - Port: nil, - }, - k8sReferenceGrants: []gwv1beta1.ReferenceGrant{ - basicValidReferenceGrant, - }, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - rv := NewReferenceValidator(tc.k8sReferenceGrants) - canReference := rv.HTTPRouteCanReferenceGateway(tc.httpRoute, tc.gatewayRef) - - require.Equal(t, tc.canReference, canReference) - }) - } -} - func TestHTTPRouteCanReferenceBackend(t *testing.T) { t.Parallel() @@ -265,85 +186,6 @@ func TestHTTPRouteCanReferenceBackend(t *testing.T) { } } -func TestTCPRouteCanReferenceGateway(t *testing.T) { - t.Parallel() - - objName := gwv1beta1.ObjectName("mygateway") - - basicValidReferenceGrant := gwv1beta1.ReferenceGrant{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: ToNamespace, - }, - Spec: gwv1beta1.ReferenceGrantSpec{ - From: []gwv1beta1.ReferenceGrantFrom{ - { - Group: Group, - Kind: TCPRouteKind, - Namespace: FromNamespace, - }, - }, - To: []gwv1beta1.ReferenceGrantTo{ - { - Group: Group, - Kind: GatewayKind, - Name: &objName, - }, - }, - }, - } - - gatewayRefGroup := gwv1beta1.Group(Group) - gatewayRefKind := gwv1beta1.Kind(GatewayKind) - gatewayRefNamespace := gwv1beta1.Namespace(ToNamespace) - - cases := map[string]struct { - canReference bool - err error - ctx context.Context - tcpRoute gwv1alpha2.TCPRoute - gatewayRef gwv1beta1.ParentReference - k8sReferenceGrants []gwv1beta1.ReferenceGrant - }{ - "tcpRoute allowed to gateway": { - canReference: true, - err: nil, - ctx: context.TODO(), - tcpRoute: gwv1alpha2.TCPRoute{ - TypeMeta: metav1.TypeMeta{ - Kind: TCPRouteKind, - APIVersion: Group + V1Alpha2, - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: FromNamespace, - }, - Spec: gwv1alpha2.TCPRouteSpec{}, - Status: gwv1alpha2.TCPRouteStatus{}, - }, - gatewayRef: gwv1beta1.ParentReference{ - Group: &gatewayRefGroup, - Kind: &gatewayRefKind, - Namespace: &gatewayRefNamespace, - Name: objName, - SectionName: nil, - Port: nil, - }, - k8sReferenceGrants: []gwv1beta1.ReferenceGrant{ - basicValidReferenceGrant, - }, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - rv := NewReferenceValidator(tc.k8sReferenceGrants) - canReference := rv.TCPRouteCanReferenceGateway(tc.tcpRoute, tc.gatewayRef) - - require.Equal(t, tc.canReference, canReference) - }) - } -} - func TestTCPRouteCanReferenceBackend(t *testing.T) { t.Parallel() diff --git a/control-plane/api-gateway/binding/result.go b/control-plane/api-gateway/binding/result.go index 65198eeaf4..dd82cd55b5 100644 --- a/control-plane/api-gateway/binding/result.go +++ b/control-plane/api-gateway/binding/result.go @@ -10,21 +10,18 @@ import ( "strings" mapset "github.com/deckarep/golang-set" - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) -var ( - // override function for tests. - timeFunc = metav1.Now + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" ) -var ( - // This is used for any error related to a lack of proper reference grant creation. - errRefNotPermitted = errors.New("reference not permitted due to lack of ReferenceGrant") -) +// override function for tests. +var timeFunc = metav1.Now + +// This is used for any error related to a lack of proper reference grant creation. +var errRefNotPermitted = errors.New("reference not permitted due to lack of ReferenceGrant") var ( // Each of the below are specified in the Gateway spec under RouteConditionReason diff --git a/control-plane/api-gateway/binding/route_binding.go b/control-plane/api-gateway/binding/route_binding.go index cc33a00e07..93e68241a0 100644 --- a/control-plane/api-gateway/binding/route_binding.go +++ b/control-plane/api-gateway/binding/route_binding.go @@ -5,13 +5,14 @@ package binding import ( mapset "github.com/deckarep/golang-set" - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" + "github.com/hashicorp/consul/api" ) // bindRoute contains the main logic for binding a route to a given gateway. @@ -104,14 +105,6 @@ func (r *Binder) bindRoute(route client.Object, boundCount map[gwv1beta1.Section var result bindResults for _, listener := range listenersFor(&r.config.Gateway, ref.SectionName) { - if !canReferenceGateway(route, ref, r.config.Resources) { - result = append(result, bindResult{ - section: listener.Name, - err: errRefNotPermitted, - }) - continue - } - if !routeKindIsAllowedForListener(supportedKindsForProtocol[listener.Protocol], groupKind) { result = append(result, bindResult{ section: listener.Name, @@ -384,16 +377,6 @@ func getRouteBackends(object client.Object) []gwv1beta1.BackendRef { return nil } -func canReferenceGateway(object client.Object, ref gwv1beta1.ParentReference, resources *common.ResourceMap) bool { - switch v := object.(type) { - case *gwv1beta1.HTTPRoute: - return resources.HTTPRouteCanReferenceGateway(*v, ref) - case *gwv1alpha2.TCPRoute: - return resources.TCPRouteCanReferenceGateway(*v, ref) - } - return false -} - func canReferenceBackend(object client.Object, ref gwv1beta1.BackendRef, resources *common.ResourceMap) bool { switch v := object.(type) { case *gwv1beta1.HTTPRoute: diff --git a/control-plane/api-gateway/common/resources.go b/control-plane/api-gateway/common/resources.go index a8f36502d3..d412c01eee 100644 --- a/control-plane/api-gateway/common/resources.go +++ b/control-plane/api-gateway/common/resources.go @@ -6,13 +6,14 @@ package common import ( mapset "github.com/deckarep/golang-set" "github.com/go-logr/logr" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul/api" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul/api" ) // ConsulUpdateOperation is an operation representing an @@ -61,9 +62,7 @@ func (k *KubernetesUpdates) Operations() []client.Object { type ReferenceValidator interface { GatewayCanReferenceSecret(gateway gwv1beta1.Gateway, secretRef gwv1beta1.SecretObjectReference) bool - HTTPRouteCanReferenceGateway(httproute gwv1beta1.HTTPRoute, parentRef gwv1beta1.ParentReference) bool HTTPRouteCanReferenceBackend(httproute gwv1beta1.HTTPRoute, backendRef gwv1beta1.BackendRef) bool - TCPRouteCanReferenceGateway(tcpRoute gwv1alpha2.TCPRoute, parentRef gwv1beta1.ParentReference) bool TCPRouteCanReferenceBackend(tcpRoute gwv1alpha2.TCPRoute, backendRef gwv1beta1.BackendRef) bool } @@ -606,14 +605,6 @@ func (s *ResourceMap) HTTPRouteCanReferenceBackend(route gwv1beta1.HTTPRoute, re return s.referenceValidator.HTTPRouteCanReferenceBackend(route, ref) } -func (s *ResourceMap) HTTPRouteCanReferenceGateway(route gwv1beta1.HTTPRoute, ref gwv1beta1.ParentReference) bool { - return s.referenceValidator.HTTPRouteCanReferenceGateway(route, ref) -} - func (s *ResourceMap) TCPRouteCanReferenceBackend(route gwv1alpha2.TCPRoute, ref gwv1beta1.BackendRef) bool { return s.referenceValidator.TCPRouteCanReferenceBackend(route, ref) } - -func (s *ResourceMap) TCPRouteCanReferenceGateway(route gwv1alpha2.TCPRoute, ref gwv1beta1.ParentReference) bool { - return s.referenceValidator.TCPRouteCanReferenceGateway(route, ref) -} diff --git a/control-plane/api-gateway/common/translation_test.go b/control-plane/api-gateway/common/translation_test.go index 029e4affa7..2c735ad4ac 100644 --- a/control-plane/api-gateway/common/translation_test.go +++ b/control-plane/api-gateway/common/translation_test.go @@ -22,6 +22,7 @@ import ( gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" logrtest "github.com/go-logr/logr/testing" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" "github.com/hashicorp/consul/api" @@ -32,15 +33,11 @@ type fakeReferenceValidator struct{} func (v fakeReferenceValidator) GatewayCanReferenceSecret(gateway gwv1beta1.Gateway, secretRef gwv1beta1.SecretObjectReference) bool { return true } -func (v fakeReferenceValidator) HTTPRouteCanReferenceGateway(httproute gwv1beta1.HTTPRoute, parentRef gwv1beta1.ParentReference) bool { - return true -} + func (v fakeReferenceValidator) HTTPRouteCanReferenceBackend(httproute gwv1beta1.HTTPRoute, backendRef gwv1beta1.BackendRef) bool { return true } -func (v fakeReferenceValidator) TCPRouteCanReferenceGateway(tcpRoute gwv1alpha2.TCPRoute, parentRef gwv1beta1.ParentReference) bool { - return true -} + func (v fakeReferenceValidator) TCPRouteCanReferenceBackend(tcpRoute gwv1alpha2.TCPRoute, backendRef gwv1beta1.BackendRef) bool { return true } From 31269554d83424ebffb611f6ece781c9fddce40f Mon Sep 17 00:00:00 2001 From: Andrew Stucki Date: Tue, 6 Jun 2023 21:17:47 -0400 Subject: [PATCH 209/340] [API Gateway] Add partition test (#2278) * Add partition test * drop superfluous sprintf * fix linter issue on acceptance test * Add predicated watch for pods --- .../bases/api-gateway/kustomization.yaml | 3 +- .../bases/api-gateway/meshservice.yaml | 9 + .../api-gateways/mesh/kustomization.yaml | 5 + .../api-gateways/mesh/proxydefaults.yaml | 12 + .../api-gateways/resolver/kustomization.yaml | 5 + .../resolver/serviceresolver.yaml | 12 + .../partitions/partitions_gateway_test.go | 340 ++++++++++++++++++ control-plane/api-gateway/cache/consul.go | 152 ++++++-- control-plane/api-gateway/common/labels.go | 16 +- .../controllers/gateway_controller.go | 42 ++- .../api-gateway/gatekeeper/dataplane.go | 3 - control-plane/api-gateway/gatekeeper/init.go | 5 - .../subcommand/inject-connect/command.go | 2 +- 13 files changed, 551 insertions(+), 55 deletions(-) create mode 100644 acceptance/tests/fixtures/bases/api-gateway/meshservice.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/mesh/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/mesh/proxydefaults.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/resolver/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/resolver/serviceresolver.yaml create mode 100644 acceptance/tests/partitions/partitions_gateway_test.go diff --git a/acceptance/tests/fixtures/bases/api-gateway/kustomization.yaml b/acceptance/tests/fixtures/bases/api-gateway/kustomization.yaml index 2049f1af0b..e2125414d9 100644 --- a/acceptance/tests/fixtures/bases/api-gateway/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/api-gateway/kustomization.yaml @@ -5,4 +5,5 @@ resources: - gatewayclassconfig.yaml - gatewayclass.yaml - apigateway.yaml - - httproute.yaml \ No newline at end of file + - httproute.yaml + - meshservice.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/api-gateway/meshservice.yaml b/acceptance/tests/fixtures/bases/api-gateway/meshservice.yaml new file mode 100644 index 0000000000..4c32452bc3 --- /dev/null +++ b/acceptance/tests/fixtures/bases/api-gateway/meshservice.yaml @@ -0,0 +1,9 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: MeshService +metadata: + name: mesh-service +spec: + name: static-server \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/api-gateways/mesh/kustomization.yaml b/acceptance/tests/fixtures/cases/api-gateways/mesh/kustomization.yaml new file mode 100644 index 0000000000..c271e6af8b --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/mesh/kustomization.yaml @@ -0,0 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - proxydefaults.yaml diff --git a/acceptance/tests/fixtures/cases/api-gateways/mesh/proxydefaults.yaml b/acceptance/tests/fixtures/cases/api-gateways/mesh/proxydefaults.yaml new file mode 100644 index 0000000000..ccc0905e32 --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/mesh/proxydefaults.yaml @@ -0,0 +1,12 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ProxyDefaults +metadata: + name: global +spec: + config: + protocol: http + meshGateway: + mode: local \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/api-gateways/resolver/kustomization.yaml b/acceptance/tests/fixtures/cases/api-gateways/resolver/kustomization.yaml new file mode 100644 index 0000000000..cdbcd688c0 --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/resolver/kustomization.yaml @@ -0,0 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - serviceresolver.yaml diff --git a/acceptance/tests/fixtures/cases/api-gateways/resolver/serviceresolver.yaml b/acceptance/tests/fixtures/cases/api-gateways/resolver/serviceresolver.yaml new file mode 100644 index 0000000000..18960a37db --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/resolver/serviceresolver.yaml @@ -0,0 +1,12 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ServiceResolver +metadata: + name: static-server +spec: + redirect: + partition: default + namespace: ns1 + service: static-server \ No newline at end of file diff --git a/acceptance/tests/partitions/partitions_gateway_test.go b/acceptance/tests/partitions/partitions_gateway_test.go new file mode 100644 index 0000000000..06bc933ce8 --- /dev/null +++ b/acceptance/tests/partitions/partitions_gateway_test.go @@ -0,0 +1,340 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package partitions + +import ( + "context" + "fmt" + "strconv" + "testing" + "time" + + terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" + "github.com/hashicorp/consul-k8s/acceptance/framework/consul" + "github.com/hashicorp/consul-k8s/acceptance/framework/environment" + "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" + "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" + "github.com/hashicorp/consul-k8s/acceptance/framework/logger" + "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/sdk/testutil/retry" + "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/types" + + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +// Test that Gateway works in a default and ACLsEnabled installations for X-Partition and in-partition networking. +func TestPartitions_Gateway(t *testing.T) { + env := suite.Environment() + cfg := suite.Config() + + if !cfg.EnableEnterprise { + t.Skipf("skipping this test because -enable-enterprise is not set") + } + + const defaultPartition = "default" + const secondaryPartition = "secondary" + + defaultPartitionClusterContext := env.DefaultContext(t) + secondaryPartitionClusterContext := env.Context(t, environment.SecondaryContextName) + + commonHelmValues := map[string]string{ + "global.adminPartitions.enabled": "true", + "global.enableConsulNamespaces": "true", + "global.logLevel": "debug", + + "global.tls.enabled": "true", + "global.tls.httpsOnly": "true", + + "global.acls.manageSystemACLs": "true", + + "connectInject.enabled": "true", + // When mirroringK8S is set, this setting is ignored. + "connectInject.consulNamespaces.consulDestinationNamespace": staticServerNamespace, + "connectInject.consulNamespaces.mirroringK8S": "true", + + "meshGateway.enabled": "true", + "meshGateway.replicas": "1", + + "dns.enabled": "true", + "dns.enableRedirection": strconv.FormatBool(cfg.EnableTransparentProxy), + } + + defaultPartitionHelmValues := make(map[string]string) + + // On Kind, there are no load balancers but since all clusters + // share the same node network (docker bridge), we can use + // a NodePort service so that we can access node(s) in a different Kind cluster. + if cfg.UseKind { + defaultPartitionHelmValues["meshGateway.service.type"] = "NodePort" + defaultPartitionHelmValues["meshGateway.service.nodePort"] = "30200" // todo: do we need to set this port? + defaultPartitionHelmValues["server.exposeService.type"] = "NodePort" + defaultPartitionHelmValues["server.exposeService.nodePort.https"] = "30000" + defaultPartitionHelmValues["server.exposeService.nodePort.grpc"] = "30100" + } + + releaseName := helpers.RandomName() + + helpers.MergeMaps(defaultPartitionHelmValues, commonHelmValues) + + // Install the consul cluster with servers in the default kubernetes context. + serverConsulCluster := consul.NewHelmCluster(t, defaultPartitionHelmValues, defaultPartitionClusterContext, cfg, releaseName) + serverConsulCluster.Create(t) + + // Get the TLS CA certificate and key secret from the server cluster and apply it to the client cluster. + caCertSecretName := fmt.Sprintf("%s-consul-ca-cert", releaseName) + + logger.Logf(t, "retrieving ca cert secret %s from the server cluster and applying to the client cluster", caCertSecretName) + k8s.CopySecret(t, defaultPartitionClusterContext, secondaryPartitionClusterContext, caCertSecretName) + + partitionToken := fmt.Sprintf("%s-consul-partitions-acl-token", releaseName) + logger.Logf(t, "retrieving partition token secret %s from the server cluster and applying to the client cluster", partitionToken) + k8s.CopySecret(t, defaultPartitionClusterContext, secondaryPartitionClusterContext, partitionToken) + + partitionServiceName := fmt.Sprintf("%s-consul-expose-servers", releaseName) + partitionSvcAddress := k8s.ServiceHost(t, cfg, defaultPartitionClusterContext, partitionServiceName) + + k8sAuthMethodHost := k8s.KubernetesAPIServerHost(t, cfg, secondaryPartitionClusterContext) + + // Create client cluster. + secondaryPartitionHelmValues := map[string]string{ + "global.enabled": "false", + + "global.adminPartitions.name": secondaryPartition, + + "global.tls.caCert.secretName": caCertSecretName, + "global.tls.caCert.secretKey": "tls.crt", + + "externalServers.enabled": "true", + "externalServers.hosts[0]": partitionSvcAddress, + "externalServers.tlsServerName": "server.dc1.consul", + } + + // Setup partition token and auth method host since ACLs enabled. + secondaryPartitionHelmValues["global.acls.bootstrapToken.secretName"] = partitionToken + secondaryPartitionHelmValues["global.acls.bootstrapToken.secretKey"] = "token" + secondaryPartitionHelmValues["externalServers.k8sAuthMethodHost"] = k8sAuthMethodHost + + if cfg.UseKind { + secondaryPartitionHelmValues["externalServers.httpsPort"] = "30000" + secondaryPartitionHelmValues["externalServers.grpcPort"] = "30100" + secondaryPartitionHelmValues["meshGateway.service.type"] = "NodePort" + secondaryPartitionHelmValues["meshGateway.service.nodePort"] = "30200" + } + + helpers.MergeMaps(secondaryPartitionHelmValues, commonHelmValues) + + // Install the consul cluster without servers in the client cluster kubernetes context. + clientConsulCluster := consul.NewHelmCluster(t, secondaryPartitionHelmValues, secondaryPartitionClusterContext, cfg, releaseName) + clientConsulCluster.Create(t) + + defaultPartitionClusterStaticServerOpts := &terratestk8s.KubectlOptions{ + ContextName: defaultPartitionClusterContext.KubectlOptions(t).ContextName, + ConfigPath: defaultPartitionClusterContext.KubectlOptions(t).ConfigPath, + Namespace: staticServerNamespace, + } + secondaryPartitionClusterStaticServerOpts := &terratestk8s.KubectlOptions{ + ContextName: secondaryPartitionClusterContext.KubectlOptions(t).ContextName, + ConfigPath: secondaryPartitionClusterContext.KubectlOptions(t).ConfigPath, + Namespace: staticServerNamespace, + } + secondaryPartitionClusterStaticClientOpts := &terratestk8s.KubectlOptions{ + ContextName: secondaryPartitionClusterContext.KubectlOptions(t).ContextName, + ConfigPath: secondaryPartitionClusterContext.KubectlOptions(t).ConfigPath, + Namespace: StaticClientNamespace, + } + + logger.Logf(t, "creating namespaces %s and %s in servers cluster", staticServerNamespace, StaticClientNamespace) + k8s.RunKubectl(t, defaultPartitionClusterContext.KubectlOptions(t), "create", "ns", staticServerNamespace) + k8s.RunKubectl(t, defaultPartitionClusterContext.KubectlOptions(t), "create", "ns", StaticClientNamespace) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.RunKubectl(t, defaultPartitionClusterContext.KubectlOptions(t), "delete", "ns", staticServerNamespace, StaticClientNamespace) + }) + + logger.Logf(t, "creating namespaces %s and %s in clients cluster", staticServerNamespace, StaticClientNamespace) + k8s.RunKubectl(t, secondaryPartitionClusterContext.KubectlOptions(t), "create", "ns", staticServerNamespace) + k8s.RunKubectl(t, secondaryPartitionClusterContext.KubectlOptions(t), "create", "ns", StaticClientNamespace) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.RunKubectl(t, secondaryPartitionClusterContext.KubectlOptions(t), "delete", "ns", staticServerNamespace, StaticClientNamespace) + }) + + consulClient, _ := serverConsulCluster.SetupConsulClient(t, true) + + serverQueryServerOpts := &api.QueryOptions{Namespace: staticServerNamespace, Partition: defaultPartition} + clientQueryServerOpts := &api.QueryOptions{Namespace: StaticClientNamespace, Partition: defaultPartition} + + serverQueryClientOpts := &api.QueryOptions{Namespace: staticServerNamespace, Partition: secondaryPartition} + clientQueryClientOpts := &api.QueryOptions{Namespace: StaticClientNamespace, Partition: secondaryPartition} + + // We need to register the cleanup function before we create the deployments + // because golang will execute them in reverse order i.e. the last registered + // cleanup function will be executed first. + t.Cleanup(func() { + retry.Run(t, func(r *retry.R) { + tokens, _, err := consulClient.ACL().TokenList(serverQueryServerOpts) + require.NoError(r, err) + for _, token := range tokens { + require.NotContains(r, token.Description, staticServerName) + } + + tokens, _, err = consulClient.ACL().TokenList(clientQueryServerOpts) + require.NoError(r, err) + for _, token := range tokens { + require.NotContains(r, token.Description, StaticClientName) + } + tokens, _, err = consulClient.ACL().TokenList(serverQueryClientOpts) + require.NoError(r, err) + for _, token := range tokens { + require.NotContains(r, token.Description, staticServerName) + } + + tokens, _, err = consulClient.ACL().TokenList(clientQueryClientOpts) + require.NoError(r, err) + for _, token := range tokens { + require.NotContains(r, token.Description, StaticClientName) + } + }) + }) + + // Create a ProxyDefaults resource to configure services to use the mesh + // gateways. + logger.Log(t, "creating proxy-defaults config") + kustomizeDir := "../fixtures/cases/api-gateways/mesh" + + k8s.KubectlApplyK(t, defaultPartitionClusterContext.KubectlOptions(t), kustomizeDir) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.KubectlDeleteK(t, defaultPartitionClusterContext.KubectlOptions(t), kustomizeDir) + }) + + k8s.KubectlApplyK(t, secondaryPartitionClusterContext.KubectlOptions(t), kustomizeDir) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.KubectlDeleteK(t, secondaryPartitionClusterContext.KubectlOptions(t), kustomizeDir) + }) + + // We use the static-client pod so that we can make calls to the api gateway + // via kubectl exec without needing a route into the cluster from the test machine. + // Since we're deploying the gateway in the secondary cluster, we create the static client + // in the secondary as well. + logger.Log(t, "creating static-client pod in secondary partition cluster") + k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-client") + + logger.Log(t, "creating api-gateway resources") + out, err := k8s.RunKubectlAndGetOutputE(t, secondaryPartitionClusterStaticServerOpts, "apply", "-k", "../fixtures/bases/api-gateway") + require.NoError(t, err, out) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + // Ignore errors here because if the test ran as expected + // the custom resources will have been deleted. + k8s.RunKubectlAndGetOutputE(t, secondaryPartitionClusterStaticServerOpts, "delete", "-k", "../fixtures/bases/api-gateway") + }) + + // Grab a kubernetes client so that we can verify binding + // behavior prior to issuing requests through the gateway. + k8sClient := secondaryPartitionClusterContext.ControllerRuntimeClient(t) + + // On startup, the controller can take upwards of 1m to perform + // leader election so we may need to wait a long time for + // the reconcile loop to run (hence the 1m timeout here). + var gatewayAddress string + counter := &retry.Counter{Count: 600, Wait: 2 * time.Second} + retry.RunWith(counter, t, func(r *retry.R) { + var gateway gwv1beta1.Gateway + err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway", Namespace: staticServerNamespace}, &gateway) + require.NoError(r, err) + + // check that we have an address to use + require.Len(r, gateway.Status.Addresses, 1) + // now we know we have an address, set it so we can use it + gatewayAddress = gateway.Status.Addresses[0].Value + }) + + targetAddress := fmt.Sprintf("http://%s/", gatewayAddress) + + // This section of the tests runs the in-partition networking tests. + t.Run("in-partition", func(t *testing.T) { + logger.Log(t, "test in-partition networking") + logger.Log(t, "creating target server in secondary partition cluster") + k8s.DeployKustomize(t, secondaryPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + + logger.Log(t, "patching route to target server") + k8s.RunKubectl(t, secondaryPartitionClusterStaticServerOpts, "patch", "httproute", "http-route", "-p", `{"spec":{"rules":[{"backendRefs":[{"group":"consul.hashicorp.com","kind":"MeshService","name":"mesh-service","port":80}]}]}}`, "--type=merge") + + logger.Log(t, "checking that the connection is not successful because there's no intention") + k8s.CheckStaticServerHTTPConnectionFailing(t, secondaryPartitionClusterStaticClientOpts, StaticClientName, targetAddress) + + intention := &api.ServiceIntentionsConfigEntry{ + Kind: api.ServiceIntentions, + Name: staticServerName, + Namespace: staticServerNamespace, + Sources: []*api.SourceIntention{ + { + Name: "gateway", + Namespace: staticServerNamespace, + Action: api.IntentionActionAllow, + }, + }, + } + + logger.Log(t, "creating intention") + _, _, err = consulClient.ConfigEntries().Set(intention, &api.WriteOptions{Partition: secondaryPartition}) + require.NoError(t, err) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + _, err = consulClient.ConfigEntries().Delete(api.ServiceIntentions, staticServerName, &api.WriteOptions{Partition: secondaryPartition}) + require.NoError(t, err) + }) + + logger.Log(t, "checking that connection is successful") + k8s.CheckStaticServerConnectionSuccessful(t, secondaryPartitionClusterStaticClientOpts, StaticClientName, targetAddress) + }) + + // This section of the tests runs the cross-partition networking tests. + t.Run("cross-partition", func(t *testing.T) { + logger.Log(t, "test cross-partition networking") + + logger.Log(t, "creating target server in default partition cluster") + k8s.DeployKustomize(t, defaultPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + + logger.Log(t, "creating exported services") + k8s.KubectlApplyK(t, defaultPartitionClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/default-partition-ns1") + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.KubectlDeleteK(t, defaultPartitionClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/default-partition-ns1") + }) + + logger.Log(t, "creating local service resolver") + k8s.KubectlApplyK(t, secondaryPartitionClusterStaticServerOpts, "../fixtures/cases/api-gateways/resolver") + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.KubectlDeleteK(t, secondaryPartitionClusterStaticServerOpts, "../fixtures/cases/api-gateways/resolver") + }) + + logger.Log(t, "patching route to target server") + k8s.RunKubectl(t, secondaryPartitionClusterStaticServerOpts, "patch", "httproute", "http-route", "-p", `{"spec":{"rules":[{"backendRefs":[{"group":"consul.hashicorp.com","kind":"MeshService","name":"mesh-service","port":80}]}]}}`, "--type=merge") + + logger.Log(t, "checking that the connection is not successful because there's no intention") + k8s.CheckStaticServerHTTPConnectionFailing(t, secondaryPartitionClusterStaticClientOpts, StaticClientName, targetAddress) + + intention := &api.ServiceIntentionsConfigEntry{ + Kind: api.ServiceIntentions, + Name: staticServerName, + Namespace: staticServerNamespace, + Sources: []*api.SourceIntention{ + { + Name: "gateway", + Namespace: staticServerNamespace, + Action: api.IntentionActionAllow, + Partition: secondaryPartition, + }, + }, + } + + logger.Log(t, "creating intention") + _, _, err = consulClient.ConfigEntries().Set(intention, &api.WriteOptions{Partition: defaultPartition}) + require.NoError(t, err) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + _, err = consulClient.ConfigEntries().Delete(api.ServiceIntentions, staticServerName, &api.WriteOptions{Partition: defaultPartition}) + require.NoError(t, err) + }) + + logger.Log(t, "checking that connection is successful") + k8s.CheckStaticServerConnectionSuccessful(t, secondaryPartitionClusterStaticClientOpts, StaticClientName, targetAddress) + }) +} diff --git a/control-plane/api-gateway/cache/consul.go b/control-plane/api-gateway/cache/consul.go index 9acfb2b1ee..e47df71522 100644 --- a/control-plane/api-gateway/cache/consul.go +++ b/control-plane/api-gateway/cache/consul.go @@ -4,10 +4,12 @@ package cache import ( + "bytes" "context" "fmt" "strings" "sync" + "text/template" "time" "github.com/go-logr/logr" @@ -20,6 +22,33 @@ import ( "github.com/hashicorp/consul/api" ) +func init() { + gatewayTpl = template.Must(template.New("root").Parse(strings.TrimSpace(gatewayRulesTpl))) +} + +type templateArgs struct { + EnableNamespaces bool +} + +var ( + gatewayTpl *template.Template + gatewayRulesTpl = ` +mesh = "read" +{{- if .EnableNamespaces }} + namespace_prefix "" { +{{- end }} + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "write" + } +{{- if .EnableNamespaces }} + } +{{- end }} +` +) + const ( namespaceWildcard = "*" apiTimeout = 5 * time.Minute @@ -282,6 +311,66 @@ func (c *Cache) Write(ctx context.Context, entry api.ConfigEntry) error { return nil } +func (c *Cache) ensurePolicy(client *api.Client) (string, error) { + policy := c.gatewayPolicy() + + created, _, err := client.ACL().PolicyCreate(&policy, &api.WriteOptions{}) + + if isPolicyExistsErr(err, policy.Name) { + existing, _, err := client.ACL().PolicyReadByName(policy.Name, &api.QueryOptions{}) + if err != nil { + return "", err + } + return existing.ID, nil + } + if err != nil { + return "", err + } + return created.ID, nil +} + +func (c *Cache) ensureRole(client *api.Client) (string, error) { + policyID, err := c.ensurePolicy(client) + if err != nil { + return "", err + } + + aclRoleName := "managed-gateway-acl-role" + aclRole, _, err := client.ACL().RoleReadByName(aclRoleName, &api.QueryOptions{}) + if err != nil { + return "", err + } + if aclRole != nil { + return aclRoleName, nil + } + + role := &api.ACLRole{ + Name: aclRoleName, + Description: "ACL Role for Managed API Gateways", + Policies: []*api.ACLLink{{ID: policyID}}, + } + + _, _, err = client.ACL().RoleCreate(role, &api.WriteOptions{}) + return aclRoleName, err +} + +func (c *Cache) gatewayPolicy() api.ACLPolicy { + var data bytes.Buffer + if err := gatewayTpl.Execute(&data, templateArgs{ + EnableNamespaces: c.namespacesEnabled, + }); err != nil { + // just panic if we can't compile the simple template + // as it means something else is going severly wrong. + panic(err) + } + + return api.ACLPolicy{ + Name: "api-gateway-token-policy", + Description: "API Gateway token Policy", + Rules: data.String(), + } +} + // Get returns a config entry from the cache that corresponds to the given resource reference. func (c *Cache) Get(ref api.ResourceReference) api.ConfigEntry { c.cacheMutex.Lock() @@ -335,56 +424,42 @@ func (c *Cache) List(kind string) []api.ConfigEntry { return refMap.Entries() } -// LinkPolicy links a mesh write policy to a token associated with the service. -func (c *Cache) LinkPolicy(ctx context.Context, name, namespace string) error { +func (c *Cache) EnsureRoleBinding(authMethod, service, namespace string) error { client, err := consul.NewClientFromConnMgr(c.config, c.serverMgr) if err != nil { return err } - options := &api.QueryOptions{} - - if c.namespacesEnabled { - options.Namespace = namespace - } - - policies, _, err := client.ACL().PolicyList(options.WithContext(ctx)) + role, err := c.ensureRole(client) if err != nil { return ignoreACLsDisabled(err) } - links := []*api.ACLLink{} - for _, policy := range policies { - if strings.HasPrefix(policy.Name, "connect-inject-policy") { - links = append(links, &api.ACLLink{ - Name: policy.Name, - }) - } + bindingRule := &api.ACLBindingRule{ + Description: fmt.Sprintf("Binding Rule for %s/%s", namespace, service), + AuthMethod: authMethod, + Selector: fmt.Sprintf("serviceaccount.name==%q and serviceaccount.namespace==%q", service, namespace), + BindType: api.BindingRuleBindTypeRole, + BindName: role, } - tokens, _, err := client.ACL().TokenList(options.WithContext(ctx)) + existingRules, _, err := client.ACL().BindingRuleList(authMethod, &api.QueryOptions{}) if err != nil { - return ignoreACLsDisabled(err) + return err } - for _, token := range tokens { - for _, identity := range token.ServiceIdentities { - if identity.ServiceName == name { - token, _, err := client.ACL().TokenRead(token.AccessorID, options.WithContext(ctx)) - if err != nil { - return ignoreACLsDisabled(err) - } - token.Policies = links - - _, _, err = client.ACL().TokenUpdate(token, &api.WriteOptions{}) - if err != nil { - return ignoreACLsDisabled(err) - } - } + for _, existingRule := range existingRules { + if existingRule.BindName == bindingRule.BindName && existingRule.Description == bindingRule.Description { + bindingRule.ID = existingRule.ID } } - return nil + if bindingRule.ID == "" { + _, _, err := client.ACL().BindingRuleCreate(bindingRule, &api.WriteOptions{}) + return err + } + _, _, err = client.ACL().BindingRuleUpdate(bindingRule, &api.WriteOptions{}) + return err } // Register registers a service in Consul. @@ -414,8 +489,19 @@ func (c *Cache) Deregister(ctx context.Context, deregistration api.CatalogDeregi } func ignoreACLsDisabled(err error) error { + if err == nil { + return nil + } if err.Error() == "Unexpected response code: 401 (ACL support disabled)" { return nil } return err } + +// isPolicyExistsErr returns true if err is due to trying to call the +// policy create API when the policy already exists. +func isPolicyExistsErr(err error, policyName string) bool { + return err != nil && + strings.Contains(err.Error(), "Unexpected response code: 500") && + strings.Contains(err.Error(), fmt.Sprintf("Invalid Policy: A Policy with Name %q already exists", policyName)) +} diff --git a/control-plane/api-gateway/common/labels.go b/control-plane/api-gateway/common/labels.go index 3ab7eaf164..cba13a603e 100644 --- a/control-plane/api-gateway/common/labels.go +++ b/control-plane/api-gateway/common/labels.go @@ -6,6 +6,8 @@ package common import ( "fmt" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) @@ -13,7 +15,7 @@ const ( nameLabel = "gateway.consul.hashicorp.com/name" namespaceLabel = "gateway.consul.hashicorp.com/namespace" createdAtLabel = "gateway.consul.hashicorp.com/created" - managedLabel = "gateway.consul.hashicorp.com/managed" + ManagedLabel = "gateway.consul.hashicorp.com/managed" ) // LabelsForGateway formats the default labels that appear on objects managed by the controllers. @@ -22,6 +24,16 @@ func LabelsForGateway(gateway *gwv1beta1.Gateway) map[string]string { nameLabel: gateway.Name, namespaceLabel: gateway.Namespace, createdAtLabel: fmt.Sprintf("%d", gateway.CreationTimestamp.Unix()), - managedLabel: "true", + ManagedLabel: "true", } } + +func GatewayFromPod(pod *corev1.Pod) (types.NamespacedName, bool) { + if pod.Labels[ManagedLabel] == "true" { + return types.NamespacedName{ + Name: pod.Labels[nameLabel], + Namespace: pod.Labels[namespaceLabel], + }, true + } + return types.NamespacedName{}, false +} diff --git a/control-plane/api-gateway/controllers/gateway_controller.go b/control-plane/api-gateway/controllers/gateway_controller.go index 80c8e83977..ab2b6af1a5 100644 --- a/control-plane/api-gateway/controllers/gateway_controller.go +++ b/control-plane/api-gateway/controllers/gateway_controller.go @@ -20,8 +20,10 @@ import ( "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" @@ -188,6 +190,11 @@ func (r *GatewayController) Reconcile(ctx context.Context, req ctrl.Request) (ct updates := binder.Snapshot() if updates.UpsertGatewayDeployment { + if err := r.cache.EnsureRoleBinding(r.HelmConfig.AuthMethod, gateway.Name, gateway.Namespace); err != nil { + log.Error(err, "error linking token policy") + return ctrl.Result{}, err + } + err := r.updateGatekeeperResources(ctx, log, &gateway, updates.GatewayClassConfig) if err != nil { log.Error(err, "unable to update gateway resources") @@ -259,16 +266,6 @@ func (r *GatewayController) Reconcile(ctx context.Context, req ctrl.Request) (ct } } - // link up policy - TODO: this is really a nasty hack to inject a known policy with - // mesh == read on the provisioned gateway token if needed, figure out some other - // way of handling it. - if updates.UpsertGatewayDeployment { - if err := r.cache.LinkPolicy(ctx, nonNormalizedConsulKey.Name, nonNormalizedConsulKey.Namespace); err != nil { - log.Error(err, "error linking token policy") - return ctrl.Result{}, err - } - } - return ctrl.Result{}, nil } @@ -326,6 +323,12 @@ func SetupGatewayControllerWithManager(ctx context.Context, mgr ctrl.Manager, co c := cache.New(cacheConfig) gwc := cache.NewGatewayCache(ctx, cacheConfig) + predicate, _ := predicate.LabelSelectorPredicate( + *metav1.SetAsLabelSelector(map[string]string{ + common.ManagedLabel: "true", + }), + ) + r := &GatewayController{ Client: mgr.GetClient(), Log: mgr.GetLogger(), @@ -376,6 +379,11 @@ func SetupGatewayControllerWithManager(ctx context.Context, mgr ctrl.Manager, co source.NewKindWithCache(&corev1.Endpoints{}, mgr.GetCache()), handler.EnqueueRequestsFromMapFunc(r.transformEndpoints(ctx)), ). + Watches( + &source.Kind{Type: &corev1.Pod{}}, + handler.EnqueueRequestsFromMapFunc(r.transformPods(ctx)), + builder.WithPredicates(predicate), + ). Watches( // Subscribe to changes from Consul for APIGateways &source.Channel{Source: c.Subscribe(ctx, api.APIGateway, r.transformConsulGateway).Events()}, @@ -565,6 +573,20 @@ func gatewayReferencesCertificate(certificateKey api.ResourceReference, gateway return false } +func (r *GatewayController) transformPods(ctx context.Context) func(o client.Object) []reconcile.Request { + return func(o client.Object) []reconcile.Request { + pod := o.(*corev1.Pod) + + if gateway, managed := common.GatewayFromPod(pod); managed { + return []reconcile.Request{ + {NamespacedName: gateway}, + } + } + + return nil + } +} + // transformEndpoints will return a list of gateways that are referenced // by a TCPRoute or HTTPRoute that references the service. func (r *GatewayController) transformEndpoints(ctx context.Context) func(o client.Object) []reconcile.Request { diff --git a/control-plane/api-gateway/gatekeeper/dataplane.go b/control-plane/api-gateway/gatekeeper/dataplane.go index 36829e2b7c..f82e12e8a4 100644 --- a/control-plane/api-gateway/gatekeeper/dataplane.go +++ b/control-plane/api-gateway/gatekeeper/dataplane.go @@ -142,9 +142,6 @@ func getDataplaneArgs(namespace string, config common.HelmConfig, bearerTokenFil "-login-bearer-token-path="+bearerTokenFile, "-login-meta="+fmt.Sprintf("gateway=%s/%s", namespace, name), ) - if config.EnableNamespaces { - args = append(args, "-login-namespace="+consulNamespace) - } if config.ConsulPartition != "" { args = append(args, "-login-partition="+config.ConsulPartition) } diff --git a/control-plane/api-gateway/gatekeeper/init.go b/control-plane/api-gateway/gatekeeper/init.go index 831380cb52..35360b7f87 100644 --- a/control-plane/api-gateway/gatekeeper/init.go +++ b/control-plane/api-gateway/gatekeeper/init.go @@ -147,11 +147,6 @@ func initContainer(config common.HelmConfig, name, namespace string) (corev1.Con Value: "pod=$(POD_NAMESPACE)/$(POD_NAME)", }) - container.Env = append(container.Env, corev1.EnvVar{ - Name: "CONSUL_LOGIN_NAMESPACE", - Value: consulNamespace, - }) - if config.ConsulPartition != "" { container.Env = append(container.Env, corev1.EnvVar{ Name: "CONSUL_LOGIN_PARTITION", diff --git a/control-plane/subcommand/inject-connect/command.go b/control-plane/subcommand/inject-connect/command.go index 9e99bd1a03..a4fcf7c99d 100644 --- a/control-plane/subcommand/inject-connect/command.go +++ b/control-plane/subcommand/inject-connect/command.go @@ -500,7 +500,7 @@ func (c *Command) Run(args []string) int { PeeringEnabled: c.flagEnablePeering, EnableOpenShift: c.flagEnableOpenShift, EnableNamespaceMirroring: c.flagEnableK8SNSMirroring, - AuthMethod: c.flagACLAuthMethod, + AuthMethod: c.consul.ConsulLogin.AuthMethod, LogLevel: c.flagLogLevel, LogJSON: c.flagLogJSON, TLSEnabled: c.consul.UseTLS, From 644e02ea206fa4d28dcba34712fce00246ef655c Mon Sep 17 00:00:00 2001 From: Andrew Stucki Date: Tue, 6 Jun 2023 21:18:45 -0400 Subject: [PATCH 210/340] Update memory defaults for connect inject controller (#2249) * Update memory defaults for connect inject controllers * Add changelog entry * Bump up Consul server statefulset memory defaults too --- .changelog/2249.txt | 3 +++ .../consul/test/unit/connect-inject-deployment.bats | 2 +- charts/consul/test/unit/server-statefulset.bats | 2 +- charts/consul/values.yaml | 12 ++++++------ 4 files changed, 11 insertions(+), 8 deletions(-) create mode 100644 .changelog/2249.txt diff --git a/.changelog/2249.txt b/.changelog/2249.txt new file mode 100644 index 0000000000..9c6a50e098 --- /dev/null +++ b/.changelog/2249.txt @@ -0,0 +1,3 @@ +```release-note:improvement +helm: Update the default amount of memory used by the connect-inject controller so that its less likely to get OOM killed. +``` diff --git a/charts/consul/test/unit/connect-inject-deployment.bats b/charts/consul/test/unit/connect-inject-deployment.bats index 26e3038759..c1bc63ffc3 100755 --- a/charts/consul/test/unit/connect-inject-deployment.bats +++ b/charts/consul/test/unit/connect-inject-deployment.bats @@ -960,7 +960,7 @@ load _helpers --set 'connectInject.enabled=true' \ . | tee /dev/stderr | yq -rc '.spec.template.spec.containers[0].resources' | tee /dev/stderr) - [ "${actual}" = '{"limits":{"cpu":"50m","memory":"50Mi"},"requests":{"cpu":"50m","memory":"50Mi"}}' ] + [ "${actual}" = '{"limits":{"cpu":"50m","memory":"200Mi"},"requests":{"cpu":"50m","memory":"200Mi"}}' ] } @test "connectInject/Deployment: can set resources" { diff --git a/charts/consul/test/unit/server-statefulset.bats b/charts/consul/test/unit/server-statefulset.bats index 29621187ab..108fd9bbf8 100755 --- a/charts/consul/test/unit/server-statefulset.bats +++ b/charts/consul/test/unit/server-statefulset.bats @@ -99,7 +99,7 @@ load _helpers -s templates/server-statefulset.yaml \ . | tee /dev/stderr | yq -rc '.spec.template.spec.containers[0].resources' | tee /dev/stderr) - [ "${actual}" = '{"limits":{"cpu":"100m","memory":"100Mi"},"requests":{"cpu":"100m","memory":"100Mi"}}' ] + [ "${actual}" = '{"limits":{"cpu":"100m","memory":"200Mi"},"requests":{"cpu":"100m","memory":"200Mi"}}' ] } @test "server/StatefulSet: resources can be overridden" { diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index c6c225b3d0..0e325ca66c 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -808,10 +808,10 @@ server: # ```yaml # resources: # requests: - # memory: '100Mi' + # memory: '200Mi' # cpu: '100m' # limits: - # memory: '100Mi' + # memory: '200Mi' # cpu: '100m' # ``` # @@ -819,10 +819,10 @@ server: # @type: map resources: requests: - memory: "100Mi" + memory: "200Mi" cpu: "100m" limits: - memory: "100Mi" + memory: "200Mi" cpu: "100m" # The security context for the server pods. This should be a YAML map corresponding to a @@ -2283,14 +2283,14 @@ connectInject: requests: # Recommended production default: 500Mi # @type: string - memory: "50Mi" + memory: "200Mi" # Recommended production default: 250m # @type: string cpu: "50m" limits: # Recommended production default: 500Mi # @type: string - memory: "50Mi" + memory: "200Mi" # Recommended production default: 250m # @type: string cpu: "50m" From 3c565586ffd0c467b14771107d023c9a42854cfe Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Wed, 7 Jun 2023 10:47:35 -0700 Subject: [PATCH 211/340] Mw/fix pipeline 1 1 6 (#2282) * update eks and aks to use latest kubernetes version * updated the terraform provider as some fields were deprecated --- charts/consul/test/terraform/aks/main.tf | 11 +++++++++-- charts/consul/test/terraform/eks/main.tf | 15 +++++++++++---- charts/consul/test/terraform/gke/main.tf | 9 ++++++++- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/charts/consul/test/terraform/aks/main.tf b/charts/consul/test/terraform/aks/main.tf index 8cf72a142c..bf8c925f15 100644 --- a/charts/consul/test/terraform/aks/main.tf +++ b/charts/consul/test/terraform/aks/main.tf @@ -1,8 +1,15 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +terraform { + required_providers { + azurerm = { + version = "3.40.0" + } + } +} + provider "azurerm" { - version = "3.40.0" features {} } @@ -48,7 +55,7 @@ resource "azurerm_kubernetes_cluster" "default" { location = azurerm_resource_group.default[count.index].location resource_group_name = azurerm_resource_group.default[count.index].name dns_prefix = "consul-k8s-${random_id.suffix[count.index].dec}" - kubernetes_version = "1.24.6" + kubernetes_version = "1.26" role_based_access_control_enabled = true // We're setting the network plugin and other network properties explicitly diff --git a/charts/consul/test/terraform/eks/main.tf b/charts/consul/test/terraform/eks/main.tf index 6301202161..efbab0e833 100644 --- a/charts/consul/test/terraform/eks/main.tf +++ b/charts/consul/test/terraform/eks/main.tf @@ -1,9 +1,16 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +terraform { + required_providers { + aws = { + version = ">= 4.0.0" + } + } +} + provider "aws" { - version = ">= 2.28.1" - region = var.region + region = var.region assume_role { role_arn = var.role_arn @@ -28,7 +35,7 @@ resource "random_string" "suffix" { module "vpc" { count = var.cluster_count source = "terraform-aws-modules/vpc/aws" - version = "3.11.0" + version = "4.0.0" name = "consul-k8s-${random_id.suffix[count.index].dec}" # The cidr range needs to be unique in each VPC to allow setting up a peering connection. @@ -61,7 +68,7 @@ module "eks" { kubeconfig_api_version = "client.authentication.k8s.io/v1beta1" cluster_name = "consul-k8s-${random_id.suffix[count.index].dec}" - cluster_version = "1.23" + cluster_version = "1.26" subnets = module.vpc[count.index].private_subnets enable_irsa = true diff --git a/charts/consul/test/terraform/gke/main.tf b/charts/consul/test/terraform/gke/main.tf index 98a063d450..fe5adc5e8d 100644 --- a/charts/consul/test/terraform/gke/main.tf +++ b/charts/consul/test/terraform/gke/main.tf @@ -1,9 +1,16 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +terraform { + required_providers { + google = { + version = "~> 4.58.0" + } + } +} + provider "google" { project = var.project - version = "~> 4.58.0" zone = var.zone } From 57fef1fc81f5620c9518a1a6bc082a4ff4342213 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Wed, 7 Jun 2023 22:51:02 -0400 Subject: [PATCH 212/340] Add bug to changelog so that go-changelog works (#2276) --- .changelog/2194.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changelog/2194.txt b/.changelog/2194.txt index 997326218b..fb265d9739 100644 --- a/.changelog/2194.txt +++ b/.changelog/2194.txt @@ -1,3 +1,3 @@ -```release-note: +```release-note:bug crd: fix bug on service intentions CRD causing some updates to be ignored. ``` From e35eaa3cf48fb0264269b65ca196e1ed2792aee4 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Thu, 8 Jun 2023 12:14:35 -0400 Subject: [PATCH 213/340] Fix retry loops that use `t` (#2311) --- acceptance/tests/cli/cli_install_test.go | 2 +- .../config_entries_namespaces_test.go | 58 +++++++++---------- .../config-entries/config_entries_test.go | 58 +++++++++---------- .../create-federation-secret/command_test.go | 6 +- 4 files changed, 62 insertions(+), 62 deletions(-) diff --git a/acceptance/tests/cli/cli_install_test.go b/acceptance/tests/cli/cli_install_test.go index 009d3140fc..bb497f913f 100644 --- a/acceptance/tests/cli/cli_install_test.go +++ b/acceptance/tests/cli/cli_install_test.go @@ -74,7 +74,7 @@ func TestInstall(t *testing.T) { retry.RunWith(retrier, t, func(r *retry.R) { for podName := range list { out, err := cli.Run(t, ctx.KubectlOptions(t), "proxy", "read", podName) - require.NoError(t, err) + require.NoError(r, err) output := string(out) logger.Log(t, output) diff --git a/acceptance/tests/config-entries/config_entries_namespaces_test.go b/acceptance/tests/config-entries/config_entries_namespaces_test.go index ced7cc8236..91d0c69df4 100644 --- a/acceptance/tests/config-entries/config_entries_namespaces_test.go +++ b/acceptance/tests/config-entries/config_entries_namespaces_test.go @@ -242,35 +242,35 @@ func TestControllerNamespaces(t *testing.T) { require.NoError(r, err) rateLimitIPConfigEntry, ok := entry.(*api.RateLimitIPConfigEntry) require.True(r, ok, "could not cast to RateLimitIPConfigEntry") - require.Equal(t, "permissive", rateLimitIPConfigEntry.Mode) - require.Equal(t, 100.0, rateLimitIPConfigEntry.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.WriteRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.ACL.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.ACL.WriteRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Catalog.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Catalog.WriteRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.ConfigEntry.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.ConfigEntry.WriteRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.ConnectCA.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.ConnectCA.WriteRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Coordinate.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Coordinate.WriteRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.DiscoveryChain.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.DiscoveryChain.WriteRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Health.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Health.WriteRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Intention.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Intention.WriteRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.KV.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.KV.WriteRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Tenancy.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Tenancy.WriteRate) - //require.Equal(t, 100.0, rateLimitIPConfigEntry.PreparedQuery.ReadRate) - //require.Equal(t, 100.0, rateLimitIPConfigEntry.PreparedQuery.WriteRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Session.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Session.WriteRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Txn.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Txn.WriteRate) + require.Equal(r, "permissive", rateLimitIPConfigEntry.Mode) + require.Equal(r, 100.0, rateLimitIPConfigEntry.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.WriteRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.ACL.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.ACL.WriteRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Catalog.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Catalog.WriteRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.ConfigEntry.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.ConfigEntry.WriteRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.ConnectCA.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.ConnectCA.WriteRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Coordinate.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Coordinate.WriteRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.DiscoveryChain.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.DiscoveryChain.WriteRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Health.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Health.WriteRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Intention.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Intention.WriteRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.KV.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.KV.WriteRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Tenancy.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Tenancy.WriteRate) + //require.Equal(r, 100.0, rateLimitIPConfigEntry.PreparedQuery.ReadRate) + //require.Equal(r, 100.0, rateLimitIPConfigEntry.PreparedQuery.WriteRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Session.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Session.WriteRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Txn.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Txn.WriteRate) }) } diff --git a/acceptance/tests/config-entries/config_entries_test.go b/acceptance/tests/config-entries/config_entries_test.go index 089f96767f..e37e3d6c7f 100644 --- a/acceptance/tests/config-entries/config_entries_test.go +++ b/acceptance/tests/config-entries/config_entries_test.go @@ -209,35 +209,35 @@ func TestController(t *testing.T) { require.NoError(r, err) rateLimitIPConfigEntry, ok := entry.(*api.RateLimitIPConfigEntry) require.True(r, ok, "could not cast to RateLimitIPConfigEntry") - require.Equal(t, "permissive", rateLimitIPConfigEntry.Mode) - require.Equal(t, 100.0, rateLimitIPConfigEntry.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.WriteRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.ACL.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.ACL.WriteRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Catalog.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Catalog.WriteRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.ConfigEntry.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.ConfigEntry.WriteRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.ConnectCA.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.ConnectCA.WriteRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Coordinate.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Coordinate.WriteRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.DiscoveryChain.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.DiscoveryChain.WriteRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Health.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Health.WriteRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Intention.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Intention.WriteRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.KV.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.KV.WriteRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Tenancy.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Tenancy.WriteRate) - //require.Equal(t, 100.0, rateLimitIPConfigEntry.PreparedQuery.ReadRate) - //require.Equal(t, 100.0, rateLimitIPConfigEntry.PreparedQuery.WriteRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Session.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Session.WriteRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Txn.ReadRate) - require.Equal(t, 100.0, rateLimitIPConfigEntry.Txn.WriteRate, 100.0) + require.Equal(r, "permissive", rateLimitIPConfigEntry.Mode) + require.Equal(r, 100.0, rateLimitIPConfigEntry.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.WriteRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.ACL.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.ACL.WriteRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Catalog.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Catalog.WriteRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.ConfigEntry.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.ConfigEntry.WriteRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.ConnectCA.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.ConnectCA.WriteRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Coordinate.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Coordinate.WriteRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.DiscoveryChain.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.DiscoveryChain.WriteRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Health.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Health.WriteRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Intention.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Intention.WriteRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.KV.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.KV.WriteRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Tenancy.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Tenancy.WriteRate) + //require.Equal(r, 100.0, rateLimitIPConfigEntry.PreparedQuery.ReadRate) + //require.Equal(r, 100.0, rateLimitIPConfigEntry.PreparedQuery.WriteRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Session.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Session.WriteRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Txn.ReadRate) + require.Equal(r, 100.0, rateLimitIPConfigEntry.Txn.WriteRate) }) } diff --git a/control-plane/subcommand/create-federation-secret/command_test.go b/control-plane/subcommand/create-federation-secret/command_test.go index 16939a2868..15f12b132c 100644 --- a/control-plane/subcommand/create-federation-secret/command_test.go +++ b/control-plane/subcommand/create-federation-secret/command_test.go @@ -528,7 +528,7 @@ func TestRun_WaitsForMeshGatewayInstances(t *testing.T) { CAFile: caFile, }, }) - require.NoError(t, err) + require.NoError(r, err) }) err = client.Agent().ServiceRegister(&api.AgentServiceRegistration{ @@ -825,7 +825,7 @@ func TestRun_ReplicationSecretDelay(t *testing.T) { }, }, metav1.CreateOptions{}) - require.NoError(t, err) + require.NoError(r, err) }) }() @@ -1005,7 +1005,7 @@ func TestRun_ConsulClientDelay(t *testing.T) { Server: randomPorts[5], } }) - require.NoError(t, err) + require.NoError(r, err) }) // Construct Consul client. From f4435acf0020c1f6bda25f875660055116ef155a Mon Sep 17 00:00:00 2001 From: skpratt Date: Thu, 8 Jun 2023 13:28:55 -0500 Subject: [PATCH 214/340] Add FIPS builds (#2165) * Add FIPS builds for linux amd64 * add version check * fix CI labels and add local dev commands * fix ci version tagging * switch to ubuntu 20.04 * add CLI version tag * add gcompat for alpine glibc cgo compatibility * remove FIPS version check from connect-init * address comments --- .github/workflows/build.yml | 92 +++++++++++++------ Makefile | 15 +++ cli/version/fips_build.go | 27 ++++++ cli/version/non_fips_build.go | 12 +++ cli/version/version.go | 6 +- control-plane/Dockerfile | 6 +- .../build-support/functions/20-build.sh | 14 ++- .../build-support/scripts/build-local.sh | 7 ++ .../subcommand/connect-init/command.go | 23 ++++- control-plane/version/fips_build.go | 27 ++++++ control-plane/version/non_fips_build.go | 12 +++ control-plane/version/version.go | 6 +- 12 files changed, 210 insertions(+), 37 deletions(-) create mode 100644 cli/version/fips_build.go create mode 100644 cli/version/non_fips_build.go create mode 100644 control-plane/version/fips_build.go create mode 100644 control-plane/version/non_fips_build.go diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 52f94ec71c..8eef629401 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -64,7 +64,7 @@ jobs: build: needs: [get-go-version, get-product-version] - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 # the GLIBC is too high on 22.04 strategy: matrix: include: @@ -79,20 +79,28 @@ jobs: - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s.exe" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "darwin", goarch: "amd64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "darwin", goarch: "arm64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s" } - # control-plane + - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto", fips: ".fips1402" } + - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "arm64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto CC=aarch64-linux-gnu-gcc", fips: ".fips1402" } + - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s.exe", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=cngcrypto", fips: ".fips1402" } + + # control-plane - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "freebsd", goarch: "386", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "freebsd", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "386", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "arm", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "arm64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } - # solaris is only built for the control plane + # solaris is only built for the control plane - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "solaris", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "386", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane.exe" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane.exe" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "darwin", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "darwin", goarch: "arm64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } - # consul-cni + - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto", fips: ".fips1402" } + - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "arm64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto CC=aarch64-linux-gnu-gcc", fips: ".fips1402" } + - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane.exe", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=cngcrypto", fips: ".fips1402" } + + # consul-cni - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "freebsd", goarch: "386", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "freebsd", goarch: "amd64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "386", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni" } @@ -104,10 +112,14 @@ jobs: - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni.exe" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "darwin", goarch: "amd64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "darwin", goarch: "arm64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni" } + - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto", fips: ".fips1402" } + - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "arm64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto CC=aarch64-linux-gnu-gcc", fips: ".fips1402" } + - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni.exe", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=cngcrypto", fips: ".fips1402" } + fail-fast: true - name: Go ${{ matrix.go }} ${{ matrix.goos }} ${{ matrix.goarch }} ${{ matrix.component }} build + name: Go ${{ matrix.go }} ${{ matrix.goos }} ${{ matrix.goarch }} ${{ matrix.component }} ${{ matrix.fips }} build steps: - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 @@ -116,6 +128,25 @@ jobs: with: go-version: ${{ matrix.go }} + - name: Replace Go for Windows FIPS with Microsoft Go + if: ${{ matrix.fips == '.fips1402' && matrix.goos == 'windows' }} + run: | + # Uninstall standard Go and use microsoft/go instead + rm -rf /home/runner/actions-runner/_work/_tool/go + curl https://aka.ms/golang/release/latest/go${{ matrix.go }}-1.linux-amd64.tar.gz -Lo go${{ matrix.go }}.linux-amd64.tar.gz + tar -C $HOME -xf go${{ matrix.go }}.linux-amd64.tar.gz + chmod +x $HOME/go/bin + export PATH=$HOME/go/bin:$PATH + if [ $(which go) != "$HOME/go/bin/go" ]; then + echo "Unable to verify microsoft/go toolchain" + exit 1 + fi + + - name: Install cross-compiler for FIPS on arm + if: ${{ matrix.fips == '.fips1402' && matrix.goarch == 'arm64' }} + run: | + sudo apt-get update --allow-releaseinfo-change-suite --allow-releaseinfo-change-version && sudo apt-get install -y gcc-aarch64-linux-gnu + - name: Build env: GOOS: ${{ matrix.goos }} @@ -130,14 +161,14 @@ jobs: export GIT_IMPORT=github.com/hashicorp/consul-k8s/${{ matrix.component }}/version export GOLDFLAGS="-X ${GIT_IMPORT}.GitCommit=${GIT_COMMIT}${GIT_DIRTY} -X ${GIT_IMPORT}.GitDescribe=${{ needs.get-product-version.outputs.product-version }}" - CGO_ENABLED=0 go build -o dist/${{ matrix.bin_name }} -ldflags "${GOLDFLAGS}" . - zip -r -j out/${{ matrix.pkg_name }}_${{ needs.get-product-version.outputs.product-version }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip dist/ + ${{ matrix.env }} go build -o dist/${{ matrix.bin_name }} -ldflags "${GOLDFLAGS}" -tags=${{ matrix.gotags }} . + zip -r -j out/${{ matrix.pkg_name }}_${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip dist/ - name: Upload built binaries uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: - name: ${{ matrix.pkg_name }}_${{ needs.get-product-version.outputs.product-version }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip - path: ${{ matrix.component}}/out/${{ matrix.pkg_name }}_${{ needs.get-product-version.outputs.product-version }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip + name: ${{ matrix.pkg_name }}_${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip + path: ${{ matrix.component}}/out/${{ matrix.pkg_name }}_${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip - name: Package rpm and deb files if: ${{ matrix.goos == 'linux' && matrix.component == 'cli' && matrix.goarch == 'amd64'}} @@ -146,7 +177,7 @@ jobs: name: consul-k8s description: "consul-k8s provides a cli interface to first-class integrations between Consul and Kubernetes." arch: ${{ matrix.goarch }} - version: ${{ needs.get-product-version.outputs.product-version }} + version: ${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }} maintainer: "HashiCorp" homepage: "https://github.com/hashicorp/consul-k8s" license: "MPL-2.0" @@ -171,7 +202,7 @@ jobs: cd /work rpm -ivh out/${{ env.RPM_PACKAGE }} CONSUL_K8S_VERSION="$(consul-k8s version | awk '{print $2}')" - VERSION="v${{ needs.get-product-version.outputs.product-version }}" + VERSION="v${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }}" if [ "${VERSION}" != "${CONSUL_K8S_VERSION}" ]; then echo "Test FAILED, expected: ${VERSION}, got: ${CONSUL_K8S_VERSION}" exit 1 @@ -196,7 +227,7 @@ jobs: cd /work apt install ./out/${{ env.DEB_PACKAGE }} CONSUL_K8S_VERSION="$(consul-k8s version | awk '{print $2}')" - VERSION="v${{ needs.get-product-version.outputs.product-version }}" + VERSION="v${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }}" if [ "${VERSION}" != "${CONSUL_K8S_VERSION}" ]; then echo "Test FAILED, expected: ${VERSION}, got: ${CONSUL_K8S_VERSION}" exit 1 @@ -211,24 +242,30 @@ jobs: path: out/${{ env.DEB_PACKAGE }} build-docker: - name: Docker ${{ matrix.arch }} default release build + name: Docker ${{ matrix.goarch }} ${{ matrix.fips }} default release build needs: [get-product-version, build] runs-on: ubuntu-latest strategy: matrix: - arch: ["arm", "arm64", "386", "amd64"] + include: + - { goos: "linux", goarch: "arm" } + - { goos: "linux", goarch: "arm64" } + - { goos: "linux", goarch: "386" } + - { goos: "linux", goarch: "amd64" } + - { goos: "linux", goarch: "amd64", fips: ".fips1402" } + - { goos: "linux", goarch: "arm64", fips: ".fips1402" } env: repo: ${{ github.event.repository.name }} - version: ${{ needs.get-product-version.outputs.product-version }} + version: ${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }} steps: - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: - name: consul-cni_${{ needs.get-product-version.outputs.product-version }}_linux_${{ matrix.arch }}.zip - path: control-plane/dist/cni/linux/${{ matrix.arch }} + name: consul-cni_${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }}_${{ matrix.goos}}_${{ matrix.goarch }}.zip + path: control-plane/dist/cni/${{ matrix.goos}}/${{ matrix.goarch }} - name: extract consul-cni zip env: - ZIP_LOCATION: control-plane/dist/cni/linux/${{ matrix.arch }} + ZIP_LOCATION: control-plane/dist/cni/${{ matrix.goos}}/${{ matrix.goarch }} run: | cd "${ZIP_LOCATION}" unzip -j *.zip @@ -244,7 +281,7 @@ jobs: echo "Test PASSED" version: ${{ env.version }} target: release-default - arch: ${{ matrix.arch }} + arch: ${{ matrix.goarch }} pkg_name: consul-k8s-control-plane_${{ env.version }} bin_name: consul-k8s-control-plane workdir: control-plane @@ -255,20 +292,22 @@ jobs: docker.io/hashicorppreview/${{ env.repo }}-control-plane:${{ env.version }}-${{ github.sha }} build-docker-ubi-redhat-registry: - name: Docker ${{ matrix.arch }} UBI build for RedHat Registry + name: Docker ${{ matrix.arch }} ${{ matrix.fips }} UBI build for RedHat Registry needs: [get-product-version, build] runs-on: ubuntu-latest strategy: matrix: - arch: ["amd64"] + include: + - { arch: "amd64" } + - { arch: "amd64", fips: ".fips1402" } env: repo: ${{ github.event.repository.name }} - version: ${{ needs.get-product-version.outputs.product-version }} + version: ${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }} steps: - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: - name: consul-cni_${{ needs.get-product-version.outputs.product-version }}_linux_${{ matrix.arch }}.zip + name: consul-cni_${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }}_linux_${{ matrix.arch }}.zip path: control-plane/dist/cni/linux/${{ matrix.arch }} - name: extract consul-cni zip env: @@ -297,20 +336,21 @@ jobs: redhat_tag: quay.io/redhat-isv-containers/611ca2f89a9b407267837100:${{env.version}}-ubi build-docker-ubi-dockerhub: - name: Docker ${{ matrix.arch }} UBI build for DockerHub + name: Docker ${{ matrix.arch }} ${{ matrix.fips }} UBI build for DockerHub needs: [ get-product-version, build ] runs-on: ubuntu-latest strategy: matrix: arch: [ "amd64" ] + fips: [ ".fips1402", "" ] env: repo: ${{ github.event.repository.name }} - version: ${{ needs.get-product-version.outputs.product-version }} + version: ${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }} steps: - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: - name: consul-cni_${{ needs.get-product-version.outputs.product-version }}_linux_${{ matrix.arch }}.zip + name: consul-cni_${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }}_linux_${{ matrix.arch }}.zip path: control-plane/dist/cni/linux/${{ matrix.arch }} - name: extract consul-cni zip env: diff --git a/Makefile b/Makefile index 5adfb55657..628b13e2f9 100644 --- a/Makefile +++ b/Makefile @@ -49,6 +49,17 @@ control-plane-dev-docker-multi-arch: check-remote-dev-image-env ## Build consul- --push \ -f $(CURDIR)/control-plane/Dockerfile $(CURDIR)/control-plane +control-plane-fips-dev-docker: ## Build consul-k8s-control-plane FIPS dev Docker image. + @$(SHELL) $(CURDIR)/control-plane/build-support/scripts/build-local.sh -o linux -a $(GOARCH) --fips + @docker build -t '$(DEV_IMAGE)' \ + --target=dev \ + --build-arg 'TARGETARCH=$(GOARCH)' \ + --build-arg 'GIT_COMMIT=$(GIT_COMMIT)' \ + --build-arg 'GIT_DIRTY=$(GIT_DIRTY)' \ + --build-arg 'GIT_DESCRIBE=$(GIT_DESCRIBE)' \ + --push \ + -f $(CURDIR)/control-plane/Dockerfile $(CURDIR)/control-plane + control-plane-test: ## Run go test for the control plane. cd control-plane; go test ./... @@ -98,6 +109,10 @@ cli-dev: @echo "==> Installing consul-k8s CLI tool for ${GOOS}/${GOARCH}" @cd cli; go build -o ./bin/consul-k8s; cp ./bin/consul-k8s ${GOPATH}/bin/ +cli-fips-dev: + @echo "==> Installing consul-k8s CLI tool for ${GOOS}/${GOARCH}" + @cd cli; CGO_ENABLED=1 GOEXPERIMENT=boringcrypto go build -o ./bin/consul-k8s -tags "fips"; cp ./bin/consul-k8s ${GOPATH}/bin/ + cli-lint: ## Run linter in the control-plane directory. cd cli; golangci-lint run -c ../.golangci.yml diff --git a/cli/version/fips_build.go b/cli/version/fips_build.go new file mode 100644 index 0000000000..4d04cc6539 --- /dev/null +++ b/cli/version/fips_build.go @@ -0,0 +1,27 @@ +//go:build fips + +package version + +// This validates during compilation that we are being built with a FIPS enabled go toolchain +import ( + _ "crypto/tls/fipsonly" + "runtime" + "strings" +) + +// IsFIPS returns true if consul-k8s is operating in FIPS-140-2 mode. +func IsFIPS() bool { + return true +} + +func GetFIPSInfo() string { + str := "Enabled" + // Try to get the crypto module name + gover := strings.Split(runtime.Version(), "X:") + if len(gover) >= 2 { + gover_last := gover[len(gover)-1] + // Able to find crypto module name; add that to status string. + str = "FIPS 140-2 Enabled, crypto module " + gover_last + } + return str +} diff --git a/cli/version/non_fips_build.go b/cli/version/non_fips_build.go new file mode 100644 index 0000000000..f72aecae73 --- /dev/null +++ b/cli/version/non_fips_build.go @@ -0,0 +1,12 @@ +//go:build !fips + +package version + +// IsFIPS returns true if consul-k8s is operating in FIPS-140-2 mode. +func IsFIPS() bool { + return false +} + +func GetFIPSInfo() string { + return "" +} diff --git a/cli/version/version.go b/cli/version/version.go index 81433c0a5f..320600c30b 100644 --- a/cli/version/version.go +++ b/cli/version/version.go @@ -39,8 +39,12 @@ func GetHumanVersion() string { release = "dev" } + if IsFIPS() { + version += ".fips1402" + } + if release != "" { - if !strings.HasSuffix(version, "-"+release) { + if !strings.Contains(version, "-"+release) { // if we tagged a prerelease version then the release is in the version already version += fmt.Sprintf("-%s", release) } diff --git a/control-plane/Dockerfile b/control-plane/Dockerfile index de0c1cf1ff..5b3d73e625 100644 --- a/control-plane/Dockerfile +++ b/control-plane/Dockerfile @@ -92,7 +92,11 @@ LABEL name=${BIN_NAME} \ ENV BIN_NAME=${BIN_NAME} ENV VERSION=${PRODUCT_VERSION} -RUN apk add --no-cache ca-certificates libcap openssl su-exec iputils libc6-compat iptables +RUN apk add --no-cache ca-certificates libcap openssl su-exec iputils gcompat libc6-compat libstdc++ iptables + +# for FIPS CGO glibc compatibility in alpine +# see https://github.com/golang/go/issues/59305 +RUN ln -s /lib/libc.so.6 /usr/lib/libresolv.so.2 # TARGETOS and TARGETARCH are set automatically when --platform is provided. ARG TARGETOS diff --git a/control-plane/build-support/functions/20-build.sh b/control-plane/build-support/functions/20-build.sh index a4f36ee3e4..e9540956c9 100644 --- a/control-plane/build-support/functions/20-build.sh +++ b/control-plane/build-support/functions/20-build.sh @@ -180,7 +180,7 @@ function build_consul_local { # * - error # # Note: - # The GOLDFLAGS and GOTAGS environment variables will be used if set + # The GOLDFLAGS, GOEXPERIMENT, and GOTAGS environment variables will be used if set # If the CONSUL_DEV environment var is truthy only the local platform/architecture is built. # If the XC_OS or the XC_ARCH environment vars are present then only those platforms/architectures # will be built. Otherwise all supported platform/architectures are built @@ -188,6 +188,14 @@ function build_consul_local { # build with go install. # The GOXPARALLEL environment variable is used if set + if [ $GOTAGS == "fips" ]; then + CGO_ENABLED=1 + else + CGO_ENABLED=0 + fi + + echo "GOEXPERIMENT: $GOEXPERIMENT, GOTAGS: $GOTAGS CGO_ENABLED: $CGO_ENABLED" >> ~/debug.txt + if ! test -d "$1" then err "ERROR: '$1' is not a directory. build_consul must be called with the path to the top level source as the first argument'" @@ -242,7 +250,7 @@ function build_consul_local { then status "Using gox for concurrent compilation" - CGO_ENABLED=0 gox \ + CGO_ENABLED=${CGO_ENABLED} GOEXPERIMENT=${GOEXPERIMENT} gox \ -os="${build_os}" \ -arch="${build_arch}" \ -ldflags="${GOLDFLAGS}" \ @@ -290,7 +298,7 @@ function build_consul_local { else OS_BIN_EXTENSION="" fi - CGO_ENABLED=0 GOOS=${os} GOARCH=${arch} go build -ldflags "${GOLDFLAGS}" -tags "${GOTAGS}" -o "${outdir}/${bin_name}" + CGO_ENABLED=${CGO_ENABLED} GOEXPERIMENT=${GOEXPERIMENT} GOOS=${os} GOARCH=${arch} go build -ldflags "${GOLDFLAGS}" -tags "${GOTAGS}" -o "${outdir}/${bin_name}" if test $? -ne 0 then err "ERROR: Failed to build Consul for ${osarch}" diff --git a/control-plane/build-support/scripts/build-local.sh b/control-plane/build-support/scripts/build-local.sh index 453310b0b7..7325e025b7 100755 --- a/control-plane/build-support/scripts/build-local.sh +++ b/control-plane/build-support/scripts/build-local.sh @@ -35,6 +35,8 @@ Options: -a | --arch ARCH Space separated string of architectures to build. + --fips FIPS Whether to use FIPS cryptography. + -h | --help Print this help text. EOF } @@ -94,6 +96,11 @@ function main { build_arch="$2" shift 2 ;; + --fips ) + GOTAGS="fips" + GOEXPERIMENT="boringcrypto" + shift 1 + ;; * ) err_usage "ERROR: Unknown argument: '$1'" return 1 diff --git a/control-plane/subcommand/connect-init/command.go b/control-plane/subcommand/connect-init/command.go index 72090d299b..4f83ea98f1 100644 --- a/control-plane/subcommand/connect-init/command.go +++ b/control-plane/subcommand/connect-init/command.go @@ -17,17 +17,19 @@ import ( "time" "github.com/cenkalti/backoff" - "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" - "github.com/hashicorp/consul-k8s/control-plane/consul" - "github.com/hashicorp/consul-k8s/control-plane/namespaces" - "github.com/hashicorp/consul-k8s/control-plane/subcommand/common" - "github.com/hashicorp/consul-k8s/control-plane/subcommand/flags" "github.com/hashicorp/consul-server-connection-manager/discovery" "github.com/hashicorp/consul/api" "github.com/hashicorp/consul/sdk/iptables" "github.com/hashicorp/go-hclog" "github.com/mitchellh/cli" "github.com/mitchellh/mapstructure" + + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" + "github.com/hashicorp/consul-k8s/control-plane/consul" + "github.com/hashicorp/consul-k8s/control-plane/namespaces" + "github.com/hashicorp/consul-k8s/control-plane/subcommand/common" + "github.com/hashicorp/consul-k8s/control-plane/subcommand/flags" + "github.com/hashicorp/consul-k8s/control-plane/version" ) const ( @@ -161,6 +163,17 @@ func (c *Command) Run(args []string) int { c.logger.Error("Unable to get client connection", "error", err) return 1 } + if version.IsFIPS() { + // make sure we are also using FIPS Consul + var versionInfo map[string]interface{} + _, err := consulClient.Raw().Query("/v1/agent/version", versionInfo, nil) + if err != nil { + c.logger.Warn("This is a FIPS build of consul-k8s, which should be used with FIPS Consul. Unable to verify FIPS Consul while setting up Consul API client.") + } + if val, ok := versionInfo["FIPS"]; !ok || val == "" { + c.logger.Warn("This is a FIPS build of consul-k8s, which should be used with FIPS Consul. A non-FIPS version of Consul was detected.") + } + } proxyService := &api.AgentService{} if c.flagGatewayKind != "" { err = backoff.Retry(c.getGatewayRegistration(consulClient), backoff.WithMaxRetries(backoff.NewConstantBackOff(1*time.Second), c.serviceRegistrationPollingAttempts)) diff --git a/control-plane/version/fips_build.go b/control-plane/version/fips_build.go new file mode 100644 index 0000000000..4d04cc6539 --- /dev/null +++ b/control-plane/version/fips_build.go @@ -0,0 +1,27 @@ +//go:build fips + +package version + +// This validates during compilation that we are being built with a FIPS enabled go toolchain +import ( + _ "crypto/tls/fipsonly" + "runtime" + "strings" +) + +// IsFIPS returns true if consul-k8s is operating in FIPS-140-2 mode. +func IsFIPS() bool { + return true +} + +func GetFIPSInfo() string { + str := "Enabled" + // Try to get the crypto module name + gover := strings.Split(runtime.Version(), "X:") + if len(gover) >= 2 { + gover_last := gover[len(gover)-1] + // Able to find crypto module name; add that to status string. + str = "FIPS 140-2 Enabled, crypto module " + gover_last + } + return str +} diff --git a/control-plane/version/non_fips_build.go b/control-plane/version/non_fips_build.go new file mode 100644 index 0000000000..f72aecae73 --- /dev/null +++ b/control-plane/version/non_fips_build.go @@ -0,0 +1,12 @@ +//go:build !fips + +package version + +// IsFIPS returns true if consul-k8s is operating in FIPS-140-2 mode. +func IsFIPS() bool { + return false +} + +func GetFIPSInfo() string { + return "" +} diff --git a/control-plane/version/version.go b/control-plane/version/version.go index 81433c0a5f..320600c30b 100644 --- a/control-plane/version/version.go +++ b/control-plane/version/version.go @@ -39,8 +39,12 @@ func GetHumanVersion() string { release = "dev" } + if IsFIPS() { + version += ".fips1402" + } + if release != "" { - if !strings.HasSuffix(version, "-"+release) { + if !strings.Contains(version, "-"+release) { // if we tagged a prerelease version then the release is in the version already version += fmt.Sprintf("-%s", release) } From 097f94550903f6ab41e14e7a47a0f12f97cfeee3 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Thu, 8 Jun 2023 12:56:03 -0700 Subject: [PATCH 215/340] activated weekly acceptance tests for 1-2-x (#2315) - making this trigger nightly until after 1.2.0 GA - leaving 0.49.x active until after 1.2.0 GA --- .github/workflows/weekly-acceptance-1-2-x.yml | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/weekly-acceptance-1-2-x.yml diff --git a/.github/workflows/weekly-acceptance-1-2-x.yml b/.github/workflows/weekly-acceptance-1-2-x.yml new file mode 100644 index 0000000000..353a086f16 --- /dev/null +++ b/.github/workflows/weekly-acceptance-1-2-x.yml @@ -0,0 +1,30 @@ +# Dispatch to the consul-k8s-workflows with a weekly cron +# +# A separate file is needed for each release because the cron schedules are different for each release. +name: weekly-acceptance-1-2-x +on: + schedule: + # * is a special character in YAML so you have to quote this string + # Run weekly on Wednesday at 3AM UTC/11PM EST/8PM PST + # - cron: '0 3 * * 3' + - cron: '0 0 * * *' # Temporarily nightly until 1.2.0 GA + + +# these should be the only settings that you will ever need to change +env: + BRANCH: "release/1.2.x" + CONTEXT: "weekly" + +jobs: + cloud: + name: cloud + runs-on: ubuntu-latest + steps: + - uses: benc-uk/workflow-dispatch@798e70c97009500150087d30d9f11c5444830385 # v1.2.2 + name: cloud + with: + workflow: cloud.yml + repo: hashicorp/consul-k8s-workflows + ref: main + token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} + inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}" }' From 61c72805622502bd13e461457a03acec40b2d1b2 Mon Sep 17 00:00:00 2001 From: Melisa Griffin Date: Thu, 8 Jun 2023 23:30:46 -0400 Subject: [PATCH 216/340] Net 4230/add tcp to basic acceptance test (#2297) * first run through, needs help * still need to make secure pass * left something uncommented * it works and also cleanup * fix acceptance tests --- acceptance/go.mod | 10 -- acceptance/go.sum | 17 ---- .../api-gateway/api_gateway_tenancy_test.go | 2 +- .../tests/api-gateway/api_gateway_test.go | 95 +++++++++++++++++-- .../anyuid-scc-rolebinding.yaml | 14 +++ .../bases/static-server-tcp/deployment.yaml | 49 ++++++++++ .../static-server-tcp/kustomization.yaml | 11 +++ .../privileged-scc-rolebinding.yaml | 14 +++ .../static-server-tcp/psp-rolebinding.yaml | 14 +++ .../bases/static-server-tcp/service.yaml | 15 +++ .../static-server-tcp/serviceaccount.yaml | 7 ++ .../static-server-tcp/servicedefaults.yaml | 7 ++ .../cases/api-gateways/tcproute/route.yaml | 14 +++ .../controllers/gateway_controller.go | 2 +- 14 files changed, 233 insertions(+), 38 deletions(-) create mode 100644 acceptance/tests/fixtures/bases/static-server-tcp/anyuid-scc-rolebinding.yaml create mode 100644 acceptance/tests/fixtures/bases/static-server-tcp/deployment.yaml create mode 100644 acceptance/tests/fixtures/bases/static-server-tcp/kustomization.yaml create mode 100644 acceptance/tests/fixtures/bases/static-server-tcp/privileged-scc-rolebinding.yaml create mode 100644 acceptance/tests/fixtures/bases/static-server-tcp/psp-rolebinding.yaml create mode 100644 acceptance/tests/fixtures/bases/static-server-tcp/service.yaml create mode 100644 acceptance/tests/fixtures/bases/static-server-tcp/serviceaccount.yaml create mode 100644 acceptance/tests/fixtures/bases/static-server-tcp/servicedefaults.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/tcproute/route.yaml diff --git a/acceptance/go.mod b/acceptance/go.mod index a63e1187fe..e2221a09c0 100644 --- a/acceptance/go.mod +++ b/acceptance/go.mod @@ -26,18 +26,14 @@ require ( github.com/aws/aws-sdk-go v1.44.262 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect - github.com/cenkalti/backoff v2.2.1+incompatible // indirect github.com/cenkalti/backoff/v3 v3.0.0 // indirect - github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/deckarep/golang-set v1.7.1 // indirect github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/fatih/color v1.13.0 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-logr/logr v1.2.3 // indirect @@ -46,7 +42,6 @@ require ( github.com/go-openapi/swag v0.22.3 // indirect github.com/go-sql-driver/mysql v1.5.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect @@ -54,15 +49,12 @@ require ( github.com/google/gofuzz v1.1.0 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gruntwork-io/gruntwork-cli v0.7.0 // indirect - github.com/hashicorp/consul-server-connection-manager v0.1.2 // indirect - github.com/hashicorp/consul/proto-public v0.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-bexpr v0.1.11 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v1.2.2 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-netaddrs v0.1.0 // indirect github.com/hashicorp/go-plugin v1.4.5 // indirect github.com/hashicorp/go-retryablehttp v0.6.6 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect @@ -105,7 +97,6 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/objx v0.5.0 // indirect github.com/urfave/cli v1.22.2 // indirect go.uber.org/atomic v1.9.0 // indirect golang.org/x/crypto v0.1.0 // indirect @@ -126,7 +117,6 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/square/go-jose.v2 v2.5.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.26.3 // indirect k8s.io/component-base v0.26.3 // indirect k8s.io/klog/v2 v2.100.1 // indirect k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect diff --git a/acceptance/go.sum b/acceptance/go.sum index 1c9bd2ad25..77a5b5875a 100644 --- a/acceptance/go.sum +++ b/acceptance/go.sum @@ -104,12 +104,8 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= -github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c= github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= -github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= -github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -147,8 +143,6 @@ github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2 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/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= -github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= @@ -195,7 +189,6 @@ github.com/frankban/quicktest v1.13.0 h1:yNZif1OkDfNoDfb9zZa9aXIpejNR4F23Wely0c+ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -254,7 +247,6 @@ github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -341,12 +333,8 @@ github.com/gruntwork-io/terratest v0.31.2 h1:xvYHA80MUq5kx670dM18HInewOrrQrAN+Xb github.com/gruntwork-io/terratest v0.31.2/go.mod h1:EEgJie28gX/4AD71IFqgMj6e99KP5mi81hEtzmDjxTo= github.com/hashicorp/consul-k8s/control-plane v0.0.0-20230601034256-0c28b9b000cb h1:9GUvDoKVoV3IW78QyfoNY4bRcKxcn26wTGLoBrz92N4= github.com/hashicorp/consul-k8s/control-plane v0.0.0-20230601034256-0c28b9b000cb/go.mod h1:jKzTEgDc/np2gX/KPdfdm1mEUfZLrU8gc71XN3B15VI= -github.com/hashicorp/consul-server-connection-manager v0.1.2 h1:tNVQHUPuMbd+cMdD8kd+qkZUYpmLmrHMAV/49f4L53I= -github.com/hashicorp/consul-server-connection-manager v0.1.2/go.mod h1:NzQoVi1KcxGI2SangsDue8+ZPuXZWs+6BKAKrDNyg+w= github.com/hashicorp/consul/api v1.10.1-0.20230530193107-04a0d0133ae4 h1:6kUTk+YBgA5X5b3gNAoI18WEK4/t75LcWSimEgmpFdg= github.com/hashicorp/consul/api v1.10.1-0.20230530193107-04a0d0133ae4/go.mod h1:tXfrC6o0yFTgAW46xd5Ic8STHc9oIBcRVBcwhX5KNCQ= -github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3usQMXZF6ziK3SzXU= -github.com/hashicorp/consul/proto-public v0.1.0/go.mod h1:vs2KkuWwtjkIgA5ezp4YKPzQp4GitV+q/+PvksrA92k= github.com/hashicorp/consul/sdk v0.13.1 h1:EygWVWWMczTzXGpO93awkHFzfUka6hLYJ0qhETd+6lY= github.com/hashicorp/consul/sdk v0.13.1/go.mod h1:SW/mM4LbKfqmMvcFu8v+eiQQ7oitXEFeiBe9StxERb0= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -370,8 +358,6 @@ github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHh github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-netaddrs v0.1.0 h1:TnlYvODD4C/wO+j7cX1z69kV5gOzI87u3OcUinANaW8= -github.com/hashicorp/go-netaddrs v0.1.0/go.mod h1:33+a/emi5R5dqRspOuZKO0E+Tuz5WV1F84eRWALkedA= github.com/hashicorp/go-plugin v1.4.5 h1:oTE/oQR4eghggRg8VY7PAz3dr++VwDNBGCcOfIvHpBo= github.com/hashicorp/go-plugin v1.4.5/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= @@ -680,7 +666,6 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= @@ -872,7 +857,6 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1104,7 +1088,6 @@ k8s.io/api v0.19.3/go.mod h1:VF+5FT1B74Pw3KxMdKyinLo+zynBaMBiAfGMuldcNDs= k8s.io/api v0.26.3 h1:emf74GIQMTik01Aum9dPP0gAypL8JTLl/lHa4V9RFSU= k8s.io/api v0.26.3/go.mod h1:PXsqwPMXBSBcL1lJ9CYDKy7kIReUydukS5JiRlxC3qE= k8s.io/apiextensions-apiserver v0.26.3 h1:5PGMm3oEzdB1W/FTMgGIDmm100vn7IaUP5er36dB+YE= -k8s.io/apiextensions-apiserver v0.26.3/go.mod h1:jdA5MdjNWGP+njw1EKMZc64xAT5fIhN6VJrElV3sfpQ= k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= k8s.io/apimachinery v0.19.3/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= k8s.io/apimachinery v0.26.3 h1:dQx6PNETJ7nODU3XPtrwkfuubs6w7sX0M8n61zHIV/k= diff --git a/acceptance/tests/api-gateway/api_gateway_tenancy_test.go b/acceptance/tests/api-gateway/api_gateway_tenancy_test.go index 2f0005da80..e7748b9226 100644 --- a/acceptance/tests/api-gateway/api_gateway_tenancy_test.go +++ b/acceptance/tests/api-gateway/api_gateway_tenancy_test.go @@ -288,7 +288,7 @@ type certificateInfo struct { func generateCertificate(t *testing.T, ca *certificateInfo, commonName string) *certificateInfo { t.Helper() - bits := 1024 + bits := 2048 privateKey, err := rsa.GenerateKey(rand.Reader, bits) require.NoError(t, err) diff --git a/acceptance/tests/api-gateway/api_gateway_test.go b/acceptance/tests/api-gateway/api_gateway_test.go index 2291587bcc..17234cadf1 100644 --- a/acceptance/tests/api-gateway/api_gateway_test.go +++ b/acceptance/tests/api-gateway/api_gateway_test.go @@ -5,7 +5,9 @@ package apigateway import ( "context" + "encoding/base64" "fmt" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" "strconv" "testing" "time" @@ -79,12 +81,39 @@ func TestAPIGateway_Basic(t *testing.T) { k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-k", "../fixtures/bases/api-gateway") }) - logger.Log(t, "creating target server") + // Create certificate secret, we do this separately since + // applying the secret will make an invalid certificate that breaks other tests + logger.Log(t, "creating certificate secret") + out, err = k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "apply", "-f", "../fixtures/bases/api-gateway/certificate.yaml") + require.NoError(t, err, out) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + // Ignore errors here because if the test ran as expected + // the custom resources will have been deleted. + k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-f", "../fixtures/bases/api-gateway/certificate.yaml") + }) + + // patch certificate with data + logger.Log(t, "patching certificate secret with generated data") + certificate := generateCertificate(t, nil, "gateway.test.local") + k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "secret", "certificate", "-p", fmt.Sprintf(`{"data":{"tls.crt":"%s","tls.key":"%s"}}`, base64.StdEncoding.EncodeToString(certificate.CertPEM), base64.StdEncoding.EncodeToString(certificate.PrivateKeyPEM)), "--type=merge") + + logger.Log(t, "creating target http server") k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") - logger.Log(t, "patching route to target server") + logger.Log(t, "patching route to target http server") k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "httproute", "http-route", "-p", `{"spec":{"rules":[{"backendRefs":[{"name":"static-server","port":80}]}]}}`, "--type=merge") + logger.Log(t, "creating target tcp server") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-server-tcp") + + logger.Log(t, "creating tcp-route") + k8s.RunKubectl(t, ctx.KubectlOptions(t), "apply", "-f", "../fixtures/cases/api-gateways/tcproute/route.yaml") + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + // Ignore errors here because if the test ran as expected + // the custom resources will have been deleted. + k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-f", "../fixtures/cases/api-gateways/tcproute/route.yaml") + }) + // We use the static-client pod so that we can make calls to the api gateway // via kubectl exec without needing a route into the cluster from the test machine. logger.Log(t, "creating static-client pod") @@ -112,18 +141,19 @@ func TestAPIGateway_Basic(t *testing.T) { checkStatusCondition(r, gateway.Status.Conditions, trueCondition("Accepted", "Accepted")) checkStatusCondition(r, gateway.Status.Conditions, trueCondition("ConsulAccepted", "Accepted")) require.Len(r, gateway.Status.Listeners, 3) + require.EqualValues(r, 1, gateway.Status.Listeners[0].AttachedRoutes) checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, trueCondition("Accepted", "Accepted")) checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, falseCondition("Conflicted", "NoConflicts")) checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) - require.EqualValues(r, 0, gateway.Status.Listeners[1].AttachedRoutes) + require.EqualValues(r, 1, gateway.Status.Listeners[1].AttachedRoutes) checkStatusCondition(r, gateway.Status.Listeners[1].Conditions, trueCondition("Accepted", "Accepted")) checkStatusCondition(r, gateway.Status.Listeners[1].Conditions, falseCondition("Conflicted", "NoConflicts")) checkStatusCondition(r, gateway.Status.Listeners[1].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) require.EqualValues(r, 1, gateway.Status.Listeners[2].AttachedRoutes) checkStatusCondition(r, gateway.Status.Listeners[2].Conditions, trueCondition("Accepted", "Accepted")) checkStatusCondition(r, gateway.Status.Listeners[2].Conditions, falseCondition("Conflicted", "NoConflicts")) - checkStatusCondition(r, gateway.Status.Listeners[2].Conditions, falseCondition("ResolvedRefs", "InvalidCertificateRef")) + checkStatusCondition(r, gateway.Status.Listeners[2].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) // check that we have an address to use require.Len(r, gateway.Status.Addresses, 1) @@ -160,6 +190,23 @@ func TestAPIGateway_Basic(t *testing.T) { checkStatusCondition(t, httproute.Status.Parents[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) checkStatusCondition(t, httproute.Status.Parents[0].Conditions, trueCondition("ConsulAccepted", "Accepted")) + // tcp route checks + var tcpRoute gwv1alpha2.TCPRoute + err = k8sClient.Get(context.Background(), types.NamespacedName{Name: "tcp-route", Namespace: "default"}, &tcpRoute) + require.NoError(t, err) + + // check our finalizers + require.Len(t, tcpRoute.Finalizers, 1) + require.EqualValues(t, gatewayFinalizer, tcpRoute.Finalizers[0]) + + // check parent status + require.Len(t, tcpRoute.Status.Parents, 1) + require.EqualValues(t, gatewayClassControllerName, tcpRoute.Status.Parents[0].ControllerName) + require.EqualValues(t, "gateway", tcpRoute.Status.Parents[0].ParentRef.Name) + checkStatusCondition(t, tcpRoute.Status.Parents[0].Conditions, trueCondition("Accepted", "Accepted")) + checkStatusCondition(t, tcpRoute.Status.Parents[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) + checkStatusCondition(t, tcpRoute.Status.Parents[0].Conditions, trueCondition("ConsulAccepted", "Accepted")) + // check that the Consul entries were created entry, _, err := consulClient.ConfigEntries().Get(api.APIGateway, "gateway", nil) require.NoError(t, err) @@ -167,21 +214,32 @@ func TestAPIGateway_Basic(t *testing.T) { entry, _, err = consulClient.ConfigEntries().Get(api.HTTPRoute, "http-route", nil) require.NoError(t, err) - route := entry.(*api.HTTPRouteConfigEntry) + httpRoute := entry.(*api.HTTPRouteConfigEntry) + + entry, _, err = consulClient.ConfigEntries().Get(api.TCPRoute, "tcp-route", nil) + require.NoError(t, err) + route := entry.(*api.TCPRouteConfigEntry) // now check the gateway status conditions checkConsulStatusCondition(t, gateway.Status.Conditions, trueConsulCondition("Accepted", "Accepted")) // and the route status conditions + checkConsulStatusCondition(t, httpRoute.Status.Conditions, trueConsulCondition("Bound", "Bound")) checkConsulStatusCondition(t, route.Status.Conditions, trueConsulCondition("Bound", "Bound")) // finally we check that we can actually route to the service via the gateway k8sOptions := ctx.KubectlOptions(t) - targetAddress := fmt.Sprintf("http://%s/", gatewayAddress) + targetHTTPAddress := fmt.Sprintf("http://%s", gatewayAddress) + targetHTTPSAddress := fmt.Sprintf("https://%s", gatewayAddress) + targetTCPAddress := fmt.Sprintf("http://%s:81", gatewayAddress) if c.secure { // check that intentions keep our connection from happening - k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, targetAddress) + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, targetHTTPAddress) + + k8s.CheckStaticServerConnectionFailing(t, k8sOptions, StaticClientName, targetTCPAddress) + + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, "-k", targetHTTPSAddress) // Now we create the allow intention. _, _, err = consulClient.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{ @@ -195,12 +253,31 @@ func TestAPIGateway_Basic(t *testing.T) { }, }, nil) require.NoError(t, err) + + // Now we create the allow intention tcp. + _, _, err = consulClient.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{ + Kind: api.ServiceIntentions, + Name: "static-server-tcp", + Sources: []*api.SourceIntention{ + { + Name: "gateway", + Action: api.IntentionActionAllow, + }, + }, + }, nil) + require.NoError(t, err) } // Test that we can make a call to the api gateway // via the static-client pod. It should route to the static-server pod. - logger.Log(t, "trying calls to api gateway") - k8s.CheckStaticServerConnectionSuccessful(t, k8sOptions, StaticClientName, targetAddress) + logger.Log(t, "trying calls to api gateway http") + k8s.CheckStaticServerConnectionSuccessful(t, k8sOptions, StaticClientName, targetHTTPAddress) + + logger.Log(t, "trying calls to api gateway tcp") + k8s.CheckStaticServerConnectionSuccessful(t, k8sOptions, StaticClientName, targetTCPAddress) + + logger.Log(t, "trying calls to api gateway https") + k8s.CheckStaticServerConnectionSuccessful(t, k8sOptions, StaticClientName, targetHTTPSAddress, "-k") }) } } diff --git a/acceptance/tests/fixtures/bases/static-server-tcp/anyuid-scc-rolebinding.yaml b/acceptance/tests/fixtures/bases/static-server-tcp/anyuid-scc-rolebinding.yaml new file mode 100644 index 0000000000..eb86dc8bae --- /dev/null +++ b/acceptance/tests/fixtures/bases/static-server-tcp/anyuid-scc-rolebinding.yaml @@ -0,0 +1,14 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: static-server-tcp-openshift-anyuid +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:openshift:scc:anyuid +subjects: + - kind: ServiceAccount + name: static-server-tcp \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/static-server-tcp/deployment.yaml b/acceptance/tests/fixtures/bases/static-server-tcp/deployment.yaml new file mode 100644 index 0000000000..9aa5177e9e --- /dev/null +++ b/acceptance/tests/fixtures/bases/static-server-tcp/deployment.yaml @@ -0,0 +1,49 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: static-server-tcp + name: static-server-tcp +spec: + replicas: 1 + selector: + matchLabels: + app: static-server-tcp + template: + metadata: + annotations: + "consul.hashicorp.com/connect-inject": "true" + labels: + app: static-server-tcp + spec: + containers: + - name: static-server + image: docker.mirror.hashicorp.services/kschoche/http-echo:latest + args: + - -text="hello world" + - -listen=:8080 + ports: + - containerPort: 8080 + name: http + livenessProbe: + httpGet: + port: 8080 + initialDelaySeconds: 1 + failureThreshold: 1 + periodSeconds: 1 + startupProbe: + httpGet: + port: 8080 + initialDelaySeconds: 1 + failureThreshold: 30 + periodSeconds: 1 + readinessProbe: + exec: + command: ['sh', '-c', 'test ! -f /tmp/unhealthy'] + initialDelaySeconds: 1 + failureThreshold: 1 + periodSeconds: 1 + serviceAccountName: static-server-tcp diff --git a/acceptance/tests/fixtures/bases/static-server-tcp/kustomization.yaml b/acceptance/tests/fixtures/bases/static-server-tcp/kustomization.yaml new file mode 100644 index 0000000000..2180aa94e1 --- /dev/null +++ b/acceptance/tests/fixtures/bases/static-server-tcp/kustomization.yaml @@ -0,0 +1,11 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - deployment.yaml + - service.yaml + - serviceaccount.yaml + - servicedefaults.yaml + - psp-rolebinding.yaml + - anyuid-scc-rolebinding.yaml + - privileged-scc-rolebinding.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/static-server-tcp/privileged-scc-rolebinding.yaml b/acceptance/tests/fixtures/bases/static-server-tcp/privileged-scc-rolebinding.yaml new file mode 100644 index 0000000000..ac28006765 --- /dev/null +++ b/acceptance/tests/fixtures/bases/static-server-tcp/privileged-scc-rolebinding.yaml @@ -0,0 +1,14 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: static-server-tcp-openshift-privileged +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:openshift:scc:privileged +subjects: + - kind: ServiceAccount + name: static-server-tcp \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/static-server-tcp/psp-rolebinding.yaml b/acceptance/tests/fixtures/bases/static-server-tcp/psp-rolebinding.yaml new file mode 100644 index 0000000000..f4f008dbea --- /dev/null +++ b/acceptance/tests/fixtures/bases/static-server-tcp/psp-rolebinding.yaml @@ -0,0 +1,14 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: static-server-tcp +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: test-psp +subjects: + - kind: ServiceAccount + name: static-server-tcp \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/static-server-tcp/service.yaml b/acceptance/tests/fixtures/bases/static-server-tcp/service.yaml new file mode 100644 index 0000000000..6ceccf940a --- /dev/null +++ b/acceptance/tests/fixtures/bases/static-server-tcp/service.yaml @@ -0,0 +1,15 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: v1 +kind: Service +metadata: + name: static-server-tcp + labels: + app: static-server-tcp +spec: + ports: + - name: http + port: 8080 + selector: + app: static-server-tcp diff --git a/acceptance/tests/fixtures/bases/static-server-tcp/serviceaccount.yaml b/acceptance/tests/fixtures/bases/static-server-tcp/serviceaccount.yaml new file mode 100644 index 0000000000..af2247af8e --- /dev/null +++ b/acceptance/tests/fixtures/bases/static-server-tcp/serviceaccount.yaml @@ -0,0 +1,7 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: v1 +kind: ServiceAccount +metadata: + name: static-server-tcp diff --git a/acceptance/tests/fixtures/bases/static-server-tcp/servicedefaults.yaml b/acceptance/tests/fixtures/bases/static-server-tcp/servicedefaults.yaml new file mode 100644 index 0000000000..500051db87 --- /dev/null +++ b/acceptance/tests/fixtures/bases/static-server-tcp/servicedefaults.yaml @@ -0,0 +1,7 @@ +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ServiceDefaults +metadata: + name: static-server-tcp + namespace: default +spec: + protocol: tcp \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/api-gateways/tcproute/route.yaml b/acceptance/tests/fixtures/cases/api-gateways/tcproute/route.yaml new file mode 100644 index 0000000000..37602c65af --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/tcproute/route.yaml @@ -0,0 +1,14 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: TCPRoute +metadata: + name: tcp-route +spec: + parentRefs: + - name: gateway + rules: + - backendRefs: + - kind: Service + name: static-server-tcp \ No newline at end of file diff --git a/control-plane/api-gateway/controllers/gateway_controller.go b/control-plane/api-gateway/controllers/gateway_controller.go index ab2b6af1a5..ec8c2e9af0 100644 --- a/control-plane/api-gateway/controllers/gateway_controller.go +++ b/control-plane/api-gateway/controllers/gateway_controller.go @@ -191,7 +191,7 @@ func (r *GatewayController) Reconcile(ctx context.Context, req ctrl.Request) (ct if updates.UpsertGatewayDeployment { if err := r.cache.EnsureRoleBinding(r.HelmConfig.AuthMethod, gateway.Name, gateway.Namespace); err != nil { - log.Error(err, "error linking token policy") + log.Error(err, "error creating role binding") return ctrl.Result{}, err } From 555d4a64a498c2f7462f3659c3858f2430f5c8e3 Mon Sep 17 00:00:00 2001 From: Andrew Stucki Date: Fri, 9 Jun 2023 00:31:40 -0400 Subject: [PATCH 217/340] [API Gateway] Add acceptance test for cluster peering (#2306) * [API Gateway] Add acceptance test for cluster peering * Fix linter * Fix random unrelated linter errors to get CI to run: revert later? * one more linter fix to later probably revert * more linter fixes * Revert "more linter fixes" This reverts commit 6210dff0e51bbcf2f754f6d666c08292ba958aaa. * Revert "one more linter fix to later probably revert" This reverts commit 030c563bbe0b0a9ef73b33cbea32464416156d8f. * Revert "Fix random unrelated linter errors to get CI to run: revert later?" This reverts commit fdeccabb2f6c4418168cad9be5b2459435b7e30b. --- .../peer-resolver/kustomization.yaml | 5 + .../peer-resolver/serviceresolver.yaml | 12 + .../tests/peering/peering_gateway_test.go | 291 ++++++++++++++++++ 3 files changed, 308 insertions(+) create mode 100644 acceptance/tests/fixtures/cases/api-gateways/peer-resolver/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/peer-resolver/serviceresolver.yaml create mode 100644 acceptance/tests/peering/peering_gateway_test.go diff --git a/acceptance/tests/fixtures/cases/api-gateways/peer-resolver/kustomization.yaml b/acceptance/tests/fixtures/cases/api-gateways/peer-resolver/kustomization.yaml new file mode 100644 index 0000000000..cdbcd688c0 --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/peer-resolver/kustomization.yaml @@ -0,0 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - serviceresolver.yaml diff --git a/acceptance/tests/fixtures/cases/api-gateways/peer-resolver/serviceresolver.yaml b/acceptance/tests/fixtures/cases/api-gateways/peer-resolver/serviceresolver.yaml new file mode 100644 index 0000000000..20874fe1f9 --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/peer-resolver/serviceresolver.yaml @@ -0,0 +1,12 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ServiceResolver +metadata: + name: static-server +spec: + redirect: + peer: server + namespace: ns1 + service: static-server \ No newline at end of file diff --git a/acceptance/tests/peering/peering_gateway_test.go b/acceptance/tests/peering/peering_gateway_test.go new file mode 100644 index 0000000000..76698102e6 --- /dev/null +++ b/acceptance/tests/peering/peering_gateway_test.go @@ -0,0 +1,291 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package peering + +import ( + "context" + "fmt" + "testing" + "time" + + terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" + "github.com/hashicorp/consul-k8s/acceptance/framework/consul" + "github.com/hashicorp/consul-k8s/acceptance/framework/environment" + "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" + "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" + "github.com/hashicorp/consul-k8s/acceptance/framework/logger" + "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/sdk/testutil/retry" + "github.com/hashicorp/go-version" + "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/types" + + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +func TestPeering_Gateway(t *testing.T) { + env := suite.Environment() + cfg := suite.Config() + + if !cfg.EnableEnterprise { + t.Skipf("skipping this test because -enable-enterprise is not set") + } + + ver, err := version.NewVersion("1.13.0") + require.NoError(t, err) + if cfg.ConsulVersion != nil && cfg.ConsulVersion.LessThan(ver) { + t.Skipf("skipping this test because peering is not supported in version %v", cfg.ConsulVersion.String()) + } + + const staticServerPeer = "server" + const staticClientPeer = "client" + + staticServerPeerClusterContext := env.DefaultContext(t) + staticClientPeerClusterContext := env.Context(t, environment.SecondaryContextName) + + commonHelmValues := map[string]string{ + "global.peering.enabled": "true", + "global.enableConsulNamespaces": "true", + + "global.tls.enabled": "true", + "global.tls.httpsOnly": "true", + + "global.acls.manageSystemACLs": "true", + + "connectInject.enabled": "true", + + // When mirroringK8S is set, this setting is ignored. + "connectInject.consulNamespaces.mirroringK8S": "true", + + "meshGateway.enabled": "true", + "meshGateway.replicas": "1", + + "dns.enabled": "true", + } + + staticServerPeerHelmValues := map[string]string{ + "global.datacenter": staticServerPeer, + } + + if !cfg.UseKind { + staticServerPeerHelmValues["server.replicas"] = "3" + } + + // On Kind, there are no load balancers but since all clusters + // share the same node network (docker bridge), we can use + // a NodePort service so that we can access node(s) in a different Kind cluster. + if cfg.UseKind { + staticServerPeerHelmValues["server.exposeGossipAndRPCPorts"] = "true" + staticServerPeerHelmValues["meshGateway.service.type"] = "NodePort" + staticServerPeerHelmValues["meshGateway.service.nodePort"] = "30100" + } + + releaseName := helpers.RandomName() + + helpers.MergeMaps(staticServerPeerHelmValues, commonHelmValues) + + // Install the first peer where static-server will be deployed in the static-server kubernetes context. + staticServerPeerCluster := consul.NewHelmCluster(t, staticServerPeerHelmValues, staticServerPeerClusterContext, cfg, releaseName) + staticServerPeerCluster.Create(t) + + staticClientPeerHelmValues := map[string]string{ + "global.datacenter": staticClientPeer, + } + + if !cfg.UseKind { + staticClientPeerHelmValues["server.replicas"] = "3" + } + + if cfg.UseKind { + staticClientPeerHelmValues["server.exposeGossipAndRPCPorts"] = "true" + staticClientPeerHelmValues["meshGateway.service.type"] = "NodePort" + staticClientPeerHelmValues["meshGateway.service.nodePort"] = "30100" + } + + helpers.MergeMaps(staticClientPeerHelmValues, commonHelmValues) + + // Install the second peer where static-client will be deployed in the static-client kubernetes context. + staticClientPeerCluster := consul.NewHelmCluster(t, staticClientPeerHelmValues, staticClientPeerClusterContext, cfg, releaseName) + staticClientPeerCluster.Create(t) + + // Create Mesh resource to use mesh gateways. + logger.Log(t, "creating mesh config") + kustomizeMeshDir := "../fixtures/bases/mesh-peering" + + k8s.KubectlApplyK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.KubectlDeleteK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) + }) + + k8s.KubectlApplyK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.KubectlDeleteK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) + }) + + staticServerPeerClient, _ := staticServerPeerCluster.SetupConsulClient(t, true) + staticClientPeerClient, _ := staticClientPeerCluster.SetupConsulClient(t, true) + + // Ensure mesh config entries are created in Consul. + timer := &retry.Timer{Timeout: 1 * time.Minute, Wait: 1 * time.Second} + retry.RunWith(timer, t, func(r *retry.R) { + ceServer, _, err := staticServerPeerClient.ConfigEntries().Get(api.MeshConfig, "mesh", &api.QueryOptions{}) + require.NoError(r, err) + configEntryServer, ok := ceServer.(*api.MeshConfigEntry) + require.True(r, ok) + require.Equal(r, configEntryServer.GetName(), "mesh") + require.NoError(r, err) + + ceClient, _, err := staticClientPeerClient.ConfigEntries().Get(api.MeshConfig, "mesh", &api.QueryOptions{}) + require.NoError(r, err) + configEntryClient, ok := ceClient.(*api.MeshConfigEntry) + require.True(r, ok) + require.Equal(r, configEntryClient.GetName(), "mesh") + require.NoError(r, err) + }) + + // Create the peering acceptor on the client peer. + k8s.KubectlApply(t, staticClientPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-acceptor.yaml") + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.KubectlDelete(t, staticClientPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-acceptor.yaml") + }) + + // Ensure the secret is created. + retry.RunWith(timer, t, func(r *retry.R) { + acceptorSecretName, err := k8s.RunKubectlAndGetOutputE(t, staticClientPeerClusterContext.KubectlOptions(t), "get", "peeringacceptor", "server", "-o", "jsonpath={.status.secret.name}") + require.NoError(r, err) + require.NotEmpty(r, acceptorSecretName) + }) + + // Copy secret from client peer to server peer. + k8s.CopySecret(t, staticClientPeerClusterContext, staticServerPeerClusterContext, "api-token") + + // Create the peering dialer on the server peer. + k8s.KubectlApply(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-dialer.yaml") + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.RunKubectl(t, staticServerPeerClusterContext.KubectlOptions(t), "delete", "secret", "api-token") + k8s.KubectlDelete(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-dialer.yaml") + }) + + staticServerOpts := &terratestk8s.KubectlOptions{ + ContextName: staticServerPeerClusterContext.KubectlOptions(t).ContextName, + ConfigPath: staticServerPeerClusterContext.KubectlOptions(t).ConfigPath, + Namespace: staticServerNamespace, + } + staticClientOpts := &terratestk8s.KubectlOptions{ + ContextName: staticClientPeerClusterContext.KubectlOptions(t).ContextName, + ConfigPath: staticClientPeerClusterContext.KubectlOptions(t).ConfigPath, + Namespace: staticClientNamespace, + } + + logger.Logf(t, "creating namespaces %s in server peer", staticServerNamespace) + k8s.RunKubectl(t, staticServerPeerClusterContext.KubectlOptions(t), "create", "ns", staticServerNamespace) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.RunKubectl(t, staticServerPeerClusterContext.KubectlOptions(t), "delete", "ns", staticServerNamespace) + }) + + logger.Logf(t, "creating namespaces %s in client peer", staticClientNamespace) + k8s.RunKubectl(t, staticClientPeerClusterContext.KubectlOptions(t), "create", "ns", staticClientNamespace) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.RunKubectl(t, staticClientPeerClusterContext.KubectlOptions(t), "delete", "ns", staticClientNamespace) + }) + + // Create a ProxyDefaults resource to configure services to use the mesh + // gateways. + logger.Log(t, "creating proxy-defaults config") + kustomizeDir := "../fixtures/cases/api-gateways/mesh" + + k8s.KubectlApplyK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeDir) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.KubectlDeleteK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeDir) + }) + + k8s.KubectlApplyK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeDir) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.KubectlDeleteK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeDir) + }) + + // We use the static-client pod so that we can make calls to the api gateway + // via kubectl exec without needing a route into the cluster from the test machine. + // Since we're deploying the gateway in the secondary cluster, we create the static client + // in the secondary as well. + logger.Log(t, "creating static-client pod in client peer") + k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-peers/non-default-namespace") + + logger.Log(t, "creating static-server in server peer") + k8s.DeployKustomize(t, staticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + + logger.Log(t, "creating exported services") + k8s.KubectlApplyK(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/cases/crd-peers/non-default-namespace") + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.KubectlDeleteK(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/cases/crd-peers/non-default-namespace") + }) + + logger.Log(t, "creating api-gateway resources in client peer") + out, err := k8s.RunKubectlAndGetOutputE(t, staticClientOpts, "apply", "-k", "../fixtures/bases/api-gateway") + require.NoError(t, err, out) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + // Ignore errors here because if the test ran as expected + // the custom resources will have been deleted. + k8s.RunKubectlAndGetOutputE(t, staticClientOpts, "delete", "-k", "../fixtures/bases/api-gateway") + }) + + // Grab a kubernetes client so that we can verify binding + // behavior prior to issuing requests through the gateway. + k8sClient := staticClientPeerClusterContext.ControllerRuntimeClient(t) + + // On startup, the controller can take upwards of 1m to perform + // leader election so we may need to wait a long time for + // the reconcile loop to run (hence the 1m timeout here). + var gatewayAddress string + counter := &retry.Counter{Count: 600, Wait: 2 * time.Second} + retry.RunWith(counter, t, func(r *retry.R) { + var gateway gwv1beta1.Gateway + err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway", Namespace: staticClientNamespace}, &gateway) + require.NoError(r, err) + + // check that we have an address to use + require.Len(r, gateway.Status.Addresses, 1) + // now we know we have an address, set it so we can use it + gatewayAddress = gateway.Status.Addresses[0].Value + }) + + targetAddress := fmt.Sprintf("http://%s/", gatewayAddress) + + logger.Log(t, "creating local service resolver") + k8s.KubectlApplyK(t, staticClientOpts, "../fixtures/cases/api-gateways/peer-resolver") + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.KubectlDeleteK(t, staticClientOpts, "../fixtures/cases/api-gateways/peer-resolver") + }) + + logger.Log(t, "patching route to target server") + k8s.RunKubectl(t, staticClientOpts, "patch", "httproute", "http-route", "-p", `{"spec":{"rules":[{"backendRefs":[{"group":"consul.hashicorp.com","kind":"MeshService","name":"mesh-service","port":80}]}]}}`, "--type=merge") + + logger.Log(t, "checking that the connection is not successful because there's no intention") + k8s.CheckStaticServerHTTPConnectionFailing(t, staticClientOpts, staticClientName, targetAddress) + + intention := &api.ServiceIntentionsConfigEntry{ + Kind: api.ServiceIntentions, + Name: staticServerName, + Namespace: staticServerNamespace, + Sources: []*api.SourceIntention{ + { + Name: "gateway", + Namespace: staticClientNamespace, + Action: api.IntentionActionAllow, + Peer: staticClientPeer, + }, + }, + } + + logger.Log(t, "creating intention") + _, _, err = staticServerPeerClient.ConfigEntries().Set(intention, &api.WriteOptions{}) + require.NoError(t, err) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + _, err = staticServerPeerClient.ConfigEntries().Delete(api.ServiceIntentions, staticServerName, &api.WriteOptions{}) + require.NoError(t, err) + }) + + logger.Log(t, "checking that connection is successful") + k8s.CheckStaticServerConnectionSuccessful(t, staticClientOpts, staticClientName, targetAddress) +} From b56b7dd4d7d90189d73a71b6a154a89ddafcd511 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Thu, 8 Jun 2023 22:39:08 -0700 Subject: [PATCH 218/340] Mw/net 3598 update kind for consul k8s acceptance tests with latest version of kind and k8s 1.27 (#2304) * update cloud tests to use 1.24, 1.25 and 1.26 version of kubernetes for more coverage * updated readme for supported kubernetes versions * added changelog --- .changelog/2304.txt | 3 +++ README.md | 2 +- charts/consul/test/terraform/aks/main.tf | 2 +- charts/consul/test/terraform/gke/main.tf | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 .changelog/2304.txt diff --git a/.changelog/2304.txt b/.changelog/2304.txt new file mode 100644 index 0000000000..c977da5acd --- /dev/null +++ b/.changelog/2304.txt @@ -0,0 +1,3 @@ +```release-note:improvement +helm: Kubernetes v1.27 is now supported. Minimum tested version of Kubernetes is now v1.24. +``` diff --git a/README.md b/README.md index 1d3a3733ab..d43a12b455 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ by contacting us at [security@hashicorp.com](mailto:security@hashicorp.com). The following pre-requisites must be met before installing Consul on Kubernetes. - * **Kubernetes 1.23.x - 1.26.x** - This represents the earliest versions of Kubernetes tested. + * **Kubernetes 1.24.x - 1.27.x** - This represents the earliest versions of Kubernetes tested. It is possible that this chart works with earlier versions, but it is untested. * Helm install diff --git a/charts/consul/test/terraform/aks/main.tf b/charts/consul/test/terraform/aks/main.tf index bf8c925f15..2683bdc1a7 100644 --- a/charts/consul/test/terraform/aks/main.tf +++ b/charts/consul/test/terraform/aks/main.tf @@ -55,7 +55,7 @@ resource "azurerm_kubernetes_cluster" "default" { location = azurerm_resource_group.default[count.index].location resource_group_name = azurerm_resource_group.default[count.index].name dns_prefix = "consul-k8s-${random_id.suffix[count.index].dec}" - kubernetes_version = "1.26" + kubernetes_version = "1.24.10" role_based_access_control_enabled = true // We're setting the network plugin and other network properties explicitly diff --git a/charts/consul/test/terraform/gke/main.tf b/charts/consul/test/terraform/gke/main.tf index fe5adc5e8d..34bb07906f 100644 --- a/charts/consul/test/terraform/gke/main.tf +++ b/charts/consul/test/terraform/gke/main.tf @@ -21,7 +21,7 @@ resource "random_id" "suffix" { data "google_container_engine_versions" "main" { location = var.zone - version_prefix = "1.25." + version_prefix = "1.25.9" } # We assume that the subnets are already created to save time. From 203c9d19766209460e782f650536795b9b82e668 Mon Sep 17 00:00:00 2001 From: Andrew Stucki Date: Fri, 9 Jun 2023 09:53:16 -0400 Subject: [PATCH 219/340] [API Gateway] WAN Federation test and fixes (#2295) * [API Gateway] WAN Federation test and fixes * Fix unit tests --- .../dc1-to-dc2-resolver/kustomization.yaml | 5 + .../dc1-to-dc2-resolver/serviceresolver.yaml | 11 + .../dc2-to-dc1-resolver/kustomization.yaml | 5 + .../dc2-to-dc1-resolver/serviceresolver.yaml | 11 + .../wan_federation_gateway_test.go | 241 ++++++++++++++++++ control-plane/api-gateway/cache/consul.go | 19 ++ .../api-gateway/cache/consul_test.go | 124 ++++++--- .../api-gateway/common/helm_config.go | 24 +- .../api-gateway/common/translation.go | 25 +- .../controllers/gateway_controller.go | 5 +- .../api/v1alpha1/serviceresolver_types.go | 2 +- .../connect-inject/constants/constants.go | 3 + .../subcommand/inject-connect/command.go | 2 + 13 files changed, 427 insertions(+), 50 deletions(-) create mode 100644 acceptance/tests/fixtures/cases/api-gateways/dc1-to-dc2-resolver/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/dc1-to-dc2-resolver/serviceresolver.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/dc2-to-dc1-resolver/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/dc2-to-dc1-resolver/serviceresolver.yaml create mode 100644 acceptance/tests/wan-federation/wan_federation_gateway_test.go diff --git a/acceptance/tests/fixtures/cases/api-gateways/dc1-to-dc2-resolver/kustomization.yaml b/acceptance/tests/fixtures/cases/api-gateways/dc1-to-dc2-resolver/kustomization.yaml new file mode 100644 index 0000000000..cdbcd688c0 --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/dc1-to-dc2-resolver/kustomization.yaml @@ -0,0 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - serviceresolver.yaml diff --git a/acceptance/tests/fixtures/cases/api-gateways/dc1-to-dc2-resolver/serviceresolver.yaml b/acceptance/tests/fixtures/cases/api-gateways/dc1-to-dc2-resolver/serviceresolver.yaml new file mode 100644 index 0000000000..ca009754b4 --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/dc1-to-dc2-resolver/serviceresolver.yaml @@ -0,0 +1,11 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ServiceResolver +metadata: + name: static-server +spec: + redirect: + service: static-server + datacenter: dc2 \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/api-gateways/dc2-to-dc1-resolver/kustomization.yaml b/acceptance/tests/fixtures/cases/api-gateways/dc2-to-dc1-resolver/kustomization.yaml new file mode 100644 index 0000000000..cdbcd688c0 --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/dc2-to-dc1-resolver/kustomization.yaml @@ -0,0 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - serviceresolver.yaml diff --git a/acceptance/tests/fixtures/cases/api-gateways/dc2-to-dc1-resolver/serviceresolver.yaml b/acceptance/tests/fixtures/cases/api-gateways/dc2-to-dc1-resolver/serviceresolver.yaml new file mode 100644 index 0000000000..af8cdb72ed --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/dc2-to-dc1-resolver/serviceresolver.yaml @@ -0,0 +1,11 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ServiceResolver +metadata: + name: static-server +spec: + redirect: + service: static-server + datacenter: dc1 \ No newline at end of file diff --git a/acceptance/tests/wan-federation/wan_federation_gateway_test.go b/acceptance/tests/wan-federation/wan_federation_gateway_test.go new file mode 100644 index 0000000000..0ef48b9920 --- /dev/null +++ b/acceptance/tests/wan-federation/wan_federation_gateway_test.go @@ -0,0 +1,241 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package wanfederation + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/hashicorp/consul-k8s/acceptance/framework/consul" + "github.com/hashicorp/consul-k8s/acceptance/framework/environment" + "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" + "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" + "github.com/hashicorp/consul-k8s/acceptance/framework/logger" + "github.com/hashicorp/consul/api" + "github.com/hashicorp/serf/testutil/retry" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +func TestWANFederation_Gateway(t *testing.T) { + env := suite.Environment() + cfg := suite.Config() + + if cfg.UseKind { + // the only way this test can currently run on kind, at least on a Mac, is via leveraging MetalLB, which + // isn't in CI, so we just skip for now. + t.Skipf("skipping wan federation tests as they currently fail on Kind even though they work on other clouds.") + } + + primaryContext := env.DefaultContext(t) + secondaryContext := env.Context(t, environment.SecondaryContextName) + + primaryHelmValues := map[string]string{ + "global.datacenter": "dc1", + + "global.tls.enabled": "true", + "global.tls.httpsOnly": "true", + + "global.federation.enabled": "true", + "global.federation.createFederationSecret": "true", + + "global.acls.manageSystemACLs": "true", + "global.acls.createReplicationToken": "true", + + "connectInject.enabled": "true", + "connectInject.replicas": "1", + + "meshGateway.enabled": "true", + "meshGateway.replicas": "1", + } + + releaseName := helpers.RandomName() + + // Install the primary consul cluster in the default kubernetes context + primaryConsulCluster := consul.NewHelmCluster(t, primaryHelmValues, primaryContext, cfg, releaseName) + primaryConsulCluster.Create(t) + + // Get the federation secret from the primary cluster and apply it to secondary cluster + federationSecretName := fmt.Sprintf("%s-consul-federation", releaseName) + logger.Logf(t, "retrieving federation secret %s from the primary cluster and applying to the secondary", federationSecretName) + federationSecret, err := primaryContext.KubernetesClient(t).CoreV1().Secrets(primaryContext.KubectlOptions(t).Namespace).Get(context.Background(), federationSecretName, metav1.GetOptions{}) + require.NoError(t, err) + federationSecret.ResourceVersion = "" + _, err = secondaryContext.KubernetesClient(t).CoreV1().Secrets(secondaryContext.KubectlOptions(t).Namespace).Create(context.Background(), federationSecret, metav1.CreateOptions{}) + require.NoError(t, err) + + var k8sAuthMethodHost string + // When running on kind, the kube API address in kubeconfig will have a localhost address + // which will not work from inside the container. That's why we need to use the endpoints address instead + // which will point the node IP. + if cfg.UseKind { + // The Kubernetes AuthMethod host is read from the endpoints for the Kubernetes service. + kubernetesEndpoint, err := secondaryContext.KubernetesClient(t).CoreV1().Endpoints("default").Get(context.Background(), "kubernetes", metav1.GetOptions{}) + require.NoError(t, err) + k8sAuthMethodHost = fmt.Sprintf("%s:%d", kubernetesEndpoint.Subsets[0].Addresses[0].IP, kubernetesEndpoint.Subsets[0].Ports[0].Port) + } else { + k8sAuthMethodHost = k8s.KubernetesAPIServerHostFromOptions(t, secondaryContext.KubectlOptions(t)) + } + + // Create secondary cluster + secondaryHelmValues := map[string]string{ + "global.datacenter": "dc2", + + "global.tls.enabled": "true", + "global.tls.httpsOnly": "false", + "global.acls.manageSystemACLs": "true", + "global.tls.caCert.secretName": federationSecretName, + "global.tls.caCert.secretKey": "caCert", + "global.tls.caKey.secretName": federationSecretName, + "global.tls.caKey.secretKey": "caKey", + + "global.federation.enabled": "true", + + "server.extraVolumes[0].type": "secret", + "server.extraVolumes[0].name": federationSecretName, + "server.extraVolumes[0].load": "true", + "server.extraVolumes[0].items[0].key": "serverConfigJSON", + "server.extraVolumes[0].items[0].path": "config.json", + + "connectInject.enabled": "true", + "connectInject.replicas": "1", + + "meshGateway.enabled": "true", + "meshGateway.replicas": "1", + + "global.acls.replicationToken.secretName": federationSecretName, + "global.acls.replicationToken.secretKey": "replicationToken", + "global.federation.k8sAuthMethodHost": k8sAuthMethodHost, + "global.federation.primaryDatacenter": "dc1", + } + + // Install the secondary consul cluster in the secondary kubernetes context + secondaryConsulCluster := consul.NewHelmCluster(t, secondaryHelmValues, secondaryContext, cfg, releaseName) + secondaryConsulCluster.Create(t) + + primaryClient, _ := primaryConsulCluster.SetupConsulClient(t, true) + secondaryClient, _ := secondaryConsulCluster.SetupConsulClient(t, true) + + // Verify federation between servers + logger.Log(t, "verifying federation was successful") + helpers.VerifyFederation(t, primaryClient, secondaryClient, releaseName, true) + + // Create a ProxyDefaults resource to configure services to use the mesh + // gateways. + logger.Log(t, "creating proxy-defaults config in dc1") + kustomizeDir := "../fixtures/cases/api-gateways/mesh" + k8s.KubectlApplyK(t, primaryContext.KubectlOptions(t), kustomizeDir) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.KubectlDeleteK(t, primaryContext.KubectlOptions(t), kustomizeDir) + }) + + // these clients are just there so we can exec in and curl on them. + logger.Log(t, "creating static-client in dc1") + k8s.DeployKustomize(t, primaryContext.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-multi-dc") + + logger.Log(t, "creating static-client in dc2") + k8s.DeployKustomize(t, secondaryContext.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-multi-dc") + + t.Run("from primary to secondary", func(t *testing.T) { + logger.Log(t, "creating static-server in dc2") + k8s.DeployKustomize(t, secondaryContext.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + + logger.Log(t, "creating api-gateway resources in dc1") + out, err := k8s.RunKubectlAndGetOutputE(t, primaryContext.KubectlOptions(t), "apply", "-k", "../fixtures/bases/api-gateway") + require.NoError(t, err, out) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + // Ignore errors here because if the test ran as expected + // the custom resources will have been deleted. + k8s.RunKubectlAndGetOutputE(t, primaryContext.KubectlOptions(t), "delete", "-k", "../fixtures/bases/api-gateway") + }) + + // create a service resolver for doing cross-dc redirects. + k8s.KubectlApplyK(t, secondaryContext.KubectlOptions(t), "../fixtures/cases/api-gateways/dc1-to-dc2-resolver") + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.KubectlDeleteK(t, secondaryContext.KubectlOptions(t), "../fixtures/cases/api-gateways/dc1-to-dc2-resolver") + }) + + // patching the route to target a MeshService since we don't have the corresponding Kubernetes service in this + // cluster. + k8s.RunKubectl(t, primaryContext.KubectlOptions(t), "patch", "httproute", "http-route", "-p", `{"spec":{"rules":[{"backendRefs":[{"group":"consul.hashicorp.com","kind":"MeshService","name":"mesh-service","port":80}]}]}}`, "--type=merge") + + checkConnectivity(t, primaryContext, primaryClient) + }) + + t.Run("from secondary to primary", func(t *testing.T) { + // Check that we can connect services over the mesh gateways + logger.Log(t, "creating static-server in dc1") + k8s.DeployKustomize(t, primaryContext.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + + logger.Log(t, "creating api-gateway resources in dc2") + out, err := k8s.RunKubectlAndGetOutputE(t, secondaryContext.KubectlOptions(t), "apply", "-k", "../fixtures/bases/api-gateway") + require.NoError(t, err, out) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + // Ignore errors here because if the test ran as expected + // the custom resources will have been deleted. + k8s.RunKubectlAndGetOutputE(t, secondaryContext.KubectlOptions(t), "delete", "-k", "../fixtures/bases/api-gateway") + }) + + // create a service resolver for doing cross-dc redirects. + k8s.KubectlApplyK(t, secondaryContext.KubectlOptions(t), "../fixtures/cases/api-gateways/dc2-to-dc1-resolver") + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.KubectlDeleteK(t, secondaryContext.KubectlOptions(t), "../fixtures/cases/api-gateways/dc2-to-dc1-resolver") + }) + + // patching the route to target a MeshService since we don't have the corresponding Kubernetes service in this + // cluster. + k8s.RunKubectl(t, secondaryContext.KubectlOptions(t), "patch", "httproute", "http-route", "-p", `{"spec":{"rules":[{"backendRefs":[{"group":"consul.hashicorp.com","kind":"MeshService","name":"mesh-service","port":80}]}]}}`, "--type=merge") + + checkConnectivity(t, secondaryContext, primaryClient) + }) +} + +func checkConnectivity(t *testing.T, ctx environment.TestContext, client *api.Client) { + k8sClient := ctx.ControllerRuntimeClient(t) + + // On startup, the controller can take upwards of 1m to perform + // leader election so we may need to wait a long time for + // the reconcile loop to run (hence the 1m timeout here). + var gatewayAddress string + counter := &retry.Counter{Count: 600, Wait: 2 * time.Second} + retry.RunWith(counter, t, func(r *retry.R) { + var gateway gwv1beta1.Gateway + err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway", Namespace: "default"}, &gateway) + require.NoError(r, err) + + // check that we have an address to use + require.Len(r, gateway.Status.Addresses, 1) + // now we know we have an address, set it so we can use it + gatewayAddress = gateway.Status.Addresses[0].Value + }) + + targetAddress := fmt.Sprintf("http://%s/", gatewayAddress) + + logger.Log(t, "checking that the connection is not successful because there's no intention") + k8s.CheckStaticServerHTTPConnectionFailing(t, ctx.KubectlOptions(t), StaticClientName, targetAddress) + + logger.Log(t, "creating intention") + _, _, err := client.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{ + Kind: api.ServiceIntentions, + Name: "static-server", + Sources: []*api.SourceIntention{ + { + Name: "gateway", + Action: api.IntentionActionAllow, + }, + }, + }, nil) + require.NoError(t, err) + defer func() { + _, err := client.ConfigEntries().Delete(api.ServiceIntentions, "static-server", &api.WriteOptions{}) + require.NoError(t, err) + }() + + logger.Log(t, "checking that connection is successful") + k8s.CheckStaticServerConnectionSuccessful(t, ctx.KubectlOptions(t), StaticClientName, targetAddress) +} diff --git a/control-plane/api-gateway/cache/consul.go b/control-plane/api-gateway/cache/consul.go index e47df71522..7737e80d57 100644 --- a/control-plane/api-gateway/cache/consul.go +++ b/control-plane/api-gateway/cache/consul.go @@ -17,6 +17,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/event" "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" "github.com/hashicorp/consul-k8s/control-plane/consul" "github.com/hashicorp/consul-k8s/control-plane/namespaces" "github.com/hashicorp/consul/api" @@ -60,6 +61,7 @@ type Config struct { ConsulClientConfig *consul.Config ConsulServerConnMgr consul.ServerConnectionManager NamespacesEnabled bool + Datacenter string CrossNamespaceACLPolicy string Logger logr.Logger } @@ -83,6 +85,8 @@ type Cache struct { synced chan struct{} kinds []string + + datacenter string } func New(config Config) *Cache { @@ -104,6 +108,7 @@ func New(config Config) *Cache { synced: make(chan struct{}, len(Kinds)), logger: config.Logger, crossNamespaceACLPolicy: config.CrossNamespaceACLPolicy, + datacenter: config.Datacenter, } } @@ -216,6 +221,19 @@ func (c *Cache) updateAndNotify(ctx context.Context, once *sync.Once, kind strin cache := common.NewReferenceMap() for _, entry := range entries { + meta := entry.GetMeta() + if meta[constants.MetaKeyKubeName] == "" || meta[constants.MetaKeyDatacenter] != c.datacenter { + // Don't process things that don't belong to us. The main reason + // for this is so that we don't garbage collect config entries that + // are either user-created or that another controller running in a + // federated datacenter creates. While we still allow for competing controllers + // syncing/overriding each other due to conflicting Kubernetes objects in + // two federated clusters (which is what the rest of the controllers also allow + // for), we don't want to delete a config entry just because we don't have + // its corresponding Kubernetes object if we know it belongs to another datacenter. + continue + } + cache.Set(common.EntryToReference(entry), entry) } @@ -336,6 +354,7 @@ func (c *Cache) ensureRole(client *api.Client) (string, error) { } aclRoleName := "managed-gateway-acl-role" + aclRole, _, err := client.ACL().RoleReadByName(aclRoleName, &api.QueryOptions{}) if err != nil { return "", err diff --git a/control-plane/api-gateway/cache/consul_test.go b/control-plane/api-gateway/cache/consul_test.go index 555e13b6c2..59570e532f 100644 --- a/control-plane/api-gateway/cache/consul_test.go +++ b/control-plane/api-gateway/cache/consul_test.go @@ -22,6 +22,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/event" "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" "github.com/hashicorp/consul-k8s/control-plane/consul" "github.com/hashicorp/consul-k8s/control-plane/helper/test" "github.com/hashicorp/consul/api" @@ -119,8 +120,10 @@ func Test_resourceCache_diff(t *testing.T) { }, }, Hostnames: []string{"hostname.com"}, - Meta: map[string]string{}, - Status: api.ConfigEntryStatus{}, + Meta: map[string]string{ + constants.MetaKeyKubeName: "name", + }, + Status: api.ConfigEntryStatus{}, }, })[api.HTTPRoute], args: args{ @@ -203,8 +206,10 @@ func Test_resourceCache_diff(t *testing.T) { }, }, Hostnames: []string{"hostname.com"}, - Meta: map[string]string{}, - Status: api.ConfigEntryStatus{}, + Meta: map[string]string{ + constants.MetaKeyKubeName: "name", + }, + Status: api.ConfigEntryStatus{}, }, })[api.HTTPRoute], }, @@ -291,8 +296,10 @@ func Test_resourceCache_diff(t *testing.T) { }, }, Hostnames: []string{"hostname.com"}, - Meta: map[string]string{}, - Status: api.ConfigEntryStatus{}, + Meta: map[string]string{ + constants.MetaKeyKubeName: "name", + }, + Status: api.ConfigEntryStatus{}, }, &api.HTTPRouteConfigEntry{ Kind: api.HTTPRoute, @@ -372,8 +379,10 @@ func Test_resourceCache_diff(t *testing.T) { }, }, Hostnames: []string{"hostname.com"}, - Meta: map[string]string{}, - Status: api.ConfigEntryStatus{}, + Meta: map[string]string{ + constants.MetaKeyKubeName: "name", + }, + Status: api.ConfigEntryStatus{}, }, })[api.HTTPRoute], args: args{ @@ -456,8 +465,10 @@ func Test_resourceCache_diff(t *testing.T) { }, }, Hostnames: []string{"hostname.com"}, - Meta: map[string]string{}, - Status: api.ConfigEntryStatus{}, + Meta: map[string]string{ + constants.MetaKeyKubeName: "name", + }, + Status: api.ConfigEntryStatus{}, }, })[api.HTTPRoute], }, @@ -540,8 +551,10 @@ func Test_resourceCache_diff(t *testing.T) { }, }, Hostnames: []string{"hostname.com"}, - Meta: map[string]string{}, - Status: api.ConfigEntryStatus{}, + Meta: map[string]string{ + constants.MetaKeyKubeName: "name", + }, + Status: api.ConfigEntryStatus{}, }, }, }, @@ -626,8 +639,10 @@ func Test_resourceCache_diff(t *testing.T) { }, }, Hostnames: []string{"hostname.com"}, - Meta: map[string]string{}, - Status: api.ConfigEntryStatus{}, + Meta: map[string]string{ + constants.MetaKeyKubeName: "name", + }, + Status: api.ConfigEntryStatus{}, }, })[api.HTTPRoute], args: args{ @@ -710,8 +725,10 @@ func Test_resourceCache_diff(t *testing.T) { }, }, Hostnames: []string{"hostname.com"}, - Meta: map[string]string{}, - Status: api.ConfigEntryStatus{}, + Meta: map[string]string{ + constants.MetaKeyKubeName: "name", + }, + Status: api.ConfigEntryStatus{}, }, &api.HTTPRouteConfigEntry{ Kind: api.HTTPRoute, @@ -791,8 +808,10 @@ func Test_resourceCache_diff(t *testing.T) { }, }, Hostnames: []string{"hostname.com"}, - Meta: map[string]string{}, - Status: api.ConfigEntryStatus{}, + Meta: map[string]string{ + constants.MetaKeyKubeName: "name", + }, + Status: api.ConfigEntryStatus{}, }, })[api.HTTPRoute], }, @@ -875,8 +894,10 @@ func Test_resourceCache_diff(t *testing.T) { }, }, Hostnames: []string{"hostname.com"}, - Meta: map[string]string{}, - Status: api.ConfigEntryStatus{}, + Meta: map[string]string{ + constants.MetaKeyKubeName: "name", + }, + Status: api.ConfigEntryStatus{}, }, }, }, @@ -962,8 +983,10 @@ func Test_resourceCache_diff(t *testing.T) { }, }, Hostnames: []string{"hostname.com"}, - Meta: map[string]string{}, - Status: api.ConfigEntryStatus{}, + Meta: map[string]string{ + constants.MetaKeyKubeName: "name", + }, + Status: api.ConfigEntryStatus{}, }, })[api.HTTPRoute], args: args{ @@ -1047,8 +1070,10 @@ func Test_resourceCache_diff(t *testing.T) { }, }, Hostnames: []string{"hostname.com"}, - Meta: map[string]string{}, - Status: api.ConfigEntryStatus{}, + Meta: map[string]string{ + constants.MetaKeyKubeName: "name", + }, + Status: api.ConfigEntryStatus{}, }, })[api.HTTPRoute], }, @@ -1132,8 +1157,10 @@ func Test_resourceCache_diff(t *testing.T) { }, }, Hostnames: []string{"hostname.com"}, - Meta: map[string]string{}, - Status: api.ConfigEntryStatus{}, + Meta: map[string]string{ + constants.MetaKeyKubeName: "name", + }, + Status: api.ConfigEntryStatus{}, }, }, }, @@ -1378,8 +1405,10 @@ func TestCache_Write(t *testing.T) { }, }, Hostnames: []string{"hostname.com"}, - Meta: map[string]string{}, - Status: api.ConfigEntryStatus{}, + Meta: map[string]string{ + constants.MetaKeyKubeName: "name", + }, + Status: api.ConfigEntryStatus{}, } err = c.Write(context.Background(), entry) @@ -1410,18 +1439,24 @@ func TestCache_Get(t *testing.T) { want: &api.APIGatewayConfigEntry{ Kind: api.APIGateway, Name: "api-gw", - Meta: map[string]string{}, + Meta: map[string]string{ + constants.MetaKeyKubeName: "name", + }, }, cache: loadedReferenceMaps([]api.ConfigEntry{ &api.APIGatewayConfigEntry{ Kind: api.APIGateway, Name: "api-gw", - Meta: map[string]string{}, + Meta: map[string]string{ + constants.MetaKeyKubeName: "name", + }, }, &api.APIGatewayConfigEntry{ Kind: api.APIGateway, Name: "api-gw-2", - Meta: map[string]string{}, + Meta: map[string]string{ + constants.MetaKeyKubeName: "name", + }, }, }), }, @@ -1438,12 +1473,16 @@ func TestCache_Get(t *testing.T) { &api.APIGatewayConfigEntry{ Kind: api.APIGateway, Name: "api-gw", - Meta: map[string]string{}, + Meta: map[string]string{ + constants.MetaKeyKubeName: "name", + }, }, &api.APIGatewayConfigEntry{ Kind: api.APIGateway, Name: "api-gw-2", - Meta: map[string]string{}, + Meta: map[string]string{ + constants.MetaKeyKubeName: "name", + }, }, }), }, @@ -1460,7 +1499,9 @@ func TestCache_Get(t *testing.T) { &api.HTTPRouteConfigEntry{ Kind: api.HTTPRoute, Name: "route", - Meta: map[string]string{}, + Meta: map[string]string{ + constants.MetaKeyKubeName: "name", + }, }, }), }, @@ -1766,7 +1807,8 @@ func setupHTTPRoutes() (*api.HTTPRouteConfigEntry, *api.HTTPRouteConfigEntry) { }, Hostnames: []string{"hostname.com"}, Meta: map[string]string{ - "metaKey": "metaVal", + "metaKey": "metaVal", + constants.MetaKeyKubeName: "name", }, Status: api.ConfigEntryStatus{}, } @@ -1849,7 +1891,8 @@ func setupHTTPRoutes() (*api.HTTPRouteConfigEntry, *api.HTTPRouteConfigEntry) { }, Hostnames: []string{"hostname.com"}, Meta: map[string]string{ - "metakey": "meta val", + "metakey": "meta val", + constants.MetaKeyKubeName: "name", }, } return routeOne, routeTwo @@ -1860,7 +1903,8 @@ func setupGateway() *api.APIGatewayConfigEntry { Kind: api.APIGateway, Name: "api-gw", Meta: map[string]string{ - "metakey": "meta val", + "metakey": "meta val", + constants.MetaKeyKubeName: "name", }, Listeners: []api.APIGatewayListener{ { @@ -1891,7 +1935,8 @@ func setupTCPRoute() *api.TCPRouteConfigEntry { }, }, Meta: map[string]string{ - "metakey": "meta val", + "metakey": "meta val", + constants.MetaKeyKubeName: "name", }, Status: api.ConfigEntryStatus{}, } @@ -1904,7 +1949,8 @@ func setupInlineCertificate() *api.InlineCertificateConfigEntry { Certificate: "cert", PrivateKey: "super secret", Meta: map[string]string{ - "metaKey": "meta val", + "metaKey": "meta val", + constants.MetaKeyKubeName: "name", }, } } diff --git a/control-plane/api-gateway/common/helm_config.go b/control-plane/api-gateway/common/helm_config.go index 2a6cc8211b..f0d4dc7988 100644 --- a/control-plane/api-gateway/common/helm_config.go +++ b/control-plane/api-gateway/common/helm_config.go @@ -3,7 +3,12 @@ package common -import "time" +import ( + "strings" + "time" +) + +const componentAuthMethod = "k8s-component-auth-method" // HelmConfig is the configuration of gateways that comes in from the user's Helm values. type HelmConfig struct { @@ -33,3 +38,20 @@ type ConsulConfig struct { HTTPPort int APITimeout time.Duration } + +func (h HelmConfig) Normalize() HelmConfig { + if h.AuthMethod != "" { + // strip off any DC naming off the back in case we're + // in a secondary DC, in which case our auth method is + // going to be a globally scoped auth method, and we want + // to target the locally scoped one, which is the auth + // method without the DC-specific suffix. + tokens := strings.Split(h.AuthMethod, componentAuthMethod) + if len(tokens) != 2 { + // skip the normalization if we can't do it. + return h + } + h.AuthMethod = tokens[0] + componentAuthMethod + } + return h +} diff --git a/control-plane/api-gateway/common/translation.go b/control-plane/api-gateway/common/translation.go index 5e577470d6..8644b64716 100644 --- a/control-plane/api-gateway/common/translation.go +++ b/control-plane/api-gateway/common/translation.go @@ -23,6 +23,7 @@ type ResourceTranslator struct { EnableK8sMirroring bool MirroringPrefix string ConsulPartition string + Datacenter string } func (t ResourceTranslator) NonNormalizedConfigEntryReference(kind string, id types.NamespacedName) api.ResourceReference { @@ -65,10 +66,10 @@ func (t ResourceTranslator) ToAPIGateway(gateway gwv1beta1.Gateway, resources *R Name: gateway.Name, Namespace: namespace, Partition: t.ConsulPartition, - Meta: map[string]string{ + Meta: t.addDatacenterToMeta(map[string]string{ constants.MetaKeyKubeNS: gateway.Namespace, constants.MetaKeyKubeName: gateway.Name, - }, + }), Listeners: listeners, } } @@ -128,10 +129,10 @@ func (t ResourceTranslator) ToHTTPRoute(route gwv1beta1.HTTPRoute, resources *Re Name: route.Name, Namespace: namespace, Partition: t.ConsulPartition, - Meta: map[string]string{ + Meta: t.addDatacenterToMeta(map[string]string{ constants.MetaKeyKubeNS: route.Namespace, constants.MetaKeyKubeName: route.Name, - }, + }), Hostnames: hostnames, Rules: rules, } @@ -292,10 +293,10 @@ func (t ResourceTranslator) ToTCPRoute(route gwv1alpha2.TCPRoute, resources *Res Name: route.Name, Namespace: namespace, Partition: t.ConsulPartition, - Meta: map[string]string{ + Meta: t.addDatacenterToMeta(map[string]string{ constants.MetaKeyKubeNS: route.Namespace, constants.MetaKeyKubeName: route.Name, - }, + }), Services: services, } } @@ -346,10 +347,10 @@ func (t ResourceTranslator) ToInlineCertificate(secret corev1.Secret) (*api.Inli Partition: t.ConsulPartition, Certificate: strings.TrimSpace(certificate), PrivateKey: strings.TrimSpace(privateKey), - Meta: map[string]string{ + Meta: t.addDatacenterToMeta(map[string]string{ constants.MetaKeyKubeNS: secret.Namespace, constants.MetaKeyKubeName: secret.Name, - }, + }), }, nil } @@ -361,3 +362,11 @@ func EntryToNamespacedName(entry api.ConfigEntry) types.NamespacedName { Name: meta[constants.MetaKeyKubeName], } } + +func (t ResourceTranslator) addDatacenterToMeta(meta map[string]string) map[string]string { + if t.Datacenter == "" { + return meta + } + meta[constants.MetaKeyDatacenter] = t.Datacenter + return meta +} diff --git a/control-plane/api-gateway/controllers/gateway_controller.go b/control-plane/api-gateway/controllers/gateway_controller.go index ec8c2e9af0..664bb98d3c 100644 --- a/control-plane/api-gateway/controllers/gateway_controller.go +++ b/control-plane/api-gateway/controllers/gateway_controller.go @@ -46,6 +46,7 @@ type GatewayControllerConfig struct { NamespacesEnabled bool CrossNamespaceACLPolicy string Partition string + Datacenter string AllowK8sNamespacesSet mapset.Set DenyK8sNamespacesSet mapset.Set } @@ -317,6 +318,7 @@ func SetupGatewayControllerWithManager(ctx context.Context, mgr ctrl.Manager, co ConsulClientConfig: config.ConsulClientConfig, ConsulServerConnMgr: config.ConsulServerConnMgr, NamespacesEnabled: config.NamespacesEnabled, + Datacenter: config.Datacenter, CrossNamespaceACLPolicy: config.CrossNamespaceACLPolicy, Logger: mgr.GetLogger(), } @@ -332,13 +334,14 @@ func SetupGatewayControllerWithManager(ctx context.Context, mgr ctrl.Manager, co r := &GatewayController{ Client: mgr.GetClient(), Log: mgr.GetLogger(), - HelmConfig: config.HelmConfig, + HelmConfig: config.HelmConfig.Normalize(), Translator: common.ResourceTranslator{ EnableConsulNamespaces: config.HelmConfig.EnableNamespaces, ConsulDestNamespace: config.HelmConfig.ConsulDestinationNamespace, EnableK8sMirroring: config.HelmConfig.EnableNamespaceMirroring, MirroringPrefix: config.HelmConfig.NamespaceMirroringPrefix, ConsulPartition: config.HelmConfig.ConsulPartition, + Datacenter: config.Datacenter, }, denyK8sNamespacesSet: config.DenyK8sNamespacesSet, allowK8sNamespacesSet: config.AllowK8sNamespacesSet, diff --git a/control-plane/api/v1alpha1/serviceresolver_types.go b/control-plane/api/v1alpha1/serviceresolver_types.go index 75aa44f6b9..d00821275d 100644 --- a/control-plane/api/v1alpha1/serviceresolver_types.go +++ b/control-plane/api/v1alpha1/serviceresolver_types.go @@ -425,7 +425,7 @@ func (in *ServiceResolverRedirect) validate(path *field.Path, consulMeta common. "service resolver redirect cannot be empty")) } - if consulMeta.Partition != "default" && in.Datacenter != "" { + if consulMeta.Partition != "default" && consulMeta.Partition != "" && in.Datacenter != "" { errs = append(errs, field.Invalid(path.Child("datacenter"), in.Datacenter, "cross-datacenter redirect is only supported in the default partition")) } diff --git a/control-plane/connect-inject/constants/constants.go b/control-plane/connect-inject/constants/constants.go index 8987a9f5e8..0a341cd577 100644 --- a/control-plane/connect-inject/constants/constants.go +++ b/control-plane/connect-inject/constants/constants.go @@ -19,6 +19,9 @@ const ( // MetaKeyKubeName is the meta key name for Kubernetes object name used for a Consul object. MetaKeyKubeName = "k8s-name" + // MetaKeyDatacenter is the datacenter that this object was registered from. + MetaKeyDatacenter = "datacenter" + // MetaKeyKubeServiceName is the meta key name for Kubernetes service name used for the Consul services. MetaKeyKubeServiceName = "k8s-service-name" diff --git a/control-plane/subcommand/inject-connect/command.go b/control-plane/subcommand/inject-connect/command.go index a4fcf7c99d..6914894096 100644 --- a/control-plane/subcommand/inject-connect/command.go +++ b/control-plane/subcommand/inject-connect/command.go @@ -515,7 +515,9 @@ func (c *Command) Run(args []string) int { NamespacesEnabled: c.flagEnableNamespaces, CrossNamespaceACLPolicy: c.flagCrossNamespaceACLPolicy, Partition: c.consul.Partition, + Datacenter: c.consul.Datacenter, }) + if err != nil { setupLog.Error(err, "unable to create controller", "controller", "Gateway") return 1 From da147c1d7503e307ee4b93e0139a27a80a073cfc Mon Sep 17 00:00:00 2001 From: Andrew Stucki Date: Fri, 9 Jun 2023 10:16:55 -0400 Subject: [PATCH 220/340] [API Gateway] fix dangling service registrations (#2321) * Fix when gateways are deleted before we get services populated into cache * a bit of cleanup --- control-plane/api-gateway/cache/gateway.go | 18 +++++++ .../controllers/gateway_controller.go | 50 +++++++++++++++---- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/control-plane/api-gateway/cache/gateway.go b/control-plane/api-gateway/cache/gateway.go index 846131d11e..d8dd37ffac 100644 --- a/control-plane/api-gateway/cache/gateway.go +++ b/control-plane/api-gateway/cache/gateway.go @@ -52,6 +52,24 @@ func (r *GatewayCache) ServicesFor(ref api.ResourceReference) []api.CatalogServi return r.data[common.NormalizeMeta(ref)] } +func (r *GatewayCache) FetchServicesFor(ctx context.Context, ref api.ResourceReference) ([]api.CatalogService, error) { + client, err := consul.NewClientFromConnMgr(r.config, r.serverMgr) + if err != nil { + return nil, err + } + + opts := &api.QueryOptions{} + if ref.Namespace != "" { + opts.Namespace = ref.Namespace + } + + services, _, err := client.Catalog().Service(ref.Name, "", opts.WithContext(ctx)) + if err != nil { + return nil, err + } + return common.DerefAll(services), nil +} + func (r *GatewayCache) EnsureSubscribed(ref api.ResourceReference, resource types.NamespacedName) { r.mutex.Lock() defer r.mutex.Unlock() diff --git a/control-plane/api-gateway/controllers/gateway_controller.go b/control-plane/api-gateway/controllers/gateway_controller.go index 664bb98d3c..f83466d67a 100644 --- a/control-plane/api-gateway/controllers/gateway_controller.go +++ b/control-plane/api-gateway/controllers/gateway_controller.go @@ -209,6 +209,12 @@ func (r *GatewayController) Reconcile(ctx context.Context, req ctrl.Request) (ct return ctrl.Result{}, err } r.gatewayCache.RemoveSubscription(nonNormalizedConsulKey) + // make sure we have deregister all services even if they haven't + // hit cache yet + if err := r.deregisterAllServices(ctx, consulKey); err != nil { + log.Error(err, "error deregistering services") + return ctrl.Result{}, err + } } for _, deletion := range updates.Consul.Deletions { @@ -235,19 +241,24 @@ func (r *GatewayController) Reconcile(ctx context.Context, req ctrl.Request) (ct } } - for _, registration := range updates.Consul.Registrations { - log.Info("registering service in Consul", "service", registration.Service.Service, "id", registration.Service.ID) - if err := r.cache.Register(ctx, registration); err != nil { - log.Error(err, "error registering service") - return ctrl.Result{}, err + if updates.UpsertGatewayDeployment { + // We only do some registration/deregistraion if we still have a valid gateway + // otherwise, we've already deregistered everything related to the gateway, so + // no need to do any of the following. + for _, registration := range updates.Consul.Registrations { + log.Info("registering service in Consul", "service", registration.Service.Service, "id", registration.Service.ID) + if err := r.cache.Register(ctx, registration); err != nil { + log.Error(err, "error registering service") + return ctrl.Result{}, err + } } - } - for _, deregistration := range updates.Consul.Deregistrations { - log.Info("deregistering service in Consul", "id", deregistration.ServiceID) - if err := r.cache.Deregister(ctx, deregistration); err != nil { - log.Error(err, "error deregistering service") - return ctrl.Result{}, err + for _, deregistration := range updates.Consul.Deregistrations { + log.Info("deregistering service in Consul", "id", deregistration.ServiceID) + if err := r.cache.Deregister(ctx, deregistration); err != nil { + log.Error(err, "error deregistering service") + return ctrl.Result{}, err + } } } @@ -270,6 +281,23 @@ func (r *GatewayController) Reconcile(ctx context.Context, req ctrl.Request) (ct return ctrl.Result{}, nil } +func (r *GatewayController) deregisterAllServices(ctx context.Context, consulKey api.ResourceReference) error { + services, err := r.gatewayCache.FetchServicesFor(ctx, consulKey) + if err != nil { + return err + } + for _, service := range services { + if err := r.cache.Deregister(ctx, api.CatalogDeregistration{ + Node: service.Node, + ServiceID: service.ServiceID, + Namespace: service.Namespace, + }); err != nil { + return err + } + } + return nil +} + func (r *GatewayController) updateAndResetStatus(ctx context.Context, o client.Object) error { // we create a copy so that we can re-update its status if need be status := reflect.ValueOf(o.DeepCopyObject()).Elem().FieldByName("Status") From 198c4433d8927e2ec9c77b4a75ef80aa02f653ff Mon Sep 17 00:00:00 2001 From: Nathan Coleman Date: Fri, 9 Jun 2023 10:36:03 -0400 Subject: [PATCH 221/340] api-gateway: add unit tests verifying scaling parameters on GatewayClassConfig are obeyed (#2272) * Add unit tests verifying that scaling parameters on GatewayClassConfig are obeyed * Add test case for scaling w/ no min or max configured --- .../api-gateway/gatekeeper/gatekeeper_test.go | 187 ++++++++++++++++++ 1 file changed, 187 insertions(+) diff --git a/control-plane/api-gateway/gatekeeper/gatekeeper_test.go b/control-plane/api-gateway/gatekeeper/gatekeeper_test.go index e2da61177f..ba58cb441f 100644 --- a/control-plane/api-gateway/gatekeeper/gatekeeper_test.go +++ b/control-plane/api-gateway/gatekeeper/gatekeeper_test.go @@ -210,6 +210,76 @@ func TestUpsert(t *testing.T) { }, }, }, + "create a new gateway where the GatewayClassConfig has a default number of instances greater than the max on the GatewayClassConfig": { + gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: gwv1beta1.GatewaySpec{ + Listeners: listeners, + }, + }, + gatewayClassConfig: v1alpha1.GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-gatewayclassconfig", + }, + Spec: v1alpha1.GatewayClassConfigSpec{ + DeploymentSpec: v1alpha1.DeploymentSpec{ + DefaultInstances: common.PointerTo(int32(8)), + MaxInstances: common.PointerTo(int32(5)), + MinInstances: common.PointerTo(int32(2)), + }, + CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, + ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), + }, + }, + helmConfig: common.HelmConfig{}, + initialResources: resources{}, + finalResources: resources{ + deployments: []*appsv1.Deployment{ + configureDeployment(name, namespace, labels, 5, nil, nil, "", "1"), + }, + roles: []*rbac.Role{}, + services: []*corev1.Service{}, + serviceAccounts: []*corev1.ServiceAccount{}, + }, + }, + "create a new gateway where the GatewayClassConfig has a default number of instances lesser than the min on the GatewayClassConfig": { + gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: gwv1beta1.GatewaySpec{ + Listeners: listeners, + }, + }, + gatewayClassConfig: v1alpha1.GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-gatewayclassconfig", + }, + Spec: v1alpha1.GatewayClassConfigSpec{ + DeploymentSpec: v1alpha1.DeploymentSpec{ + DefaultInstances: common.PointerTo(int32(1)), + MaxInstances: common.PointerTo(int32(5)), + MinInstances: common.PointerTo(int32(2)), + }, + CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, + ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), + }, + }, + helmConfig: common.HelmConfig{}, + initialResources: resources{}, + finalResources: resources{ + deployments: []*appsv1.Deployment{ + configureDeployment(name, namespace, labels, 2, nil, nil, "", "1"), + }, + roles: []*rbac.Role{}, + services: []*corev1.Service{}, + serviceAccounts: []*corev1.ServiceAccount{}, + }, + }, "update a gateway, adding a listener to a service": { gateway: gwv1beta1.Gateway{ ObjectMeta: metav1.ObjectMeta{ @@ -409,6 +479,123 @@ func TestUpsert(t *testing.T) { serviceAccounts: []*corev1.ServiceAccount{}, }, }, + "update a gateway deployment by scaling it when no min or max number of instances is defined on the GatewayClassConfig": { + gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: gwv1beta1.GatewaySpec{ + Listeners: listeners, + }, + }, + gatewayClassConfig: v1alpha1.GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-gatewayclassconfig", + }, + Spec: v1alpha1.GatewayClassConfigSpec{ + DeploymentSpec: v1alpha1.DeploymentSpec{ + DefaultInstances: common.PointerTo(int32(3)), + MaxInstances: nil, + MinInstances: nil, + }, + CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, + ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), + }, + }, + helmConfig: common.HelmConfig{}, + initialResources: resources{ + deployments: []*appsv1.Deployment{ + configureDeployment(name, namespace, labels, 8, nil, nil, "", "1"), + }, + }, + finalResources: resources{ + deployments: []*appsv1.Deployment{ + configureDeployment(name, namespace, labels, 8, nil, nil, "", "1"), + }, + roles: []*rbac.Role{}, + services: []*corev1.Service{}, + serviceAccounts: []*corev1.ServiceAccount{}, + }, + }, + "update a gateway deployment by scaling it lower than the min number of instances on the GatewayClassConfig": { + gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: gwv1beta1.GatewaySpec{ + Listeners: listeners, + }, + }, + gatewayClassConfig: v1alpha1.GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-gatewayclassconfig", + }, + Spec: v1alpha1.GatewayClassConfigSpec{ + DeploymentSpec: v1alpha1.DeploymentSpec{ + DefaultInstances: common.PointerTo(int32(3)), + MaxInstances: common.PointerTo(int32(5)), + MinInstances: common.PointerTo(int32(2)), + }, + CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, + ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), + }, + }, + helmConfig: common.HelmConfig{}, + initialResources: resources{ + deployments: []*appsv1.Deployment{ + configureDeployment(name, namespace, labels, 1, nil, nil, "", "1"), + }, + }, + finalResources: resources{ + deployments: []*appsv1.Deployment{ + configureDeployment(name, namespace, labels, 2, nil, nil, "", "1"), + }, + roles: []*rbac.Role{}, + services: []*corev1.Service{}, + serviceAccounts: []*corev1.ServiceAccount{}, + }, + }, + "update a gateway deployment by scaling it higher than the max number of instances on the GatewayClassConfig": { + gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: gwv1beta1.GatewaySpec{ + Listeners: listeners, + }, + }, + gatewayClassConfig: v1alpha1.GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-gatewayclassconfig", + }, + Spec: v1alpha1.GatewayClassConfigSpec{ + DeploymentSpec: v1alpha1.DeploymentSpec{ + DefaultInstances: common.PointerTo(int32(3)), + MaxInstances: common.PointerTo(int32(5)), + MinInstances: common.PointerTo(int32(2)), + }, + CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, + ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), + }, + }, + helmConfig: common.HelmConfig{}, + initialResources: resources{ + deployments: []*appsv1.Deployment{ + configureDeployment(name, namespace, labels, 10, nil, nil, "", "1"), + }, + }, + finalResources: resources{ + deployments: []*appsv1.Deployment{ + configureDeployment(name, namespace, labels, 5, nil, nil, "", "1"), + }, + roles: []*rbac.Role{}, + services: []*corev1.Service{}, + serviceAccounts: []*corev1.ServiceAccount{}, + }, + }, } for name, tc := range cases { From 8245efc1f3ccb26dd051126a876b293cb5c2e28f Mon Sep 17 00:00:00 2001 From: Thomas Eckert Date: Fri, 9 Jun 2023 16:34:49 -0400 Subject: [PATCH 222/340] Rename GatewayClassController to prevent name collision (#2317) * Rename GatewayClassController to prevent name collision * Use gateway instead of gatewayclass in name * Use the constant in ownership checks * Change GatewayClass name to "consul" * Change GatewayClass name in cases * Change ApiGatewayClass back --- acceptance/tests/api-gateway/api_gateway_test.go | 2 +- .../fixtures/bases/api-gateway/gatewayclass.yaml | 2 +- .../cases/api-gateways/gateway/gateway.yaml | 4 ++-- charts/consul/templates/gateway-cleanup-job.yaml | 2 +- .../consul/templates/gateway-resources-job.yaml | 4 ++-- control-plane/api-gateway/common/constants.go | 2 ++ .../api-gateway/controllers/gateway_controller.go | 13 +++++++------ .../controllers/gatewayclass_controller.go | 2 -- .../controllers/gatewayclass_controller_test.go | 15 ++++++++------- ...roller.go => gatewayclassconfig_controller.go} | 0 ...t.go => gatewayclassconfig_controller_test.go} | 0 .../subcommand/inject-connect/command.go | 2 +- 12 files changed, 25 insertions(+), 23 deletions(-) rename control-plane/api-gateway/controllers/{gateway_class_config_controller.go => gatewayclassconfig_controller.go} (100%) rename control-plane/api-gateway/controllers/{gateway_class_config_controller_test.go => gatewayclassconfig_controller_test.go} (100%) diff --git a/acceptance/tests/api-gateway/api_gateway_test.go b/acceptance/tests/api-gateway/api_gateway_test.go index 17234cadf1..143b793bf8 100644 --- a/acceptance/tests/api-gateway/api_gateway_test.go +++ b/acceptance/tests/api-gateway/api_gateway_test.go @@ -27,7 +27,7 @@ import ( const ( StaticClientName = "static-client" - gatewayClassControllerName = "hashicorp.com/consul-api-gateway-controller" + gatewayClassControllerName = "consul.hashicorp.com/gateway-controller" gatewayClassFinalizer = "gateway-exists-finalizer.consul.hashicorp.com" gatewayFinalizer = "gateway-finalizer.consul.hashicorp.com" ) diff --git a/acceptance/tests/fixtures/bases/api-gateway/gatewayclass.yaml b/acceptance/tests/fixtures/bases/api-gateway/gatewayclass.yaml index 872faeb78c..9ff985fd49 100644 --- a/acceptance/tests/fixtures/bases/api-gateway/gatewayclass.yaml +++ b/acceptance/tests/fixtures/bases/api-gateway/gatewayclass.yaml @@ -6,7 +6,7 @@ kind: GatewayClass metadata: name: gateway-class spec: - controllerName: "hashicorp.com/consul-api-gateway-controller" + controllerName: "consul.hashicorp.com/gateway-controller" parametersRef: group: consul.hashicorp.com kind: GatewayClassConfig diff --git a/acceptance/tests/fixtures/cases/api-gateways/gateway/gateway.yaml b/acceptance/tests/fixtures/cases/api-gateways/gateway/gateway.yaml index 14c39978b7..7f0428b039 100644 --- a/acceptance/tests/fixtures/cases/api-gateways/gateway/gateway.yaml +++ b/acceptance/tests/fixtures/cases/api-gateways/gateway/gateway.yaml @@ -6,7 +6,7 @@ kind: Gateway metadata: name: gateway spec: - gatewayClassName: consul-api-gateway + gatewayClassName: consul listeners: - protocol: HTTPS port: 8080 @@ -17,4 +17,4 @@ spec: namespace: "default" allowedRoutes: namespaces: - from: "All" \ No newline at end of file + from: "All" diff --git a/charts/consul/templates/gateway-cleanup-job.yaml b/charts/consul/templates/gateway-cleanup-job.yaml index ff6f295357..44f032b5fd 100644 --- a/charts/consul/templates/gateway-cleanup-job.yaml +++ b/charts/consul/templates/gateway-cleanup-job.yaml @@ -41,7 +41,7 @@ spec: - consul-k8s-control-plane args: - gateway-cleanup - - -gateway-class-name=consul-api-gateway + - -gateway-class-name=consul - -gateway-class-config-name=consul-api-gateway resources: requests: diff --git a/charts/consul/templates/gateway-resources-job.yaml b/charts/consul/templates/gateway-resources-job.yaml index f8f92f799d..441e64eb14 100644 --- a/charts/consul/templates/gateway-resources-job.yaml +++ b/charts/consul/templates/gateway-resources-job.yaml @@ -41,9 +41,9 @@ spec: - consul-k8s-control-plane args: - gateway-resources - - -gateway-class-name=consul-api-gateway + - -gateway-class-name=consul - -gateway-class-config-name=consul-api-gateway - - -controller-name=hashicorp.com/consul-api-gateway-controller + - -controller-name=consul.hashicorp.com/gateway-controller - -app={{template "consul.name" .}} - -chart={{template "consul.chart" .}} - -heritage={{ .Release.Service }} diff --git a/control-plane/api-gateway/common/constants.go b/control-plane/api-gateway/common/constants.go index 68abfc96b1..c1ec0685a4 100644 --- a/control-plane/api-gateway/common/constants.go +++ b/control-plane/api-gateway/common/constants.go @@ -4,5 +4,7 @@ package common const ( + GatewayClassControllerName = "consul.hashicorp.com/gateway-controller" + AnnotationGatewayClassConfig = "consul.hashicorp.com/gateway-class-config" ) diff --git a/control-plane/api-gateway/controllers/gateway_controller.go b/control-plane/api-gateway/controllers/gateway_controller.go index f83466d67a..97805ccdfb 100644 --- a/control-plane/api-gateway/controllers/gateway_controller.go +++ b/control-plane/api-gateway/controllers/gateway_controller.go @@ -54,9 +54,10 @@ type GatewayControllerConfig struct { // GatewayController reconciles a Gateway object. // The Gateway is responsible for defining the behavior of API gateways. type GatewayController struct { - HelmConfig common.HelmConfig - Log logr.Logger - Translator common.ResourceTranslator + HelmConfig common.HelmConfig + Log logr.Logger + Translator common.ResourceTranslator + cache *cache.Cache gatewayCache *cache.GatewayCache allowK8sNamespacesSet mapset.Set @@ -174,7 +175,7 @@ func (r *GatewayController) Reconcile(ctx context.Context, req ctrl.Request) (ct binder := binding.NewBinder(binding.BinderConfig{ Logger: log, Translator: r.Translator, - ControllerName: GatewayClassControllerName, + ControllerName: common.GatewayClassControllerName, Namespaces: namespaces, GatewayClassConfig: gatewayClassConfig, GatewayClass: gatewayClass, @@ -786,7 +787,7 @@ func (c *GatewayController) getConfigForGatewayClass(ctx context.Context, gatewa if ref := gatewayClassConfig.Spec.ParametersRef; ref != nil { if string(ref.Group) != v1alpha1.GroupVersion.Group || ref.Kind != v1alpha1.GatewayClassConfigKind || - gatewayClassConfig.Spec.ControllerName != GatewayClassControllerName { + gatewayClassConfig.Spec.ControllerName != common.GatewayClassControllerName { // we don't have supported params, so return nil return nil, nil } @@ -813,7 +814,7 @@ func (c *GatewayController) fetchControlledGateways(ctx context.Context, resourc list := gwv1beta1.GatewayClassList{} if err := c.Client.List(ctx, &list, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(GatewayClass_ControllerNameIndex, GatewayClassControllerName), + FieldSelector: fields.OneTermEqualSelector(GatewayClass_ControllerNameIndex, common.GatewayClassControllerName), }); err != nil { return err } diff --git a/control-plane/api-gateway/controllers/gatewayclass_controller.go b/control-plane/api-gateway/controllers/gatewayclass_controller.go index 4180157616..e37d1b3bcd 100644 --- a/control-plane/api-gateway/controllers/gatewayclass_controller.go +++ b/control-plane/api-gateway/controllers/gatewayclass_controller.go @@ -22,8 +22,6 @@ import ( ) const ( - GatewayClassControllerName = "hashicorp.com/consul-api-gateway-controller" - gatewayClassFinalizer = "gateway-exists-finalizer.consul.hashicorp.com" // GatewayClass status fields. diff --git a/control-plane/api-gateway/controllers/gatewayclass_controller_test.go b/control-plane/api-gateway/controllers/gatewayclass_controller_test.go index ac5be25205..0eeaf4c1de 100644 --- a/control-plane/api-gateway/controllers/gatewayclass_controller_test.go +++ b/control-plane/api-gateway/controllers/gatewayclass_controller_test.go @@ -22,6 +22,7 @@ import ( "sigs.k8s.io/gateway-api/apis/v1beta1" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" ) @@ -57,7 +58,7 @@ func TestGatewayClassReconciler(t *testing.T) { Finalizers: []string{gatewayClassFinalizer}, }, Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: GatewayClassControllerName, + ControllerName: common.GatewayClassControllerName, }, }, expectedResult: ctrl.Result{}, @@ -81,7 +82,7 @@ func TestGatewayClassReconciler(t *testing.T) { Finalizers: []string{}, }, Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: GatewayClassControllerName, + ControllerName: common.GatewayClassControllerName, }, }, expectedResult: ctrl.Result{}, @@ -127,7 +128,7 @@ func TestGatewayClassReconciler(t *testing.T) { Finalizers: []string{gatewayClassFinalizer}, }, Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: GatewayClassControllerName, + ControllerName: common.GatewayClassControllerName, ParametersRef: &gwv1beta1.ParametersReference{ Kind: "some-nonsense", }, @@ -153,7 +154,7 @@ func TestGatewayClassReconciler(t *testing.T) { Finalizers: []string{gatewayClassFinalizer}, }, Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: GatewayClassControllerName, + ControllerName: common.GatewayClassControllerName, ParametersRef: &gwv1beta1.ParametersReference{ Kind: v1alpha1.GatewayClassConfigKind, Name: "does-not-exist", @@ -189,7 +190,7 @@ func TestGatewayClassReconciler(t *testing.T) { DeletionTimestamp: &deletionTimestamp, }, Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: GatewayClassControllerName, + ControllerName: common.GatewayClassControllerName, }, }, expectedResult: ctrl.Result{}, @@ -208,7 +209,7 @@ func TestGatewayClassReconciler(t *testing.T) { DeletionTimestamp: &deletionTimestamp, }, Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: GatewayClassControllerName, + ControllerName: common.GatewayClassControllerName, }, }, k8sObjects: []runtime.Object{ @@ -246,7 +247,7 @@ func TestGatewayClassReconciler(t *testing.T) { r := &GatewayClassController{ Client: fakeClient, - ControllerName: GatewayClassControllerName, + ControllerName: common.GatewayClassControllerName, Log: logrtest.New(t), } result, err := r.Reconcile(context.Background(), req) diff --git a/control-plane/api-gateway/controllers/gateway_class_config_controller.go b/control-plane/api-gateway/controllers/gatewayclassconfig_controller.go similarity index 100% rename from control-plane/api-gateway/controllers/gateway_class_config_controller.go rename to control-plane/api-gateway/controllers/gatewayclassconfig_controller.go diff --git a/control-plane/api-gateway/controllers/gateway_class_config_controller_test.go b/control-plane/api-gateway/controllers/gatewayclassconfig_controller_test.go similarity index 100% rename from control-plane/api-gateway/controllers/gateway_class_config_controller_test.go rename to control-plane/api-gateway/controllers/gatewayclassconfig_controller_test.go diff --git a/control-plane/subcommand/inject-connect/command.go b/control-plane/subcommand/inject-connect/command.go index 6914894096..8bada415db 100644 --- a/control-plane/subcommand/inject-connect/command.go +++ b/control-plane/subcommand/inject-connect/command.go @@ -476,7 +476,7 @@ func (c *Command) Run(args []string) int { } if err := (&gatewaycontrollers.GatewayClassController{ - ControllerName: gatewaycontrollers.GatewayClassControllerName, + ControllerName: gatewaycommon.GatewayClassControllerName, Client: mgr.GetClient(), Log: ctrl.Log.WithName("controllers").WithName("GatewayClass"), }).SetupWithManager(ctx, mgr); err != nil { From f07736b53f431f4c7956f5c69ac5b3de0fd4a481 Mon Sep 17 00:00:00 2001 From: Andrew Stucki Date: Fri, 9 Jun 2023 16:51:39 -0400 Subject: [PATCH 223/340] [API Gateway] Conformance Test Fixes (#2326) * Fix SupportedKinds array to be what Conformance test expects * Fix cert validation status condition for listeners * Add programmed condition for listeners * Fix unit test --------- Co-authored-by: Nathan Coleman --- control-plane/api-gateway/binding/binder.go | 14 +++++- .../api-gateway/binding/binder_test.go | 5 ++ control-plane/api-gateway/binding/result.go | 49 +++++++++++++++++-- .../api-gateway/binding/validation.go | 26 +++++++++- 4 files changed, 89 insertions(+), 5 deletions(-) diff --git a/control-plane/api-gateway/binding/binder.go b/control-plane/api-gateway/binding/binder.go index b677d69253..28a26985a8 100644 --- a/control-plane/api-gateway/binding/binder.go +++ b/control-plane/api-gateway/binding/binder.go @@ -214,7 +214,7 @@ func (b *Binder) Snapshot() *Snapshot { for i, listener := range b.config.Gateway.Spec.Listeners { status.Listeners = append(status.Listeners, gwv1beta1.ListenerStatus{ Name: listener.Name, - SupportedKinds: supportedKindsForProtocol[listener.Protocol], + SupportedKinds: supportedKinds(listener), AttachedRoutes: int32(boundCounts[listener.Name]), Conditions: listenerValidation.Conditions(b.config.Gateway.Generation, i), }) @@ -374,3 +374,15 @@ func addressesFromPodHosts(pods []corev1.Pod) []gwv1beta1.GatewayAddress { func isDeleted(object client.Object) bool { return !object.GetDeletionTimestamp().IsZero() } + +func supportedKinds(listener gwv1beta1.Listener) []gwv1beta1.RouteGroupKind { + if listener.AllowedRoutes != nil && listener.AllowedRoutes.Kinds != nil { + return common.Filter(listener.AllowedRoutes.Kinds, func(kind gwv1beta1.RouteGroupKind) bool { + if _, ok := allSupportedRouteKinds[kind.Kind]; !ok { + return true + } + return !common.NilOrEqual(kind.Group, gwv1beta1.GroupVersion.Group) + }) + } + return supportedKindsForProtocol[listener.Protocol] +} diff --git a/control-plane/api-gateway/binding/binder_test.go b/control-plane/api-gateway/binding/binder_test.go index 65cca94419..a3af702dab 100644 --- a/control-plane/api-gateway/binding/binder_test.go +++ b/control-plane/api-gateway/binding/binder_test.go @@ -233,6 +233,11 @@ func TestBinder_Lifecycle(t *testing.T) { Status: metav1.ConditionTrue, Reason: "Accepted", Message: "listener accepted", + }, { + Type: "Programmed", + Status: metav1.ConditionTrue, + Reason: "Programmed", + Message: "listener programmed", }, { Type: "Conflicted", Status: metav1.ConditionFalse, diff --git a/control-plane/api-gateway/binding/result.go b/control-plane/api-gateway/binding/result.go index dd82cd55b5..3ccb645c32 100644 --- a/control-plane/api-gateway/binding/result.go +++ b/control-plane/api-gateway/binding/result.go @@ -213,8 +213,8 @@ func (p parentBindResults) boundSections() mapset.Set { } var ( - // Each of the below are specified in the Gateway spec under ListenerConditionReason - // the general usage is that each error is specified as errListener* where * corresponds + // Each of the below are specified in the Gateway spec under ListenerConditionReason. + // The general usage is that each error is specified as errListener* where * corresponds // to the ListenerConditionReason given in the spec. If a reason is overloaded and can // be used with two different types of things (i.e. something is not found or it's not supported) // then we distinguish those two usages with errListener*_Usage. @@ -225,6 +225,8 @@ var ( errListenerInvalidCertificateRef_NotFound = errors.New("certificate not found") errListenerInvalidCertificateRef_NotSupported = errors.New("certificate type is not supported") errListenerInvalidCertificateRef_InvalidData = errors.New("certificate is invalid or does not contain a supported server name") + errListenerInvalidRouteKinds = errors.New("allowed route kind is invalid") + errListenerProgrammed_Invalid = errors.New("listener cannot be programmed because it is invalid") // Below is where any custom generic listener validation errors should go. // We map anything under here to a custom ListenerConditionReason of Invalid on @@ -243,7 +245,36 @@ type listenerValidationResult struct { conflictedErr error // status type: ResolvedRefs refErr error - // TODO: programmed + // status type: ResolvedRefs (but with internal validation) + routeKindErr error +} + +// programmedCondition constructs the condition for the Programmed status type. +// If there are no validation errors for the listener, we mark it as programmed. +// If there are validation errors for the listener, we mark it as invalid. +func (l listenerValidationResult) programmedCondition(generation int64) metav1.Condition { + now := timeFunc() + + switch { + case l.acceptedErr != nil, l.conflictedErr != nil, l.refErr != nil, l.routeKindErr != nil: + return metav1.Condition{ + Type: "Programmed", + Status: metav1.ConditionFalse, + Reason: "Invalid", + ObservedGeneration: generation, + Message: errListenerProgrammed_Invalid.Error(), + LastTransitionTime: now, + } + default: + return metav1.Condition{ + Type: "Programmed", + Status: metav1.ConditionTrue, + Reason: "Programmed", + ObservedGeneration: generation, + Message: "listener programmed", + LastTransitionTime: now, + } + } } // acceptedCondition constructs the condition for the Accepted status type. @@ -329,6 +360,17 @@ func (l listenerValidationResult) conflictedCondition(generation int64) metav1.C func (l listenerValidationResult) resolvedRefsCondition(generation int64) metav1.Condition { now := timeFunc() + if l.routeKindErr != nil { + return metav1.Condition{ + Type: "ResolvedRefs", + Status: metav1.ConditionFalse, + Reason: "InvalidRouteKinds", + ObservedGeneration: generation, + Message: l.routeKindErr.Error(), + LastTransitionTime: now, + } + } + switch l.refErr { case errListenerInvalidCertificateRef_NotFound, errListenerInvalidCertificateRef_NotSupported, errListenerInvalidCertificateRef_InvalidData: return metav1.Condition{ @@ -364,6 +406,7 @@ func (l listenerValidationResult) resolvedRefsCondition(generation int64) metav1 func (l listenerValidationResult) Conditions(generation int64) []metav1.Condition { return []metav1.Condition{ l.acceptedCondition(generation), + l.programmedCondition(generation), l.conflictedCondition(generation), l.resolvedRefsCondition(generation), } diff --git a/control-plane/api-gateway/binding/validation.go b/control-plane/api-gateway/binding/validation.go index 41c9484483..0e17e2b306 100644 --- a/control-plane/api-gateway/binding/validation.go +++ b/control-plane/api-gateway/binding/validation.go @@ -35,6 +35,10 @@ var ( Kind: "TCPRoute", }}, } + allSupportedRouteKinds = map[gwv1beta1.Kind]struct{}{ + gwv1beta1.Kind("HTTPRoute"): {}, + gwv1beta1.Kind("TCPRoute"): {}, + } ) // validateRefs validates backend references for a route, determining whether or @@ -202,7 +206,10 @@ func validateTLS(gateway gwv1beta1.Gateway, tls *gwv1beta1.GatewayTLSConfig, res func validateCertificateData(secret corev1.Secret) error { _, _, err := common.ParseCertificateData(secret) - return err + if err != nil { + return errListenerInvalidCertificateRef_InvalidData + } + return nil } // validateListeners validates the given listeners both internally and with respect to each @@ -231,6 +238,8 @@ func validateListeners(gateway gwv1beta1.Gateway, listeners []gwv1beta1.Listener } else if listener.Port == 20000 { //admin port result.acceptedErr = errListenerPortUnavailable } + + result.routeKindErr = validateListenerAllowedRouteKinds(listener.AllowedRoutes) } if err := merged[listener.Port].validateProtocol(); err != nil { @@ -244,6 +253,21 @@ func validateListeners(gateway gwv1beta1.Gateway, listeners []gwv1beta1.Listener return results } +func validateListenerAllowedRouteKinds(allowedRoutes *gwv1beta1.AllowedRoutes) error { + if allowedRoutes == nil { + return nil + } + for _, kind := range allowedRoutes.Kinds { + if _, ok := allSupportedRouteKinds[kind.Kind]; !ok { + return errListenerInvalidRouteKinds + } + if !common.NilOrEqual(kind.Group, gwv1beta1.GroupVersion.Group) { + return errListenerInvalidRouteKinds + } + } + return nil +} + // routeAllowedForListenerNamespaces determines whether the route is allowed // to bind to the Gateway based on the AllowedRoutes namespace selectors. func routeAllowedForListenerNamespaces(gatewayNamespace string, allowedRoutes *gwv1beta1.AllowedRoutes, namespace corev1.Namespace) bool { From 6933efec7fc2c05d4f6a2f7f3035c5b4bc0beb9f Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Fri, 9 Jun 2023 16:03:32 -0700 Subject: [PATCH 224/340] pin for 1.2.x-rc latest Consul submodules (#2327) --- acceptance/go.mod | 26 +++---- acceptance/go.sum | 56 ++++++++------ cli/go.mod | 57 +++++++------- cli/go.sum | 176 ++++++++++++++++--------------------------- control-plane/go.mod | 42 +++++------ control-plane/go.sum | 85 +++++++++++---------- 6 files changed, 209 insertions(+), 233 deletions(-) diff --git a/acceptance/go.mod b/acceptance/go.mod index e2221a09c0..ddce3e4e5c 100644 --- a/acceptance/go.mod +++ b/acceptance/go.mod @@ -4,14 +4,14 @@ go 1.20 require ( github.com/gruntwork-io/terratest v0.31.2 - github.com/hashicorp/consul-k8s/control-plane v0.0.0-20230601034256-0c28b9b000cb - github.com/hashicorp/consul/api v1.10.1-0.20230530193107-04a0d0133ae4 - github.com/hashicorp/consul/sdk v0.13.1 + github.com/hashicorp/consul-k8s/control-plane v0.0.0-20230609143603-198c4433d892 + github.com/hashicorp/consul/api v1.22.0-rc1 + github.com/hashicorp/consul/sdk v0.14.0-rc1 github.com/hashicorp/go-uuid v1.0.3 github.com/hashicorp/go-version v1.6.0 github.com/hashicorp/serf v0.10.1 github.com/hashicorp/vault/api v1.8.3 - github.com/stretchr/testify v1.8.2 + github.com/stretchr/testify v1.8.3 gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.26.3 k8s.io/apimachinery v0.26.3 @@ -29,11 +29,11 @@ require ( github.com/cenkalti/backoff/v3 v3.0.0 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect - github.com/fatih/color v1.13.0 // indirect + github.com/fatih/color v1.14.1 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-logr/logr v1.2.3 // indirect @@ -52,7 +52,7 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-bexpr v0.1.11 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-hclog v1.2.2 // indirect + github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-plugin v1.4.5 // indirect @@ -71,8 +71,8 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect github.com/miekg/dns v1.1.50 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect @@ -88,7 +88,7 @@ require ( github.com/oklog/run v1.0.0 // indirect github.com/pierrec/lz4 v2.5.2+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/pquerna/otp v1.2.0 // indirect github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect @@ -102,10 +102,10 @@ require ( golang.org/x/crypto v0.1.0 // indirect golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect golang.org/x/mod v0.9.0 // indirect - golang.org/x/net v0.8.0 // indirect + golang.org/x/net v0.10.0 // indirect golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/term v0.6.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/term v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.7.0 // indirect diff --git a/acceptance/go.sum b/acceptance/go.sum index 77a5b5875a..bd7421de60 100644 --- a/acceptance/go.sum +++ b/acceptance/go.sum @@ -141,8 +141,9 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= @@ -182,8 +183,9 @@ github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJ github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= +github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/frankban/quicktest v1.13.0 h1:yNZif1OkDfNoDfb9zZa9aXIpejNR4F23Wely0c+Qdqk= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -331,12 +333,12 @@ github.com/gruntwork-io/gruntwork-cli v0.7.0 h1:YgSAmfCj9c61H+zuvHwKfYUwlMhu5arn github.com/gruntwork-io/gruntwork-cli v0.7.0/go.mod h1:jp6Z7NcLF2avpY8v71fBx6hds9eOFPELSuD/VPv7w00= github.com/gruntwork-io/terratest v0.31.2 h1:xvYHA80MUq5kx670dM18HInewOrrQrAN+XbVVtytUHg= github.com/gruntwork-io/terratest v0.31.2/go.mod h1:EEgJie28gX/4AD71IFqgMj6e99KP5mi81hEtzmDjxTo= -github.com/hashicorp/consul-k8s/control-plane v0.0.0-20230601034256-0c28b9b000cb h1:9GUvDoKVoV3IW78QyfoNY4bRcKxcn26wTGLoBrz92N4= -github.com/hashicorp/consul-k8s/control-plane v0.0.0-20230601034256-0c28b9b000cb/go.mod h1:jKzTEgDc/np2gX/KPdfdm1mEUfZLrU8gc71XN3B15VI= -github.com/hashicorp/consul/api v1.10.1-0.20230530193107-04a0d0133ae4 h1:6kUTk+YBgA5X5b3gNAoI18WEK4/t75LcWSimEgmpFdg= -github.com/hashicorp/consul/api v1.10.1-0.20230530193107-04a0d0133ae4/go.mod h1:tXfrC6o0yFTgAW46xd5Ic8STHc9oIBcRVBcwhX5KNCQ= -github.com/hashicorp/consul/sdk v0.13.1 h1:EygWVWWMczTzXGpO93awkHFzfUka6hLYJ0qhETd+6lY= -github.com/hashicorp/consul/sdk v0.13.1/go.mod h1:SW/mM4LbKfqmMvcFu8v+eiQQ7oitXEFeiBe9StxERb0= +github.com/hashicorp/consul-k8s/control-plane v0.0.0-20230609143603-198c4433d892 h1:4iI0ztWbVPTSDax+m1/XDs4jIRorxY4kSMyuM0fX+Dc= +github.com/hashicorp/consul-k8s/control-plane v0.0.0-20230609143603-198c4433d892/go.mod h1:iZ8BJGSnY52wnxJTo2VIfGX63CPjqiNzbuqdOtJCKnI= +github.com/hashicorp/consul/api v1.22.0-rc1 h1:ePmGqndeMgaI38KUbSA/CqTzeEAIogXyWnfNJzglo70= +github.com/hashicorp/consul/api v1.22.0-rc1/go.mod h1:wtduXtbAqSGtBdi3tyA5SSAYGAG51rBejV9SEUBciMY= +github.com/hashicorp/consul/sdk v0.14.0-rc1 h1:PuETOfN0uxl28i0Pq6rK7TBCrIl7psMbL0YTSje4KvM= +github.com/hashicorp/consul/sdk v0.14.0-rc1/go.mod h1:gHYeuDa0+0qRAD6Wwr6yznMBvBwHKoxSBoW5l73+saE= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -347,13 +349,13 @@ github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v1.2.2 h1:ihRI7YFwcZdiSD7SIenIhHfQH3OuDvWerAUBZbeQS3M= -github.com/hashicorp/go-hclog v1.2.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= +github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= @@ -444,8 +446,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -462,15 +464,18 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= @@ -547,8 +552,9 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= @@ -588,6 +594,7 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -636,8 +643,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= @@ -768,8 +775,8 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 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= @@ -790,7 +797,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -857,14 +864,15 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/cli/go.mod b/cli/go.mod index 9633fd5dd2..cdee871f38 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -5,19 +5,19 @@ go 1.20 require ( github.com/bgentry/speakeasy v0.1.0 github.com/cenkalti/backoff v2.2.1+incompatible - github.com/fatih/color v1.13.0 - github.com/google/go-cmp v0.5.8 + github.com/fatih/color v1.14.1 + github.com/google/go-cmp v0.5.9 github.com/hashicorp/consul-k8s/charts v0.0.0-00010101000000-000000000000 - github.com/hashicorp/consul/troubleshoot v0.1.2 - github.com/hashicorp/go-hclog v1.2.1 + github.com/hashicorp/consul/troubleshoot v0.3.0-rc1 + github.com/hashicorp/go-hclog v1.5.0 github.com/hashicorp/hcp-sdk-go v0.23.1-0.20220921131124-49168300a7dc github.com/kr/text v0.2.0 - github.com/mattn/go-isatty v0.0.16 + github.com/mattn/go-isatty v0.0.17 github.com/mitchellh/cli v1.1.2 github.com/olekukonko/tablewriter v0.0.5 github.com/posener/complete v1.2.3 - github.com/stretchr/testify v1.8.0 - golang.org/x/text v0.7.0 + github.com/stretchr/testify v1.8.3 + golang.org/x/text v0.9.0 helm.sh/helm/v3 v3.9.4 k8s.io/api v0.25.0 k8s.io/apiextensions-apiserver v0.25.0 @@ -28,10 +28,11 @@ require ( sigs.k8s.io/yaml v1.3.0 ) -require go.opentelemetry.io/proto/otlp v0.11.0 // indirect +require go.opentelemetry.io/proto/otlp v0.19.0 // indirect require ( - cloud.google.com/go v0.99.0 // indirect + cloud.google.com/go/compute v1.19.0 // indirect + cloud.google.com/go/compute/metadata v0.2.3 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.27 // indirect @@ -49,14 +50,14 @@ require ( github.com/Masterminds/squirrel v1.5.3 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect - github.com/armon/go-metrics v0.3.10 // indirect + github.com/armon/go-metrics v0.4.1 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect - github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc // indirect + github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195 // indirect github.com/containerd/containerd v1.6.6 // indirect github.com/cyphar/filepath-securejoin v0.2.3 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect @@ -68,8 +69,9 @@ require ( github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-units v0.4.0 // indirect github.com/emicklei/go-restful/v3 v3.8.0 // indirect - github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1 // indirect - github.com/envoyproxy/protoc-gen-validate v0.9.1 // indirect + github.com/envoyproxy/go-control-plane v0.11.0 // indirect + github.com/envoyproxy/go-control-plane/xdsmatcher v0.0.0-20230524161521-aaaacbfbe53e // indirect + github.com/envoyproxy/protoc-gen-validate v0.10.0 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect @@ -90,7 +92,7 @@ require ( github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.2.0 // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/google/btree v1.0.1 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/gofuzz v1.2.0 // indirect @@ -99,8 +101,8 @@ require ( github.com/gorilla/mux v1.8.0 // indirect github.com/gosuri/uitable v0.0.4 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect - github.com/hashicorp/consul/api v1.20.0 // indirect - github.com/hashicorp/consul/envoyextensions v0.1.2 // indirect + github.com/hashicorp/consul/api v1.22.0-rc1 // indirect + github.com/hashicorp/consul/envoyextensions v0.3.0-rc1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect @@ -147,7 +149,7 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.12.2 // indirect - github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect github.com/rubenv/sql-migrate v1.1.1 // indirect @@ -165,16 +167,17 @@ require ( go.mongodb.org/mongo-driver v1.11.1 // indirect go.starlark.net v0.0.0-20230128213706-3f75dec8e403 // indirect golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 // indirect - golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/term v0.5.0 // indirect + golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/oauth2 v0.6.0 // indirect + golang.org/x/sync v0.2.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/term v0.8.0 // indirect golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737 // indirect - google.golang.org/grpc v1.49.0 // indirect - google.golang.org/protobuf v1.28.1 // indirect + google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect + google.golang.org/grpc v1.55.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/cli/go.sum b/cli/go.sum index b5ee614f52..41b515d2e4 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -18,21 +18,16 @@ cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmW cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0 h1:y/cM2iqGgGi5D5DQZl6D9STN/3dR/Vx5Mp8s752oJTY= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v1.19.0 h1:+9zda3WGgW1ZSTlVppLCYFIr48Pa35q1uG2N1itbCEQ= +cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= @@ -104,8 +99,8 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk5 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.3.10 h1:FR+drcQStOe+32sYyJYyZ7FIdgoGGBnwLl+flodp8Uo= -github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= +github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= +github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= @@ -132,12 +127,13 @@ github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXe github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0 h1:t/LhUZLVitR1Ow2YOnduCsavhwFUklBMoGVYUCqmCqk= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 h1:7aWHqerlJ41y6FOsEUvknqgXnGmJyJSbjhAWq5pO4F8= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -153,10 +149,9 @@ github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XP github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc h1:PYXxkRUBGUMa5xgMVMDl62vEklZvKpVaxQeN9ie7Hfk= -github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195 h1:58f1tJ1ra+zFINPlwLWvQsR9CzAKt2e+EWV2yX9oXQ4= +github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= github.com/containerd/containerd v1.6.6 h1:xJNPhbrmz8xAMDNoVjHy9YHtWwEQNS+CDkcIRh7t8Y0= github.com/containerd/containerd v1.6.6/go.mod h1:ZoP1geJldzCVY3Tonoz7b1IXk8rIX0Nltt5QE4OMNk0= @@ -208,11 +203,13 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1 h1:xvqufLtNVwAhN8NMyWklVgxnWohi+wtMGQMhtxexlm0= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/go-control-plane v0.11.0 h1:jtLewhRR2vMRNnq2ZZUoCjUlgut+Y0+sDDWPOfwOi1o= +github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI= +github.com/envoyproxy/go-control-plane/xdsmatcher v0.0.0-20230524161521-aaaacbfbe53e h1:g8euodkL4GdSpVAjfzhssb07KgVmOUqyF4QOmwFumTs= +github.com/envoyproxy/go-control-plane/xdsmatcher v0.0.0-20230524161521-aaaacbfbe53e/go.mod h1:/NGEcKqwNq3HAS2vCqHfsPx9sJZbkiNQ6dGx9gTE/NA= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.9.1 h1:PS7VIOgmSVhWUEeZwTe7z7zouA22Cr590PzXKbZHOVY= -github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= +github.com/envoyproxy/protoc-gen-validate v0.10.0 h1:oIfnZFdC0YhpNNEX+SuIqko4cqqVZeN9IGTrhZje83Y= +github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -221,8 +218,9 @@ github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZM github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= +github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -336,6 +334,7 @@ github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8 github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -349,7 +348,6 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -366,10 +364,10 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -390,8 +388,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/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.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= @@ -399,7 +397,6 @@ github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -411,9 +408,6 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= @@ -423,8 +417,6 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= @@ -436,15 +428,16 @@ github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16 github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.20.0 h1:9IHTjNVSZ7MIwjlW3N3a7iGiykCMDpxZu8jsxFJh0yc= -github.com/hashicorp/consul/api v1.20.0/go.mod h1:nR64eD44KQ59Of/ECwt2vUmIK2DKsDzAwTmwmLl8Wpo= -github.com/hashicorp/consul/envoyextensions v0.1.2 h1:PvPqJ/td3UpOeIKQl5ycFPUy46XZP9awfhAUCduDeI4= -github.com/hashicorp/consul/envoyextensions v0.1.2/go.mod h1:N94DQQkgITiA40zuTQ/UdPOLAAWobgHfVT5u7wxE/aU= +github.com/hashicorp/consul/api v1.22.0-rc1 h1:ePmGqndeMgaI38KUbSA/CqTzeEAIogXyWnfNJzglo70= +github.com/hashicorp/consul/api v1.22.0-rc1/go.mod h1:wtduXtbAqSGtBdi3tyA5SSAYGAG51rBejV9SEUBciMY= +github.com/hashicorp/consul/envoyextensions v0.3.0-rc1 h1:weclrwjvLeX+vxPOyo4b4dCDxSpnDl60Z9K16nnCVnI= +github.com/hashicorp/consul/envoyextensions v0.3.0-rc1/go.mod h1:ckxoPHMiWXAe6dhyxmKsX1XqO4KTV64KWIyTu44z8UI= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.13.1 h1:EygWVWWMczTzXGpO93awkHFzfUka6hLYJ0qhETd+6lY= -github.com/hashicorp/consul/troubleshoot v0.1.2 h1:c6uMTSt/qTMhK3e18nl4xW4j7JcANdQNHOEYhoXH1P8= -github.com/hashicorp/consul/troubleshoot v0.1.2/go.mod h1:q35QOtN7K5kFLPm2SXHBDD+PzsuBekcqTZuuoOTzbWA= +github.com/hashicorp/consul/sdk v0.14.0-rc1 h1:PuETOfN0uxl28i0Pq6rK7TBCrIl7psMbL0YTSje4KvM= +github.com/hashicorp/consul/troubleshoot v0.3.0-rc1 h1:Z6ZUEKILsf85wA/zXK3XMop6IGtjui4ZZ0bAu+JIAz4= +github.com/hashicorp/consul/troubleshoot v0.3.0-rc1/go.mod h1:2WfcYZ8M4vpLtTv9M5Dp3egqSPZ16l5XsqMpO9QUYxc= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -452,8 +445,8 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v1.2.1 h1:YQsLlGDJgwhXFpucSPyVbCBviQtjlHv3jLTlp8YmtEw= -github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= +github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -472,7 +465,7 @@ github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0S github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= @@ -582,8 +575,9 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= @@ -711,8 +705,9 @@ github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrb 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= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= 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.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= @@ -779,8 +774,8 @@ github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -789,8 +784,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= @@ -835,8 +831,8 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= @@ -848,8 +844,8 @@ go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.11.0 h1:cLDgIBTf4lLOlztkhzAEdQsJ4Lj+i5Wc9k6Nn0K1VyU= -go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= +go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= +go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= go.starlark.net v0.0.0-20230128213706-3f75dec8e403 h1:jPeC7Exc+m8OBJUlWbBLh0O5UZPM7yU5W4adnhhbG4U= go.starlark.net v0.0.0-20230128213706-3f75dec8e403/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= @@ -889,6 +885,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -959,14 +957,13 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 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= @@ -980,12 +977,9 @@ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 h1:lxqLZaMad/dJHMFZH0NiNpiEZI/nhgWhe4wgzpE+MuA= -golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= +golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= 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= @@ -998,8 +992,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1066,19 +1060,13 @@ golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1087,14 +1075,14 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1104,8 +1092,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1171,10 +1159,7 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= @@ -1204,15 +1189,6 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1262,27 +1238,11 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737 h1:K1zaaMdYBXRyX+cwFnxj7M6zwDyumLQMZ5xqwGvjreQ= -google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737/go.mod h1:2r/26NEF3bFmT3eC3aZreahSal0C3Shl8Gi6vyDYqOQ= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1303,15 +1263,11 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw= -google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= 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= @@ -1325,8 +1281,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/control-plane/go.mod b/control-plane/go.mod index c916acb745..71396e2fd1 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -10,11 +10,11 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83383d github.com/hashicorp/consul-server-connection-manager v0.1.2 - github.com/hashicorp/consul/api v1.10.1-0.20230530193107-04a0d0133ae4 - github.com/hashicorp/consul/sdk v0.13.1 + github.com/hashicorp/consul/api v1.22.0-rc1 + github.com/hashicorp/consul/sdk v0.14.0-rc1 github.com/hashicorp/go-bexpr v0.1.11 github.com/hashicorp/go-discover v0.0.0-20230519164032-214571b6a530 - github.com/hashicorp/go-hclog v1.2.2 + github.com/hashicorp/go-hclog v1.5.0 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-netaddrs v0.1.0 github.com/hashicorp/go-rootcerts v1.0.2 @@ -26,20 +26,20 @@ require ( github.com/mitchellh/cli v1.1.0 github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/mapstructure v1.5.0 - github.com/stretchr/testify v1.8.1 + github.com/stretchr/testify v1.8.3 go.uber.org/zap v1.24.0 golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 golang.org/x/text v0.9.0 golang.org/x/time v0.3.0 gomodules.xyz/jsonpatch/v2 v2.3.0 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.26.1 - k8s.io/apimachinery v0.26.1 - k8s.io/client-go v0.26.1 - k8s.io/klog/v2 v2.90.1 + k8s.io/api v0.26.3 + k8s.io/apimachinery v0.26.3 + k8s.io/client-go v0.26.3 + k8s.io/klog/v2 v2.100.1 k8s.io/utils v0.0.0-20230209194617-a36077c30491 sigs.k8s.io/controller-runtime v0.14.6 - sigs.k8s.io/gateway-api v0.6.2 + sigs.k8s.io/gateway-api v0.7.1 ) require ( @@ -63,14 +63,14 @@ require ( github.com/cenkalti/backoff/v3 v3.0.0 // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661 // indirect github.com/digitalocean/godo v1.7.5 // indirect github.com/dimchansky/utfbom v1.1.0 // indirect github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect - github.com/fatih/color v1.13.0 // indirect + github.com/fatih/color v1.14.1 // indirect github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect github.com/go-logr/zapr v1.2.3 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect @@ -96,7 +96,7 @@ require ( github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect github.com/hashicorp/go-sockaddr v1.0.2 // indirect - github.com/hashicorp/go-uuid v1.0.2 // indirect + github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/mdns v1.0.4 // indirect @@ -110,8 +110,8 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/linode/linodego v0.7.1 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/go-testing-interface v1.0.0 // indirect @@ -125,7 +125,7 @@ require ( github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c // indirect github.com/pierrec/lz4 v2.5.2+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/posener/complete v1.2.3 // indirect github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect @@ -145,11 +145,11 @@ require ( go.uber.org/multierr v1.6.0 // indirect golang.org/x/crypto v0.1.0 // indirect golang.org/x/mod v0.9.0 // indirect - golang.org/x/net v0.8.0 // indirect + golang.org/x/net v0.10.0 // indirect golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/term v0.6.0 // indirect + golang.org/x/sync v0.2.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/term v0.8.0 // indirect golang.org/x/tools v0.7.0 // indirect google.golang.org/api v0.30.0 // indirect google.golang.org/appengine v1.6.7 // indirect @@ -160,8 +160,8 @@ require ( gopkg.in/resty.v1 v1.12.0 // indirect gopkg.in/square/go-jose.v2 v2.5.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.26.1 // indirect - k8s.io/component-base v0.26.1 // indirect + k8s.io/apiextensions-apiserver v0.26.3 // indirect + k8s.io/component-base v0.26.3 // indirect k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect diff --git a/control-plane/go.sum b/control-plane/go.sum index 3248aba347..2f7cbde2fc 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -113,8 +113,9 @@ github.com/containernetworking/cni v1.1.1 h1:ky20T7c0MvKvbMOwS/FrlbNwjEoqJEUUYfs github.com/containernetworking/cni v1.1.1/go.mod h1:sDpYKmGVENF3s6uvMvGgldDWeG8dMxakj/u+i9ht9vw= 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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 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/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661 h1:lrWnAyy/F72MbxIxFUzKmcMCdt9Oi8RzpAxzTNQHD7o= @@ -140,8 +141,9 @@ github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJ github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= +github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= @@ -262,12 +264,12 @@ github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83 github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83383d/go.mod h1:IHIHMzkoMwlv6rLsgwcoFBVYupR7/1pKEOHBMjD4L0k= github.com/hashicorp/consul-server-connection-manager v0.1.2 h1:tNVQHUPuMbd+cMdD8kd+qkZUYpmLmrHMAV/49f4L53I= github.com/hashicorp/consul-server-connection-manager v0.1.2/go.mod h1:NzQoVi1KcxGI2SangsDue8+ZPuXZWs+6BKAKrDNyg+w= -github.com/hashicorp/consul/api v1.10.1-0.20230530193107-04a0d0133ae4 h1:6kUTk+YBgA5X5b3gNAoI18WEK4/t75LcWSimEgmpFdg= -github.com/hashicorp/consul/api v1.10.1-0.20230530193107-04a0d0133ae4/go.mod h1:tXfrC6o0yFTgAW46xd5Ic8STHc9oIBcRVBcwhX5KNCQ= +github.com/hashicorp/consul/api v1.22.0-rc1 h1:ePmGqndeMgaI38KUbSA/CqTzeEAIogXyWnfNJzglo70= +github.com/hashicorp/consul/api v1.22.0-rc1/go.mod h1:wtduXtbAqSGtBdi3tyA5SSAYGAG51rBejV9SEUBciMY= github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3usQMXZF6ziK3SzXU= github.com/hashicorp/consul/proto-public v0.1.0/go.mod h1:vs2KkuWwtjkIgA5ezp4YKPzQp4GitV+q/+PvksrA92k= -github.com/hashicorp/consul/sdk v0.13.1 h1:EygWVWWMczTzXGpO93awkHFzfUka6hLYJ0qhETd+6lY= -github.com/hashicorp/consul/sdk v0.13.1/go.mod h1:SW/mM4LbKfqmMvcFu8v+eiQQ7oitXEFeiBe9StxERb0= +github.com/hashicorp/consul/sdk v0.14.0-rc1 h1:PuETOfN0uxl28i0Pq6rK7TBCrIl7psMbL0YTSje4KvM= +github.com/hashicorp/consul/sdk v0.14.0-rc1/go.mod h1:gHYeuDa0+0qRAD6Wwr6yznMBvBwHKoxSBoW5l73+saE= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -280,13 +282,13 @@ github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/S github.com/hashicorp/go-discover v0.0.0-20230519164032-214571b6a530 h1:WUwSDou+memX/pb6xnjA0PfAqEEJtdWSrK00kl8ySK8= github.com/hashicorp/go-discover v0.0.0-20230519164032-214571b6a530/go.mod h1:RH2Jr1/cCsZ1nRLmAOC65hp/gRehf55SsUIYV2+NAxI= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v1.2.2 h1:ihRI7YFwcZdiSD7SIenIhHfQH3OuDvWerAUBZbeQS3M= -github.com/hashicorp/go-hclog v1.2.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= +github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= @@ -313,8 +315,8 @@ github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjG github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= -github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -372,8 +374,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -386,14 +388,17 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM= github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= @@ -457,8 +462,9 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= @@ -495,6 +501,7 @@ github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 h1:Wdi9nwnhFNAlseAOe github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03/go.mod h1:gRAiPF5C5Nd0eyyRdqIu9qTiFSoZzpTq727b5B8fkkU= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= @@ -524,8 +531,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.480 h1:Dwnfdrk3KXpYRH9Kwrk9sHpZSOmrE7P9LBoNsYUJKR4= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.480/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.480 h1:YEDZmv2ABU8QvwXEVTOQgVEQzDOByhz73vdjL6sERkE= @@ -647,8 +655,8 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 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= @@ -669,8 +677,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -732,15 +740,16 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -939,18 +948,18 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.26.1 h1:f+SWYiPd/GsiWwVRz+NbFyCgvv75Pk9NK6dlkZgpCRQ= -k8s.io/api v0.26.1/go.mod h1:xd/GBNgR0f707+ATNyPmQ1oyKSgndzXij81FzWGsejg= -k8s.io/apiextensions-apiserver v0.26.1 h1:cB8h1SRk6e/+i3NOrQgSFij1B2S0Y0wDoNl66bn8RMI= -k8s.io/apiextensions-apiserver v0.26.1/go.mod h1:AptjOSXDGuE0JICx/Em15PaoO7buLwTs0dGleIHixSM= -k8s.io/apimachinery v0.26.1 h1:8EZ/eGJL+hY/MYCNwhmDzVqq2lPl3N3Bo8rvweJwXUQ= -k8s.io/apimachinery v0.26.1/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= -k8s.io/client-go v0.26.1 h1:87CXzYJnAMGaa/IDDfRdhTzxk/wzGZ+/HUQpqgVSZXU= -k8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE= -k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4= -k8s.io/component-base v0.26.1/go.mod h1:VHrLR0b58oC035w6YQiBSbtsf0ThuSwXP+p5dD/kAWU= -k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= -k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/api v0.26.3 h1:emf74GIQMTik01Aum9dPP0gAypL8JTLl/lHa4V9RFSU= +k8s.io/api v0.26.3/go.mod h1:PXsqwPMXBSBcL1lJ9CYDKy7kIReUydukS5JiRlxC3qE= +k8s.io/apiextensions-apiserver v0.26.3 h1:5PGMm3oEzdB1W/FTMgGIDmm100vn7IaUP5er36dB+YE= +k8s.io/apiextensions-apiserver v0.26.3/go.mod h1:jdA5MdjNWGP+njw1EKMZc64xAT5fIhN6VJrElV3sfpQ= +k8s.io/apimachinery v0.26.3 h1:dQx6PNETJ7nODU3XPtrwkfuubs6w7sX0M8n61zHIV/k= +k8s.io/apimachinery v0.26.3/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I= +k8s.io/client-go v0.26.3 h1:k1UY+KXfkxV2ScEL3gilKcF7761xkYsSD6BC9szIu8s= +k8s.io/client-go v0.26.3/go.mod h1:ZPNu9lm8/dbRIPAgteN30RSXea6vrCpFvq+MateTUuQ= +k8s.io/component-base v0.26.3 h1:oC0WMK/ggcbGDTkdcqefI4wIZRYdK3JySx9/HADpV0g= +k8s.io/component-base v0.26.3/go.mod h1:5kj1kZYwSC6ZstHJN7oHBqcJC6yyn41eR+Sqa/mQc8E= +k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= +k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= @@ -960,8 +969,8 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/controller-runtime v0.14.6 h1:oxstGVvXGNnMvY7TAESYk+lzr6S3V5VFxQ6d92KcwQA= sigs.k8s.io/controller-runtime v0.14.6/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= -sigs.k8s.io/gateway-api v0.6.2 h1:583XHiX2M2bKEA0SAdkoxL1nY73W1+/M+IAm8LJvbEA= -sigs.k8s.io/gateway-api v0.6.2/go.mod h1:EYJT+jlPWTeNskjV0JTki/03WX1cyAnBhwBJfYHpV/0= +sigs.k8s.io/gateway-api v0.7.1 h1:Tts2jeepVkPA5rVG/iO+S43s9n7Vp7jCDhZDQYtPigQ= +sigs.k8s.io/gateway-api v0.7.1/go.mod h1:Xv0+ZMxX0lu1nSSDIIPEfbVztgNZ+3cfiYrJsa2Ooso= 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/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= From 7f6e1cb5c4c2d8797944c1a3e0dcd12943f75138 Mon Sep 17 00:00:00 2001 From: John Maguire Date: Fri, 9 Jun 2023 19:12:13 -0400 Subject: [PATCH 225/340] Ensure Reconciliation Stops (#2305) * first pass at halting: got httproute and api-gateway done * clean up test * Handle all set for infinite reconcile check * Add table tests for minimal setup * Added some odd field names to test normalization is handled correctly * Use funky casing http routes --- .../gateway_controller_integration_test.go | 1320 +++++++++++++++++ 1 file changed, 1320 insertions(+) create mode 100644 control-plane/api-gateway/controllers/gateway_controller_integration_test.go diff --git a/control-plane/api-gateway/controllers/gateway_controller_integration_test.go b/control-plane/api-gateway/controllers/gateway_controller_integration_test.go new file mode 100644 index 0000000000..5dd5357d77 --- /dev/null +++ b/control-plane/api-gateway/controllers/gateway_controller_integration_test.go @@ -0,0 +1,1320 @@ +package controllers + +import ( + "context" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "math/big" + "sync" + "testing" + "time" + + mapset "github.com/deckarep/golang-set" + logrtest "github.com/go-logr/logr/testr" + "github.com/stretchr/testify/require" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/cache" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul-k8s/control-plane/helper/test" + "github.com/hashicorp/consul/api" +) + +func TestControllerDoesNotInfinitelyReconcile(t *testing.T) { + s := runtime.NewScheme() + require.NoError(t, clientgoscheme.AddToScheme(s)) + require.NoError(t, gwv1alpha2.Install(s)) + require.NoError(t, gwv1beta1.Install(s)) + require.NoError(t, v1alpha1.AddToScheme(s)) + + testCases := map[string]struct { + namespace string + certFn func(*testing.T, context.Context, client.WithWatch, string) *corev1.Secret + gwFn func(*testing.T, context.Context, client.WithWatch, string) *gwv1beta1.Gateway + httpRouteFn func(*testing.T, context.Context, client.WithWatch, *gwv1beta1.Gateway) *gwv1beta1.HTTPRoute + tcpRouteFn func(*testing.T, context.Context, client.WithWatch, *gwv1beta1.Gateway) *v1alpha2.TCPRoute + }{ + "all fields set": { + namespace: "consul", + certFn: createCert, + gwFn: createAllFieldsSetAPIGW, + httpRouteFn: createAllFieldsSetHTTPRoute, + tcpRouteFn: createAllFieldsSetTCPRoute, + }, + "minimal fields set": { + namespace: "", + certFn: createCert, + gwFn: minimalFieldsSetAPIGW, + httpRouteFn: minimalFieldsSetHTTPRoute, + tcpRouteFn: minimalFieldsSetTCPRoute, + }, + "funky casing to test normalization doesnt cause infinite reconciliation": { + namespace: "", + certFn: createCert, + gwFn: createFunkyCasingFieldsAPIGW, + httpRouteFn: createFunkyCasingFieldsHTTPRoute, + tcpRouteFn: createFunkyCasingFieldsTCPRoute, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + k8sClient := registerFieldIndexersForTest(fake.NewClientBuilder().WithScheme(s)).Build() + consulTestServerClient := test.TestServerWithMockConnMgrWatcher(t, nil) + ctx, cancel := context.WithCancel(context.Background()) + + t.Cleanup(func() { + cancel() + }) + logger := logrtest.New(t) + + cacheCfg := cache.Config{ + ConsulClientConfig: consulTestServerClient.Cfg, + ConsulServerConnMgr: consulTestServerClient.Watcher, + Logger: logger, + } + resourceCache := cache.New(cacheCfg) + + gwCache := cache.NewGatewayCache(ctx, cacheCfg) + + gwCtrl := GatewayController{ + HelmConfig: common.HelmConfig{}, + Log: logger, + Translator: common.ResourceTranslator{}, + cache: resourceCache, + gatewayCache: gwCache, + Client: k8sClient, + allowK8sNamespacesSet: mapset.NewSet(), + denyK8sNamespacesSet: mapset.NewSet(), + } + + go func() { + resourceCache.Run(ctx) + }() + + resourceCache.WaitSynced(ctx) + + gwSub := resourceCache.Subscribe(ctx, api.APIGateway, gwCtrl.transformConsulGateway) + httpRouteSub := resourceCache.Subscribe(ctx, api.HTTPRoute, gwCtrl.transformConsulHTTPRoute(ctx)) + tcpRouteSub := resourceCache.Subscribe(ctx, api.TCPRoute, gwCtrl.transformConsulTCPRoute(ctx)) + inlineCertSub := resourceCache.Subscribe(ctx, api.InlineCertificate, gwCtrl.transformConsulInlineCertificate(ctx)) + + cert := tc.certFn(t, ctx, k8sClient, tc.namespace) + k8sGWObj := tc.gwFn(t, ctx, k8sClient, tc.namespace) + + // reconcile so we add the finalizer + _, err := gwCtrl.Reconcile(ctx, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: k8sGWObj.Namespace, + Name: k8sGWObj.Name, + }, + }) + require.NoError(t, err) + + // reconcile again so that we get the creation with the finalizer + _, err = gwCtrl.Reconcile(ctx, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: k8sGWObj.Namespace, + Name: k8sGWObj.Name, + }, + }) + require.NoError(t, err) + + httpRouteObj := tc.httpRouteFn(t, ctx, k8sClient, k8sGWObj) + tcpRouteObj := tc.tcpRouteFn(t, ctx, k8sClient, k8sGWObj) + + // reconcile again so that we get the route bound to the gateway + _, err = gwCtrl.Reconcile(ctx, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: k8sGWObj.Namespace, + Name: k8sGWObj.Name, + }, + }) + require.NoError(t, err) + + // reconcile again so that we get the route bound to the gateway + _, err = gwCtrl.Reconcile(ctx, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: k8sGWObj.Namespace, + Name: k8sGWObj.Name, + }, + }) + require.NoError(t, err) + + wg := &sync.WaitGroup{} + // we never get the event from the cert because when it's created there are no gateways that reference it + wg.Add(3) + go func(w *sync.WaitGroup) { + gwDone := false + httpRouteDone := false + tcpRouteDone := false + for { + // get the creation events from the upsert and then continually read from channel so we dont block other subs + select { + case <-ctx.Done(): + return + case <-gwSub.Events(): + if !gwDone { + gwDone = true + wg.Done() + } + case <-httpRouteSub.Events(): + if !httpRouteDone { + httpRouteDone = true + wg.Done() + } + case <-tcpRouteSub.Events(): + if !tcpRouteDone { + tcpRouteDone = true + wg.Done() + } + case <-inlineCertSub.Events(): + } + } + }(wg) + + wg.Wait() + + gwNamespaceName := types.NamespacedName{ + Name: k8sGWObj.Name, + Namespace: k8sGWObj.Namespace, + } + + httpRouteNamespaceName := types.NamespacedName{ + Name: httpRouteObj.Name, + Namespace: httpRouteObj.Namespace, + } + + tcpRouteNamespaceName := types.NamespacedName{ + Name: tcpRouteObj.Name, + Namespace: tcpRouteObj.Namespace, + } + + certNamespaceName := types.NamespacedName{ + Name: cert.Name, + Namespace: cert.Namespace, + } + + gwRef := gwCtrl.Translator.ConfigEntryReference(api.APIGateway, gwNamespaceName) + httpRouteRef := gwCtrl.Translator.ConfigEntryReference(api.HTTPRoute, httpRouteNamespaceName) + tcpRouteRef := gwCtrl.Translator.ConfigEntryReference(api.TCPRoute, tcpRouteNamespaceName) + certRef := gwCtrl.Translator.ConfigEntryReference(api.InlineCertificate, certNamespaceName) + + curGWModifyIndex := resourceCache.Get(gwRef).GetModifyIndex() + curHTTPRouteModifyIndex := resourceCache.Get(httpRouteRef).GetModifyIndex() + curTCPRouteModifyIndex := resourceCache.Get(tcpRouteRef).GetModifyIndex() + curCertModifyIndex := resourceCache.Get(certRef).GetModifyIndex() + + err = k8sClient.Get(ctx, gwNamespaceName, k8sGWObj) + require.NoError(t, err) + curGWResourceVersion := k8sGWObj.ResourceVersion + + err = k8sClient.Get(ctx, httpRouteNamespaceName, httpRouteObj) + require.NoError(t, err) + curHTTPRouteResourceVersion := httpRouteObj.ResourceVersion + + err = k8sClient.Get(ctx, tcpRouteNamespaceName, tcpRouteObj) + require.NoError(t, err) + curTCPRouteResourceVersion := tcpRouteObj.ResourceVersion + + err = k8sClient.Get(ctx, certNamespaceName, cert) + require.NoError(t, err) + curCertResourceVersion := cert.ResourceVersion + + go func() { + // reconcile multiple times with no changes to be sure + for i := 0; i < 5; i++ { + _, err = gwCtrl.Reconcile(ctx, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: k8sGWObj.Namespace, + }, + }) + require.NoError(t, err) + } + }() + + require.Never(t, func() bool { + err = k8sClient.Get(ctx, gwNamespaceName, k8sGWObj) + require.NoError(t, err) + newGWResourceVersion := k8sGWObj.ResourceVersion + + err = k8sClient.Get(ctx, httpRouteNamespaceName, httpRouteObj) + require.NoError(t, err) + newHTTPRouteResourceVersion := httpRouteObj.ResourceVersion + + err = k8sClient.Get(ctx, tcpRouteNamespaceName, tcpRouteObj) + require.NoError(t, err) + newTCPRouteResourceVersion := tcpRouteObj.ResourceVersion + + err = k8sClient.Get(ctx, certNamespaceName, cert) + require.NoError(t, err) + newCertResourceVersion := cert.ResourceVersion + + return curGWModifyIndex == resourceCache.Get(gwRef).GetModifyIndex() && + curGWResourceVersion == newGWResourceVersion && + curHTTPRouteModifyIndex == resourceCache.Get(httpRouteRef).GetModifyIndex() && + curHTTPRouteResourceVersion == newHTTPRouteResourceVersion && + curTCPRouteModifyIndex == resourceCache.Get(tcpRouteRef).GetModifyIndex() && + curTCPRouteResourceVersion == newTCPRouteResourceVersion && + curCertModifyIndex == resourceCache.Get(certRef).GetModifyIndex() && + curCertResourceVersion == newCertResourceVersion + }, time.Duration(2*time.Second), time.Duration(500*time.Millisecond), fmt.Sprintf("curGWModifyIndex: %d, newIndx: %d", curGWModifyIndex, resourceCache.Get(gwRef).GetModifyIndex()), + ) + }) + } +} + +func createAllFieldsSetAPIGW(t *testing.T, ctx context.Context, k8sClient client.WithWatch, namespace string) *gwv1beta1.Gateway { + // listener one configuration + listenerOneName := "listener-one" + listenerOneHostname := "*.consul.io" + listenerOnePort := 3366 + listenerOneProtocol := "https" + + // listener two configuration + listenerTwoName := "listener-two" + listenerTwoHostname := "*.consul.io" + listenerTwoPort := 5432 + listenerTwoProtocol := "http" + + // listener three configuration + listenerThreeName := "listener-three" + listenerThreePort := 8081 + listenerThreeProtocol := "tcp" + + // listener four configuration + listenerFourName := "listener-four" + listenerFourHostname := "*.consul.io" + listenerFourPort := 5433 + listenerFourProtocol := "http" + + // Write gw to k8s + gwClassCfg := &v1alpha1.GatewayClassConfig{ + TypeMeta: metav1.TypeMeta{ + Kind: "GatewayClassConfig", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway-class-config", + }, + Spec: v1alpha1.GatewayClassConfigSpec{}, + } + gwClass := &gwv1beta1.GatewayClass{ + TypeMeta: metav1.TypeMeta{ + Kind: "GatewayClass", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "gatewayclass", + }, + Spec: gwv1beta1.GatewayClassSpec{ + ControllerName: "hashicorp.com/consul-api-gateway-controller", + ParametersRef: &gwv1beta1.ParametersReference{ + Group: "consul.hashicorp.com", + Kind: "GatewayClassConfig", + Name: "gateway-class-config", + }, + Description: new(string), + }, + } + gw := &gwv1beta1.Gateway{ + TypeMeta: metav1.TypeMeta{ + Kind: "Gateway", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "gw", + Namespace: namespace, + Annotations: make(map[string]string), + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gwv1beta1.ObjectName(gwClass.Name), + Listeners: []gwv1beta1.Listener{ + { + Name: gwv1beta1.SectionName(listenerOneName), + Hostname: common.PointerTo(gwv1beta1.Hostname(listenerOneHostname)), + Port: gwv1beta1.PortNumber(listenerOnePort), + Protocol: gwv1beta1.ProtocolType(listenerOneProtocol), + TLS: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{ + { + Kind: common.PointerTo(gwv1beta1.Kind("Secret")), + Name: gwv1beta1.ObjectName("one-cert"), + Namespace: common.PointerTo(gwv1beta1.Namespace(namespace)), + }, + }, + }, + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: common.PointerTo(gwv1beta1.FromNamespaces("All")), + }, + }, + }, + { + Name: gwv1beta1.SectionName(listenerTwoName), + Hostname: common.PointerTo(gwv1beta1.Hostname(listenerTwoHostname)), + Port: gwv1beta1.PortNumber(listenerTwoPort), + Protocol: gwv1beta1.ProtocolType(listenerTwoProtocol), + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: common.PointerTo(gwv1beta1.FromNamespaces("Same")), + }, + }, + }, + { + Name: gwv1beta1.SectionName(listenerThreeName), + Port: gwv1beta1.PortNumber(listenerThreePort), + Protocol: gwv1beta1.ProtocolType(listenerThreeProtocol), + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: common.PointerTo(gwv1beta1.FromNamespaces("All")), + }, + }, + }, + { + Name: gwv1beta1.SectionName(listenerFourName), + Hostname: common.PointerTo(gwv1beta1.Hostname(listenerFourHostname)), + Port: gwv1beta1.PortNumber(listenerFourPort), + Protocol: gwv1beta1.ProtocolType(listenerFourProtocol), + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: common.PointerTo(gwv1beta1.FromNamespaces("Selector")), + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + common.NamespaceNameLabel: "consul", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{}, + }, + }, + }, + }, + }, + }, + } + + err := k8sClient.Create(ctx, gwClassCfg) + require.NoError(t, err) + + err = k8sClient.Create(ctx, gwClass) + require.NoError(t, err) + + err = k8sClient.Create(ctx, gw) + require.NoError(t, err) + + return gw +} + +func createAllFieldsSetHTTPRoute(t *testing.T, ctx context.Context, k8sClient client.WithWatch, gw *gwv1beta1.Gateway) *gwv1beta1.HTTPRoute { + svcDefault := &v1alpha1.ServiceDefaults{ + TypeMeta: metav1.TypeMeta{ + Kind: "ServiceDefaults", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "Service", + }, + Spec: v1alpha1.ServiceDefaultsSpec{ + Protocol: "http", + }, + } + + svc := &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "Service", + Labels: map[string]string{"app": "Service"}, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "high", + Protocol: "TCP", + Port: 8080, + }, + }, + Selector: map[string]string{"app": "Service"}, + }, + } + + serviceAccount := &corev1.ServiceAccount{ + TypeMeta: metav1.TypeMeta{ + Kind: "ServiceAccount", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "Service", + }, + } + + deployment := &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + Kind: "Deployment", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "Service", + Labels: map[string]string{"app": "Service"}, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: common.PointerTo(int32(1)), + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "Service"}, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{}, + Spec: corev1.PodSpec{}, + }, + }, + } + + err := k8sClient.Create(ctx, svcDefault) + require.NoError(t, err) + + err = k8sClient.Create(ctx, svc) + require.NoError(t, err) + + err = k8sClient.Create(ctx, serviceAccount) + require.NoError(t, err) + + err = k8sClient.Create(ctx, deployment) + require.NoError(t, err) + + route := &gwv1beta1.HTTPRoute{ + TypeMeta: metav1.TypeMeta{ + Kind: "HTTPRoute", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "http-route", + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + { + Kind: (*gwv1beta1.Kind)(&gw.Kind), + Namespace: (*gwv1beta1.Namespace)(&gw.Namespace), + Name: gwv1beta1.ObjectName(gw.Name), + SectionName: &gw.Spec.Listeners[0].Name, + Port: &gw.Spec.Listeners[0].Port, + }, + }, + }, + Hostnames: []gwv1beta1.Hostname{"route.consul.io"}, + Rules: []gwv1beta1.HTTPRouteRule{ + { + Matches: []gwv1beta1.HTTPRouteMatch{ + { + Path: &gwv1beta1.HTTPPathMatch{ + Type: common.PointerTo(gwv1beta1.PathMatchType("PathPrefix")), + Value: common.PointerTo("/v1"), + }, + Headers: []gwv1beta1.HTTPHeaderMatch{ + { + Type: common.PointerTo(gwv1beta1.HeaderMatchExact), + Name: "version", + Value: "version", + }, + }, + QueryParams: []gwv1beta1.HTTPQueryParamMatch{ + { + Type: common.PointerTo(gwv1beta1.QueryParamMatchExact), + Name: "search", + Value: "q", + }, + }, + Method: common.PointerTo(gwv1beta1.HTTPMethod("GET")), + }, + }, + Filters: []gwv1beta1.HTTPRouteFilter{ + { + Type: gwv1beta1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ + Set: []gwv1beta1.HTTPHeader{ + { + Name: "foo", + Value: "bax", + }, + }, + Add: []gwv1beta1.HTTPHeader{ + { + Name: "arc", + Value: "reactor", + }, + }, + Remove: []string{"remove"}, + }, + }, + { + Type: gwv1beta1.HTTPRouteFilterURLRewrite, + URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ + Hostname: common.PointerTo(gwv1beta1.PreciseHostname("host.com")), + Path: &gwv1beta1.HTTPPathModifier{ + Type: gwv1beta1.FullPathHTTPPathModifier, + ReplaceFullPath: common.PointerTo("/foobar"), + }, + }, + }, + + { + Type: gwv1beta1.HTTPRouteFilterURLRewrite, + URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ + Hostname: common.PointerTo(gwv1beta1.PreciseHostname("host.com")), + Path: &gwv1beta1.HTTPPathModifier{ + Type: gwv1beta1.PrefixMatchHTTPPathModifier, + ReplacePrefixMatch: common.PointerTo("/foo"), + }, + }, + }, + }, + BackendRefs: []gwv1beta1.HTTPBackendRef{ + { + BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "Service", + Port: common.PointerTo(gwv1beta1.PortNumber(8080)), + }, + Weight: common.PointerTo(int32(50)), + }, + }, + }, + }, + }, + }, + } + + err = k8sClient.Create(ctx, route) + require.NoError(t, err) + + return route +} + +func createAllFieldsSetTCPRoute(t *testing.T, ctx context.Context, k8sClient client.WithWatch, gw *gwv1beta1.Gateway) *v1alpha2.TCPRoute { + route := &v1alpha2.TCPRoute{ + TypeMeta: metav1.TypeMeta{ + Kind: "TCPRoute", + APIVersion: "gateway.networking.k8s.io/v1alpha2", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "tcp-route", + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + { + Kind: (*gwv1beta1.Kind)(&gw.Kind), + Namespace: (*gwv1beta1.Namespace)(&gw.Namespace), + Name: gwv1beta1.ObjectName(gw.Name), + SectionName: &gw.Spec.Listeners[2].Name, + Port: &gw.Spec.Listeners[2].Port, + }, + }, + }, + Rules: []gwv1alpha2.TCPRouteRule{ + { + BackendRefs: []gwv1beta1.BackendRef{ + { + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "Service", + Port: common.PointerTo(gwv1beta1.PortNumber(25000)), + }, + Weight: common.PointerTo(int32(50)), + }, + }, + }, + }, + }, + } + + err := k8sClient.Create(ctx, route) + require.NoError(t, err) + + return route +} + +func createCert(t *testing.T, ctx context.Context, k8sClient client.WithWatch, certNS string) *corev1.Secret { + // listener one tls config + certName := "one-cert" + + privateKey, err := rsa.GenerateKey(rand.Reader, 1024) + require.NoError(t, err) + + usage := x509.KeyUsageCertSign + expiration := time.Now().AddDate(10, 0, 0) + + cert := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: "consul.test", + }, + IsCA: true, + NotBefore: time.Now().Add(-10 * time.Minute), + NotAfter: expiration, + SubjectKeyId: []byte{1, 2, 3, 4, 6}, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: usage, + BasicConstraintsValid: true, + } + caCert := cert + caPrivateKey := privateKey + + data, err := x509.CreateCertificate(rand.Reader, cert, caCert, &privateKey.PublicKey, caPrivateKey) + require.NoError(t, err) + + certBytes := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: data, + }) + + privateKeyBytes := pem.EncodeToMemory(&pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(privateKey), + }) + + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: certNS, + Name: certName, + }, + Data: map[string][]byte{ + corev1.TLSCertKey: certBytes, + corev1.TLSPrivateKeyKey: privateKeyBytes, + }, + } + + err = k8sClient.Create(ctx, secret) + require.NoError(t, err) + + return secret +} + +func minimalFieldsSetAPIGW(t *testing.T, ctx context.Context, k8sClient client.WithWatch, namespace string) *gwv1beta1.Gateway { + // listener one configuration + listenerOneName := "listener-one" + listenerOneHostname := "*.consul.io" + listenerOnePort := 3366 + listenerOneProtocol := "https" + + // listener three configuration + listenerThreeName := "listener-three" + listenerThreePort := 8081 + listenerThreeProtocol := "tcp" + + // Write gw to k8s + gwClassCfg := &v1alpha1.GatewayClassConfig{ + TypeMeta: metav1.TypeMeta{ + Kind: "GatewayClassConfig", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway-class-config", + }, + Spec: v1alpha1.GatewayClassConfigSpec{}, + } + gwClass := &gwv1beta1.GatewayClass{ + TypeMeta: metav1.TypeMeta{ + Kind: "GatewayClass", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "gatewayclass", + }, + Spec: gwv1beta1.GatewayClassSpec{ + ControllerName: "hashicorp.com/consul-api-gateway-controller", + ParametersRef: &gwv1beta1.ParametersReference{ + Group: "consul.hashicorp.com", + Kind: "GatewayClassConfig", + Name: "gateway-class-config", + }, + Description: new(string), + }, + } + gw := &gwv1beta1.Gateway{ + TypeMeta: metav1.TypeMeta{ + Kind: "Gateway", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "gw", + Annotations: make(map[string]string), + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gwv1beta1.ObjectName(gwClass.Name), + Listeners: []gwv1beta1.Listener{ + { + Name: gwv1beta1.SectionName(listenerOneName), + Hostname: common.PointerTo(gwv1beta1.Hostname(listenerOneHostname)), + Port: gwv1beta1.PortNumber(listenerOnePort), + Protocol: gwv1beta1.ProtocolType(listenerOneProtocol), + TLS: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{ + { + Kind: common.PointerTo(gwv1beta1.Kind("Secret")), + Name: gwv1beta1.ObjectName("one-cert"), + Namespace: common.PointerTo(gwv1beta1.Namespace(namespace)), + }, + }, + }, + }, + { + Name: gwv1beta1.SectionName(listenerThreeName), + Port: gwv1beta1.PortNumber(listenerThreePort), + Protocol: gwv1beta1.ProtocolType(listenerThreeProtocol), + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: common.PointerTo(gwv1beta1.FromNamespaces("All")), + }, + }, + }, + }, + }, + } + + err := k8sClient.Create(ctx, gwClassCfg) + require.NoError(t, err) + + err = k8sClient.Create(ctx, gwClass) + require.NoError(t, err) + + err = k8sClient.Create(ctx, gw) + require.NoError(t, err) + + return gw +} + +func minimalFieldsSetHTTPRoute(t *testing.T, ctx context.Context, k8sClient client.WithWatch, gw *gwv1beta1.Gateway) *gwv1beta1.HTTPRoute { + svcDefault := &v1alpha1.ServiceDefaults{ + TypeMeta: metav1.TypeMeta{ + Kind: "ServiceDefaults", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "Service", + }, + Spec: v1alpha1.ServiceDefaultsSpec{ + Protocol: "http", + }, + } + + svc := &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "Service", + Labels: map[string]string{"app": "Service"}, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "high", + Protocol: "TCP", + Port: 8080, + }, + }, + Selector: map[string]string{"app": "Service"}, + }, + } + + serviceAccount := &corev1.ServiceAccount{ + TypeMeta: metav1.TypeMeta{ + Kind: "ServiceAccount", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "Service", + }, + } + + deployment := &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + Kind: "Deployment", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "Service", + Labels: map[string]string{"app": "Service"}, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: common.PointerTo(int32(1)), + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "Service"}, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{}, + Spec: corev1.PodSpec{}, + }, + }, + } + + err := k8sClient.Create(ctx, svcDefault) + require.NoError(t, err) + + err = k8sClient.Create(ctx, svc) + require.NoError(t, err) + + err = k8sClient.Create(ctx, serviceAccount) + require.NoError(t, err) + + err = k8sClient.Create(ctx, deployment) + require.NoError(t, err) + + route := &gwv1beta1.HTTPRoute{ + TypeMeta: metav1.TypeMeta{ + Kind: "HTTPRoute", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "http-route", + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + { + Kind: (*gwv1beta1.Kind)(&gw.Kind), + Namespace: (*gwv1beta1.Namespace)(&gw.Namespace), + Name: gwv1beta1.ObjectName(gw.Name), + SectionName: &gw.Spec.Listeners[0].Name, + Port: &gw.Spec.Listeners[0].Port, + }, + }, + }, + Hostnames: []gwv1beta1.Hostname{"route.consul.io"}, + Rules: []gwv1beta1.HTTPRouteRule{ + { + BackendRefs: []gwv1beta1.HTTPBackendRef{ + { + BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "Service", + Port: common.PointerTo(gwv1beta1.PortNumber(8080)), + }, + }, + }, + }, + }, + }, + }, + } + + err = k8sClient.Create(ctx, route) + require.NoError(t, err) + + return route +} + +func minimalFieldsSetTCPRoute(t *testing.T, ctx context.Context, k8sClient client.WithWatch, gw *gwv1beta1.Gateway) *v1alpha2.TCPRoute { + route := &v1alpha2.TCPRoute{ + TypeMeta: metav1.TypeMeta{ + Kind: "TCPRoute", + APIVersion: "gateway.networking.k8s.io/v1alpha2", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "tcp-route", + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + { + Kind: (*gwv1beta1.Kind)(&gw.Kind), + Namespace: (*gwv1beta1.Namespace)(&gw.Namespace), + Name: gwv1beta1.ObjectName(gw.Name), + SectionName: &gw.Spec.Listeners[1].Name, + Port: &gw.Spec.Listeners[1].Port, + }, + }, + }, + Rules: []gwv1alpha2.TCPRouteRule{ + { + BackendRefs: []gwv1beta1.BackendRef{ + { + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "Service", + Port: common.PointerTo(gwv1beta1.PortNumber(25000)), + }, + }, + }, + }, + }, + }, + } + + err := k8sClient.Create(ctx, route) + require.NoError(t, err) + + return route +} + +func createFunkyCasingFieldsAPIGW(t *testing.T, ctx context.Context, k8sClient client.WithWatch, namespace string) *gwv1beta1.Gateway { + // listener one configuration + listenerOneName := "listener-one" + listenerOneHostname := "*.consul.io" + listenerOnePort := 3366 + listenerOneProtocol := "hTtPs" + + // listener two configuration + listenerTwoName := "listener-two" + listenerTwoHostname := "*.consul.io" + listenerTwoPort := 5432 + listenerTwoProtocol := "HTTP" + + // listener three configuration + listenerThreeName := "listener-three" + listenerThreePort := 8081 + listenerThreeProtocol := "tCp" + + // listener four configuration + listenerFourName := "listener-four" + listenerFourHostname := "*.consul.io" + listenerFourPort := 5433 + listenerFourProtocol := "hTTp" + + // Write gw to k8s + gwClassCfg := &v1alpha1.GatewayClassConfig{ + TypeMeta: metav1.TypeMeta{ + Kind: "GatewayClassConfig", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway-class-config", + }, + Spec: v1alpha1.GatewayClassConfigSpec{}, + } + gwClass := &gwv1beta1.GatewayClass{ + TypeMeta: metav1.TypeMeta{ + Kind: "GatewayClass", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "gatewayclass", + }, + Spec: gwv1beta1.GatewayClassSpec{ + ControllerName: "hashicorp.com/consul-api-gateway-controller", + ParametersRef: &gwv1beta1.ParametersReference{ + Group: "consul.hashicorp.com", + Kind: "GatewayClassConfig", + Name: "gateway-class-config", + }, + Description: new(string), + }, + } + gw := &gwv1beta1.Gateway{ + TypeMeta: metav1.TypeMeta{ + Kind: "Gateway", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "gw", + Namespace: namespace, + Annotations: make(map[string]string), + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gwv1beta1.ObjectName(gwClass.Name), + Listeners: []gwv1beta1.Listener{ + { + Name: gwv1beta1.SectionName(listenerOneName), + Hostname: common.PointerTo(gwv1beta1.Hostname(listenerOneHostname)), + Port: gwv1beta1.PortNumber(listenerOnePort), + Protocol: gwv1beta1.ProtocolType(listenerOneProtocol), + TLS: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{ + { + Kind: common.PointerTo(gwv1beta1.Kind("Secret")), + Name: gwv1beta1.ObjectName("one-cert"), + Namespace: common.PointerTo(gwv1beta1.Namespace(namespace)), + }, + }, + }, + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: common.PointerTo(gwv1beta1.FromNamespaces("All")), + }, + }, + }, + { + Name: gwv1beta1.SectionName(listenerTwoName), + Hostname: common.PointerTo(gwv1beta1.Hostname(listenerTwoHostname)), + Port: gwv1beta1.PortNumber(listenerTwoPort), + Protocol: gwv1beta1.ProtocolType(listenerTwoProtocol), + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: common.PointerTo(gwv1beta1.FromNamespaces("Same")), + }, + }, + }, + { + Name: gwv1beta1.SectionName(listenerThreeName), + Port: gwv1beta1.PortNumber(listenerThreePort), + Protocol: gwv1beta1.ProtocolType(listenerThreeProtocol), + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: common.PointerTo(gwv1beta1.FromNamespaces("All")), + }, + }, + }, + { + Name: gwv1beta1.SectionName(listenerFourName), + Hostname: common.PointerTo(gwv1beta1.Hostname(listenerFourHostname)), + Port: gwv1beta1.PortNumber(listenerFourPort), + Protocol: gwv1beta1.ProtocolType(listenerFourProtocol), + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: common.PointerTo(gwv1beta1.FromNamespaces("Selector")), + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + common.NamespaceNameLabel: "consul", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{}, + }, + }, + }, + }, + }, + }, + } + + err := k8sClient.Create(ctx, gwClassCfg) + require.NoError(t, err) + + err = k8sClient.Create(ctx, gwClass) + require.NoError(t, err) + + err = k8sClient.Create(ctx, gw) + require.NoError(t, err) + + return gw +} + +func createFunkyCasingFieldsHTTPRoute(t *testing.T, ctx context.Context, k8sClient client.WithWatch, gw *gwv1beta1.Gateway) *gwv1beta1.HTTPRoute { + svcDefault := &v1alpha1.ServiceDefaults{ + TypeMeta: metav1.TypeMeta{ + Kind: "ServiceDefaults", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "Service", + }, + Spec: v1alpha1.ServiceDefaultsSpec{ + Protocol: "hTtp", + }, + } + + svc := &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "Service", + Labels: map[string]string{"app": "Service"}, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "high", + Protocol: "TCP", + Port: 8080, + }, + }, + Selector: map[string]string{"app": "Service"}, + }, + } + + serviceAccount := &corev1.ServiceAccount{ + TypeMeta: metav1.TypeMeta{ + Kind: "ServiceAccount", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "Service", + }, + } + + deployment := &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + Kind: "Deployment", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "Service", + Labels: map[string]string{"app": "Service"}, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: common.PointerTo(int32(1)), + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "Service"}, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{}, + Spec: corev1.PodSpec{}, + }, + }, + } + + err := k8sClient.Create(ctx, svcDefault) + require.NoError(t, err) + + err = k8sClient.Create(ctx, svc) + require.NoError(t, err) + + err = k8sClient.Create(ctx, serviceAccount) + require.NoError(t, err) + + err = k8sClient.Create(ctx, deployment) + require.NoError(t, err) + + route := &gwv1beta1.HTTPRoute{ + TypeMeta: metav1.TypeMeta{ + Kind: "HTTPRoute", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "http-route", + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + { + Namespace: (*gwv1beta1.Namespace)(&gw.Namespace), + Name: gwv1beta1.ObjectName(gw.Name), + SectionName: &gw.Spec.Listeners[0].Name, + Port: &gw.Spec.Listeners[0].Port, + }, + }, + }, + Hostnames: []gwv1beta1.Hostname{"route.consul.io"}, + Rules: []gwv1beta1.HTTPRouteRule{ + { + Matches: []gwv1beta1.HTTPRouteMatch{ + { + Path: &gwv1beta1.HTTPPathMatch{ + Type: common.PointerTo(gwv1beta1.PathMatchPathPrefix), + }, + Headers: []gwv1beta1.HTTPHeaderMatch{ + { + Type: common.PointerTo(gwv1beta1.HeaderMatchExact), + Name: "version", + Value: "version", + }, + }, + QueryParams: []gwv1beta1.HTTPQueryParamMatch{ + { + Type: common.PointerTo(gwv1beta1.QueryParamMatchExact), + Name: "search", + Value: "q", + }, + }, + Method: common.PointerTo(gwv1beta1.HTTPMethod("geT")), + }, + }, + Filters: []gwv1beta1.HTTPRouteFilter{ + { + Type: gwv1beta1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ + Set: []gwv1beta1.HTTPHeader{ + { + Name: "foo", + Value: "bax", + }, + }, + Add: []gwv1beta1.HTTPHeader{ + { + Name: "arc", + Value: "reactor", + }, + }, + Remove: []string{"remove"}, + }, + }, + { + Type: gwv1beta1.HTTPRouteFilterURLRewrite, + URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ + Hostname: common.PointerTo(gwv1beta1.PreciseHostname("host.com")), + Path: &gwv1beta1.HTTPPathModifier{ + Type: gwv1beta1.FullPathHTTPPathModifier, + ReplaceFullPath: common.PointerTo("/foobar"), + }, + }, + }, + + { + Type: gwv1beta1.HTTPRouteFilterURLRewrite, + URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ + Hostname: common.PointerTo(gwv1beta1.PreciseHostname("host.com")), + Path: &gwv1beta1.HTTPPathModifier{ + Type: gwv1beta1.PrefixMatchHTTPPathModifier, + ReplacePrefixMatch: common.PointerTo("/foo"), + }, + }, + }, + }, + BackendRefs: []gwv1beta1.HTTPBackendRef{ + { + BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "Service", + Port: common.PointerTo(gwv1beta1.PortNumber(8080)), + }, + Weight: common.PointerTo(int32(-50)), + }, + }, + }, + }, + }, + }, + } + + err = k8sClient.Create(ctx, route) + require.NoError(t, err) + + return route +} + +func createFunkyCasingFieldsTCPRoute(t *testing.T, ctx context.Context, k8sClient client.WithWatch, gw *gwv1beta1.Gateway) *v1alpha2.TCPRoute { + route := &v1alpha2.TCPRoute{ + TypeMeta: metav1.TypeMeta{ + Kind: "TCPRoute", + APIVersion: "gateway.networking.k8s.io/v1alpha2", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "tcp-route", + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + { + Namespace: (*gwv1beta1.Namespace)(&gw.Namespace), + Name: gwv1beta1.ObjectName(gw.Name), + SectionName: &gw.Spec.Listeners[2].Name, + Port: &gw.Spec.Listeners[2].Port, + }, + }, + }, + Rules: []gwv1alpha2.TCPRouteRule{ + { + BackendRefs: []gwv1beta1.BackendRef{ + { + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "Service", + Port: common.PointerTo(gwv1beta1.PortNumber(25000)), + }, + Weight: common.PointerTo(int32(-50)), + }, + }, + }, + }, + }, + } + + err := k8sClient.Create(ctx, route) + require.NoError(t, err) + + return route +} From 7e076bb6f9d95e6214b0a2ef66caeb8450740d84 Mon Sep 17 00:00:00 2001 From: skpratt Date: Fri, 9 Jun 2023 23:24:35 -0500 Subject: [PATCH 226/340] Add CRT docker changes for release workflow (#2333) --- .github/workflows/build.yml | 74 +++++++++++++++++++++++++++++++++++-- control-plane/Dockerfile | 5 +++ 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8eef629401..e46e16c9a3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -271,6 +271,7 @@ jobs: unzip -j *.zip - name: Docker Build (Action) uses: hashicorp/actions-docker-build@v1 + if: ${{ !matrix.fips }} with: smoke_test: | TEST_VERSION="$(docker run "${IMAGE_NAME}" consul-k8s-control-plane version | awk '{print $2}')" @@ -291,6 +292,29 @@ jobs: hashicorppreview/${{ env.repo }}-control-plane:${{ env.version }} docker.io/hashicorppreview/${{ env.repo }}-control-plane:${{ env.version }}-${{ github.sha }} + - name: Docker FIPS Build (Action) + uses: hashicorp/actions-docker-build@v1 + if: ${{ matrix.fips }} + with: + smoke_test: | + TEST_VERSION="$(docker run "${IMAGE_NAME}" consul-k8s-control-plane version | awk '{print $2}')" + if [ "${TEST_VERSION}" != "v${version}" ]; then + echo "Test FAILED" + exit 1 + fi + echo "Test PASSED" + version: ${{ env.version }} + target: release-default-fips # duplicate target to distinguish FIPS builds in CRT machinery + arch: ${{ matrix.goarch }} + pkg_name: consul-k8s-control-plane_${{ env.version }} + bin_name: consul-k8s-control-plane + workdir: control-plane + tags: | + docker.io/hashicorp/${{ env.repo }}-control-plane-fips:${{ env.version }} + dev_tags: | + hashicorppreview/${{ env.repo }}-control-plane-fips:${{ env.version }} + docker.io/hashicorppreview/${{ env.repo }}-control-plane-fips:${{ env.version }}-${{ github.sha }} + build-docker-ubi-redhat-registry: name: Docker ${{ matrix.arch }} ${{ matrix.fips }} UBI build for RedHat Registry needs: [get-product-version, build] @@ -318,7 +342,9 @@ jobs: - name: Copy LICENSE run: cp LICENSE ./control-plane - - uses: hashicorp/actions-docker-build@v1 + - name: Docker Build (Action) + if: ${{ !matrix.fips }} + uses: hashicorp/actions-docker-build@v1 with: smoke_test: | TEST_VERSION="$(docker run "${IMAGE_NAME}" consul-k8s-control-plane version | awk '{print $2}')" @@ -334,6 +360,24 @@ jobs: bin_name: consul-k8s-control-plane workdir: control-plane redhat_tag: quay.io/redhat-isv-containers/611ca2f89a9b407267837100:${{env.version}}-ubi + - name: Docker FIPS Build (Action) + if: ${{ matrix.fips }} + uses: hashicorp/actions-docker-build@v1 + with: + smoke_test: | + TEST_VERSION="$(docker run "${IMAGE_NAME}" consul-k8s-control-plane version | awk '{print $2}')" + if [ "${TEST_VERSION}" != "v${version}" ]; then + echo "Test FAILED" + exit 1 + fi + echo "Test PASSED" + version: ${{ env.version }} + target: ubi-fips # duplicate target to distinguish FIPS builds in CRT machinery + arch: ${{ matrix.arch }} + pkg_name: consul-k8s-control-plane_${{ env.version }} + bin_name: consul-k8s-control-plane + workdir: control-plane + redhat_tag: quay.io/redhat-isv-containers/6483ed53b430df51b731406c:${{env.version}}-ubi # this is different than the non-FIPS one build-docker-ubi-dockerhub: name: Docker ${{ matrix.arch }} ${{ matrix.fips }} UBI build for DockerHub @@ -361,7 +405,9 @@ jobs: - name: Copy LICENSE run: cp LICENSE ./control-plane - - uses: hashicorp/actions-docker-build@v1 + - name: Docker Build (Action) + uses: hashicorp/actions-docker-build@v1 + if: ${{ !matrix.fips }} with: smoke_test: | TEST_VERSION="$(docker run "${IMAGE_NAME}" consul-k8s-control-plane version | awk '{print $2}')" @@ -380,4 +426,26 @@ jobs: docker.io/hashicorp/${{ env.repo }}-control-plane:${{ env.version }}-ubi dev_tags: | hashicorppreview/${{ env.repo }}-control-plane:${{ env.version }}-ubi - docker.io/hashicorppreview/${{ env.repo }}-control-plane:${{ env.version }}-ubi-${{ github.sha }} + docker.io/hashicorppreview/${{ env.repo }}-control-plane:${{ env.version }}-ubi-${{ github.sha }} + - name: Docker FIPS Build (Action) + uses: hashicorp/actions-docker-build@v1 + if: ${{ matrix.fips }} + with: + smoke_test: | + TEST_VERSION="$(docker run "${IMAGE_NAME}" consul-k8s-control-plane version | awk '{print $2}')" + if [ "${TEST_VERSION}" != "v${version}" ]; then + echo "Test FAILED" + exit 1 + fi + echo "Test PASSED" + version: ${{ env.version }} + target: ubi-fips # duplicate target to distinguish FIPS builds in CRT machinery + arch: ${{ matrix.arch }} + pkg_name: consul-k8s-control-plane_${{ env.version }} + bin_name: consul-k8s-control-plane + workdir: control-plane + tags: | + docker.io/hashicorp/${{ env.repo }}-control-plane-fips:${{ env.version }}-ubi + dev_tags: | + hashicorppreview/${{ env.repo }}-control-plane-fips:${{ env.version }}-ubi + docker.io/hashicorppreview/${{ env.repo }}-control-plane-fips:${{ env.version }}-ubi-${{ github.sha }} diff --git a/control-plane/Dockerfile b/control-plane/Dockerfile index 5b3d73e625..f401ac8262 100644 --- a/control-plane/Dockerfile +++ b/control-plane/Dockerfile @@ -113,6 +113,9 @@ COPY dist/cni/${TARGETOS}/${TARGETARCH}/${CNI_BIN_NAME} /bin/ USER 100 CMD /bin/${BIN_NAME} +# Duplicate target for FIPS builds +FROM release-default AS release-default-fips + # ----------------------------------- # Dockerfile target for consul-k8s with UBI as its base image. Used for running on # OpenShift. @@ -175,6 +178,8 @@ COPY dist/cni/${TARGETOS}/${TARGETARCH}/${CNI_BIN_NAME} /bin/ USER 100 CMD /bin/${BIN_NAME} +# Duplicate target for FIPS builds +FROM ubi AS ubi-fips # =================================== # # Set default target to 'dev'. From 497621505160edd41ef209522f8924c90166c44d Mon Sep 17 00:00:00 2001 From: skpratt Date: Sun, 11 Jun 2023 16:45:14 -0500 Subject: [PATCH 227/340] Update var check with appropriate quotes (#2330) --- control-plane/build-support/functions/20-build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/control-plane/build-support/functions/20-build.sh b/control-plane/build-support/functions/20-build.sh index e9540956c9..dac626b88f 100644 --- a/control-plane/build-support/functions/20-build.sh +++ b/control-plane/build-support/functions/20-build.sh @@ -188,7 +188,7 @@ function build_consul_local { # build with go install. # The GOXPARALLEL environment variable is used if set - if [ $GOTAGS == "fips" ]; then + if [ "${GOTAGS:-}" == "fips" ]; then CGO_ENABLED=1 else CGO_ENABLED=0 From 60b214e19707f4739b628ed0b979a24eac903b3a Mon Sep 17 00:00:00 2001 From: Nathan Coleman Date: Mon, 12 Jun 2023 12:50:16 -0400 Subject: [PATCH 228/340] Revert "Ensure Reconciliation Stops (#2305)" (#2341) This reverts commit 7f6e1cb5c4c2d8797944c1a3e0dcd12943f75138. --- .../gateway_controller_integration_test.go | 1320 ----------------- 1 file changed, 1320 deletions(-) delete mode 100644 control-plane/api-gateway/controllers/gateway_controller_integration_test.go diff --git a/control-plane/api-gateway/controllers/gateway_controller_integration_test.go b/control-plane/api-gateway/controllers/gateway_controller_integration_test.go deleted file mode 100644 index 5dd5357d77..0000000000 --- a/control-plane/api-gateway/controllers/gateway_controller_integration_test.go +++ /dev/null @@ -1,1320 +0,0 @@ -package controllers - -import ( - "context" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "fmt" - "math/big" - "sync" - "testing" - "time" - - mapset "github.com/deckarep/golang-set" - logrtest "github.com/go-logr/logr/testr" - "github.com/stretchr/testify/require" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/cache" - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul-k8s/control-plane/helper/test" - "github.com/hashicorp/consul/api" -) - -func TestControllerDoesNotInfinitelyReconcile(t *testing.T) { - s := runtime.NewScheme() - require.NoError(t, clientgoscheme.AddToScheme(s)) - require.NoError(t, gwv1alpha2.Install(s)) - require.NoError(t, gwv1beta1.Install(s)) - require.NoError(t, v1alpha1.AddToScheme(s)) - - testCases := map[string]struct { - namespace string - certFn func(*testing.T, context.Context, client.WithWatch, string) *corev1.Secret - gwFn func(*testing.T, context.Context, client.WithWatch, string) *gwv1beta1.Gateway - httpRouteFn func(*testing.T, context.Context, client.WithWatch, *gwv1beta1.Gateway) *gwv1beta1.HTTPRoute - tcpRouteFn func(*testing.T, context.Context, client.WithWatch, *gwv1beta1.Gateway) *v1alpha2.TCPRoute - }{ - "all fields set": { - namespace: "consul", - certFn: createCert, - gwFn: createAllFieldsSetAPIGW, - httpRouteFn: createAllFieldsSetHTTPRoute, - tcpRouteFn: createAllFieldsSetTCPRoute, - }, - "minimal fields set": { - namespace: "", - certFn: createCert, - gwFn: minimalFieldsSetAPIGW, - httpRouteFn: minimalFieldsSetHTTPRoute, - tcpRouteFn: minimalFieldsSetTCPRoute, - }, - "funky casing to test normalization doesnt cause infinite reconciliation": { - namespace: "", - certFn: createCert, - gwFn: createFunkyCasingFieldsAPIGW, - httpRouteFn: createFunkyCasingFieldsHTTPRoute, - tcpRouteFn: createFunkyCasingFieldsTCPRoute, - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - k8sClient := registerFieldIndexersForTest(fake.NewClientBuilder().WithScheme(s)).Build() - consulTestServerClient := test.TestServerWithMockConnMgrWatcher(t, nil) - ctx, cancel := context.WithCancel(context.Background()) - - t.Cleanup(func() { - cancel() - }) - logger := logrtest.New(t) - - cacheCfg := cache.Config{ - ConsulClientConfig: consulTestServerClient.Cfg, - ConsulServerConnMgr: consulTestServerClient.Watcher, - Logger: logger, - } - resourceCache := cache.New(cacheCfg) - - gwCache := cache.NewGatewayCache(ctx, cacheCfg) - - gwCtrl := GatewayController{ - HelmConfig: common.HelmConfig{}, - Log: logger, - Translator: common.ResourceTranslator{}, - cache: resourceCache, - gatewayCache: gwCache, - Client: k8sClient, - allowK8sNamespacesSet: mapset.NewSet(), - denyK8sNamespacesSet: mapset.NewSet(), - } - - go func() { - resourceCache.Run(ctx) - }() - - resourceCache.WaitSynced(ctx) - - gwSub := resourceCache.Subscribe(ctx, api.APIGateway, gwCtrl.transformConsulGateway) - httpRouteSub := resourceCache.Subscribe(ctx, api.HTTPRoute, gwCtrl.transformConsulHTTPRoute(ctx)) - tcpRouteSub := resourceCache.Subscribe(ctx, api.TCPRoute, gwCtrl.transformConsulTCPRoute(ctx)) - inlineCertSub := resourceCache.Subscribe(ctx, api.InlineCertificate, gwCtrl.transformConsulInlineCertificate(ctx)) - - cert := tc.certFn(t, ctx, k8sClient, tc.namespace) - k8sGWObj := tc.gwFn(t, ctx, k8sClient, tc.namespace) - - // reconcile so we add the finalizer - _, err := gwCtrl.Reconcile(ctx, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Namespace: k8sGWObj.Namespace, - Name: k8sGWObj.Name, - }, - }) - require.NoError(t, err) - - // reconcile again so that we get the creation with the finalizer - _, err = gwCtrl.Reconcile(ctx, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Namespace: k8sGWObj.Namespace, - Name: k8sGWObj.Name, - }, - }) - require.NoError(t, err) - - httpRouteObj := tc.httpRouteFn(t, ctx, k8sClient, k8sGWObj) - tcpRouteObj := tc.tcpRouteFn(t, ctx, k8sClient, k8sGWObj) - - // reconcile again so that we get the route bound to the gateway - _, err = gwCtrl.Reconcile(ctx, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Namespace: k8sGWObj.Namespace, - Name: k8sGWObj.Name, - }, - }) - require.NoError(t, err) - - // reconcile again so that we get the route bound to the gateway - _, err = gwCtrl.Reconcile(ctx, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Namespace: k8sGWObj.Namespace, - Name: k8sGWObj.Name, - }, - }) - require.NoError(t, err) - - wg := &sync.WaitGroup{} - // we never get the event from the cert because when it's created there are no gateways that reference it - wg.Add(3) - go func(w *sync.WaitGroup) { - gwDone := false - httpRouteDone := false - tcpRouteDone := false - for { - // get the creation events from the upsert and then continually read from channel so we dont block other subs - select { - case <-ctx.Done(): - return - case <-gwSub.Events(): - if !gwDone { - gwDone = true - wg.Done() - } - case <-httpRouteSub.Events(): - if !httpRouteDone { - httpRouteDone = true - wg.Done() - } - case <-tcpRouteSub.Events(): - if !tcpRouteDone { - tcpRouteDone = true - wg.Done() - } - case <-inlineCertSub.Events(): - } - } - }(wg) - - wg.Wait() - - gwNamespaceName := types.NamespacedName{ - Name: k8sGWObj.Name, - Namespace: k8sGWObj.Namespace, - } - - httpRouteNamespaceName := types.NamespacedName{ - Name: httpRouteObj.Name, - Namespace: httpRouteObj.Namespace, - } - - tcpRouteNamespaceName := types.NamespacedName{ - Name: tcpRouteObj.Name, - Namespace: tcpRouteObj.Namespace, - } - - certNamespaceName := types.NamespacedName{ - Name: cert.Name, - Namespace: cert.Namespace, - } - - gwRef := gwCtrl.Translator.ConfigEntryReference(api.APIGateway, gwNamespaceName) - httpRouteRef := gwCtrl.Translator.ConfigEntryReference(api.HTTPRoute, httpRouteNamespaceName) - tcpRouteRef := gwCtrl.Translator.ConfigEntryReference(api.TCPRoute, tcpRouteNamespaceName) - certRef := gwCtrl.Translator.ConfigEntryReference(api.InlineCertificate, certNamespaceName) - - curGWModifyIndex := resourceCache.Get(gwRef).GetModifyIndex() - curHTTPRouteModifyIndex := resourceCache.Get(httpRouteRef).GetModifyIndex() - curTCPRouteModifyIndex := resourceCache.Get(tcpRouteRef).GetModifyIndex() - curCertModifyIndex := resourceCache.Get(certRef).GetModifyIndex() - - err = k8sClient.Get(ctx, gwNamespaceName, k8sGWObj) - require.NoError(t, err) - curGWResourceVersion := k8sGWObj.ResourceVersion - - err = k8sClient.Get(ctx, httpRouteNamespaceName, httpRouteObj) - require.NoError(t, err) - curHTTPRouteResourceVersion := httpRouteObj.ResourceVersion - - err = k8sClient.Get(ctx, tcpRouteNamespaceName, tcpRouteObj) - require.NoError(t, err) - curTCPRouteResourceVersion := tcpRouteObj.ResourceVersion - - err = k8sClient.Get(ctx, certNamespaceName, cert) - require.NoError(t, err) - curCertResourceVersion := cert.ResourceVersion - - go func() { - // reconcile multiple times with no changes to be sure - for i := 0; i < 5; i++ { - _, err = gwCtrl.Reconcile(ctx, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Namespace: k8sGWObj.Namespace, - }, - }) - require.NoError(t, err) - } - }() - - require.Never(t, func() bool { - err = k8sClient.Get(ctx, gwNamespaceName, k8sGWObj) - require.NoError(t, err) - newGWResourceVersion := k8sGWObj.ResourceVersion - - err = k8sClient.Get(ctx, httpRouteNamespaceName, httpRouteObj) - require.NoError(t, err) - newHTTPRouteResourceVersion := httpRouteObj.ResourceVersion - - err = k8sClient.Get(ctx, tcpRouteNamespaceName, tcpRouteObj) - require.NoError(t, err) - newTCPRouteResourceVersion := tcpRouteObj.ResourceVersion - - err = k8sClient.Get(ctx, certNamespaceName, cert) - require.NoError(t, err) - newCertResourceVersion := cert.ResourceVersion - - return curGWModifyIndex == resourceCache.Get(gwRef).GetModifyIndex() && - curGWResourceVersion == newGWResourceVersion && - curHTTPRouteModifyIndex == resourceCache.Get(httpRouteRef).GetModifyIndex() && - curHTTPRouteResourceVersion == newHTTPRouteResourceVersion && - curTCPRouteModifyIndex == resourceCache.Get(tcpRouteRef).GetModifyIndex() && - curTCPRouteResourceVersion == newTCPRouteResourceVersion && - curCertModifyIndex == resourceCache.Get(certRef).GetModifyIndex() && - curCertResourceVersion == newCertResourceVersion - }, time.Duration(2*time.Second), time.Duration(500*time.Millisecond), fmt.Sprintf("curGWModifyIndex: %d, newIndx: %d", curGWModifyIndex, resourceCache.Get(gwRef).GetModifyIndex()), - ) - }) - } -} - -func createAllFieldsSetAPIGW(t *testing.T, ctx context.Context, k8sClient client.WithWatch, namespace string) *gwv1beta1.Gateway { - // listener one configuration - listenerOneName := "listener-one" - listenerOneHostname := "*.consul.io" - listenerOnePort := 3366 - listenerOneProtocol := "https" - - // listener two configuration - listenerTwoName := "listener-two" - listenerTwoHostname := "*.consul.io" - listenerTwoPort := 5432 - listenerTwoProtocol := "http" - - // listener three configuration - listenerThreeName := "listener-three" - listenerThreePort := 8081 - listenerThreeProtocol := "tcp" - - // listener four configuration - listenerFourName := "listener-four" - listenerFourHostname := "*.consul.io" - listenerFourPort := 5433 - listenerFourProtocol := "http" - - // Write gw to k8s - gwClassCfg := &v1alpha1.GatewayClassConfig{ - TypeMeta: metav1.TypeMeta{ - Kind: "GatewayClassConfig", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway-class-config", - }, - Spec: v1alpha1.GatewayClassConfigSpec{}, - } - gwClass := &gwv1beta1.GatewayClass{ - TypeMeta: metav1.TypeMeta{ - Kind: "GatewayClass", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "gatewayclass", - }, - Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: "hashicorp.com/consul-api-gateway-controller", - ParametersRef: &gwv1beta1.ParametersReference{ - Group: "consul.hashicorp.com", - Kind: "GatewayClassConfig", - Name: "gateway-class-config", - }, - Description: new(string), - }, - } - gw := &gwv1beta1.Gateway{ - TypeMeta: metav1.TypeMeta{ - Kind: "Gateway", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "gw", - Namespace: namespace, - Annotations: make(map[string]string), - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gwv1beta1.ObjectName(gwClass.Name), - Listeners: []gwv1beta1.Listener{ - { - Name: gwv1beta1.SectionName(listenerOneName), - Hostname: common.PointerTo(gwv1beta1.Hostname(listenerOneHostname)), - Port: gwv1beta1.PortNumber(listenerOnePort), - Protocol: gwv1beta1.ProtocolType(listenerOneProtocol), - TLS: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{ - { - Kind: common.PointerTo(gwv1beta1.Kind("Secret")), - Name: gwv1beta1.ObjectName("one-cert"), - Namespace: common.PointerTo(gwv1beta1.Namespace(namespace)), - }, - }, - }, - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.FromNamespaces("All")), - }, - }, - }, - { - Name: gwv1beta1.SectionName(listenerTwoName), - Hostname: common.PointerTo(gwv1beta1.Hostname(listenerTwoHostname)), - Port: gwv1beta1.PortNumber(listenerTwoPort), - Protocol: gwv1beta1.ProtocolType(listenerTwoProtocol), - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.FromNamespaces("Same")), - }, - }, - }, - { - Name: gwv1beta1.SectionName(listenerThreeName), - Port: gwv1beta1.PortNumber(listenerThreePort), - Protocol: gwv1beta1.ProtocolType(listenerThreeProtocol), - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.FromNamespaces("All")), - }, - }, - }, - { - Name: gwv1beta1.SectionName(listenerFourName), - Hostname: common.PointerTo(gwv1beta1.Hostname(listenerFourHostname)), - Port: gwv1beta1.PortNumber(listenerFourPort), - Protocol: gwv1beta1.ProtocolType(listenerFourProtocol), - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.FromNamespaces("Selector")), - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - common.NamespaceNameLabel: "consul", - }, - MatchExpressions: []metav1.LabelSelectorRequirement{}, - }, - }, - }, - }, - }, - }, - } - - err := k8sClient.Create(ctx, gwClassCfg) - require.NoError(t, err) - - err = k8sClient.Create(ctx, gwClass) - require.NoError(t, err) - - err = k8sClient.Create(ctx, gw) - require.NoError(t, err) - - return gw -} - -func createAllFieldsSetHTTPRoute(t *testing.T, ctx context.Context, k8sClient client.WithWatch, gw *gwv1beta1.Gateway) *gwv1beta1.HTTPRoute { - svcDefault := &v1alpha1.ServiceDefaults{ - TypeMeta: metav1.TypeMeta{ - Kind: "ServiceDefaults", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "Service", - }, - Spec: v1alpha1.ServiceDefaultsSpec{ - Protocol: "http", - }, - } - - svc := &corev1.Service{ - TypeMeta: metav1.TypeMeta{ - Kind: "Service", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "Service", - Labels: map[string]string{"app": "Service"}, - }, - Spec: corev1.ServiceSpec{ - Ports: []corev1.ServicePort{ - { - Name: "high", - Protocol: "TCP", - Port: 8080, - }, - }, - Selector: map[string]string{"app": "Service"}, - }, - } - - serviceAccount := &corev1.ServiceAccount{ - TypeMeta: metav1.TypeMeta{ - Kind: "ServiceAccount", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "Service", - }, - } - - deployment := &appsv1.Deployment{ - TypeMeta: metav1.TypeMeta{ - Kind: "Deployment", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "Service", - Labels: map[string]string{"app": "Service"}, - }, - Spec: appsv1.DeploymentSpec{ - Replicas: common.PointerTo(int32(1)), - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"app": "Service"}, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{}, - Spec: corev1.PodSpec{}, - }, - }, - } - - err := k8sClient.Create(ctx, svcDefault) - require.NoError(t, err) - - err = k8sClient.Create(ctx, svc) - require.NoError(t, err) - - err = k8sClient.Create(ctx, serviceAccount) - require.NoError(t, err) - - err = k8sClient.Create(ctx, deployment) - require.NoError(t, err) - - route := &gwv1beta1.HTTPRoute{ - TypeMeta: metav1.TypeMeta{ - Kind: "HTTPRoute", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "http-route", - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - { - Kind: (*gwv1beta1.Kind)(&gw.Kind), - Namespace: (*gwv1beta1.Namespace)(&gw.Namespace), - Name: gwv1beta1.ObjectName(gw.Name), - SectionName: &gw.Spec.Listeners[0].Name, - Port: &gw.Spec.Listeners[0].Port, - }, - }, - }, - Hostnames: []gwv1beta1.Hostname{"route.consul.io"}, - Rules: []gwv1beta1.HTTPRouteRule{ - { - Matches: []gwv1beta1.HTTPRouteMatch{ - { - Path: &gwv1beta1.HTTPPathMatch{ - Type: common.PointerTo(gwv1beta1.PathMatchType("PathPrefix")), - Value: common.PointerTo("/v1"), - }, - Headers: []gwv1beta1.HTTPHeaderMatch{ - { - Type: common.PointerTo(gwv1beta1.HeaderMatchExact), - Name: "version", - Value: "version", - }, - }, - QueryParams: []gwv1beta1.HTTPQueryParamMatch{ - { - Type: common.PointerTo(gwv1beta1.QueryParamMatchExact), - Name: "search", - Value: "q", - }, - }, - Method: common.PointerTo(gwv1beta1.HTTPMethod("GET")), - }, - }, - Filters: []gwv1beta1.HTTPRouteFilter{ - { - Type: gwv1beta1.HTTPRouteFilterRequestHeaderModifier, - RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ - Set: []gwv1beta1.HTTPHeader{ - { - Name: "foo", - Value: "bax", - }, - }, - Add: []gwv1beta1.HTTPHeader{ - { - Name: "arc", - Value: "reactor", - }, - }, - Remove: []string{"remove"}, - }, - }, - { - Type: gwv1beta1.HTTPRouteFilterURLRewrite, - URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ - Hostname: common.PointerTo(gwv1beta1.PreciseHostname("host.com")), - Path: &gwv1beta1.HTTPPathModifier{ - Type: gwv1beta1.FullPathHTTPPathModifier, - ReplaceFullPath: common.PointerTo("/foobar"), - }, - }, - }, - - { - Type: gwv1beta1.HTTPRouteFilterURLRewrite, - URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ - Hostname: common.PointerTo(gwv1beta1.PreciseHostname("host.com")), - Path: &gwv1beta1.HTTPPathModifier{ - Type: gwv1beta1.PrefixMatchHTTPPathModifier, - ReplacePrefixMatch: common.PointerTo("/foo"), - }, - }, - }, - }, - BackendRefs: []gwv1beta1.HTTPBackendRef{ - { - BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "Service", - Port: common.PointerTo(gwv1beta1.PortNumber(8080)), - }, - Weight: common.PointerTo(int32(50)), - }, - }, - }, - }, - }, - }, - } - - err = k8sClient.Create(ctx, route) - require.NoError(t, err) - - return route -} - -func createAllFieldsSetTCPRoute(t *testing.T, ctx context.Context, k8sClient client.WithWatch, gw *gwv1beta1.Gateway) *v1alpha2.TCPRoute { - route := &v1alpha2.TCPRoute{ - TypeMeta: metav1.TypeMeta{ - Kind: "TCPRoute", - APIVersion: "gateway.networking.k8s.io/v1alpha2", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "tcp-route", - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - { - Kind: (*gwv1beta1.Kind)(&gw.Kind), - Namespace: (*gwv1beta1.Namespace)(&gw.Namespace), - Name: gwv1beta1.ObjectName(gw.Name), - SectionName: &gw.Spec.Listeners[2].Name, - Port: &gw.Spec.Listeners[2].Port, - }, - }, - }, - Rules: []gwv1alpha2.TCPRouteRule{ - { - BackendRefs: []gwv1beta1.BackendRef{ - { - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "Service", - Port: common.PointerTo(gwv1beta1.PortNumber(25000)), - }, - Weight: common.PointerTo(int32(50)), - }, - }, - }, - }, - }, - } - - err := k8sClient.Create(ctx, route) - require.NoError(t, err) - - return route -} - -func createCert(t *testing.T, ctx context.Context, k8sClient client.WithWatch, certNS string) *corev1.Secret { - // listener one tls config - certName := "one-cert" - - privateKey, err := rsa.GenerateKey(rand.Reader, 1024) - require.NoError(t, err) - - usage := x509.KeyUsageCertSign - expiration := time.Now().AddDate(10, 0, 0) - - cert := &x509.Certificate{ - SerialNumber: big.NewInt(1), - Subject: pkix.Name{ - CommonName: "consul.test", - }, - IsCA: true, - NotBefore: time.Now().Add(-10 * time.Minute), - NotAfter: expiration, - SubjectKeyId: []byte{1, 2, 3, 4, 6}, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, - KeyUsage: usage, - BasicConstraintsValid: true, - } - caCert := cert - caPrivateKey := privateKey - - data, err := x509.CreateCertificate(rand.Reader, cert, caCert, &privateKey.PublicKey, caPrivateKey) - require.NoError(t, err) - - certBytes := pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: data, - }) - - privateKeyBytes := pem.EncodeToMemory(&pem.Block{ - Type: "RSA PRIVATE KEY", - Bytes: x509.MarshalPKCS1PrivateKey(privateKey), - }) - - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: certNS, - Name: certName, - }, - Data: map[string][]byte{ - corev1.TLSCertKey: certBytes, - corev1.TLSPrivateKeyKey: privateKeyBytes, - }, - } - - err = k8sClient.Create(ctx, secret) - require.NoError(t, err) - - return secret -} - -func minimalFieldsSetAPIGW(t *testing.T, ctx context.Context, k8sClient client.WithWatch, namespace string) *gwv1beta1.Gateway { - // listener one configuration - listenerOneName := "listener-one" - listenerOneHostname := "*.consul.io" - listenerOnePort := 3366 - listenerOneProtocol := "https" - - // listener three configuration - listenerThreeName := "listener-three" - listenerThreePort := 8081 - listenerThreeProtocol := "tcp" - - // Write gw to k8s - gwClassCfg := &v1alpha1.GatewayClassConfig{ - TypeMeta: metav1.TypeMeta{ - Kind: "GatewayClassConfig", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway-class-config", - }, - Spec: v1alpha1.GatewayClassConfigSpec{}, - } - gwClass := &gwv1beta1.GatewayClass{ - TypeMeta: metav1.TypeMeta{ - Kind: "GatewayClass", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "gatewayclass", - }, - Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: "hashicorp.com/consul-api-gateway-controller", - ParametersRef: &gwv1beta1.ParametersReference{ - Group: "consul.hashicorp.com", - Kind: "GatewayClassConfig", - Name: "gateway-class-config", - }, - Description: new(string), - }, - } - gw := &gwv1beta1.Gateway{ - TypeMeta: metav1.TypeMeta{ - Kind: "Gateway", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "gw", - Annotations: make(map[string]string), - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gwv1beta1.ObjectName(gwClass.Name), - Listeners: []gwv1beta1.Listener{ - { - Name: gwv1beta1.SectionName(listenerOneName), - Hostname: common.PointerTo(gwv1beta1.Hostname(listenerOneHostname)), - Port: gwv1beta1.PortNumber(listenerOnePort), - Protocol: gwv1beta1.ProtocolType(listenerOneProtocol), - TLS: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{ - { - Kind: common.PointerTo(gwv1beta1.Kind("Secret")), - Name: gwv1beta1.ObjectName("one-cert"), - Namespace: common.PointerTo(gwv1beta1.Namespace(namespace)), - }, - }, - }, - }, - { - Name: gwv1beta1.SectionName(listenerThreeName), - Port: gwv1beta1.PortNumber(listenerThreePort), - Protocol: gwv1beta1.ProtocolType(listenerThreeProtocol), - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.FromNamespaces("All")), - }, - }, - }, - }, - }, - } - - err := k8sClient.Create(ctx, gwClassCfg) - require.NoError(t, err) - - err = k8sClient.Create(ctx, gwClass) - require.NoError(t, err) - - err = k8sClient.Create(ctx, gw) - require.NoError(t, err) - - return gw -} - -func minimalFieldsSetHTTPRoute(t *testing.T, ctx context.Context, k8sClient client.WithWatch, gw *gwv1beta1.Gateway) *gwv1beta1.HTTPRoute { - svcDefault := &v1alpha1.ServiceDefaults{ - TypeMeta: metav1.TypeMeta{ - Kind: "ServiceDefaults", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "Service", - }, - Spec: v1alpha1.ServiceDefaultsSpec{ - Protocol: "http", - }, - } - - svc := &corev1.Service{ - TypeMeta: metav1.TypeMeta{ - Kind: "Service", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "Service", - Labels: map[string]string{"app": "Service"}, - }, - Spec: corev1.ServiceSpec{ - Ports: []corev1.ServicePort{ - { - Name: "high", - Protocol: "TCP", - Port: 8080, - }, - }, - Selector: map[string]string{"app": "Service"}, - }, - } - - serviceAccount := &corev1.ServiceAccount{ - TypeMeta: metav1.TypeMeta{ - Kind: "ServiceAccount", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "Service", - }, - } - - deployment := &appsv1.Deployment{ - TypeMeta: metav1.TypeMeta{ - Kind: "Deployment", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "Service", - Labels: map[string]string{"app": "Service"}, - }, - Spec: appsv1.DeploymentSpec{ - Replicas: common.PointerTo(int32(1)), - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"app": "Service"}, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{}, - Spec: corev1.PodSpec{}, - }, - }, - } - - err := k8sClient.Create(ctx, svcDefault) - require.NoError(t, err) - - err = k8sClient.Create(ctx, svc) - require.NoError(t, err) - - err = k8sClient.Create(ctx, serviceAccount) - require.NoError(t, err) - - err = k8sClient.Create(ctx, deployment) - require.NoError(t, err) - - route := &gwv1beta1.HTTPRoute{ - TypeMeta: metav1.TypeMeta{ - Kind: "HTTPRoute", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "http-route", - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - { - Kind: (*gwv1beta1.Kind)(&gw.Kind), - Namespace: (*gwv1beta1.Namespace)(&gw.Namespace), - Name: gwv1beta1.ObjectName(gw.Name), - SectionName: &gw.Spec.Listeners[0].Name, - Port: &gw.Spec.Listeners[0].Port, - }, - }, - }, - Hostnames: []gwv1beta1.Hostname{"route.consul.io"}, - Rules: []gwv1beta1.HTTPRouteRule{ - { - BackendRefs: []gwv1beta1.HTTPBackendRef{ - { - BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "Service", - Port: common.PointerTo(gwv1beta1.PortNumber(8080)), - }, - }, - }, - }, - }, - }, - }, - } - - err = k8sClient.Create(ctx, route) - require.NoError(t, err) - - return route -} - -func minimalFieldsSetTCPRoute(t *testing.T, ctx context.Context, k8sClient client.WithWatch, gw *gwv1beta1.Gateway) *v1alpha2.TCPRoute { - route := &v1alpha2.TCPRoute{ - TypeMeta: metav1.TypeMeta{ - Kind: "TCPRoute", - APIVersion: "gateway.networking.k8s.io/v1alpha2", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "tcp-route", - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - { - Kind: (*gwv1beta1.Kind)(&gw.Kind), - Namespace: (*gwv1beta1.Namespace)(&gw.Namespace), - Name: gwv1beta1.ObjectName(gw.Name), - SectionName: &gw.Spec.Listeners[1].Name, - Port: &gw.Spec.Listeners[1].Port, - }, - }, - }, - Rules: []gwv1alpha2.TCPRouteRule{ - { - BackendRefs: []gwv1beta1.BackendRef{ - { - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "Service", - Port: common.PointerTo(gwv1beta1.PortNumber(25000)), - }, - }, - }, - }, - }, - }, - } - - err := k8sClient.Create(ctx, route) - require.NoError(t, err) - - return route -} - -func createFunkyCasingFieldsAPIGW(t *testing.T, ctx context.Context, k8sClient client.WithWatch, namespace string) *gwv1beta1.Gateway { - // listener one configuration - listenerOneName := "listener-one" - listenerOneHostname := "*.consul.io" - listenerOnePort := 3366 - listenerOneProtocol := "hTtPs" - - // listener two configuration - listenerTwoName := "listener-two" - listenerTwoHostname := "*.consul.io" - listenerTwoPort := 5432 - listenerTwoProtocol := "HTTP" - - // listener three configuration - listenerThreeName := "listener-three" - listenerThreePort := 8081 - listenerThreeProtocol := "tCp" - - // listener four configuration - listenerFourName := "listener-four" - listenerFourHostname := "*.consul.io" - listenerFourPort := 5433 - listenerFourProtocol := "hTTp" - - // Write gw to k8s - gwClassCfg := &v1alpha1.GatewayClassConfig{ - TypeMeta: metav1.TypeMeta{ - Kind: "GatewayClassConfig", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway-class-config", - }, - Spec: v1alpha1.GatewayClassConfigSpec{}, - } - gwClass := &gwv1beta1.GatewayClass{ - TypeMeta: metav1.TypeMeta{ - Kind: "GatewayClass", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "gatewayclass", - }, - Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: "hashicorp.com/consul-api-gateway-controller", - ParametersRef: &gwv1beta1.ParametersReference{ - Group: "consul.hashicorp.com", - Kind: "GatewayClassConfig", - Name: "gateway-class-config", - }, - Description: new(string), - }, - } - gw := &gwv1beta1.Gateway{ - TypeMeta: metav1.TypeMeta{ - Kind: "Gateway", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "gw", - Namespace: namespace, - Annotations: make(map[string]string), - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gwv1beta1.ObjectName(gwClass.Name), - Listeners: []gwv1beta1.Listener{ - { - Name: gwv1beta1.SectionName(listenerOneName), - Hostname: common.PointerTo(gwv1beta1.Hostname(listenerOneHostname)), - Port: gwv1beta1.PortNumber(listenerOnePort), - Protocol: gwv1beta1.ProtocolType(listenerOneProtocol), - TLS: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{ - { - Kind: common.PointerTo(gwv1beta1.Kind("Secret")), - Name: gwv1beta1.ObjectName("one-cert"), - Namespace: common.PointerTo(gwv1beta1.Namespace(namespace)), - }, - }, - }, - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.FromNamespaces("All")), - }, - }, - }, - { - Name: gwv1beta1.SectionName(listenerTwoName), - Hostname: common.PointerTo(gwv1beta1.Hostname(listenerTwoHostname)), - Port: gwv1beta1.PortNumber(listenerTwoPort), - Protocol: gwv1beta1.ProtocolType(listenerTwoProtocol), - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.FromNamespaces("Same")), - }, - }, - }, - { - Name: gwv1beta1.SectionName(listenerThreeName), - Port: gwv1beta1.PortNumber(listenerThreePort), - Protocol: gwv1beta1.ProtocolType(listenerThreeProtocol), - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.FromNamespaces("All")), - }, - }, - }, - { - Name: gwv1beta1.SectionName(listenerFourName), - Hostname: common.PointerTo(gwv1beta1.Hostname(listenerFourHostname)), - Port: gwv1beta1.PortNumber(listenerFourPort), - Protocol: gwv1beta1.ProtocolType(listenerFourProtocol), - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.FromNamespaces("Selector")), - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - common.NamespaceNameLabel: "consul", - }, - MatchExpressions: []metav1.LabelSelectorRequirement{}, - }, - }, - }, - }, - }, - }, - } - - err := k8sClient.Create(ctx, gwClassCfg) - require.NoError(t, err) - - err = k8sClient.Create(ctx, gwClass) - require.NoError(t, err) - - err = k8sClient.Create(ctx, gw) - require.NoError(t, err) - - return gw -} - -func createFunkyCasingFieldsHTTPRoute(t *testing.T, ctx context.Context, k8sClient client.WithWatch, gw *gwv1beta1.Gateway) *gwv1beta1.HTTPRoute { - svcDefault := &v1alpha1.ServiceDefaults{ - TypeMeta: metav1.TypeMeta{ - Kind: "ServiceDefaults", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "Service", - }, - Spec: v1alpha1.ServiceDefaultsSpec{ - Protocol: "hTtp", - }, - } - - svc := &corev1.Service{ - TypeMeta: metav1.TypeMeta{ - Kind: "Service", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "Service", - Labels: map[string]string{"app": "Service"}, - }, - Spec: corev1.ServiceSpec{ - Ports: []corev1.ServicePort{ - { - Name: "high", - Protocol: "TCP", - Port: 8080, - }, - }, - Selector: map[string]string{"app": "Service"}, - }, - } - - serviceAccount := &corev1.ServiceAccount{ - TypeMeta: metav1.TypeMeta{ - Kind: "ServiceAccount", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "Service", - }, - } - - deployment := &appsv1.Deployment{ - TypeMeta: metav1.TypeMeta{ - Kind: "Deployment", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "Service", - Labels: map[string]string{"app": "Service"}, - }, - Spec: appsv1.DeploymentSpec{ - Replicas: common.PointerTo(int32(1)), - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"app": "Service"}, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{}, - Spec: corev1.PodSpec{}, - }, - }, - } - - err := k8sClient.Create(ctx, svcDefault) - require.NoError(t, err) - - err = k8sClient.Create(ctx, svc) - require.NoError(t, err) - - err = k8sClient.Create(ctx, serviceAccount) - require.NoError(t, err) - - err = k8sClient.Create(ctx, deployment) - require.NoError(t, err) - - route := &gwv1beta1.HTTPRoute{ - TypeMeta: metav1.TypeMeta{ - Kind: "HTTPRoute", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "http-route", - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - { - Namespace: (*gwv1beta1.Namespace)(&gw.Namespace), - Name: gwv1beta1.ObjectName(gw.Name), - SectionName: &gw.Spec.Listeners[0].Name, - Port: &gw.Spec.Listeners[0].Port, - }, - }, - }, - Hostnames: []gwv1beta1.Hostname{"route.consul.io"}, - Rules: []gwv1beta1.HTTPRouteRule{ - { - Matches: []gwv1beta1.HTTPRouteMatch{ - { - Path: &gwv1beta1.HTTPPathMatch{ - Type: common.PointerTo(gwv1beta1.PathMatchPathPrefix), - }, - Headers: []gwv1beta1.HTTPHeaderMatch{ - { - Type: common.PointerTo(gwv1beta1.HeaderMatchExact), - Name: "version", - Value: "version", - }, - }, - QueryParams: []gwv1beta1.HTTPQueryParamMatch{ - { - Type: common.PointerTo(gwv1beta1.QueryParamMatchExact), - Name: "search", - Value: "q", - }, - }, - Method: common.PointerTo(gwv1beta1.HTTPMethod("geT")), - }, - }, - Filters: []gwv1beta1.HTTPRouteFilter{ - { - Type: gwv1beta1.HTTPRouteFilterRequestHeaderModifier, - RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ - Set: []gwv1beta1.HTTPHeader{ - { - Name: "foo", - Value: "bax", - }, - }, - Add: []gwv1beta1.HTTPHeader{ - { - Name: "arc", - Value: "reactor", - }, - }, - Remove: []string{"remove"}, - }, - }, - { - Type: gwv1beta1.HTTPRouteFilterURLRewrite, - URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ - Hostname: common.PointerTo(gwv1beta1.PreciseHostname("host.com")), - Path: &gwv1beta1.HTTPPathModifier{ - Type: gwv1beta1.FullPathHTTPPathModifier, - ReplaceFullPath: common.PointerTo("/foobar"), - }, - }, - }, - - { - Type: gwv1beta1.HTTPRouteFilterURLRewrite, - URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ - Hostname: common.PointerTo(gwv1beta1.PreciseHostname("host.com")), - Path: &gwv1beta1.HTTPPathModifier{ - Type: gwv1beta1.PrefixMatchHTTPPathModifier, - ReplacePrefixMatch: common.PointerTo("/foo"), - }, - }, - }, - }, - BackendRefs: []gwv1beta1.HTTPBackendRef{ - { - BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "Service", - Port: common.PointerTo(gwv1beta1.PortNumber(8080)), - }, - Weight: common.PointerTo(int32(-50)), - }, - }, - }, - }, - }, - }, - } - - err = k8sClient.Create(ctx, route) - require.NoError(t, err) - - return route -} - -func createFunkyCasingFieldsTCPRoute(t *testing.T, ctx context.Context, k8sClient client.WithWatch, gw *gwv1beta1.Gateway) *v1alpha2.TCPRoute { - route := &v1alpha2.TCPRoute{ - TypeMeta: metav1.TypeMeta{ - Kind: "TCPRoute", - APIVersion: "gateway.networking.k8s.io/v1alpha2", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "tcp-route", - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - { - Namespace: (*gwv1beta1.Namespace)(&gw.Namespace), - Name: gwv1beta1.ObjectName(gw.Name), - SectionName: &gw.Spec.Listeners[2].Name, - Port: &gw.Spec.Listeners[2].Port, - }, - }, - }, - Rules: []gwv1alpha2.TCPRouteRule{ - { - BackendRefs: []gwv1beta1.BackendRef{ - { - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "Service", - Port: common.PointerTo(gwv1beta1.PortNumber(25000)), - }, - Weight: common.PointerTo(int32(-50)), - }, - }, - }, - }, - }, - } - - err := k8sClient.Create(ctx, route) - require.NoError(t, err) - - return route -} From 8f474854c4a58e5b11290fb79b8ae434a430962e Mon Sep 17 00:00:00 2001 From: Ganesh S Date: Mon, 12 Jun 2023 15:50:08 -0700 Subject: [PATCH 229/340] Improvement- [NET-189] Added helm inputs for managing audit logs (#2265) * Added helm inputs for managing audit logs * Remove unwanted changes from values --- .changelog/2265.txt | 3 + .../templates/server-config-configmap.yaml | 25 +++- .../test/unit/server-config-configmap.bats | 140 ++++++++++++++++++ charts/consul/values.yaml | 54 +++++++ 4 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 .changelog/2265.txt diff --git a/.changelog/2265.txt b/.changelog/2265.txt new file mode 100644 index 0000000000..1cf6813c94 --- /dev/null +++ b/.changelog/2265.txt @@ -0,0 +1,3 @@ +```release-note:improvement +(Consul Enterprise) Add support to provide inputs via helm for audit log related configuration +``` diff --git a/charts/consul/templates/server-config-configmap.yaml b/charts/consul/templates/server-config-configmap.yaml index 1ad04a42b5..d3a0206afd 100644 --- a/charts/consul/templates/server-config-configmap.yaml +++ b/charts/consul/templates/server-config-configmap.yaml @@ -1,6 +1,6 @@ {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} {{- if (not (or (eq .Values.server.limits.requestLimits.mode "disabled") (eq .Values.server.limits.requestLimits.mode "permissive") (eq .Values.server.limits.requestLimits.mode "enforce"))) }}{{fail "server.limits.requestLimits.mode must be one of the following values: disabled, permissive, and enforce." }}{{ end -}} - +{{- if and .Values.server.auditLogs.enabled (not .Values.global.acls.manageSystemACLs) }}{{fail "ACLs must be enabled inorder to configure audit logs"}}{{ end -}} # StatefulSet to run the actual Consul server cluster. apiVersion: v1 kind: ConfigMap @@ -187,4 +187,27 @@ data: } } {{- end }} + {{- if and .Values.server.auditLogs.enabled .Values.global.acls.manageSystemACLs }} + audit-logging.json: |- + { + "audit": { + "enabled": "true", + "sink": { + {{- range $index, $element := .Values.server.auditLogs.sinks }} + {{- if ne $index 0 }},{{end}} + "{{ $element.name }}": { + {{- $firstKeyValuePair := false }} + {{- range $k, $v := $element }} + {{- if ne $k "name" }} + {{- if ne $firstKeyValuePair false }},{{end}} + {{- $firstKeyValuePair = true }} + "{{ $k }}": "{{ $v }}" + {{- end }} + {{- end }} + } + {{- end }} + } + } + } + {{- end }} {{- end }} diff --git a/charts/consul/test/unit/server-config-configmap.bats b/charts/consul/test/unit/server-config-configmap.bats index 2c8a83f4ca..d55c10dd3a 100755 --- a/charts/consul/test/unit/server-config-configmap.bats +++ b/charts/consul/test/unit/server-config-configmap.bats @@ -1057,3 +1057,143 @@ load _helpers [ "${actual}" = "100" ] } + +#-------------------------------------------------------------------- +# server.auditLogs + +@test "server/ConfigMap: server.auditLogs is disabled by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'server.auditLogs.enabled=false' \ + . | tee /dev/stderr | + yq -r '.data["audit-logging.json"]' | jq -r .audit | tee /dev/stderr) + + [ "${actual}" = "null" ] +} + +@test "server/ConfigMap: server.auditLogs is enabled but ACLs are disabled" { + cd `chart_dir` + run helm template \ + -s templates/server-config-configmap.yaml \ + --set 'server.auditLogs.enabled=true' \ + --set 'server.auditLogs.sinks[0].name=MySink' \ + --set 'server.auditLogs.sinks[0].type=file' \ + --set 'server.auditLogs.sinks[0].format=json' \ + --set 'server.auditLogs.sinks[0].delivery_guarantee=best-effort' \ + --set 'server.auditLogs.sinks[0].rotate_duration=24h' \ + --set 'server.auditLogs.sinks[0].path=/tmp/audit.json' \ + . + + [ "$status" -eq 1 ] + [[ "$output" =~ "ACLs must be enabled inorder to configure audit logs" ]] +} + +@test "server/ConfigMap: server.auditLogs is enabled without sink inputs" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'server.auditLogs.enabled=true' \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq -r '.data["audit-logging.json"]' | jq -r .audit.sink | tee /dev/stderr) + + [ "${actual}" = "{}" ] +} + +@test "server/ConfigMap: server.auditLogs is enabled with 1 sink input object" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'server.auditLogs.enabled=true' \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'server.auditLogs.sinks[0].name=MySink' \ + --set 'server.auditLogs.sinks[0].type=file' \ + --set 'server.auditLogs.sinks[0].format=json' \ + --set 'server.auditLogs.sinks[0].delivery_guarantee=best-effort' \ + --set 'server.auditLogs.sinks[0].rotate_duration=24h' \ + --set 'server.auditLogs.sinks[0].path=/tmp/audit.json' \ + . | tee /dev/stderr | + yq -r '.data["audit-logging.json"]' | tee /dev/stderr) + + local actual=$(echo $object | jq -r .audit.sink.MySink.path | tee /dev/stderr) + [ "${actual}" = "/tmp/audit.json" ] + + local actual=$(echo $object | jq -r .audit.sink.MySink.delivery_guarantee | tee /dev/stderr) + [ "${actual}" = "best-effort" ] + + local actual=$(echo $object | jq -r .audit.sink.MySink.rotate_duration | tee /dev/stderr) + [ "${actual}" = "24h" ] +} + +@test "server/ConfigMap: server.auditLogs is enabled with 1 sink input object and it does not contain the name attribute" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'server.auditLogs.enabled=true' \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'server.auditLogs.sinks[0].name=MySink' \ + --set 'server.auditLogs.sinks[0].type=file' \ + --set 'server.auditLogs.sinks[0].format=json' \ + --set 'server.auditLogs.sinks[0].delivery_guarantee=best-effort' \ + --set 'server.auditLogs.sinks[0].rotate_duration=24h' \ + --set 'server.auditLogs.sinks[0].path=/tmp/audit.json' \ + . | tee /dev/stderr | + yq -r '.data["audit-logging.json"]' | jq -r .audit.sink.name | tee /dev/stderr) + + [ "${actual}" = "null" ] +} + +@test "server/ConfigMap: server.auditLogs is enabled with multiple sink input objects" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'server.auditLogs.enabled=true' \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'server.auditLogs.sinks[0].name=MySink1' \ + --set 'server.auditLogs.sinks[0].type=file' \ + --set 'server.auditLogs.sinks[0].format=json' \ + --set 'server.auditLogs.sinks[0].delivery_guarantee=best-effort' \ + --set 'server.auditLogs.sinks[0].rotate_duration=24h' \ + --set 'server.auditLogs.sinks[0].path=/tmp/audit.json' \ + --set 'server.auditLogs.sinks[1].name=MySink2' \ + --set 'server.auditLogs.sinks[1].type=file' \ + --set 'server.auditLogs.sinks[1].format=json' \ + --set 'server.auditLogs.sinks[1].delivery_guarantee=best-effort' \ + --set 'server.auditLogs.sinks[1].rotate_max_files=15' \ + --set 'server.auditLogs.sinks[1].rotate_duration=24h' \ + --set 'server.auditLogs.sinks[1].path=/tmp/audit-2.json' \ + --set 'server.auditLogs.sinks[2].name=MySink3' \ + --set 'server.auditLogs.sinks[2].type=file' \ + --set 'server.auditLogs.sinks[2].format=json' \ + --set 'server.auditLogs.sinks[2].delivery_guarantee=best-effort' \ + --set 'server.auditLogs.sinks[2].rotate_max_files=20' \ + --set 'server.auditLogs.sinks[2].rotate_duration=18h' \ + --set 'server.auditLogs.sinks[2].path=/tmp/audit-3.json' \ + . | tee /dev/stderr | + yq -r '.data["audit-logging.json"]' | tee /dev/stderr) + + local actual=$(echo $object | jq -r .audit.sink.MySink1.path | tee /dev/stderr) + [ "${actual}" = "/tmp/audit.json" ] + + local actual=$(echo $object | jq -r .audit.sink.MySink3.path | tee /dev/stderr) + [ "${actual}" = "/tmp/audit-3.json" ] + + local actual=$(echo $object | jq -r .audit.sink.MySink2.path | tee /dev/stderr) + [ "${actual}" = "/tmp/audit-2.json" ] + + local actual=$(echo $object | jq -r .audit.sink.MySink1.name | tee /dev/stderr) + [ "${actual}" = "null" ] + + local actual=$(echo $object | jq -r .audit.sink.MySink3.delivery_guarantee | tee /dev/stderr) + [ "${actual}" = "best-effort" ] + + local actual=$(echo $object | jq -r .audit.sink.MySink2.rotate_duration | tee /dev/stderr) + [ "${actual}" = "24h" ] + + local actual=$(echo $object | jq -r .audit.sink.MySink1.format | tee /dev/stderr) + [ "${actual}" = "json" ] + + local actual=$(echo $object | jq -r .audit.sink.MySink3.type | tee /dev/stderr) + [ "${actual}" = "file" ] +} \ No newline at end of file diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 0e325ca66c..6514fde75a 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -1144,6 +1144,60 @@ server: # @type: string caCert: null + # [Enterprise Only] Added in Consul 1.8, the audit object allow users to enable auditing + # and configure a sink and filters for their audit logs. Please refer to + # [audit logs](https://developer.hashicorp.com/consul/docs/enterprise/audit-logging) documentation + # for further information. + auditLogs: + # Controls whether Consul logs out each time a user performs an operation. + # global.acls.manageSystemACLs must be enabled to use this feature. + enabled: false + + # A single entry of the sink object provides configuration for the destination to which Consul + # will log auditing events. + # + # Example: + # + # ```yaml + # sinks: + # - name: My Sink + # type: file + # format: json + # path: /tmp/audit.json + # delivery_guarantee: best-effort + # rotate_duration: 24h + # rotate_max_files: 15 + # rotate_bytes: 25165824 + # + # ``` + # + # The sink object supports the following keys: + # + # - `name` - Name of the sink. + # + # - `type` - Type specifies what kind of sink this is. Currently only file sinks are available + # + # - `format` - Format specifies what format the events will be emitted with. Currently only `json` + # events are emitted. + # + # - `path` - The directory and filename to write audit events to. + # + # - `delivery_guarantee` - Specifies the rules governing how audit events are written. Consul + # only supports `best-effort` event delivery. + # + # - `mode` - The permissions to set on the audit log files. + # + # - `rotate_duration` - Specifies the interval by which the system rotates to a new log file. + # At least one of `rotate_duration` or `rotate_bytes` must be configured to enable audit logging. + # + # - `rotate_bytes` - Specifies how large an individual log file can grow before Consul rotates to a new file. + # At least one of rotate_bytes or rotate_duration must be configured to enable audit logging. + # + # - `rotate_max_files` - Defines the limit that Consul should follow before it deletes old log files. + # + # @type: array + sinks: [] + # Settings for potentially limiting timeouts, rate limiting on clients as well # as servers, and other settings to limit exposure too many requests, requests # waiting for too long, and other runtime considerations. From fc40d5e5d09087818c3ee860f6c47c80c957d789 Mon Sep 17 00:00:00 2001 From: Eric Haberkorn Date: Tue, 13 Jun 2023 11:28:28 -0400 Subject: [PATCH 230/340] Set Consul service instance localities from K8s node labels (#2346) --- .changelog/2346.txt | 3 ++ .../endpoints/endpoints_controller.go | 20 ++++++++ .../endpoints/endpoints_controller_test.go | 48 ++++++++++++++++++- 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 .changelog/2346.txt diff --git a/.changelog/2346.txt b/.changelog/2346.txt new file mode 100644 index 0000000000..fb062ee0fb --- /dev/null +++ b/.changelog/2346.txt @@ -0,0 +1,3 @@ +```release-note:feature +Set locality on services registered with connect-inject. +``` diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go index eeaeeab485..7b236792ab 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go @@ -318,6 +318,20 @@ func (r *Controller) registerServicesAndHealthCheck(apiClient *api.Client, pod c return nil } +func parseLocality(node corev1.Node) *api.Locality { + region := node.Labels[corev1.LabelTopologyRegion] + zone := node.Labels[corev1.LabelTopologyZone] + + if region == "" { + return nil + } + + return &api.Locality{ + Region: region, + Zone: zone, + } +} + // registerGateway creates Consul registrations for the Connect Gateways and registers them with Consul. // It also upserts a Kubernetes health check for the service based on whether the endpoint address is ready. func (r *Controller) registerGateway(apiClient *api.Client, pod corev1.Pod, serviceEndpoints corev1.Endpoints, healthStatus string, endpointAddressMap map[string]bool) error { @@ -405,6 +419,11 @@ func (r *Controller) createServiceRegistrations(pod corev1.Pod, serviceEndpoints } } + var node corev1.Node + // Ignore errors because we don't want failures to block running services. + _ = r.Client.Get(context.Background(), types.NamespacedName{Name: pod.Spec.NodeName, Namespace: pod.Namespace}, &node) + locality := parseLocality(node) + // We only want that annotation to be present when explicitly overriding the consul svc name // Otherwise, the Consul service name should equal the Kubernetes Service name. // The service name in Consul defaults to the Endpoints object name, and is overridden by the pod @@ -440,6 +459,7 @@ func (r *Controller) createServiceRegistrations(pod corev1.Pod, serviceEndpoints Meta: meta, Namespace: consulNS, Tags: tags, + Locality: locality, } serviceRegistration := &api.CatalogRegistration{ Node: common.ConsulNodeNameFromK8sNode(pod.Spec.NodeName), diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go index acf62b2b0e..ea1ce686d6 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go @@ -1938,6 +1938,17 @@ func TestReconcileCreateEndpoint(t *testing.T) { pod1.Annotations[constants.AnnotationUpstreams] = "upstream1:1234" pod1.Annotations[constants.AnnotationEnableMetrics] = "true" pod1.Annotations[constants.AnnotationPrometheusScrapePort] = "12345" + pod1.Spec.NodeName = "my-node" + node := &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-node", + Namespace: "default", + Labels: map[string]string{ + corev1.LabelTopologyRegion: "us-west-1", + corev1.LabelTopologyZone: "us-west-1a", + }, + }, + } endpoint := &corev1.Endpoints{ ObjectMeta: metav1.ObjectMeta{ Name: "service-created", @@ -1958,7 +1969,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { }, }, } - return []runtime.Object{pod1, endpoint} + return []runtime.Object{pod1, node, endpoint} }, expectedConsulSvcInstances: []*api.CatalogService{ { @@ -1978,6 +1989,10 @@ func TestReconcileCreateEndpoint(t *testing.T) { }, ServiceTags: []string{"abc,123", "pod1"}, ServiceProxy: &api.AgentServiceConnectProxyConfig{}, + ServiceLocality: &api.Locality{ + Region: "us-west-1", + Zone: "us-west-1a", + }, }, }, expectedProxySvcInstances: []*api.CatalogService{ @@ -2190,6 +2205,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { require.Equal(t, tt.expectedConsulSvcInstances[i].ServicePort, instance.ServicePort) require.Equal(t, tt.expectedConsulSvcInstances[i].ServiceMeta, instance.ServiceMeta) require.Equal(t, tt.expectedConsulSvcInstances[i].ServiceTags, instance.ServiceTags) + require.Equal(t, tt.expectedConsulSvcInstances[i].ServiceLocality, instance.ServiceLocality) require.Equal(t, tt.expectedConsulSvcInstances[i].ServiceTaggedAddresses, instance.ServiceTaggedAddresses) require.Equal(t, tt.expectedConsulSvcInstances[i].ServiceProxy, instance.ServiceProxy) if tt.nodeMeta != nil { @@ -2236,6 +2252,36 @@ func TestReconcileCreateEndpoint(t *testing.T) { } } +func TestParseLocality(t *testing.T) { + t.Run("no labels", func(t *testing.T) { + n := corev1.Node{} + require.Nil(t, parseLocality(n)) + }) + + t.Run("zone only", func(t *testing.T) { + n := corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + corev1.LabelTopologyZone: "us-west-1a", + }, + }, + } + require.Nil(t, parseLocality(n)) + }) + + t.Run("everything", func(t *testing.T) { + n := corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + corev1.LabelTopologyRegion: "us-west-1", + corev1.LabelTopologyZone: "us-west-1a", + }, + }, + } + require.Equal(t, &api.Locality{Region: "us-west-1", Zone: "us-west-1a"}, parseLocality(n)) + }) +} + // Tests updating an Endpoints object. // - Tests updates via the register codepath: // - When an address in an Endpoint is updated, that the corresponding service instance in Consul is updated. From 345f62cec6fe7a3b6e9661e994c24ac5b3333cab Mon Sep 17 00:00:00 2001 From: Nathan Coleman Date: Tue, 13 Jun 2023 11:40:51 -0400 Subject: [PATCH 231/340] fix: use correct flag when translating namespaces (#2353) * fix: use correct flag when translating namespaces * Use non-normalized namespace when deregistering services * Guard against namespace queries when namespaces not enabled in cache --- control-plane/api-gateway/cache/gateway.go | 12 +-- .../api-gateway/common/translation.go | 2 +- .../api-gateway/common/translation_test.go | 73 +++++++++++++++++++ .../controllers/gateway_controller.go | 2 +- 4 files changed, 81 insertions(+), 8 deletions(-) diff --git a/control-plane/api-gateway/cache/gateway.go b/control-plane/api-gateway/cache/gateway.go index d8dd37ffac..d1de8dd7cc 100644 --- a/control-plane/api-gateway/cache/gateway.go +++ b/control-plane/api-gateway/cache/gateway.go @@ -18,7 +18,7 @@ import ( ) type GatewayCache struct { - config *consul.Config + config Config serverMgr consul.ServerConnectionManager logger logr.Logger @@ -35,7 +35,7 @@ type GatewayCache struct { func NewGatewayCache(ctx context.Context, config Config) *GatewayCache { return &GatewayCache{ - config: config.ConsulClientConfig, + config: config, serverMgr: config.ConsulServerConnMgr, logger: config.Logger, events: make(chan event.GenericEvent), @@ -53,13 +53,13 @@ func (r *GatewayCache) ServicesFor(ref api.ResourceReference) []api.CatalogServi } func (r *GatewayCache) FetchServicesFor(ctx context.Context, ref api.ResourceReference) ([]api.CatalogService, error) { - client, err := consul.NewClientFromConnMgr(r.config, r.serverMgr) + client, err := consul.NewClientFromConnMgr(r.config.ConsulClientConfig, r.serverMgr) if err != nil { return nil, err } opts := &api.QueryOptions{} - if ref.Namespace != "" { + if r.config.NamespacesEnabled && ref.Namespace != "" { opts.Namespace = ref.Namespace } @@ -95,7 +95,7 @@ func (r *GatewayCache) RemoveSubscription(ref api.ResourceReference) { func (r *GatewayCache) subscribeToGateway(ctx context.Context, ref api.ResourceReference, resource types.NamespacedName) { opts := &api.QueryOptions{} - if ref.Namespace != "" { + if r.config.NamespacesEnabled && ref.Namespace != "" { opts.Namespace = ref.Namespace } @@ -117,7 +117,7 @@ func (r *GatewayCache) subscribeToGateway(ctx context.Context, ref api.ResourceR retryBackoff := backoff.WithMaxRetries(backoff.NewExponentialBackOff(), 10) if err := backoff.Retry(func() error { - client, err := consul.NewClientFromConnMgr(r.config, r.serverMgr) + client, err := consul.NewClientFromConnMgr(r.config.ConsulClientConfig, r.serverMgr) if err != nil { return err } diff --git a/control-plane/api-gateway/common/translation.go b/control-plane/api-gateway/common/translation.go index 8644b64716..fd69c8601f 100644 --- a/control-plane/api-gateway/common/translation.go +++ b/control-plane/api-gateway/common/translation.go @@ -50,7 +50,7 @@ func (t ResourceTranslator) NormalizedResourceReference(kind, namespace string, } func (t ResourceTranslator) Namespace(namespace string) string { - return namespaces.ConsulNamespace(namespace, t.EnableK8sMirroring, t.ConsulDestNamespace, t.EnableK8sMirroring, t.MirroringPrefix) + return namespaces.ConsulNamespace(namespace, t.EnableConsulNamespaces, t.ConsulDestNamespace, t.EnableK8sMirroring, t.MirroringPrefix) } // ToAPIGateway translates a kuberenetes API gateway into a Consul APIGateway Config Entry. diff --git a/control-plane/api-gateway/common/translation_test.go b/control-plane/api-gateway/common/translation_test.go index 2c735ad4ac..caa3efbcac 100644 --- a/control-plane/api-gateway/common/translation_test.go +++ b/control-plane/api-gateway/common/translation_test.go @@ -9,11 +9,13 @@ import ( "crypto/x509" "crypto/x509/pkix" "encoding/pem" + "fmt" "math/big" "testing" "time" "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -42,6 +44,77 @@ func (v fakeReferenceValidator) TCPRouteCanReferenceBackend(tcpRoute gwv1alpha2. return true } +func TestTranslator_Namespace(t *testing.T) { + testCases := []struct { + EnableConsulNamespaces bool + ConsulDestNamespace string + EnableK8sMirroring bool + MirroringPrefix string + Input, ExpectedOutput string + }{ + { + EnableConsulNamespaces: false, + ConsulDestNamespace: "default", + EnableK8sMirroring: false, + MirroringPrefix: "", + Input: "namespace-1", + ExpectedOutput: "", + }, + { + EnableConsulNamespaces: false, + ConsulDestNamespace: "default", + EnableK8sMirroring: true, + MirroringPrefix: "", + Input: "namespace-1", + ExpectedOutput: "", + }, + { + EnableConsulNamespaces: false, + ConsulDestNamespace: "default", + EnableK8sMirroring: true, + MirroringPrefix: "pre-", + Input: "namespace-1", + ExpectedOutput: "", + }, + { + EnableConsulNamespaces: true, + ConsulDestNamespace: "default", + EnableK8sMirroring: false, + MirroringPrefix: "", + Input: "namespace-1", + ExpectedOutput: "default", + }, + { + EnableConsulNamespaces: true, + ConsulDestNamespace: "default", + EnableK8sMirroring: true, + MirroringPrefix: "", + Input: "namespace-1", + ExpectedOutput: "namespace-1", + }, + { + EnableConsulNamespaces: true, + ConsulDestNamespace: "default", + EnableK8sMirroring: true, + MirroringPrefix: "pre-", + Input: "namespace-1", + ExpectedOutput: "pre-namespace-1", + }, + } + + for i, tc := range testCases { + t.Run(fmt.Sprintf("%s_%d", t.Name(), i), func(t *testing.T) { + translator := ResourceTranslator{ + EnableConsulNamespaces: tc.EnableConsulNamespaces, + ConsulDestNamespace: tc.ConsulDestNamespace, + EnableK8sMirroring: tc.EnableK8sMirroring, + MirroringPrefix: tc.MirroringPrefix, + } + assert.Equal(t, tc.ExpectedOutput, translator.Namespace(tc.Input)) + }) + } +} + func TestTranslator_ToAPIGateway(t *testing.T) { t.Parallel() k8sObjectName := "my-k8s-gw" diff --git a/control-plane/api-gateway/controllers/gateway_controller.go b/control-plane/api-gateway/controllers/gateway_controller.go index 97805ccdfb..8569508769 100644 --- a/control-plane/api-gateway/controllers/gateway_controller.go +++ b/control-plane/api-gateway/controllers/gateway_controller.go @@ -212,7 +212,7 @@ func (r *GatewayController) Reconcile(ctx context.Context, req ctrl.Request) (ct r.gatewayCache.RemoveSubscription(nonNormalizedConsulKey) // make sure we have deregister all services even if they haven't // hit cache yet - if err := r.deregisterAllServices(ctx, consulKey); err != nil { + if err := r.deregisterAllServices(ctx, nonNormalizedConsulKey); err != nil { log.Error(err, "error deregistering services") return ctrl.Result{}, err } From 285096241e0d5c5b6d53dd8a37889ab3ea5a8af2 Mon Sep 17 00:00:00 2001 From: aahel Date: Tue, 13 Jun 2023 21:36:51 +0530 Subject: [PATCH 232/340] added imagePullPolicy for images in values.yaml (#2310) * added imagePullPolicy for images in values.yaml * fix: renamed pullPolicy key according to image * fixed dafault always in tmpl * changed structure of image in yaml * revert changes * added global imagePullPolicy * fixed typo * added changelog file --- .changelog/2310.txt | 3 +++ .../consul/templates/api-gateway-controller-deployment.yaml | 3 +++ charts/consul/templates/api-gateway-gatewayclassconfig.yaml | 1 + charts/consul/templates/client-daemonset.yaml | 1 + charts/consul/templates/cni-daemonset.yaml | 1 + charts/consul/templates/enterprise-license-job.yaml | 1 + charts/consul/templates/gateway-cleanup-job.yaml | 1 + charts/consul/templates/gateway-resources-job.yaml | 1 + charts/consul/templates/mesh-gateway-deployment.yaml | 2 ++ charts/consul/templates/partition-init-job.yaml | 1 + charts/consul/templates/server-acl-init-cleanup-job.yaml | 1 + charts/consul/templates/server-acl-init-job.yaml | 1 + charts/consul/templates/server-statefulset.yaml | 1 + .../consul/templates/webhook-cert-manager-deployment.yaml | 1 + charts/consul/values.yaml | 6 ++++++ 15 files changed, 25 insertions(+) create mode 100644 .changelog/2310.txt diff --git a/.changelog/2310.txt b/.changelog/2310.txt new file mode 100644 index 0000000000..5e37de44ea --- /dev/null +++ b/.changelog/2310.txt @@ -0,0 +1,3 @@ +```release-note:feature +helm: Added imagePullPolicy global field which can be configured to override the default behaviour. +``` \ No newline at end of file diff --git a/charts/consul/templates/api-gateway-controller-deployment.yaml b/charts/consul/templates/api-gateway-controller-deployment.yaml index 8c5c2fa73e..c942576034 100644 --- a/charts/consul/templates/api-gateway-controller-deployment.yaml +++ b/charts/consul/templates/api-gateway-controller-deployment.yaml @@ -57,6 +57,7 @@ spec: containers: - name: api-gateway-controller image: {{ .Values.apiGateway.image }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} ports: - containerPort: 9090 name: sds @@ -219,6 +220,7 @@ spec: {{- if .Values.global.acls.manageSystemACLs }} - name: copy-consul-bin image: {{ .Values.global.image | quote }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} command: - cp - /bin/consul @@ -256,6 +258,7 @@ spec: {{- end}} {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 8 }} image: {{ .Values.global.imageK8S }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} volumeMounts: - mountPath: /consul/login name: consul-data diff --git a/charts/consul/templates/api-gateway-gatewayclassconfig.yaml b/charts/consul/templates/api-gateway-gatewayclassconfig.yaml index ba0e6c63db..8688ee6ae7 100644 --- a/charts/consul/templates/api-gateway-gatewayclassconfig.yaml +++ b/charts/consul/templates/api-gateway-gatewayclassconfig.yaml @@ -65,6 +65,7 @@ spec: image: consulAPIGateway: {{ .Values.apiGateway.image }} envoy: {{ .Values.apiGateway.imageEnvoy }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} {{- if .Values.apiGateway.managedGatewayClass.nodeSelector }} nodeSelector: {{ tpl .Values.apiGateway.managedGatewayClass.nodeSelector . | indent 4 | trim }} diff --git a/charts/consul/templates/client-daemonset.yaml b/charts/consul/templates/client-daemonset.yaml index 09a70b394e..ba19343652 100644 --- a/charts/consul/templates/client-daemonset.yaml +++ b/charts/consul/templates/client-daemonset.yaml @@ -493,6 +493,7 @@ spec: {{- if .Values.global.acls.manageSystemACLs }} - name: client-acl-init image: {{ .Values.global.imageK8S }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} env: - name: NAMESPACE valueFrom: diff --git a/charts/consul/templates/cni-daemonset.yaml b/charts/consul/templates/cni-daemonset.yaml index ae04d9e657..33ffb0a77e 100644 --- a/charts/consul/templates/cni-daemonset.yaml +++ b/charts/consul/templates/cni-daemonset.yaml @@ -61,6 +61,7 @@ spec: # This container installs the consul CNI binaries and CNI network config file on each node - name: install-cni image: {{ .Values.global.imageK8S }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} securityContext: privileged: true command: diff --git a/charts/consul/templates/enterprise-license-job.yaml b/charts/consul/templates/enterprise-license-job.yaml index 0122690104..2a3fa01d00 100644 --- a/charts/consul/templates/enterprise-license-job.yaml +++ b/charts/consul/templates/enterprise-license-job.yaml @@ -124,6 +124,7 @@ spec: initContainers: - name: ent-license-acl-init image: {{ .Values.global.imageK8S }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} command: - "/bin/sh" - "-ec" diff --git a/charts/consul/templates/gateway-cleanup-job.yaml b/charts/consul/templates/gateway-cleanup-job.yaml index 44f032b5fd..e4656916de 100644 --- a/charts/consul/templates/gateway-cleanup-job.yaml +++ b/charts/consul/templates/gateway-cleanup-job.yaml @@ -37,6 +37,7 @@ spec: containers: - name: gateway-cleanup image: {{ .Values.global.imageK8S }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} command: - consul-k8s-control-plane args: diff --git a/charts/consul/templates/gateway-resources-job.yaml b/charts/consul/templates/gateway-resources-job.yaml index 441e64eb14..ea38d7af32 100644 --- a/charts/consul/templates/gateway-resources-job.yaml +++ b/charts/consul/templates/gateway-resources-job.yaml @@ -37,6 +37,7 @@ spec: containers: - name: gateway-resources image: {{ .Values.global.imageK8S }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} command: - consul-k8s-control-plane args: diff --git a/charts/consul/templates/mesh-gateway-deployment.yaml b/charts/consul/templates/mesh-gateway-deployment.yaml index 449d6ae492..4150b2bdfd 100644 --- a/charts/consul/templates/mesh-gateway-deployment.yaml +++ b/charts/consul/templates/mesh-gateway-deployment.yaml @@ -121,6 +121,7 @@ spec: initContainers: - name: mesh-gateway-init image: {{ .Values.global.imageK8S }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} env: - name: NAMESPACE valueFrom: @@ -179,6 +180,7 @@ spec: containers: - name: mesh-gateway image: {{ .Values.global.imageConsulDataplane | quote }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} {{- if .Values.meshGateway.resources }} resources: {{- if eq (typeOf .Values.meshGateway.resources) "string" }} diff --git a/charts/consul/templates/partition-init-job.yaml b/charts/consul/templates/partition-init-job.yaml index db73ef783b..b351d10027 100644 --- a/charts/consul/templates/partition-init-job.yaml +++ b/charts/consul/templates/partition-init-job.yaml @@ -81,6 +81,7 @@ spec: containers: - name: partition-init-job image: {{ .Values.global.imageK8S }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} env: {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 10 }} {{- if (and .Values.global.acls.bootstrapToken.secretName .Values.global.acls.bootstrapToken.secretKey) }} diff --git a/charts/consul/templates/server-acl-init-cleanup-job.yaml b/charts/consul/templates/server-acl-init-cleanup-job.yaml index 35b0877ab4..3676144b40 100644 --- a/charts/consul/templates/server-acl-init-cleanup-job.yaml +++ b/charts/consul/templates/server-acl-init-cleanup-job.yaml @@ -53,6 +53,7 @@ spec: containers: - name: server-acl-init-cleanup image: {{ .Values.global.imageK8S }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} command: - consul-k8s-control-plane args: diff --git a/charts/consul/templates/server-acl-init-job.yaml b/charts/consul/templates/server-acl-init-job.yaml index e62db41ec2..e42a073c42 100644 --- a/charts/consul/templates/server-acl-init-job.yaml +++ b/charts/consul/templates/server-acl-init-job.yaml @@ -122,6 +122,7 @@ spec: containers: - name: server-acl-init-job image: {{ .Values.global.imageK8S }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} env: - name: NAMESPACE valueFrom: diff --git a/charts/consul/templates/server-statefulset.yaml b/charts/consul/templates/server-statefulset.yaml index 0bde9b881a..0c2eb1bffa 100644 --- a/charts/consul/templates/server-statefulset.yaml +++ b/charts/consul/templates/server-statefulset.yaml @@ -225,6 +225,7 @@ spec: initContainers: - name: locality-init image: {{ .Values.global.imageK8S }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} env: - name: NODE_NAME valueFrom: diff --git a/charts/consul/templates/webhook-cert-manager-deployment.yaml b/charts/consul/templates/webhook-cert-manager-deployment.yaml index dd93c039d2..25e382be84 100644 --- a/charts/consul/templates/webhook-cert-manager-deployment.yaml +++ b/charts/consul/templates/webhook-cert-manager-deployment.yaml @@ -50,6 +50,7 @@ spec: -deployment-name={{ template "consul.fullname" . }}-webhook-cert-manager \ -deployment-namespace={{ .Release.Namespace }} image: {{ .Values.global.imageK8S }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} name: webhook-cert-manager resources: limits: diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 6514fde75a..5c77a27f33 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -52,6 +52,12 @@ global: # Changing the partition name would require an un-install and a re-install with the updated name. # Must be "default" in the server cluster ie the Kubernetes cluster that the Consul server pods are deployed onto. name: "default" + + # Set imagePullPolicy for all images used. This is applies to all the images being used. + # One of "IfNotPresent", "Always", "Never" + # Refer to https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy + # @type: string + imagePullPolicy: "" # The name (and tag) of the Consul Docker image for clients and servers. # This can be overridden per component. This should be pinned to a specific From f2c166fc8c77ad0e103b8ac0dcfe35e81860e97f Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Tue, 13 Jun 2023 12:59:55 -0400 Subject: [PATCH 233/340] [chore]: Pin github action workflows (#2356) --- .github/workflows/build.yml | 6 +++--- .github/workflows/jira-issues.yaml | 6 +++--- .github/workflows/jira-pr.yaml | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e46e16c9a3..711c228e11 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -124,7 +124,7 @@ jobs: - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Setup go - uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0 + uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1 with: go-version: ${{ matrix.go }} @@ -193,7 +193,7 @@ jobs: - name: Test rpm package if: ${{ matrix.goos == 'linux' && matrix.component == 'cli' && matrix.goarch == 'amd64'}} - uses: addnab/docker-run-action@v3 # TSCCR: no entry for repository "addnab/docker-run-action" + uses: addnab/docker-run-action@v3 # TSCCR: no entry for repository "addnab/docker-run-action" with: image: registry.access.redhat.com/ubi9/ubi:latest options: -v ${{ github.workspace }}:/work @@ -218,7 +218,7 @@ jobs: - name: Test debian package if: ${{ matrix.goos == 'linux' && matrix.component == 'cli' && matrix.goarch == 'amd64'}} - uses: addnab/docker-run-action@v3 # TSCCR: no entry for repository "addnab/docker-run-action" + uses: addnab/docker-run-action@v3 # TSCCR: no entry for repository "addnab/docker-run-action" with: image: ubuntu:latest options: -v ${{ github.workspace }}:/work diff --git a/.github/workflows/jira-issues.yaml b/.github/workflows/jira-issues.yaml index 700803a45f..bddc69c83f 100644 --- a/.github/workflows/jira-issues.yaml +++ b/.github/workflows/jira-issues.yaml @@ -15,7 +15,7 @@ jobs: name: Jira Community Issue sync steps: - name: Login - uses: atlassian/gajira-login@45fd029b9f1d6d8926c6f04175aa80c0e42c9026 # v3.0.1 + uses: atlassian/gajira-login@ca13f8850ea309cf44a6e4e0c49d9aa48ac3ca4c # v3 env: JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} @@ -72,14 +72,14 @@ jobs: - name: Close ticket if: ( github.event.action == 'closed' || github.event.action == 'deleted' ) && steps.search.outputs.issue - uses: atlassian/gajira-transition@38fc9cd61b03d6a53dd35fcccda172fe04b36de3 # v3.0.1 + uses: atlassian/gajira-transition@4749176faf14633954d72af7a44d7f2af01cc92b # v3 with: issue: ${{ steps.search.outputs.issue }} transition: "Closed" - name: Reopen ticket if: github.event.action == 'reopened' && steps.search.outputs.issue - uses: atlassian/gajira-transition@38fc9cd61b03d6a53dd35fcccda172fe04b36de3 # v3.0.1 + uses: atlassian/gajira-transition@4749176faf14633954d72af7a44d7f2af01cc92b # v3 with: issue: ${{ steps.search.outputs.issue }} transition: "To Do" diff --git a/.github/workflows/jira-pr.yaml b/.github/workflows/jira-pr.yaml index a285c4c176..078365ac88 100644 --- a/.github/workflows/jira-pr.yaml +++ b/.github/workflows/jira-pr.yaml @@ -13,7 +13,7 @@ jobs: name: Jira sync steps: - name: Login - uses: atlassian/gajira-login@45fd029b9f1d6d8926c6f04175aa80c0e42c9026 # v3.0.1 + uses: atlassian/gajira-login@ca13f8850ea309cf44a6e4e0c49d9aa48ac3ca4c # v3 env: JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} @@ -86,14 +86,14 @@ jobs: - name: Close ticket if: ( github.event.action == 'closed' || github.event.action == 'deleted' ) && steps.search.outputs.issue - uses: atlassian/gajira-transition@38fc9cd61b03d6a53dd35fcccda172fe04b36de3 # v3.0.1 + uses: atlassian/gajira-transition@4749176faf14633954d72af7a44d7f2af01cc92b # v3 with: issue: ${{ steps.search.outputs.issue }} transition: "Closed" - name: Reopen ticket if: github.event.action == 'reopened' && steps.search.outputs.issue - uses: atlassian/gajira-transition@38fc9cd61b03d6a53dd35fcccda172fe04b36de3 # v3.0.1 + uses: atlassian/gajira-transition@4749176faf14633954d72af7a44d7f2af01cc92b # v3 with: issue: ${{ steps.search.outputs.issue }} transition: "To Do" From 80b1f52437e912349a1c86c19c58e653a607d80d Mon Sep 17 00:00:00 2001 From: Nathan Coleman Date: Tue, 13 Jun 2023 13:14:31 -0400 Subject: [PATCH 234/340] ci: update backport assistant to 0.3.4 (#2365) This brings consul-k8s in line with consul. Most importantly, the backport assistant was updated to automatically assign created PRs to the author of the PR that is being backported. --- .github/workflows/backport.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 7f39b4e5a0..dadde6a651 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -13,7 +13,7 @@ jobs: backport: if: github.event.pull_request.merged runs-on: ubuntu-latest - container: hashicorpdev/backport-assistant:0.3.3 + container: hashicorpdev/backport-assistant:0.3.4 steps: - name: Run Backport Assistant run: backport-assistant backport -merge-method=squash -gh-automerge From e691f464bafe3050360bc4a45fd1adb70b19255f Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Tue, 13 Jun 2023 10:41:16 -0700 Subject: [PATCH 235/340] update changelog based on changes made to 1.2.x (#2348) * update changelog based on changes made to 1.2.x * fixed test cases - enterprise cases were in the OSS test cases --- .changelog/1975.txt | 11 - .changelog/1976.txt | 3 - .changelog/2048.txt | 3 + .changelog/2075.txt | 3 + .changelog/2086.txt | 3 + .changelog/2097.txt | 3 + .changelog/2102.txt | 9 + .changelog/2165.txt | 3 + .../configentry_controller_ent_test.go | 313 ++++++++++++++++++ .../configentry_controller_test.go | 313 ------------------ 10 files changed, 337 insertions(+), 327 deletions(-) delete mode 100644 .changelog/1975.txt delete mode 100644 .changelog/1976.txt create mode 100644 .changelog/2048.txt create mode 100644 .changelog/2075.txt create mode 100644 .changelog/2086.txt create mode 100644 .changelog/2097.txt create mode 100644 .changelog/2165.txt diff --git a/.changelog/1975.txt b/.changelog/1975.txt deleted file mode 100644 index ba26b1ab1e..0000000000 --- a/.changelog/1975.txt +++ /dev/null @@ -1,11 +0,0 @@ -```release-note:security -upgrade to use Go 1.19.6. This resolves vulnerabilities CVE-2022-41724 in crypto/tls and CVE-2022-41723 in net/http. -``` - -```release-note:improvement -cli: update minimum go version for project to 1.19. -``` - -```release-note:improvement -control-plane: update minimum go version for project to 1.19. -``` \ No newline at end of file diff --git a/.changelog/1976.txt b/.changelog/1976.txt deleted file mode 100644 index 65024aa6f9..0000000000 --- a/.changelog/1976.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:security -upgrade to use Go 1.19.6. This resolves vulnerabilities CVE-2022-41724 in crypto/tls and CVE-2022-41723 in net/http. -``` \ No newline at end of file diff --git a/.changelog/2048.txt b/.changelog/2048.txt new file mode 100644 index 0000000000..5796ce2397 --- /dev/null +++ b/.changelog/2048.txt @@ -0,0 +1,3 @@ +```release-note:improvement +helm: add samenessGroup CRD +``` \ No newline at end of file diff --git a/.changelog/2075.txt b/.changelog/2075.txt new file mode 100644 index 0000000000..2f0f0344eb --- /dev/null +++ b/.changelog/2075.txt @@ -0,0 +1,3 @@ +```release-note:improvement +helm: add samenessGroup field to exported services CRD +``` \ No newline at end of file diff --git a/.changelog/2086.txt b/.changelog/2086.txt new file mode 100644 index 0000000000..d4e43a630d --- /dev/null +++ b/.changelog/2086.txt @@ -0,0 +1,3 @@ +```release-note:improvement +helm: add samenessGroup field to service resolver CRD +``` \ No newline at end of file diff --git a/.changelog/2097.txt b/.changelog/2097.txt new file mode 100644 index 0000000000..60e99a8515 --- /dev/null +++ b/.changelog/2097.txt @@ -0,0 +1,3 @@ +```release-note:improvement +helm: add samenessGroup field to source intention CRD +``` \ No newline at end of file diff --git a/.changelog/2102.txt b/.changelog/2102.txt index 59d120f747..7adf361d2d 100644 --- a/.changelog/2102.txt +++ b/.changelog/2102.txt @@ -10,3 +10,12 @@ Also, `golang.org/x/net` has been updated to v0.7.0 to resolve CVEs [CVE-2022-41 ](https://github.com/advisories/GHSA-vvpx-j8f3-3w6h .) ``` + +```release-note:improvement +cli: update minimum go version for project to 1.20. +``` + +```release-note:improvement +control-plane: update minimum go version for project to 1.20. +``` + diff --git a/.changelog/2165.txt b/.changelog/2165.txt new file mode 100644 index 0000000000..15c4bdb1e0 --- /dev/null +++ b/.changelog/2165.txt @@ -0,0 +1,3 @@ +```release-note:improvement +control-plane: add FIPS support +``` \ No newline at end of file diff --git a/control-plane/controllers/configentry_controller_ent_test.go b/control-plane/controllers/configentry_controller_ent_test.go index ab6c70b9ad..14ae477a56 100644 --- a/control-plane/controllers/configentry_controller_ent_test.go +++ b/control-plane/controllers/configentry_controller_ent_test.go @@ -87,6 +87,119 @@ func TestConfigEntryController_createsEntConfigEntry(t *testing.T) { require.Equal(t, "", resource.Members[0].Partition) }, }, + { + kubeKind: "ControlPlaneRequestLimit", + consulKind: capi.RateLimitIPConfig, + configEntryResource: &v1alpha1.ControlPlaneRequestLimit{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: kubeNS, + }, + Spec: v1alpha1.ControlPlaneRequestLimitSpec{ + Mode: "permissive", + ReadWriteRatesConfig: v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ACL: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Catalog: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ConfigEntry: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ConnectCA: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Coordinate: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + DiscoveryChain: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Health: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Intention: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + KV: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Tenancy: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + PreparedQuery: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Session: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Txn: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + }, + }, + reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) testReconciler { + return &ControlPlaneRequestLimitController{ + Client: client, + Log: logger, + ConfigEntryController: &ConfigEntryController{ + ConsulClientConfig: cfg, + ConsulServerConnMgr: watcher, + DatacenterName: datacenterName, + }, + } + }, + compare: func(t *testing.T, consulEntry capi.ConfigEntry) { + resource, ok := consulEntry.(*capi.RateLimitIPConfigEntry) + require.True(t, ok, "cast error") + require.Equal(t, "permissive", resource.Mode) + require.Equal(t, 100.0, resource.ReadRate) + require.Equal(t, 100.0, resource.WriteRate) + require.Equal(t, 100.0, resource.ACL.ReadRate) + require.Equal(t, 100.0, resource.ACL.WriteRate) + require.Equal(t, 100.0, resource.Catalog.ReadRate) + require.Equal(t, 100.0, resource.Catalog.WriteRate) + require.Equal(t, 100.0, resource.ConfigEntry.ReadRate) + require.Equal(t, 100.0, resource.ConfigEntry.WriteRate) + require.Equal(t, 100.0, resource.ConnectCA.ReadRate) + require.Equal(t, 100.0, resource.ConnectCA.WriteRate) + require.Equal(t, 100.0, resource.Coordinate.ReadRate) + require.Equal(t, 100.0, resource.Coordinate.WriteRate) + require.Equal(t, 100.0, resource.DiscoveryChain.ReadRate) + require.Equal(t, 100.0, resource.DiscoveryChain.WriteRate) + require.Equal(t, 100.0, resource.Health.ReadRate) + require.Equal(t, 100.0, resource.Health.WriteRate) + require.Equal(t, 100.0, resource.Intention.ReadRate) + require.Equal(t, 100.0, resource.Intention.WriteRate) + require.Equal(t, 100.0, resource.KV.ReadRate) + require.Equal(t, 100.0, resource.KV.WriteRate) + require.Equal(t, 100.0, resource.Tenancy.ReadRate) + require.Equal(t, 100.0, resource.Tenancy.WriteRate) + require.Equal(t, 100.0, resource.PreparedQuery.ReadRate) + require.Equal(t, 100.0, resource.PreparedQuery.WriteRate) + require.Equal(t, 100.0, resource.Session.ReadRate) + require.Equal(t, 100.0, resource.Session.WriteRate) + require.Equal(t, 100.0, resource.Txn.ReadRate) + require.Equal(t, 100.0, resource.Txn.WriteRate, 100.0) + }, + }, } for _, c := range cases { @@ -191,6 +304,123 @@ func TestConfigEntryController_updatesEntConfigEntry(t *testing.T) { require.Equal(t, "", resource.Members[0].Partition) }, }, + { + kubeKind: "ControlPlaneRequestLimit", + consulKind: capi.RateLimitIPConfig, + configEntryResource: &v1alpha1.ControlPlaneRequestLimit{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: kubeNS, + }, + Spec: v1alpha1.ControlPlaneRequestLimitSpec{ + Mode: "permissive", + ReadWriteRatesConfig: v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ACL: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Catalog: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ConfigEntry: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ConnectCA: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Coordinate: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + DiscoveryChain: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Health: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Intention: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + KV: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Tenancy: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + PreparedQuery: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Session: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Txn: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + }, + }, + reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) testReconciler { + return &ControlPlaneRequestLimitController{ + Client: client, + Log: logger, + ConfigEntryController: &ConfigEntryController{ + ConsulClientConfig: cfg, + ConsulServerConnMgr: watcher, + DatacenterName: datacenterName, + }, + } + }, + updateF: func(resource common.ConfigEntryResource) { + ipRateLimit := resource.(*v1alpha1.ControlPlaneRequestLimit) + ipRateLimit.Spec.Mode = "enforcing" + }, + compare: func(t *testing.T, consulEntry capi.ConfigEntry) { + resource, ok := consulEntry.(*capi.RateLimitIPConfigEntry) + require.True(t, ok, "cast error") + require.Equal(t, "enforcing", resource.Mode) + require.Equal(t, 100.0, resource.ReadRate) + require.Equal(t, 100.0, resource.WriteRate) + require.Equal(t, 100.0, resource.ACL.ReadRate) + require.Equal(t, 100.0, resource.ACL.WriteRate) + require.Equal(t, 100.0, resource.Catalog.ReadRate) + require.Equal(t, 100.0, resource.Catalog.WriteRate) + require.Equal(t, 100.0, resource.ConfigEntry.ReadRate) + require.Equal(t, 100.0, resource.ConfigEntry.WriteRate) + require.Equal(t, 100.0, resource.ConnectCA.ReadRate) + require.Equal(t, 100.0, resource.ConnectCA.WriteRate) + require.Equal(t, 100.0, resource.Coordinate.ReadRate) + require.Equal(t, 100.0, resource.Coordinate.WriteRate) + require.Equal(t, 100.0, resource.DiscoveryChain.ReadRate) + require.Equal(t, 100.0, resource.DiscoveryChain.WriteRate) + require.Equal(t, 100.0, resource.Health.ReadRate) + require.Equal(t, 100.0, resource.Health.WriteRate) + require.Equal(t, 100.0, resource.Intention.ReadRate) + require.Equal(t, 100.0, resource.Intention.WriteRate) + require.Equal(t, 100.0, resource.KV.ReadRate) + require.Equal(t, 100.0, resource.KV.WriteRate) + require.Equal(t, 100.0, resource.Tenancy.ReadRate) + require.Equal(t, 100.0, resource.Tenancy.WriteRate) + require.Equal(t, 100.0, resource.PreparedQuery.ReadRate) + require.Equal(t, 100.0, resource.PreparedQuery.WriteRate) + require.Equal(t, 100.0, resource.Session.ReadRate) + require.Equal(t, 100.0, resource.Session.WriteRate) + require.Equal(t, 100.0, resource.Txn.ReadRate) + require.Equal(t, 100.0, resource.Txn.WriteRate) + }, + }, } for _, c := range cases { @@ -296,6 +526,89 @@ func TestConfigEntryController_deletesEntConfigEntry(t *testing.T) { } }, }, + { + + kubeKind: "ControlPlaneRequestLimit", + consulKind: capi.RateLimitIPConfig, + configEntryResourceWithDeletion: &v1alpha1.ControlPlaneRequestLimit{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: kubeNS, + DeletionTimestamp: &metav1.Time{Time: time.Now()}, + Finalizers: []string{FinalizerName}, + }, + Spec: v1alpha1.ControlPlaneRequestLimitSpec{ + Mode: "permissive", + ReadWriteRatesConfig: v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ACL: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Catalog: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ConfigEntry: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + ConnectCA: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Coordinate: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + DiscoveryChain: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Health: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Intention: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + KV: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Tenancy: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + PreparedQuery: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Session: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + Txn: &v1alpha1.ReadWriteRatesConfig{ + ReadRate: 100.0, + WriteRate: 100.0, + }, + }, + }, + reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) testReconciler { + return &ControlPlaneRequestLimitController{ + Client: client, + Log: logger, + ConfigEntryController: &ConfigEntryController{ + ConsulClientConfig: cfg, + ConsulServerConnMgr: watcher, + DatacenterName: datacenterName, + }, + } + }, + }, } for _, c := range cases { diff --git a/control-plane/controllers/configentry_controller_test.go b/control-plane/controllers/configentry_controller_test.go index 0d5f8af5bf..071d67ca6f 100644 --- a/control-plane/controllers/configentry_controller_test.go +++ b/control-plane/controllers/configentry_controller_test.go @@ -476,119 +476,6 @@ func TestConfigEntryControllers_createsConfigEntry(t *testing.T) { require.Equal(t, "test-issuer", jwt.Issuer) }, }, - { - kubeKind: "ControlPlaneRequestLimit", - consulKind: capi.RateLimitIPConfig, - configEntryResource: &v1alpha1.ControlPlaneRequestLimit{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: kubeNS, - }, - Spec: v1alpha1.ControlPlaneRequestLimitSpec{ - Mode: "permissive", - ReadWriteRatesConfig: v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ACL: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Catalog: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ConfigEntry: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ConnectCA: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Coordinate: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - DiscoveryChain: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Health: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Intention: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - KV: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Tenancy: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - PreparedQuery: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Session: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Txn: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - }, - }, - reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) testReconciler { - return &ControlPlaneRequestLimitController{ - Client: client, - Log: logger, - ConfigEntryController: &ConfigEntryController{ - ConsulClientConfig: cfg, - ConsulServerConnMgr: watcher, - DatacenterName: datacenterName, - }, - } - }, - compare: func(t *testing.T, consulEntry capi.ConfigEntry) { - resource, ok := consulEntry.(*capi.RateLimitIPConfigEntry) - require.True(t, ok, "cast error") - require.Equal(t, "permissive", resource.Mode) - require.Equal(t, 100.0, resource.ReadRate) - require.Equal(t, 100.0, resource.WriteRate) - require.Equal(t, 100.0, resource.ACL.ReadRate) - require.Equal(t, 100.0, resource.ACL.WriteRate) - require.Equal(t, 100.0, resource.Catalog.ReadRate) - require.Equal(t, 100.0, resource.Catalog.WriteRate) - require.Equal(t, 100.0, resource.ConfigEntry.ReadRate) - require.Equal(t, 100.0, resource.ConfigEntry.WriteRate) - require.Equal(t, 100.0, resource.ConnectCA.ReadRate) - require.Equal(t, 100.0, resource.ConnectCA.WriteRate) - require.Equal(t, 100.0, resource.Coordinate.ReadRate) - require.Equal(t, 100.0, resource.Coordinate.WriteRate) - require.Equal(t, 100.0, resource.DiscoveryChain.ReadRate) - require.Equal(t, 100.0, resource.DiscoveryChain.WriteRate) - require.Equal(t, 100.0, resource.Health.ReadRate) - require.Equal(t, 100.0, resource.Health.WriteRate) - require.Equal(t, 100.0, resource.Intention.ReadRate) - require.Equal(t, 100.0, resource.Intention.WriteRate) - require.Equal(t, 100.0, resource.KV.ReadRate) - require.Equal(t, 100.0, resource.KV.WriteRate) - require.Equal(t, 100.0, resource.Tenancy.ReadRate) - require.Equal(t, 100.0, resource.Tenancy.WriteRate) - require.Equal(t, 100.0, resource.PreparedQuery.ReadRate) - require.Equal(t, 100.0, resource.PreparedQuery.WriteRate) - require.Equal(t, 100.0, resource.Session.ReadRate) - require.Equal(t, 100.0, resource.Session.WriteRate) - require.Equal(t, 100.0, resource.Txn.ReadRate) - require.Equal(t, 100.0, resource.Txn.WriteRate, 100.0) - }, - }, } for _, c := range cases { @@ -1116,123 +1003,6 @@ func TestConfigEntryControllers_updatesConfigEntry(t *testing.T) { require.Equal(t, []string{"aud1"}, jwt.Audiences) }, }, - { - kubeKind: "ControlPlaneRequestLimit", - consulKind: capi.RateLimitIPConfig, - configEntryResource: &v1alpha1.ControlPlaneRequestLimit{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: kubeNS, - }, - Spec: v1alpha1.ControlPlaneRequestLimitSpec{ - Mode: "permissive", - ReadWriteRatesConfig: v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ACL: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Catalog: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ConfigEntry: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ConnectCA: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Coordinate: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - DiscoveryChain: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Health: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Intention: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - KV: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Tenancy: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - PreparedQuery: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Session: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Txn: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - }, - }, - reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) testReconciler { - return &ControlPlaneRequestLimitController{ - Client: client, - Log: logger, - ConfigEntryController: &ConfigEntryController{ - ConsulClientConfig: cfg, - ConsulServerConnMgr: watcher, - DatacenterName: datacenterName, - }, - } - }, - updateF: func(resource common.ConfigEntryResource) { - ipRateLimit := resource.(*v1alpha1.ControlPlaneRequestLimit) - ipRateLimit.Spec.Mode = "enforcing" - }, - compare: func(t *testing.T, consulEntry capi.ConfigEntry) { - resource, ok := consulEntry.(*capi.RateLimitIPConfigEntry) - require.True(t, ok, "cast error") - require.Equal(t, "enforcing", resource.Mode) - require.Equal(t, 100.0, resource.ReadRate) - require.Equal(t, 100.0, resource.WriteRate) - require.Equal(t, 100.0, resource.ACL.ReadRate) - require.Equal(t, 100.0, resource.ACL.WriteRate) - require.Equal(t, 100.0, resource.Catalog.ReadRate) - require.Equal(t, 100.0, resource.Catalog.WriteRate) - require.Equal(t, 100.0, resource.ConfigEntry.ReadRate) - require.Equal(t, 100.0, resource.ConfigEntry.WriteRate) - require.Equal(t, 100.0, resource.ConnectCA.ReadRate) - require.Equal(t, 100.0, resource.ConnectCA.WriteRate) - require.Equal(t, 100.0, resource.Coordinate.ReadRate) - require.Equal(t, 100.0, resource.Coordinate.WriteRate) - require.Equal(t, 100.0, resource.DiscoveryChain.ReadRate) - require.Equal(t, 100.0, resource.DiscoveryChain.WriteRate) - require.Equal(t, 100.0, resource.Health.ReadRate) - require.Equal(t, 100.0, resource.Health.WriteRate) - require.Equal(t, 100.0, resource.Intention.ReadRate) - require.Equal(t, 100.0, resource.Intention.WriteRate) - require.Equal(t, 100.0, resource.KV.ReadRate) - require.Equal(t, 100.0, resource.KV.WriteRate) - require.Equal(t, 100.0, resource.Tenancy.ReadRate) - require.Equal(t, 100.0, resource.Tenancy.WriteRate) - require.Equal(t, 100.0, resource.PreparedQuery.ReadRate) - require.Equal(t, 100.0, resource.PreparedQuery.WriteRate) - require.Equal(t, 100.0, resource.Session.ReadRate) - require.Equal(t, 100.0, resource.Session.WriteRate) - require.Equal(t, 100.0, resource.Txn.ReadRate) - require.Equal(t, 100.0, resource.Txn.WriteRate) - }, - }, } for _, c := range cases { @@ -1665,89 +1435,6 @@ func TestConfigEntryControllers_deletesConfigEntry(t *testing.T) { } }, }, - { - - kubeKind: "ControlPlaneRequestLimit", - consulKind: capi.RateLimitIPConfig, - configEntryResourceWithDeletion: &v1alpha1.ControlPlaneRequestLimit{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: kubeNS, - DeletionTimestamp: &metav1.Time{Time: time.Now()}, - Finalizers: []string{FinalizerName}, - }, - Spec: v1alpha1.ControlPlaneRequestLimitSpec{ - Mode: "permissive", - ReadWriteRatesConfig: v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ACL: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Catalog: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ConfigEntry: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ConnectCA: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Coordinate: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - DiscoveryChain: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Health: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Intention: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - KV: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Tenancy: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - PreparedQuery: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Session: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Txn: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - }, - }, - reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) testReconciler { - return &ControlPlaneRequestLimitController{ - Client: client, - Log: logger, - ConfigEntryController: &ConfigEntryController{ - ConsulClientConfig: cfg, - ConsulServerConnMgr: watcher, - DatacenterName: datacenterName, - }, - } - }, - }, } for _, c := range cases { From 9121afcef8e2738f086b2e2bf76ead4ea4157b95 Mon Sep 17 00:00:00 2001 From: sarahalsmiller <100602640+sarahalsmiller@users.noreply.github.com> Date: Wed, 14 Jun 2023 09:23:46 -0500 Subject: [PATCH 236/340] api-gateway: nightly conformance test action (#2257) * trigger conformance tests nightly, squash * remove extra line * Update nightly-api-gateway-conformance.yml --- .../nightly-api-gateway-conformance.yml | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/nightly-api-gateway-conformance.yml diff --git a/.github/workflows/nightly-api-gateway-conformance.yml b/.github/workflows/nightly-api-gateway-conformance.yml new file mode 100644 index 0000000000..cd63ee8467 --- /dev/null +++ b/.github/workflows/nightly-api-gateway-conformance.yml @@ -0,0 +1,28 @@ +# Dispatch to the consul-k8s-workflows with a nightly cron +name: nightly-api-gateway-conformance +on: + schedule: + # * is a special character in YAML so you have to quote this string + # Run nightly at 12AM UTC/8PM EST/5PM PST. + - cron: '0 0 * * *' + + +# these should be the only settings that you will ever need to change +env: + CONSUL_IMAGE: hashicorppreview/consul-enterprise:1.16-dev # Consul's enterprise version to use in tests + BRANCH: ${{ github.ref_name }} + CONTEXT: "nightly" + +jobs: + api-gateway-conformance: + name: api-gateway-conformance + runs-on: ubuntu-latest + steps: + - uses: benc-uk/workflow-dispatch@798e70c97009500150087d30d9f11c5444830385 # v1.2.2 + name: conformance + with: + workflow: api-gateway-conformance.yml + repo: hashicorp/consul-k8s-workflows + ref: main + token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} + inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}" }' From 3ce33026a35947f4de13261d1a79e1d823fc5c49 Mon Sep 17 00:00:00 2001 From: Eric Haberkorn Date: Thu, 15 Jun 2023 09:12:59 -0400 Subject: [PATCH 237/340] add crds for prioritize by locality (#2357) --- .changelog/2357.txt | 3 + .../templates/crd-serviceresolvers.yaml | 9 +++ .../api/v1alpha1/serviceresolver_types.go | 59 ++++++++++++++++--- .../v1alpha1/serviceresolver_types_test.go | 28 +++++++++ ...consul.hashicorp.com_serviceresolvers.yaml | 9 +++ 5 files changed, 99 insertions(+), 9 deletions(-) create mode 100644 .changelog/2357.txt diff --git a/.changelog/2357.txt b/.changelog/2357.txt new file mode 100644 index 0000000000..7cc35f595a --- /dev/null +++ b/.changelog/2357.txt @@ -0,0 +1,3 @@ +```release-note:feature +Add the `PrioritizeByLocality` field to the `ServiceResolver` CRD. +``` diff --git a/charts/consul/templates/crd-serviceresolvers.yaml b/charts/consul/templates/crd-serviceresolvers.yaml index ed95c15846..99cc1bb090 100644 --- a/charts/consul/templates/crd-serviceresolvers.yaml +++ b/charts/consul/templates/crd-serviceresolvers.yaml @@ -227,6 +227,15 @@ spec: type: integer type: object type: object + prioritizeByLocality: + description: PrioritizeByLocality contains the configuration for + locality aware routing. + properties: + mode: + description: Mode specifies the behavior of PrioritizeByLocality + routing. Valid values are "", "none", and "failover". + type: string + type: object redirect: description: Redirect when configured, all attempts to resolve the service this resolver defines will be substituted for the supplied diff --git a/control-plane/api/v1alpha1/serviceresolver_types.go b/control-plane/api/v1alpha1/serviceresolver_types.go index d00821275d..e5ce3c6fa6 100644 --- a/control-plane/api/v1alpha1/serviceresolver_types.go +++ b/control-plane/api/v1alpha1/serviceresolver_types.go @@ -79,6 +79,16 @@ type ServiceResolverSpec struct { // LoadBalancer determines the load balancing policy and configuration for services // issuing requests to this upstream service. LoadBalancer *LoadBalancer `json:"loadBalancer,omitempty"` + // PrioritizeByLocality controls whether the locality of services within the + // local partition will be used to prioritize connectivity. + PrioritizeByLocality *ServiceResolverPrioritizeByLocality `json:"prioritizeByLocality,omitempty"` +} + +type ServiceResolverPrioritizeByLocality struct { + // Mode specifies the type of prioritization that will be performed + // when selecting nodes in the local partition. + // Valid values are: "" (default "none"), "none", and "failover". + Mode string `json:"mode,omitempty"` } type ServiceResolverRedirect struct { @@ -300,15 +310,16 @@ func (in *ServiceResolver) SyncedConditionStatus() corev1.ConditionStatus { // ToConsul converts the entry into its Consul equivalent struct. func (in *ServiceResolver) ToConsul(datacenter string) capi.ConfigEntry { return &capi.ServiceResolverConfigEntry{ - Kind: in.ConsulKind(), - Name: in.ConsulName(), - DefaultSubset: in.Spec.DefaultSubset, - Subsets: in.Spec.Subsets.toConsul(), - Redirect: in.Spec.Redirect.toConsul(), - Failover: in.Spec.Failover.toConsul(), - ConnectTimeout: in.Spec.ConnectTimeout.Duration, - LoadBalancer: in.Spec.LoadBalancer.toConsul(), - Meta: meta(datacenter), + Kind: in.ConsulKind(), + Name: in.ConsulName(), + DefaultSubset: in.Spec.DefaultSubset, + Subsets: in.Spec.Subsets.toConsul(), + Redirect: in.Spec.Redirect.toConsul(), + Failover: in.Spec.Failover.toConsul(), + ConnectTimeout: in.Spec.ConnectTimeout.Duration, + LoadBalancer: in.Spec.LoadBalancer.toConsul(), + PrioritizeByLocality: in.Spec.PrioritizeByLocality.toConsul(), + Meta: meta(datacenter), } } @@ -338,6 +349,7 @@ func (in *ServiceResolver) Validate(consulMeta common.ConsulMeta) error { } errs = append(errs, in.Spec.Redirect.validate(path.Child("redirect"), consulMeta)...) + errs = append(errs, in.Spec.PrioritizeByLocality.validate(path.Child("prioritizeByLocality"))...) errs = append(errs, in.Spec.Subsets.validate(path.Child("subsets"))...) errs = append(errs, in.Spec.LoadBalancer.validate(path.Child("loadBalancer"))...) errs = append(errs, in.validateEnterprise(consulMeta)...) @@ -520,6 +532,16 @@ func (in *ServiceResolverFailover) toConsul() *capi.ServiceResolverFailover { } } +func (in *ServiceResolverPrioritizeByLocality) toConsul() *capi.ServiceResolverPrioritizeByLocality { + if in == nil { + return nil + } + + return &capi.ServiceResolverPrioritizeByLocality{ + Mode: in.Mode, + } +} + func (in ServiceResolverFailoverTarget) toConsul() capi.ServiceResolverFailoverTarget { return capi.ServiceResolverFailoverTarget{ Service: in.Service, @@ -629,6 +651,25 @@ func (in *ServiceResolverFailover) isEmpty() bool { return in.Service == "" && in.ServiceSubset == "" && in.Namespace == "" && len(in.Datacenters) == 0 && len(in.Targets) == 0 && in.Policy == nil && in.SamenessGroup == "" } +func (in *ServiceResolverPrioritizeByLocality) validate(path *field.Path) field.ErrorList { + var errs field.ErrorList + + if in == nil { + return nil + } + + switch in.Mode { + case "": + case "none": + case "failover": + default: + asJSON, _ := json.Marshal(in) + errs = append(errs, field.Invalid(path, string(asJSON), + "mode must be one of '', 'none', or 'failover'")) + } + return errs +} + func (in *ServiceResolverFailover) validate(path *field.Path, consulMeta common.ConsulMeta) field.ErrorList { var errs field.ErrorList if in.isEmpty() { diff --git a/control-plane/api/v1alpha1/serviceresolver_types_test.go b/control-plane/api/v1alpha1/serviceresolver_types_test.go index d09f0809c8..3a3d5a6016 100644 --- a/control-plane/api/v1alpha1/serviceresolver_types_test.go +++ b/control-plane/api/v1alpha1/serviceresolver_types_test.go @@ -66,6 +66,9 @@ func TestServiceResolver_MatchesConsul(t *testing.T) { Datacenter: "redirect_datacenter", Peer: "redirect_peer", }, + PrioritizeByLocality: &ServiceResolverPrioritizeByLocality{ + Mode: "failover", + }, Failover: map[string]ServiceResolverFailover{ "failover1": { Service: "failover1", @@ -147,6 +150,9 @@ func TestServiceResolver_MatchesConsul(t *testing.T) { Datacenter: "redirect_datacenter", Peer: "redirect_peer", }, + PrioritizeByLocality: &capi.ServiceResolverPrioritizeByLocality{ + Mode: "failover", + }, Failover: map[string]capi.ServiceResolverFailover{ "failover1": { Service: "failover1", @@ -277,6 +283,9 @@ func TestServiceResolver_ToConsul(t *testing.T) { Datacenter: "redirect_datacenter", Partition: "redirect_partition", }, + PrioritizeByLocality: &ServiceResolverPrioritizeByLocality{ + Mode: "none", + }, Failover: map[string]ServiceResolverFailover{ "failover1": { Service: "failover1", @@ -358,6 +367,9 @@ func TestServiceResolver_ToConsul(t *testing.T) { Datacenter: "redirect_datacenter", Partition: "redirect_partition", }, + PrioritizeByLocality: &capi.ServiceResolverPrioritizeByLocality{ + Mode: "none", + }, Failover: map[string]capi.ServiceResolverFailover{ "failover1": { Service: "failover1", @@ -882,6 +894,22 @@ func TestServiceResolver_Validate(t *testing.T) { "spec.failover[failB].namespace: Invalid value: \"namespace-b\": Consul Enterprise namespaces must be enabled to set failover.namespace", }, }, + "prioritize by locality none": { + input: &ServiceResolver{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: ServiceResolverSpec{ + PrioritizeByLocality: &ServiceResolverPrioritizeByLocality{ + Mode: "bad", + }, + }, + }, + namespacesEnabled: false, + expectedErrMsgs: []string{ + "mode must be one of '', 'none', or 'failover'", + }, + }, } for name, testCase := range cases { t.Run(name, func(t *testing.T) { diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml index 3cd3b37324..ec52c04e05 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml @@ -223,6 +223,15 @@ spec: type: integer type: object type: object + prioritizeByLocality: + description: PrioritizeByLocality contains the configuration for + locality aware routing. + properties: + mode: + description: Mode specifies the behavior of PrioritizeByLocality + routing. Valid values are "", "none", and "failover". + type: string + type: object redirect: description: Redirect when configured, all attempts to resolve the service this resolver defines will be substituted for the supplied From 19d2fb50658fb88ed1bb79149f2929101ee355b9 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Thu, 15 Jun 2023 13:56:41 -0400 Subject: [PATCH 238/340] set everything to correct version (#2342) making scripts more robust and removing changing helm chart --- Makefile | 4 ++++ control-plane/build-support/functions/10-util.sh | 11 +++-------- .../scripts/consul-enterprise-version.sh | 11 +++++++++++ control-plane/build-support/scripts/consul-version.sh | 4 ++++ 4 files changed, 22 insertions(+), 8 deletions(-) create mode 100755 control-plane/build-support/scripts/consul-enterprise-version.sh diff --git a/Makefile b/Makefile index 628b13e2f9..9547fd3547 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ VERSION = $(shell ./control-plane/build-support/scripts/version.sh control-plane/version/version.go) CONSUL_IMAGE_VERSION = $(shell ./control-plane/build-support/scripts/consul-version.sh charts/consul/values.yaml) +CONSUL_ENTERPRISE_IMAGE_VERSION = $(shell ./control-plane/build-support/scripts/consul-enterprise-version.sh charts/consul/values.yaml) CONSUL_DATAPLANE_IMAGE_VERSION = $(shell ./control-plane/build-support/scripts/consul-dataplane-version.sh charts/consul/values.yaml) # ===========> Helm Targets @@ -180,6 +181,9 @@ version: consul-version: @echo $(CONSUL_IMAGE_VERSION) +consul-enterprise-version: + @echo $(CONSUL_ENTERPRISE_IMAGE_VERSION) + consul-dataplane-version: @echo $(CONSUL_DATAPLANE_IMAGE_VERSION) diff --git a/control-plane/build-support/functions/10-util.sh b/control-plane/build-support/functions/10-util.sh index 9ba6c26da9..72ce91720c 100644 --- a/control-plane/build-support/functions/10-util.sh +++ b/control-plane/build-support/functions/10-util.sh @@ -631,13 +631,8 @@ function update_version_helm { sed_i ${SED_EXT} -e "s/(version:[[:space:]]*)[^\"]*/\1${full_version}/g" "${cfile}" sed_i ${SED_EXT} -e "s/(appVersion:[[:space:]]*)[^\"]*/\1${full_consul_version}/g" "${cfile}" sed_i ${SED_EXT} -e "s/(image:.*\/consul-k8s-control-plane:)[^\"]*/image: $4${full_version}/g" "${cfile}" - if ! test -z "$3"; then - sed_i ${SED_EXT} -e "s/(image:.*\/consul:)[^\"]*/image: $6:${full_consul_version}/g" "${cfile}" - sed_i ${SED_EXT} -e "s/(image:.*\/consul:)[^\"]*/image: $6:${full_consul_version}/g" "${vfile}" - else - sed_i ${SED_EXT} -e "s/(image:.*\/consul-enterprise:)[^\"]*/image: $6:${full_consul_version}/g" "${cfile}" - sed_i ${SED_EXT} -e "s/(image:.*\/consul-enterprise:)[^\"]*/image: $6:${full_consul_version}/g" "${vfile}" - fi + sed_i ${SED_EXT} -e "s/(image:.*\/consul:)[^\"]*/image: $6:${full_consul_version}/g" "${cfile}" + sed_i ${SED_EXT} -e "s/(image:.*\/consul:)[^\"]*/image: $6:${full_consul_version}/g" "${vfile}" if test -z "$3"; then sed_i ${SED_EXT} -e "s/(artifacthub.io\/prerelease:[[:space:]]*)[^\"]*/\1false/g" "${cfile}" @@ -774,7 +769,7 @@ function prepare_dev { # * - error echo "prepare_dev: dir:$1 consul-k8s:$5 consul:$6 date:"$3" mode:dev" - set_version "$1" "$5" "$3" "dev" "docker.mirror.hashicorp.services\/hashicorppreview\/consul-k8s-control-plane:" "$6" "docker.mirror.hashicorp.services\/hashicorppreview\/consul-enterprise" + set_version "$1" "$5" "$3" "dev" "docker.mirror.hashicorp.services\/hashicorppreview\/consul-k8s-control-plane:" "$6" "docker.mirror.hashicorp.services\/hashicorppreview\/consul" return 0 } diff --git a/control-plane/build-support/scripts/consul-enterprise-version.sh b/control-plane/build-support/scripts/consul-enterprise-version.sh new file mode 100755 index 0000000000..6b48bb4678 --- /dev/null +++ b/control-plane/build-support/scripts/consul-enterprise-version.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 +FILE=$1 +VERSION=$(yq .global.image $FILE) + +if [[ !"${VERSION}" == *"consul:"* ]]; then + VERSION=$(echo ${VERSION} | sed "s/consul:/consul-enterprise:/g") +fi + +echo "${VERSION}" diff --git a/control-plane/build-support/scripts/consul-version.sh b/control-plane/build-support/scripts/consul-version.sh index e245e2a239..faaed33b20 100755 --- a/control-plane/build-support/scripts/consul-version.sh +++ b/control-plane/build-support/scripts/consul-version.sh @@ -4,4 +4,8 @@ FILE=$1 VERSION=$(yq .global.image $FILE) +if [[ "${VERSION}" == *"consul-enterprise:"* ]]; then + VERSION=$(echo ${VERSION} | sed "s/consul-enterprise:/consul:/g") +fi + echo "${VERSION}" From c4617fcfe573df6c39839c593eef6143bcb047c4 Mon Sep 17 00:00:00 2001 From: Thomas Eckert Date: Thu, 15 Jun 2023 16:29:58 -0400 Subject: [PATCH 239/340] api-gateway: fix cache and service deletion issue (#2377) * Fix cache and service deletion issue * Add comments * add in acceptance test * Fix indentation * Fix unit test for deleting gateway w/ consul services * Remove redundant service deregistration code * Exit loop early once registration is found for service * Fix import blocking * Set status on pods added to test * Apply suggestions from code review * Reduce count of test gateways to 10 from 100 --------- Co-authored-by: Nathan Coleman Co-authored-by: Sarah Alsmiller --- acceptance/go.mod | 3 +- acceptance/go.sum | 2 + .../api_gateway_gatewayclassconfig_test.go | 207 ++++++++++++++++++ control-plane/api-gateway/binding/binder.go | 1 + .../api-gateway/binding/binder_test.go | 25 ++- control-plane/api-gateway/cache/gateway.go | 17 -- 6 files changed, 236 insertions(+), 19 deletions(-) create mode 100644 acceptance/tests/api-gateway/api_gateway_gatewayclassconfig_test.go diff --git a/acceptance/go.mod b/acceptance/go.mod index ddce3e4e5c..59cbbab79f 100644 --- a/acceptance/go.mod +++ b/acceptance/go.mod @@ -16,6 +16,7 @@ require ( k8s.io/api v0.26.3 k8s.io/apimachinery v0.26.3 k8s.io/client-go v0.26.3 + k8s.io/utils v0.0.0-20230209194617-a36077c30491 sigs.k8s.io/controller-runtime v0.14.6 sigs.k8s.io/gateway-api v0.7.0 ) @@ -30,6 +31,7 @@ require ( github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/deckarep/golang-set v1.7.1 // indirect github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect @@ -120,7 +122,6 @@ require ( k8s.io/component-base v0.26.3 // indirect k8s.io/klog/v2 v2.100.1 // indirect k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect - k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/acceptance/go.sum b/acceptance/go.sum index bd7421de60..578a95b1dd 100644 --- a/acceptance/go.sum +++ b/acceptance/go.sum @@ -144,6 +144,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/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= +github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= diff --git a/acceptance/tests/api-gateway/api_gateway_gatewayclassconfig_test.go b/acceptance/tests/api-gateway/api_gateway_gatewayclassconfig_test.go new file mode 100644 index 0000000000..add65b89af --- /dev/null +++ b/acceptance/tests/api-gateway/api_gateway_gatewayclassconfig_test.go @@ -0,0 +1,207 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package apigateway + +import ( + "context" + "testing" + + "github.com/hashicorp/consul-k8s/acceptance/framework/consul" + "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" + "github.com/hashicorp/consul-k8s/acceptance/framework/logger" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/sdk/testutil/retry" + "github.com/stretchr/testify/require" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/pointer" + "sigs.k8s.io/controller-runtime/pkg/client" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +// GatewayClassConfig tests the creation of a gatewayclassconfig object and makes sure that its configuration +// is properly applied to any child gateway objects, namely that the number of gateway instances match the defined +// minInstances,maxInstances and defaultInstances parameters, and that changing the parent gateway does not affect +// the child gateways. +func TestAPIGateway_GatewayClassConfig(t *testing.T) { + ctx := suite.Environment().DefaultContext(t) + cfg := suite.Config() + helmValues := map[string]string{ + "global.logLevel": "trace", + "connectInject.enabled": "true", + } + releaseName := helpers.RandomName() + consulCluster := consul.NewHelmCluster(t, helmValues, ctx, cfg, releaseName) + consulCluster.Create(t) + // Override the default proxy config settings for this test. + consulClient, _ := consulCluster.SetupConsulClient(t, false) + _, _, err := consulClient.ConfigEntries().Set(&api.ProxyConfigEntry{ + Kind: api.ProxyDefaults, + Name: api.ProxyConfigGlobal, + Config: map[string]interface{}{ + "protocol": "http", + }, + }, nil) + require.NoError(t, err) + + k8sClient := ctx.ControllerRuntimeClient(t) + namespace := "gateway-namespace" + + //create clean namespace + err = k8sClient.Create(context.Background(), &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: namespace, + }, + }) + require.NoError(t, err) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + logger.Log(t, "deleting gateway namesapce") + k8sClient.Delete(context.Background(), &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: namespace, + }, + }) + }) + + defaultInstances := pointer.Int32(2) + maxInstances := pointer.Int32(8) + minInstances := pointer.Int32(1) + // create a GatewayClassConfig with configuration set + gatewayClassConfigName := "gateway-class-config" + gatewayClassConfig := &v1alpha1.GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: gatewayClassConfigName, + }, + Spec: v1alpha1.GatewayClassConfigSpec{ + DeploymentSpec: v1alpha1.DeploymentSpec{ + DefaultInstances: defaultInstances, + MaxInstances: maxInstances, + MinInstances: minInstances, + }, + }, + } + logger.Log(t, "creating gateway class config") + err = k8sClient.Create(context.Background(), gatewayClassConfig) + require.NoError(t, err) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + logger.Log(t, "deleting all gateway class configs") + k8sClient.DeleteAllOf(context.Background(), &v1alpha1.GatewayClassConfig{}) + }) + + gatewayParametersRef := &gwv1beta1.ParametersReference{ + Group: gwv1beta1.Group(v1alpha1.ConsulHashicorpGroup), + Kind: gwv1beta1.Kind(v1alpha1.GatewayClassConfigKind), + Name: gatewayClassConfigName, + } + + // create gateway class referencing gateway-class-config + gatewayClassName := "gateway-class" + logger.Log(t, "creating controlled gateway class") + createGatewayClass(t, k8sClient, gatewayClassName, gatewayClassControllerName, gatewayParametersRef) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + logger.Log(t, "deleting all gateway classes") + k8sClient.DeleteAllOf(context.Background(), &gwv1beta1.GatewayClass{}) + }) + + // Create a certificate to reference in listeners + certificateInfo := generateCertificate(t, nil, "certificate.consul.local") + certificateName := "certificate" + certificate := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: certificateName, + Namespace: namespace, + Labels: map[string]string{ + "test-certificate": "true", + }, + }, + Type: corev1.SecretTypeTLS, + Data: map[string][]byte{ + corev1.TLSCertKey: certificateInfo.CertPEM, + corev1.TLSPrivateKeyKey: certificateInfo.PrivateKeyPEM, + }, + } + logger.Log(t, "creating certificate") + err = k8sClient.Create(context.Background(), certificate) + require.NoError(t, err) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8sClient.Delete(context.Background(), certificate) + }) + + // Create gateway referencing gateway class config + gatewayName := "gateway" + logger.Log(t, "creating controlled gateway") + gateway := createGateway(t, k8sClient, gatewayName, namespace, gatewayClassName, certificateName) + // make sure it exists + logger.Log(t, "checking that gateway one is synchronized to Consul") + checkConsulExists(t, consulClient, api.APIGateway, gatewayName) + + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + logger.Log(t, "deleting all gateways") + k8sClient.DeleteAllOf(context.Background(), &gwv1beta1.Gateway{}, client.InNamespace(namespace)) + }) + + // Scenario: Gateway deployment should match the default instances defined on the gateway class config + logger.Log(t, "checking that gateway instances match defined gateway class config") + checkNumberOfInstances(t, k8sClient, consulClient, gateway.Name, gateway.Namespace, defaultInstances, gateway) + + //Scenario: Updating the GatewayClassConfig should not affect gateways that have already been created + logger.Log(t, "updating gatewayclassconfig values") + err = k8sClient.Get(context.Background(), types.NamespacedName{Name: gatewayClassConfigName, Namespace: namespace}, gatewayClassConfig) + require.NoError(t, err) + gatewayClassConfig.Spec.DeploymentSpec.DefaultInstances = pointer.Int32(8) + gatewayClassConfig.Spec.DeploymentSpec.MinInstances = pointer.Int32(5) + err = k8sClient.Update(context.Background(), gatewayClassConfig) + require.NoError(t, err) + checkNumberOfInstances(t, k8sClient, consulClient, gateway.Name, gateway.Namespace, defaultInstances, gateway) + + //Scenario: gateways should be able to scale independently and not get overridden by the controller unless it's above the max + scale(t, k8sClient, gateway.Name, gateway.Namespace, maxInstances) + checkNumberOfInstances(t, k8sClient, consulClient, gateway.Name, gateway.Namespace, maxInstances, gateway) + scale(t, k8sClient, gateway.Name, gateway.Namespace, pointer.Int32(10)) + checkNumberOfInstances(t, k8sClient, consulClient, gateway.Name, gateway.Namespace, maxInstances, gateway) + scale(t, k8sClient, gateway.Name, gateway.Namespace, pointer.Int32(0)) + checkNumberOfInstances(t, k8sClient, consulClient, gateway.Name, gateway.Namespace, minInstances, gateway) + +} + +func scale(t *testing.T, client client.Client, name, namespace string, scaleTo *int32) { + t.Helper() + + retryCheck(t, 30, func(r *retry.R) { + var deployment appsv1.Deployment + err := client.Get(context.Background(), types.NamespacedName{Name: name, Namespace: namespace}, &deployment) + require.NoError(r, err) + + deployment.Spec.Replicas = scaleTo + err = client.Update(context.Background(), &deployment) + require.NoError(r, err) + }) +} + +func checkNumberOfInstances(t *testing.T, k8client client.Client, consulClient *api.Client, name, namespace string, wantNumber *int32, gateway *gwv1beta1.Gateway) { + t.Helper() + + retryCheck(t, 30, func(r *retry.R) { + //first check to make sure the number of replicas has been set properly + var deployment appsv1.Deployment + err := k8client.Get(context.Background(), types.NamespacedName{Name: name, Namespace: namespace}, &deployment) + require.NoError(r, err) + require.EqualValues(r, *wantNumber, *deployment.Spec.Replicas) + + //then check to make sure the number of gateway pods matches the replicas generated + podList := corev1.PodList{} + labels := common.LabelsForGateway(gateway) + err = k8client.List(context.Background(), &podList, client.InNamespace(namespace), client.MatchingLabels(labels)) + require.NoError(r, err) + require.EqualValues(r, *wantNumber, len(podList.Items)) + + services, _, err := consulClient.Catalog().Service(name, "", nil) + require.NoError(r, err) + require.EqualValues(r, *wantNumber, len(services)) + }) +} diff --git a/control-plane/api-gateway/binding/binder.go b/control-plane/api-gateway/binding/binder.go index 28a26985a8..7fbf18d412 100644 --- a/control-plane/api-gateway/binding/binder.go +++ b/control-plane/api-gateway/binding/binder.go @@ -197,6 +197,7 @@ func (b *Binder) Snapshot() *Snapshot { for _, registration := range registrations { if service.ServiceID == registration.Service.ID { found = true + break } } if !found { diff --git a/control-plane/api-gateway/binding/binder_test.go b/control-plane/api-gateway/binding/binder_test.go index a3af702dab..bb30b98f52 100644 --- a/control-plane/api-gateway/binding/binder_test.go +++ b/control-plane/api-gateway/binding/binder_test.go @@ -15,6 +15,7 @@ import ( logrtest "github.com/go-logr/logr/testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/consul/api" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -25,7 +26,6 @@ import ( "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul/api" ) func init() { @@ -809,6 +809,29 @@ func TestBinder_Registrations(t *testing.T) { {Node: "test", ServiceID: "pod2", Namespace: "namespace1"}, {Node: "test", ServiceID: "pod3", Namespace: "namespace1"}, }, + Pods: []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{Name: "pod1"}, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + Conditions: []corev1.PodCondition{{Type: corev1.PodReady, Status: corev1.ConditionTrue}}, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "pod2"}, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + Conditions: []corev1.PodCondition{{Type: corev1.PodReady, Status: corev1.ConditionTrue}}, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "pod3"}, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + Conditions: []corev1.PodCondition{{Type: corev1.PodReady, Status: corev1.ConditionTrue}}, + }, + }, + }, }), expectedDeregistrations: []api.CatalogDeregistration{ {Node: "test", ServiceID: "pod1", Namespace: "namespace1"}, diff --git a/control-plane/api-gateway/cache/gateway.go b/control-plane/api-gateway/cache/gateway.go index d1de8dd7cc..0d79542eec 100644 --- a/control-plane/api-gateway/cache/gateway.go +++ b/control-plane/api-gateway/cache/gateway.go @@ -14,7 +14,6 @@ import ( "github.com/hashicorp/consul-k8s/control-plane/consul" "github.com/hashicorp/consul/api" "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/event" ) type GatewayCache struct { @@ -22,8 +21,6 @@ type GatewayCache struct { serverMgr consul.ServerConnectionManager logger logr.Logger - events chan event.GenericEvent - data map[api.ResourceReference][]api.CatalogService dataMutex sync.RWMutex @@ -38,7 +35,6 @@ func NewGatewayCache(ctx context.Context, config Config) *GatewayCache { config: config, serverMgr: config.ConsulServerConnMgr, logger: config.Logger, - events: make(chan event.GenericEvent), data: make(map[api.ResourceReference][]api.CatalogService), subscribedGateways: make(map[api.ResourceReference]context.CancelFunc), ctx: ctx, @@ -140,18 +136,5 @@ func (r *GatewayCache) subscribeToGateway(ctx context.Context, ref api.ResourceR r.dataMutex.Lock() r.data[common.NormalizeMeta(ref)] = derefed r.dataMutex.Unlock() - - event := event.GenericEvent{ - Object: newConfigEntryObject(resource), - } - - select { - case <-ctx.Done(): - r.dataMutex.Lock() - delete(r.data, ref) - r.dataMutex.Unlock() - return - case r.events <- event: - } } } From 47d40635a33ab8bb86b1d2d3953d2aecf151520b Mon Sep 17 00:00:00 2001 From: Rahul Date: Mon, 19 Jun 2023 10:57:36 +0530 Subject: [PATCH 240/340] Adding support for weighted k8s service (#2293) * Adding support for weighted k8s service * Adding changelog * if per-app weight is 0 then pull the weight to 1 * Addressing review comments * Addressing review comments * Addressing review comments * Comment update * Comment update * Parameterized table test * Parameterized table test * fixing linting issue * fixing linting issue --------- Co-authored-by: srahul3 --- .changelog/2293.txt | 3 + control-plane/catalog/to-consul/annotation.go | 6 + control-plane/catalog/to-consul/resource.go | 41 ++++++ .../catalog/to-consul/resource_test.go | 133 ++++++++++++++++++ 4 files changed, 183 insertions(+) create mode 100644 .changelog/2293.txt diff --git a/.changelog/2293.txt b/.changelog/2293.txt new file mode 100644 index 0000000000..ce6d888bcd --- /dev/null +++ b/.changelog/2293.txt @@ -0,0 +1,3 @@ +```release-note:feature +sync-catalog: add ability to support weighted loadbalancing by service annotation `consul.hashicorp.com/service-weight: ` +``` \ No newline at end of file diff --git a/control-plane/catalog/to-consul/annotation.go b/control-plane/catalog/to-consul/annotation.go index 27e37ca217..edca70b60c 100644 --- a/control-plane/catalog/to-consul/annotation.go +++ b/control-plane/catalog/to-consul/annotation.go @@ -26,4 +26,10 @@ const ( // annotationServiceMetaPrefix is the prefix for setting meta key/value // for a service. The remainder of the key is the meta key. annotationServiceMetaPrefix = "consul.hashicorp.com/service-meta-" + + // annotationServiceWeight is the key of the annotation that determines + // the traffic weight of the service which is spanned over multiple k8s cluster. + // e.g. Service `backend` in k8s cluster `A` receives 25% of the traffic + // compared to same `backend` service in k8s cluster `B`. + annotationServiceWeight = "consul.hashicorp.com/service-weight" ) diff --git a/control-plane/catalog/to-consul/resource.go b/control-plane/catalog/to-consul/resource.go index 6a4e913d80..08aeec8821 100644 --- a/control-plane/catalog/to-consul/resource.go +++ b/control-plane/catalog/to-consul/resource.go @@ -511,6 +511,19 @@ func (t *ServiceResource) generateRegistrations(key string) { r.Service = &rs r.Service.ID = serviceID(r.Service.Service, ip) r.Service.Address = ip + // Adding information about service weight. + // Overrides the existing weight if present. + if weight, ok := svc.Annotations[annotationServiceWeight]; ok && weight != "" { + weightI, err := getServiceWeight(weight) + if err == nil { + r.Service.Weights = consulapi.AgentWeights{ + Passing: weightI, + } + } else { + t.Log.Debug("[generateRegistrations] service weight err: ", err) + } + } + t.consulMap[key] = append(t.consulMap[key], &r) } @@ -547,6 +560,19 @@ func (t *ServiceResource) generateRegistrations(key string) { r.Service.ID = serviceID(r.Service.Service, addr) r.Service.Address = addr + // Adding information about service weight. + // Overrides the existing weight if present. + if weight, ok := svc.Annotations[annotationServiceWeight]; ok && weight != "" { + weightI, err := getServiceWeight(weight) + if err == nil { + r.Service.Weights = consulapi.AgentWeights{ + Passing: weightI, + } + } else { + t.Log.Debug("[generateRegistrations] service weight err: ", err) + } + } + t.consulMap[key] = append(t.consulMap[key], &r) } } @@ -999,3 +1025,18 @@ func (t *ServiceResource) isIngressService(key string) bool { func consulHealthCheckID(k8sNS string, serviceID string) string { return fmt.Sprintf("%s/%s", k8sNS, serviceID) } + +// Calculates the passing service weight. +func getServiceWeight(weight string) (int, error) { + // error validation if the input param is a number. + weightI, err := strconv.Atoi(weight) + if err != nil { + return -1, err + } + + if weightI <= 1 { + return -1, fmt.Errorf("expecting the service annotation %s value to be greater than 1", annotationServiceWeight) + } + + return weightI, nil +} diff --git a/control-plane/catalog/to-consul/resource_test.go b/control-plane/catalog/to-consul/resource_test.go index 3c01088c0d..3b8fb78497 100644 --- a/control-plane/catalog/to-consul/resource_test.go +++ b/control-plane/catalog/to-consul/resource_test.go @@ -56,6 +56,139 @@ func TestServiceResource_createDelete(t *testing.T) { }) } +// Test that Loadbalancer service weight is set from service annotation. +func TestServiceWeight_ingress(t *testing.T) { + t.Parallel() + client := fake.NewSimpleClientset() + syncer := newTestSyncer() + serviceResource := defaultServiceResource(client, syncer) + + // Start the controller + closer := controller.TestControllerRun(&serviceResource) + defer closer() + + // Insert an LB service + svc := lbService("foo", metav1.NamespaceDefault, "1.2.3.4") + svc.Annotations[annotationServiceWeight] = "22" + svc.Status.LoadBalancer.Ingress = append( + svc.Status.LoadBalancer.Ingress, + corev1.LoadBalancerIngress{IP: "3.3.3.3"}, + ) + + svc.Status.LoadBalancer.Ingress = append( + svc.Status.LoadBalancer.Ingress, + corev1.LoadBalancerIngress{IP: "4.4.4.4"}, + ) + + _, err := client.CoreV1().Services(metav1.NamespaceDefault).Create(context.Background(), svc, metav1.CreateOptions{}) + require.NoError(t, err) + + // Verify what we got + retry.Run(t, func(r *retry.R) { + syncer.Lock() + defer syncer.Unlock() + actual := syncer.Registrations + require.Len(r, actual, 3) + require.Equal(r, "foo", actual[1].Service.Service) + require.Equal(r, "3.3.3.3", actual[1].Service.Address) + require.Equal(r, 22, actual[1].Service.Weights.Passing) + require.Equal(r, "foo", actual[2].Service.Service) + require.Equal(r, "4.4.4.4", actual[2].Service.Address) + require.Equal(r, 22, actual[2].Service.Weights.Passing) + require.NotEqual(r, actual[1].Service.ID, actual[2].Service.ID) + }) +} + +// Test that Loadbalancer service weight is set from service annotation. +func TestServiceWeight_externalIP(t *testing.T) { + t.Parallel() + client := fake.NewSimpleClientset() + syncer := newTestSyncer() + serviceResource := defaultServiceResource(client, syncer) + + // Start the controller + closer := controller.TestControllerRun(&serviceResource) + defer closer() + + // Insert an LB service + svc := lbService("foo", metav1.NamespaceDefault, "1.2.3.4") + svc.Annotations[annotationServiceWeight] = "22" + svc.Spec.ExternalIPs = []string{"3.3.3.3", "4.4.4.4"} + + _, err := client.CoreV1().Services(metav1.NamespaceDefault).Create(context.Background(), svc, metav1.CreateOptions{}) + require.NoError(t, err) + + // Verify what we got + retry.Run(t, func(r *retry.R) { + syncer.Lock() + defer syncer.Unlock() + actual := syncer.Registrations + require.Len(r, actual, 2) + require.Equal(r, "foo", actual[0].Service.Service) + require.Equal(r, "3.3.3.3", actual[0].Service.Address) + require.Equal(r, 22, actual[0].Service.Weights.Passing) + require.Equal(r, "foo", actual[1].Service.Service) + require.Equal(r, "4.4.4.4", actual[1].Service.Address) + require.Equal(r, 22, actual[1].Service.Weights.Passing) + require.NotEqual(r, actual[0].Service.ID, actual[1].Service.ID) + }) +} + +// Test service weight. +func TestServiceWeight(t *testing.T) { + t.Parallel() + cases := map[string]struct { + Weight string + ExpectError bool + ExtectedWeight int + }{ + "external-IP": { + Weight: "22", + ExpectError: false, + ExtectedWeight: 22, + }, + "non-int-weight": { + Weight: "non-int", + ExpectError: true, + ExtectedWeight: 0, + }, + "one-weight": { + Weight: "1", + ExpectError: true, + ExtectedWeight: 0, + }, + "zero-weight": { + Weight: "0", + ExpectError: true, + ExtectedWeight: 0, + }, + "negative-weight": { + Weight: "-2", + ExpectError: true, + ExtectedWeight: 0, + }, + "greater-than-100-is-allowed": { + Weight: "1000", + ExpectError: false, + ExtectedWeight: 1000, + }, + } + + for name, c := range cases { + t.Run(name, func(tt *testing.T) { + weightI, err := getServiceWeight(c.Weight) + // Verify what we got + retry.Run(tt, func(r *retry.R) { + if c.ExpectError { + require.Error(r, err) + } else { + require.Equal(r, c.ExtectedWeight, weightI) + } + }) + }) + } +} + // Test that we're default enabled. func TestServiceResource_defaultEnable(t *testing.T) { t.Parallel() From fe4857e34b1eeea5aec9d588cb3c762ca83025a8 Mon Sep 17 00:00:00 2001 From: Bryan Eastes Date: Mon, 19 Jun 2023 06:42:30 -0700 Subject: [PATCH 241/340] Bumping go-discover to the lastest version (#2390) * Bumping go-discover to the lastest version --- .changelog/2390.txt | 3 +++ control-plane/Dockerfile | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 .changelog/2390.txt diff --git a/.changelog/2390.txt b/.changelog/2390.txt new file mode 100644 index 0000000000..a4546bd781 --- /dev/null +++ b/.changelog/2390.txt @@ -0,0 +1,3 @@ +```release-note:security +Update [Go-Discover](https://github.com/hashicorp/go-discover) in the container has been updated to address [CVE-2020-14040](https://github.com/advisories/GHSA-5rcv-m4m3-hfh7) +``` diff --git a/control-plane/Dockerfile b/control-plane/Dockerfile index f401ac8262..c09f5ecf80 100644 --- a/control-plane/Dockerfile +++ b/control-plane/Dockerfile @@ -17,7 +17,7 @@ # go-discover builds the discover binary (which we don't currently publish # either). FROM golang:1.19.2-alpine as go-discover -RUN CGO_ENABLED=0 go install github.com/hashicorp/go-discover/cmd/discover@49f60c093101c9c5f6b04d5b1c80164251a761a6 +RUN CGO_ENABLED=0 go install github.com/hashicorp/go-discover/cmd/discover@214571b6a5309addf3db7775f4ee8cf4d264fd5f # dev copies the binary from a local build # ----------------------------------- From a3c8771b8600f7045c08fccc67013c64e1276ec5 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Mon, 19 Jun 2023 08:43:48 -0700 Subject: [PATCH 242/340] Pin Kind versions on release branches (#2384) * pinned kind configuration for CI tests - created a yaml file with the desired pinned versions - created a script to read the yaml - added a make target which can be used in CI to get the desired kind inputs/config --------- Co-authored-by: Curt Bushko --- Makefile | 15 +++++++++++++-- acceptance/ci-inputs/kind-inputs.yaml | 3 +++ .../build-support/scripts/read-yaml-config.sh | 10 ++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 acceptance/ci-inputs/kind-inputs.yaml create mode 100755 control-plane/build-support/scripts/read-yaml-config.sh diff --git a/Makefile b/Makefile index 9547fd3547..34497b1f07 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,9 @@ VERSION = $(shell ./control-plane/build-support/scripts/version.sh control-plane CONSUL_IMAGE_VERSION = $(shell ./control-plane/build-support/scripts/consul-version.sh charts/consul/values.yaml) CONSUL_ENTERPRISE_IMAGE_VERSION = $(shell ./control-plane/build-support/scripts/consul-enterprise-version.sh charts/consul/values.yaml) CONSUL_DATAPLANE_IMAGE_VERSION = $(shell ./control-plane/build-support/scripts/consul-dataplane-version.sh charts/consul/values.yaml) +KIND_VERSION= $(shell ./control-plane/build-support/scripts/read-yaml-config.sh acceptance/ci-inputs/kind-inputs.yaml .kindVersion) +KIND_NODE_IMAGE= $(shell ./control-plane/build-support/scripts/read-yaml-config.sh acceptance/ci-inputs/kind-inputs.yaml .kindNodeImage) +KUBECTL_VERSION= $(shell ./control-plane/build-support/scripts/read-yaml-config.sh acceptance/ci-inputs/kind-inputs.yaml .kubectlVersion) # ===========> Helm Targets @@ -105,7 +108,6 @@ terraform-fmt: # ===========> CLI Targets - cli-dev: @echo "==> Installing consul-k8s CLI tool for ${GOOS}/${GOARCH}" @cd cli; go build -o ./bin/consul-k8s; cp ./bin/consul-k8s ${GOPATH}/bin/ @@ -114,7 +116,6 @@ cli-fips-dev: @echo "==> Installing consul-k8s CLI tool for ${GOOS}/${GOARCH}" @cd cli; CGO_ENABLED=1 GOEXPERIMENT=boringcrypto go build -o ./bin/consul-k8s -tags "fips"; cp ./bin/consul-k8s ${GOPATH}/bin/ - cli-lint: ## Run linter in the control-plane directory. cd cli; golangci-lint run -c ../.golangci.yml @@ -187,6 +188,16 @@ consul-enterprise-version: consul-dataplane-version: @echo $(CONSUL_DATAPLANE_IMAGE_VERSION) +kind-version: + @echo $(KIND_VERSION) + +kind-node-image: + @echo $(KIND_NODE_IMAGE) + +kubectl-version: + @echo $(KUBECTL_VERSION) + + # ===========> Release Targets diff --git a/acceptance/ci-inputs/kind-inputs.yaml b/acceptance/ci-inputs/kind-inputs.yaml new file mode 100644 index 0000000000..615ff302ba --- /dev/null +++ b/acceptance/ci-inputs/kind-inputs.yaml @@ -0,0 +1,3 @@ +kindVersion: v0.19.0 +kindNodeImage: kindest/node:v1.27.1 +kubectlVersion: v1.27.1 diff --git a/control-plane/build-support/scripts/read-yaml-config.sh b/control-plane/build-support/scripts/read-yaml-config.sh new file mode 100755 index 0000000000..37cfd0cc17 --- /dev/null +++ b/control-plane/build-support/scripts/read-yaml-config.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 +INPUT_FILE=$1 +FIELD=$2 + +VALUE=$(yq $FIELD $INPUT_FILE) + +echo "${VALUE}" From aaa54c26105283690b181ec60d05c993404ea6ac Mon Sep 17 00:00:00 2001 From: "hashicorp-copywrite[bot]" <110428419+hashicorp-copywrite[bot]@users.noreply.github.com> Date: Tue, 20 Jun 2023 10:39:41 -0400 Subject: [PATCH 243/340] [COMPLIANCE] Add Copyright and License Headers (#2400) Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com> --- acceptance/ci-inputs/kind-inputs.yaml | 3 +++ .../fixtures/bases/static-server-tcp/servicedefaults.yaml | 3 +++ cli/version/fips_build.go | 3 +++ cli/version/non_fips_build.go | 3 +++ control-plane/version/fips_build.go | 3 +++ control-plane/version/non_fips_build.go | 3 +++ 6 files changed, 18 insertions(+) diff --git a/acceptance/ci-inputs/kind-inputs.yaml b/acceptance/ci-inputs/kind-inputs.yaml index 615ff302ba..ba21d2cdaf 100644 --- a/acceptance/ci-inputs/kind-inputs.yaml +++ b/acceptance/ci-inputs/kind-inputs.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + kindVersion: v0.19.0 kindNodeImage: kindest/node:v1.27.1 kubectlVersion: v1.27.1 diff --git a/acceptance/tests/fixtures/bases/static-server-tcp/servicedefaults.yaml b/acceptance/tests/fixtures/bases/static-server-tcp/servicedefaults.yaml index 500051db87..f89765cf6d 100644 --- a/acceptance/tests/fixtures/bases/static-server-tcp/servicedefaults.yaml +++ b/acceptance/tests/fixtures/bases/static-server-tcp/servicedefaults.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceDefaults metadata: diff --git a/cli/version/fips_build.go b/cli/version/fips_build.go index 4d04cc6539..63e0e68883 100644 --- a/cli/version/fips_build.go +++ b/cli/version/fips_build.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + //go:build fips package version diff --git a/cli/version/non_fips_build.go b/cli/version/non_fips_build.go index f72aecae73..ce99575d2c 100644 --- a/cli/version/non_fips_build.go +++ b/cli/version/non_fips_build.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + //go:build !fips package version diff --git a/control-plane/version/fips_build.go b/control-plane/version/fips_build.go index 4d04cc6539..63e0e68883 100644 --- a/control-plane/version/fips_build.go +++ b/control-plane/version/fips_build.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + //go:build fips package version diff --git a/control-plane/version/non_fips_build.go b/control-plane/version/non_fips_build.go index f72aecae73..ce99575d2c 100644 --- a/control-plane/version/non_fips_build.go +++ b/control-plane/version/non_fips_build.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + //go:build !fips package version From 63c76820712082fe7a2e62277843f5891ec68bb9 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Tue, 20 Jun 2023 10:42:55 -0400 Subject: [PATCH 244/340] update consul-dataplane on main to use 1.2-dev (#2325) --- charts/consul/Chart.yaml | 2 +- charts/consul/values.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/consul/Chart.yaml b/charts/consul/Chart.yaml index c55c6be6a2..64d7ed4ed0 100644 --- a/charts/consul/Chart.yaml +++ b/charts/consul/Chart.yaml @@ -20,7 +20,7 @@ annotations: - name: consul-k8s-control-plane image: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.2.0-dev - name: consul-dataplane - image: hashicorp/consul-dataplane:1.1.0 + image: docker.mirror.hashicorp.services/hashicorppreview/consul-dataplane:1.2-dev - name: envoy image: envoyproxy/envoy:v1.25.1 artifacthub.io/license: MPL-2.0 diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 5c77a27f33..faf7f5bcdf 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -563,7 +563,7 @@ global: # The name (and tag) of the consul-dataplane Docker image used for the # connect-injected sidecar proxies and mesh, terminating, and ingress gateways. # @default: hashicorp/consul-dataplane: - imageConsulDataplane: "hashicorppreview/consul-dataplane:1.1-dev" + imageConsulDataplane: "docker.mirror.hashicorp.services/hashicorppreview/consul-dataplane:1.2-dev" # Configuration for running this Helm chart on the Red Hat OpenShift platform. # This Helm chart currently supports OpenShift v4.x+. From 4141f6f1d35af6bc4802bb820c8d16455fbf9ed9 Mon Sep 17 00:00:00 2001 From: Paul Glass Date: Tue, 20 Jun 2023 09:53:04 -0500 Subject: [PATCH 245/340] Acceptance test for permissive mTLS (#2378) --- .../tests/connect/permissive_mtls_test.go | 94 +++++++++++++++++++ .../mesh-config-permissive-allowed.yaml | 6 ++ ...ice-defaults-static-server-permissive.yaml | 7 ++ ...service-defaults-static-server-strict.yaml | 7 ++ 4 files changed, 114 insertions(+) create mode 100644 acceptance/tests/connect/permissive_mtls_test.go create mode 100644 acceptance/tests/fixtures/cases/permissive-mtls/mesh-config-permissive-allowed.yaml create mode 100644 acceptance/tests/fixtures/cases/permissive-mtls/service-defaults-static-server-permissive.yaml create mode 100644 acceptance/tests/fixtures/cases/permissive-mtls/service-defaults-static-server-strict.yaml diff --git a/acceptance/tests/connect/permissive_mtls_test.go b/acceptance/tests/connect/permissive_mtls_test.go new file mode 100644 index 0000000000..f0a55779ee --- /dev/null +++ b/acceptance/tests/connect/permissive_mtls_test.go @@ -0,0 +1,94 @@ +package connect + +import ( + "context" + "testing" + + "github.com/hashicorp/consul-k8s/acceptance/framework/config" + "github.com/hashicorp/consul-k8s/acceptance/framework/connhelper" + "github.com/hashicorp/consul-k8s/acceptance/framework/consul" + "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" + "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" + "github.com/hashicorp/consul-k8s/acceptance/framework/logger" + "github.com/hashicorp/consul/sdk/testutil/retry" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestConnectInject_PermissiveMTLS(t *testing.T) { + cfg := suite.Config() + if !cfg.EnableTransparentProxy { + t.Skipf("skipping this because -enable-transparent-proxy is not set") + } + + ctx := suite.Environment().DefaultContext(t) + + releaseName := helpers.RandomName() + connHelper := connhelper.ConnectHelper{ + ClusterKind: consul.Helm, + Secure: true, + ReleaseName: releaseName, + Ctx: ctx, + Cfg: cfg, + } + connHelper.Setup(t) + connHelper.Install(t) + + deployNonMeshClient(t, connHelper) + deployStaticServer(t, cfg, connHelper) + + kubectlOpts := connHelper.Ctx.KubectlOptions(t) + logger.Logf(t, "Check that incoming non-mTLS connection fails in MutualTLSMode = strict") + k8s.CheckStaticServerConnectionFailing(t, kubectlOpts, "static-client", "http://static-server") + + logger.Log(t, "Set allowEnablingPermissiveMutualTLS = true") + writeCrd(t, connHelper, "../fixtures/cases/permissive-mtls/mesh-config-permissive-allowed.yaml") + + logger.Log(t, "Set mutualTLSMode = permissive for static-server") + writeCrd(t, connHelper, "../fixtures/cases/permissive-mtls/service-defaults-static-server-permissive.yaml") + + logger.Log(t, "Check that incoming mTLS connection is successful in MutualTLSMode = permissive") + k8s.CheckStaticServerConnectionSuccessful(t, kubectlOpts, "static-client", "http://static-server") +} + +func deployNonMeshClient(t *testing.T, ch connhelper.ConnectHelper) { + t.Helper() + + logger.Log(t, "Creating static-client deployment with connect-inject=false") + k8s.DeployKustomize(t, ch.Ctx.KubectlOptions(t), ch.Cfg.NoCleanupOnFailure, ch.Cfg.DebugDirectory, "../fixtures/bases/static-client") + requirePodContainers(t, ch, "app=static-client", 1) +} + +func deployStaticServer(t *testing.T, cfg *config.TestConfig, ch connhelper.ConnectHelper) { + t.Helper() + + logger.Log(t, "Creating static-server deployment with connect-inject=true") + k8s.DeployKustomize(t, ch.Ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + requirePodContainers(t, ch, "app=static-server", 2) +} + +func writeCrd(t *testing.T, ch connhelper.ConnectHelper, path string) { + t.Helper() + + t.Cleanup(func() { + _, _ = k8s.RunKubectlAndGetOutputE(t, ch.Ctx.KubectlOptions(t), "delete", "-f", path) + }) + + _, err := k8s.RunKubectlAndGetOutputE(t, ch.Ctx.KubectlOptions(t), "apply", "-f", path) + require.NoError(t, err) +} + +func requirePodContainers(t *testing.T, ch connhelper.ConnectHelper, selector string, nContainers int) { + t.Helper() + + opts := ch.Ctx.KubectlOptions(t) + client := ch.Ctx.KubernetesClient(t) + retry.Run(t, func(r *retry.R) { + podList, err := client.CoreV1(). + Pods(opts.Namespace). + List(context.Background(), metav1.ListOptions{LabelSelector: selector}) + require.NoError(r, err) + require.Len(r, podList.Items, 1) + require.Len(r, podList.Items[0].Spec.Containers, nContainers) + }) +} diff --git a/acceptance/tests/fixtures/cases/permissive-mtls/mesh-config-permissive-allowed.yaml b/acceptance/tests/fixtures/cases/permissive-mtls/mesh-config-permissive-allowed.yaml new file mode 100644 index 0000000000..944792588a --- /dev/null +++ b/acceptance/tests/fixtures/cases/permissive-mtls/mesh-config-permissive-allowed.yaml @@ -0,0 +1,6 @@ +apiVersion: consul.hashicorp.com/v1alpha1 +kind: Mesh +metadata: + name: mesh +spec: + allowEnablingPermissiveMutualTLS: true diff --git a/acceptance/tests/fixtures/cases/permissive-mtls/service-defaults-static-server-permissive.yaml b/acceptance/tests/fixtures/cases/permissive-mtls/service-defaults-static-server-permissive.yaml new file mode 100644 index 0000000000..6fd335b361 --- /dev/null +++ b/acceptance/tests/fixtures/cases/permissive-mtls/service-defaults-static-server-permissive.yaml @@ -0,0 +1,7 @@ +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ServiceDefaults +metadata: + name: static-server + namespace: default +spec: + mutualTLSMode: "permissive" diff --git a/acceptance/tests/fixtures/cases/permissive-mtls/service-defaults-static-server-strict.yaml b/acceptance/tests/fixtures/cases/permissive-mtls/service-defaults-static-server-strict.yaml new file mode 100644 index 0000000000..e47ae7aa5d --- /dev/null +++ b/acceptance/tests/fixtures/cases/permissive-mtls/service-defaults-static-server-strict.yaml @@ -0,0 +1,7 @@ +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ServiceDefaults +metadata: + name: static-server + namespace: default +spec: + mutualTLSMode: "strict" From 08534e3454b6621bc654abc08a64a4720e8a9760 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Wed, 21 Jun 2023 16:01:15 -0400 Subject: [PATCH 246/340] Revert "added imagePullPolicy for images in values.yaml (#2310)" (#2415) This reverts commit 285096241e0d5c5b6d53dd8a37889ab3ea5a8af2. --- .changelog/2310.txt | 3 --- .../consul/templates/api-gateway-controller-deployment.yaml | 3 --- charts/consul/templates/api-gateway-gatewayclassconfig.yaml | 1 - charts/consul/templates/client-daemonset.yaml | 1 - charts/consul/templates/cni-daemonset.yaml | 1 - charts/consul/templates/enterprise-license-job.yaml | 1 - charts/consul/templates/gateway-cleanup-job.yaml | 1 - charts/consul/templates/gateway-resources-job.yaml | 1 - charts/consul/templates/mesh-gateway-deployment.yaml | 2 -- charts/consul/templates/partition-init-job.yaml | 1 - charts/consul/templates/server-acl-init-cleanup-job.yaml | 1 - charts/consul/templates/server-acl-init-job.yaml | 1 - charts/consul/templates/server-statefulset.yaml | 1 - .../consul/templates/webhook-cert-manager-deployment.yaml | 1 - charts/consul/values.yaml | 6 ------ 15 files changed, 25 deletions(-) delete mode 100644 .changelog/2310.txt diff --git a/.changelog/2310.txt b/.changelog/2310.txt deleted file mode 100644 index 5e37de44ea..0000000000 --- a/.changelog/2310.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:feature -helm: Added imagePullPolicy global field which can be configured to override the default behaviour. -``` \ No newline at end of file diff --git a/charts/consul/templates/api-gateway-controller-deployment.yaml b/charts/consul/templates/api-gateway-controller-deployment.yaml index c942576034..8c5c2fa73e 100644 --- a/charts/consul/templates/api-gateway-controller-deployment.yaml +++ b/charts/consul/templates/api-gateway-controller-deployment.yaml @@ -57,7 +57,6 @@ spec: containers: - name: api-gateway-controller image: {{ .Values.apiGateway.image }} - imagePullPolicy: {{ .Values.global.imagePullPolicy }} ports: - containerPort: 9090 name: sds @@ -220,7 +219,6 @@ spec: {{- if .Values.global.acls.manageSystemACLs }} - name: copy-consul-bin image: {{ .Values.global.image | quote }} - imagePullPolicy: {{ .Values.global.imagePullPolicy }} command: - cp - /bin/consul @@ -258,7 +256,6 @@ spec: {{- end}} {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 8 }} image: {{ .Values.global.imageK8S }} - imagePullPolicy: {{ .Values.global.imagePullPolicy }} volumeMounts: - mountPath: /consul/login name: consul-data diff --git a/charts/consul/templates/api-gateway-gatewayclassconfig.yaml b/charts/consul/templates/api-gateway-gatewayclassconfig.yaml index 8688ee6ae7..ba0e6c63db 100644 --- a/charts/consul/templates/api-gateway-gatewayclassconfig.yaml +++ b/charts/consul/templates/api-gateway-gatewayclassconfig.yaml @@ -65,7 +65,6 @@ spec: image: consulAPIGateway: {{ .Values.apiGateway.image }} envoy: {{ .Values.apiGateway.imageEnvoy }} - imagePullPolicy: {{ .Values.global.imagePullPolicy }} {{- if .Values.apiGateway.managedGatewayClass.nodeSelector }} nodeSelector: {{ tpl .Values.apiGateway.managedGatewayClass.nodeSelector . | indent 4 | trim }} diff --git a/charts/consul/templates/client-daemonset.yaml b/charts/consul/templates/client-daemonset.yaml index ba19343652..09a70b394e 100644 --- a/charts/consul/templates/client-daemonset.yaml +++ b/charts/consul/templates/client-daemonset.yaml @@ -493,7 +493,6 @@ spec: {{- if .Values.global.acls.manageSystemACLs }} - name: client-acl-init image: {{ .Values.global.imageK8S }} - imagePullPolicy: {{ .Values.global.imagePullPolicy }} env: - name: NAMESPACE valueFrom: diff --git a/charts/consul/templates/cni-daemonset.yaml b/charts/consul/templates/cni-daemonset.yaml index 33ffb0a77e..ae04d9e657 100644 --- a/charts/consul/templates/cni-daemonset.yaml +++ b/charts/consul/templates/cni-daemonset.yaml @@ -61,7 +61,6 @@ spec: # This container installs the consul CNI binaries and CNI network config file on each node - name: install-cni image: {{ .Values.global.imageK8S }} - imagePullPolicy: {{ .Values.global.imagePullPolicy }} securityContext: privileged: true command: diff --git a/charts/consul/templates/enterprise-license-job.yaml b/charts/consul/templates/enterprise-license-job.yaml index 2a3fa01d00..0122690104 100644 --- a/charts/consul/templates/enterprise-license-job.yaml +++ b/charts/consul/templates/enterprise-license-job.yaml @@ -124,7 +124,6 @@ spec: initContainers: - name: ent-license-acl-init image: {{ .Values.global.imageK8S }} - imagePullPolicy: {{ .Values.global.imagePullPolicy }} command: - "/bin/sh" - "-ec" diff --git a/charts/consul/templates/gateway-cleanup-job.yaml b/charts/consul/templates/gateway-cleanup-job.yaml index e4656916de..44f032b5fd 100644 --- a/charts/consul/templates/gateway-cleanup-job.yaml +++ b/charts/consul/templates/gateway-cleanup-job.yaml @@ -37,7 +37,6 @@ spec: containers: - name: gateway-cleanup image: {{ .Values.global.imageK8S }} - imagePullPolicy: {{ .Values.global.imagePullPolicy }} command: - consul-k8s-control-plane args: diff --git a/charts/consul/templates/gateway-resources-job.yaml b/charts/consul/templates/gateway-resources-job.yaml index ea38d7af32..441e64eb14 100644 --- a/charts/consul/templates/gateway-resources-job.yaml +++ b/charts/consul/templates/gateway-resources-job.yaml @@ -37,7 +37,6 @@ spec: containers: - name: gateway-resources image: {{ .Values.global.imageK8S }} - imagePullPolicy: {{ .Values.global.imagePullPolicy }} command: - consul-k8s-control-plane args: diff --git a/charts/consul/templates/mesh-gateway-deployment.yaml b/charts/consul/templates/mesh-gateway-deployment.yaml index 4150b2bdfd..449d6ae492 100644 --- a/charts/consul/templates/mesh-gateway-deployment.yaml +++ b/charts/consul/templates/mesh-gateway-deployment.yaml @@ -121,7 +121,6 @@ spec: initContainers: - name: mesh-gateway-init image: {{ .Values.global.imageK8S }} - imagePullPolicy: {{ .Values.global.imagePullPolicy }} env: - name: NAMESPACE valueFrom: @@ -180,7 +179,6 @@ spec: containers: - name: mesh-gateway image: {{ .Values.global.imageConsulDataplane | quote }} - imagePullPolicy: {{ .Values.global.imagePullPolicy }} {{- if .Values.meshGateway.resources }} resources: {{- if eq (typeOf .Values.meshGateway.resources) "string" }} diff --git a/charts/consul/templates/partition-init-job.yaml b/charts/consul/templates/partition-init-job.yaml index b351d10027..db73ef783b 100644 --- a/charts/consul/templates/partition-init-job.yaml +++ b/charts/consul/templates/partition-init-job.yaml @@ -81,7 +81,6 @@ spec: containers: - name: partition-init-job image: {{ .Values.global.imageK8S }} - imagePullPolicy: {{ .Values.global.imagePullPolicy }} env: {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 10 }} {{- if (and .Values.global.acls.bootstrapToken.secretName .Values.global.acls.bootstrapToken.secretKey) }} diff --git a/charts/consul/templates/server-acl-init-cleanup-job.yaml b/charts/consul/templates/server-acl-init-cleanup-job.yaml index 3676144b40..35b0877ab4 100644 --- a/charts/consul/templates/server-acl-init-cleanup-job.yaml +++ b/charts/consul/templates/server-acl-init-cleanup-job.yaml @@ -53,7 +53,6 @@ spec: containers: - name: server-acl-init-cleanup image: {{ .Values.global.imageK8S }} - imagePullPolicy: {{ .Values.global.imagePullPolicy }} command: - consul-k8s-control-plane args: diff --git a/charts/consul/templates/server-acl-init-job.yaml b/charts/consul/templates/server-acl-init-job.yaml index e42a073c42..e62db41ec2 100644 --- a/charts/consul/templates/server-acl-init-job.yaml +++ b/charts/consul/templates/server-acl-init-job.yaml @@ -122,7 +122,6 @@ spec: containers: - name: server-acl-init-job image: {{ .Values.global.imageK8S }} - imagePullPolicy: {{ .Values.global.imagePullPolicy }} env: - name: NAMESPACE valueFrom: diff --git a/charts/consul/templates/server-statefulset.yaml b/charts/consul/templates/server-statefulset.yaml index 0c2eb1bffa..0bde9b881a 100644 --- a/charts/consul/templates/server-statefulset.yaml +++ b/charts/consul/templates/server-statefulset.yaml @@ -225,7 +225,6 @@ spec: initContainers: - name: locality-init image: {{ .Values.global.imageK8S }} - imagePullPolicy: {{ .Values.global.imagePullPolicy }} env: - name: NODE_NAME valueFrom: diff --git a/charts/consul/templates/webhook-cert-manager-deployment.yaml b/charts/consul/templates/webhook-cert-manager-deployment.yaml index 25e382be84..dd93c039d2 100644 --- a/charts/consul/templates/webhook-cert-manager-deployment.yaml +++ b/charts/consul/templates/webhook-cert-manager-deployment.yaml @@ -50,7 +50,6 @@ spec: -deployment-name={{ template "consul.fullname" . }}-webhook-cert-manager \ -deployment-namespace={{ .Release.Namespace }} image: {{ .Values.global.imageK8S }} - imagePullPolicy: {{ .Values.global.imagePullPolicy }} name: webhook-cert-manager resources: limits: diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index faf7f5bcdf..eff13a24bc 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -52,12 +52,6 @@ global: # Changing the partition name would require an un-install and a re-install with the updated name. # Must be "default" in the server cluster ie the Kubernetes cluster that the Consul server pods are deployed onto. name: "default" - - # Set imagePullPolicy for all images used. This is applies to all the images being used. - # One of "IfNotPresent", "Always", "Never" - # Refer to https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy - # @type: string - imagePullPolicy: "" # The name (and tag) of the Consul Docker image for clients and servers. # This can be overridden per component. This should be pinned to a specific From 883fbdcfa4947cf38dd3e1062c2913021b2545ac Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Thu, 22 Jun 2023 09:22:59 -0700 Subject: [PATCH 247/340] update with new make targets (#2411) - allow configuration of acceptance testing matrices --- Makefile | 11 +++++++++++ .../ci-inputs/aks_acceptance_test_packages.yaml | 3 +++ .../ci-inputs/eks_acceptance_test_packages.yaml | 3 +++ .../ci-inputs/gke_acceptance_test_packages.yaml | 3 +++ .../ci-inputs/kind_acceptance_test_packages.yaml | 6 ++++++ .../build-support/scripts/set_test_package_matrix.sh | 12 ++++++++++++ 6 files changed, 38 insertions(+) create mode 100644 acceptance/ci-inputs/aks_acceptance_test_packages.yaml create mode 100644 acceptance/ci-inputs/eks_acceptance_test_packages.yaml create mode 100644 acceptance/ci-inputs/gke_acceptance_test_packages.yaml create mode 100644 acceptance/ci-inputs/kind_acceptance_test_packages.yaml create mode 100755 control-plane/build-support/scripts/set_test_package_matrix.sh diff --git a/Makefile b/Makefile index 34497b1f07..de35275868 100644 --- a/Makefile +++ b/Makefile @@ -197,6 +197,17 @@ kind-node-image: kubectl-version: @echo $(KUBECTL_VERSION) +kind-test-packages: + @./control-plane/build-support/scripts/set_test_package_matrix.sh "acceptance/ci-inputs/kind_acceptance_test_packages.yaml" + +gke-test-packages: + @./control-plane/build-support/scripts/set_test_package_matrix.sh "acceptance/ci-inputs/gke_acceptance_test_packages.yaml" + +eks-test-packages: + @./control-plane/build-support/scripts/set_test_package_matrix.sh "acceptance/ci-inputs/eks_acceptance_test_packages.yaml" + +aks-test-packages: + @./control-plane/build-support/scripts/set_test_package_matrix.sh "acceptance/ci-inputs/aks_acceptance_test_packages.yaml" # ===========> Release Targets diff --git a/acceptance/ci-inputs/aks_acceptance_test_packages.yaml b/acceptance/ci-inputs/aks_acceptance_test_packages.yaml new file mode 100644 index 0000000000..cef04a3205 --- /dev/null +++ b/acceptance/ci-inputs/aks_acceptance_test_packages.yaml @@ -0,0 +1,3 @@ +- {runner: 0, test-packages: "connect peering snapshot-agent wan-federation"} +- {runner: 1, test-packages: "consul-dns example partitions metrics sync"} +- {runner: 2, test-packages: "basic cli config-entries api-gateway ingress-gateway terminating-gateway vault"} \ No newline at end of file diff --git a/acceptance/ci-inputs/eks_acceptance_test_packages.yaml b/acceptance/ci-inputs/eks_acceptance_test_packages.yaml new file mode 100644 index 0000000000..cef04a3205 --- /dev/null +++ b/acceptance/ci-inputs/eks_acceptance_test_packages.yaml @@ -0,0 +1,3 @@ +- {runner: 0, test-packages: "connect peering snapshot-agent wan-federation"} +- {runner: 1, test-packages: "consul-dns example partitions metrics sync"} +- {runner: 2, test-packages: "basic cli config-entries api-gateway ingress-gateway terminating-gateway vault"} \ No newline at end of file diff --git a/acceptance/ci-inputs/gke_acceptance_test_packages.yaml b/acceptance/ci-inputs/gke_acceptance_test_packages.yaml new file mode 100644 index 0000000000..cef04a3205 --- /dev/null +++ b/acceptance/ci-inputs/gke_acceptance_test_packages.yaml @@ -0,0 +1,3 @@ +- {runner: 0, test-packages: "connect peering snapshot-agent wan-federation"} +- {runner: 1, test-packages: "consul-dns example partitions metrics sync"} +- {runner: 2, test-packages: "basic cli config-entries api-gateway ingress-gateway terminating-gateway vault"} \ No newline at end of file diff --git a/acceptance/ci-inputs/kind_acceptance_test_packages.yaml b/acceptance/ci-inputs/kind_acceptance_test_packages.yaml new file mode 100644 index 0000000000..74991abd76 --- /dev/null +++ b/acceptance/ci-inputs/kind_acceptance_test_packages.yaml @@ -0,0 +1,6 @@ +- {runner: 0, test-packages: "partitions"} +- {runner: 1, test-packages: "peering"} +- {runner: 2, test-packages: "connect snapshot-agent wan-federation"} +- {runner: 3, test-packages: "cli vault metrics"} +- {runner: 4, test-packages: "api-gateway ingress-gateway sync example consul-dns"} +- {runner: 5, test-packages: "config-entries terminating-gateway basic"} \ No newline at end of file diff --git a/control-plane/build-support/scripts/set_test_package_matrix.sh b/control-plane/build-support/scripts/set_test_package_matrix.sh new file mode 100755 index 0000000000..b248cbad07 --- /dev/null +++ b/control-plane/build-support/scripts/set_test_package_matrix.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +INPUT_FILE=$1 + +# convert readable yaml to json for github actions consumption +# do not include any pretty print, print to single line with -I 0 +VALUE=$(yq eval 'select(fileIndex == 0)' "$INPUT_FILE" -o json -I 0) + +echo "$VALUE" \ No newline at end of file From 5b1856e3a26a3b03ac35cc0d5ea58c0ce20e3b80 Mon Sep 17 00:00:00 2001 From: Dan Stough Date: Fri, 23 Jun 2023 10:10:35 -0400 Subject: [PATCH 248/340] feat(helm): add configurable server-acl-init and cleanup resource limits (#2416) * feat(helm): add configurable server-acl-init and cleanup resource limits * Apply suggestions from code review Co-authored-by: Ashwin Venkatesh * bugfix yaml path * fix bats test --------- Co-authored-by: Ashwin Venkatesh --- .changelog/2416.txt | 3 +++ .../server-acl-init-cleanup-job.yaml | 9 +++---- .../consul/templates/server-acl-init-job.yaml | 9 +++---- .../unit/server-acl-init-cleanup-job.bats | 24 +++++++++++++++++ .../consul/test/unit/server-acl-init-job.bats | 24 +++++++++++++++++ charts/consul/values.yaml | 27 +++++++++++++++++++ 6 files changed, 84 insertions(+), 12 deletions(-) create mode 100644 .changelog/2416.txt diff --git a/.changelog/2416.txt b/.changelog/2416.txt new file mode 100644 index 0000000000..e261758542 --- /dev/null +++ b/.changelog/2416.txt @@ -0,0 +1,3 @@ +```release-note:feature +helm: Adds `acls.resources` field which can be configured to override the `resource` settings for the `server-acl-init` and `server-acl-init-cleanup` Jobs. +``` diff --git a/charts/consul/templates/server-acl-init-cleanup-job.yaml b/charts/consul/templates/server-acl-init-cleanup-job.yaml index 35b0877ab4..38ecfcf1b0 100644 --- a/charts/consul/templates/server-acl-init-cleanup-job.yaml +++ b/charts/consul/templates/server-acl-init-cleanup-job.yaml @@ -61,13 +61,10 @@ spec: - -log-json={{ .Values.global.logJSON }} - -k8s-namespace={{ .Release.Namespace }} - {{ template "consul.fullname" . }}-server-acl-init + {{- if .Values.global.acls.resources }} resources: - requests: - memory: "50Mi" - cpu: "50m" - limits: - memory: "50Mi" - cpu: "50m" + {{- toYaml .Values.global.acls.resources | nindent 12 }} + {{- end }} {{- if .Values.global.acls.tolerations }} tolerations: {{ tpl .Values.global.acls.tolerations . | indent 8 | trim }} diff --git a/charts/consul/templates/server-acl-init-job.yaml b/charts/consul/templates/server-acl-init-job.yaml index e62db41ec2..27272d0f76 100644 --- a/charts/consul/templates/server-acl-init-job.yaml +++ b/charts/consul/templates/server-acl-init-job.yaml @@ -307,13 +307,10 @@ spec: {{- end }} {{- end }} {{- end }} + {{- if .Values.global.acls.resources }} resources: - requests: - memory: "50Mi" - cpu: "50m" - limits: - memory: "50Mi" - cpu: "50m" + {{- toYaml .Values.global.acls.resources | nindent 10 }} + {{- end }} {{- if .Values.global.acls.tolerations }} tolerations: {{ tpl .Values.global.acls.tolerations . | indent 8 | trim }} diff --git a/charts/consul/test/unit/server-acl-init-cleanup-job.bats b/charts/consul/test/unit/server-acl-init-cleanup-job.bats index 947cfa9b42..bf6a455d0e 100644 --- a/charts/consul/test/unit/server-acl-init-cleanup-job.bats +++ b/charts/consul/test/unit/server-acl-init-cleanup-job.bats @@ -159,3 +159,27 @@ load _helpers [ "${actualTemplateFoo}" = "bar" ] [ "${actualTemplateBaz}" = "qux" ] } + +#-------------------------------------------------------------------- +# resources + +@test "serverACLInitCleanup/Job: resources defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-cleanup-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq -rc '.spec.template.spec.containers[0].resources' | tee /dev/stderr) + [ "${actual}" = '{"limits":{"cpu":"50m","memory":"50Mi"},"requests":{"cpu":"50m","memory":"50Mi"}}' ] +} + +@test "serverACLInitCleanup/Job: resources can be overridden" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-cleanup-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.acls.resources.foo=bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} diff --git a/charts/consul/test/unit/server-acl-init-job.bats b/charts/consul/test/unit/server-acl-init-job.bats index 81064c95eb..a0d0950e89 100644 --- a/charts/consul/test/unit/server-acl-init-job.bats +++ b/charts/consul/test/unit/server-acl-init-job.bats @@ -2202,3 +2202,27 @@ load _helpers [ "${actualTemplateFoo}" = "bar" ] [ "${actualTemplateBaz}" = "qux" ] } + +#-------------------------------------------------------------------- +# resources + +@test "serverACLInit/Job: resources defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq -rc '.spec.template.spec.containers[0].resources' | tee /dev/stderr) + [ "${actual}" = '{"limits":{"cpu":"50m","memory":"50Mi"},"requests":{"cpu":"50m","memory":"50Mi"}}' ] +} + +@test "serverACLInit/Job: resources can be overridden" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.acls.resources.foo=bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index eff13a24bc..89336e319e 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -430,6 +430,33 @@ global: # @type: string secretKey: null + # The resource requests (CPU, memory, etc.) for the server-acl-init and server-acl-init-cleanup pods. + # This should be a YAML map corresponding to a Kubernetes + # [`ResourceRequirements``](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#resourcerequirements-v1-core) + # object. + # + # Example: + # + # ```yaml + # resources: + # requests: + # memory: '200Mi' + # cpu: '100m' + # limits: + # memory: '200Mi' + # cpu: '100m' + # ``` + # + # @recurse: false + # @type: map + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" + # partitionToken references a Vault secret containing the ACL token to be used in non-default partitions. # This value should only be provided in the default partition and only when setting # the `global.secretsBackend.vault.enabled` value to true. From c6c5d521176749cbbb0e302afddd2620853aca7c Mon Sep 17 00:00:00 2001 From: Alvin Huang <17609145+alvin-huang@users.noreply.github.com> Date: Fri, 23 Jun 2023 13:17:07 -0400 Subject: [PATCH 249/340] update redhat registry id (#2337) --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 711c228e11..917456e9d0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -377,7 +377,7 @@ jobs: pkg_name: consul-k8s-control-plane_${{ env.version }} bin_name: consul-k8s-control-plane workdir: control-plane - redhat_tag: quay.io/redhat-isv-containers/6483ed53b430df51b731406c:${{env.version}}-ubi # this is different than the non-FIPS one + redhat_tag: quay.io/redhat-isv-containers/6486b1beabfc4e51588c0416:${{env.version}}-ubi # this is different than the non-FIPS one build-docker-ubi-dockerhub: name: Docker ${{ matrix.arch }} ${{ matrix.fips }} UBI build for DockerHub From f783f7e924cf93e0d39e5113f113dff8eaef4d1a Mon Sep 17 00:00:00 2001 From: Thomas Eckert Date: Fri, 23 Jun 2023 14:18:58 -0400 Subject: [PATCH 250/340] Fix auditlog config (#2434) --- charts/consul/templates/server-config-configmap.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/consul/templates/server-config-configmap.yaml b/charts/consul/templates/server-config-configmap.yaml index d3a0206afd..7e3d251001 100644 --- a/charts/consul/templates/server-config-configmap.yaml +++ b/charts/consul/templates/server-config-configmap.yaml @@ -191,7 +191,7 @@ data: audit-logging.json: |- { "audit": { - "enabled": "true", + "enabled": true, "sink": { {{- range $index, $element := .Values.server.auditLogs.sinks }} {{- if ne $index 0 }},{{end}} From 79db26379e814694e57e5ddae0cbd5cb76ac03cd Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Fri, 23 Jun 2023 14:48:36 -0400 Subject: [PATCH 251/340] Add acceptance test to test sync + ingress (#2421) --- acceptance/framework/config/config.go | 1 + acceptance/framework/flags/flags.go | 4 + .../tests/fixtures/bases/ingress/ingress.yaml | 23 ++++++ .../fixtures/bases/ingress/kustomization.yaml | 5 ++ acceptance/tests/sync/sync_catalog_test.go | 81 +++++++++++++++++++ 5 files changed, 114 insertions(+) create mode 100644 acceptance/tests/fixtures/bases/ingress/ingress.yaml create mode 100644 acceptance/tests/fixtures/bases/ingress/kustomization.yaml diff --git a/acceptance/framework/config/config.go b/acceptance/framework/config/config.go index 8a5ba7893e..18673ca260 100644 --- a/acceptance/framework/config/config.go +++ b/acceptance/framework/config/config.go @@ -62,6 +62,7 @@ type TestConfig struct { DebugDirectory string UseAKS bool + UseEKS bool UseGKE bool UseKind bool diff --git a/acceptance/framework/flags/flags.go b/acceptance/framework/flags/flags.go index 3b542c5294..fd1831c5e6 100644 --- a/acceptance/framework/flags/flags.go +++ b/acceptance/framework/flags/flags.go @@ -50,6 +50,7 @@ type TestFlags struct { flagDebugDirectory string flagUseAKS bool + flagUseEKS bool flagUseGKE bool flagUseKind bool @@ -121,6 +122,8 @@ func (t *TestFlags) init() { flag.BoolVar(&t.flagUseAKS, "use-aks", false, "If true, the tests will assume they are running against an AKS cluster(s).") + flag.BoolVar(&t.flagUseEKS, "use-eks", false, + "If true, the tests will assume they are running against an EKS cluster(s).") flag.BoolVar(&t.flagUseGKE, "use-gke", false, "If true, the tests will assume they are running against a GKE cluster(s).") flag.BoolVar(&t.flagUseKind, "use-kind", false, @@ -191,6 +194,7 @@ func (t *TestFlags) TestConfigFromFlags() *config.TestConfig { NoCleanupOnFailure: t.flagNoCleanupOnFailure, DebugDirectory: tempDir, UseAKS: t.flagUseAKS, + UseEKS: t.flagUseEKS, UseGKE: t.flagUseGKE, UseKind: t.flagUseKind, } diff --git a/acceptance/tests/fixtures/bases/ingress/ingress.yaml b/acceptance/tests/fixtures/bases/ingress/ingress.yaml new file mode 100644 index 0000000000..7632a187d6 --- /dev/null +++ b/acceptance/tests/fixtures/bases/ingress/ingress.yaml @@ -0,0 +1,23 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: test-ingress + annotations: + kubernetes.io/ingress.class: "alb" + alb.ingress.kubernetes.io/scheme: internet-facing + alb.ingress.kubernetes.io/target-type: ip +spec: + rules: + - http: + paths: + - path: / + pathType: ImplementationSpecific + backend: + service: + name: static-server + port: + number: 80 + host: test.acceptance.com \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/ingress/kustomization.yaml b/acceptance/tests/fixtures/bases/ingress/kustomization.yaml new file mode 100644 index 0000000000..09fd1b7d0b --- /dev/null +++ b/acceptance/tests/fixtures/bases/ingress/kustomization.yaml @@ -0,0 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ingress.yaml diff --git a/acceptance/tests/sync/sync_catalog_test.go b/acceptance/tests/sync/sync_catalog_test.go index 7407126580..2ca8b1ee1f 100644 --- a/acceptance/tests/sync/sync_catalog_test.go +++ b/acceptance/tests/sync/sync_catalog_test.go @@ -79,3 +79,84 @@ func TestSyncCatalog(t *testing.T) { }) } } + +// Test that sync catalog works in both the default installation and +// the secure installation when TLS and ACLs are enabled with an Ingress resource. +// The test will create a test service and a pod and will +// wait for the service to be synced *to* consul. +func TestSyncCatalogWithIngress(t *testing.T) { + cfg := suite.Config() + if cfg.EnableCNI { + t.Skipf("skipping because -enable-cni is set and sync catalog is already tested with regular tproxy") + } + if !cfg.UseEKS { + t.Skipf("skipping because -use-eks is not set and the ingress test only runs on EKS") + } + + cases := map[string]struct { + secure bool + }{ + "non-secure": {secure: false}, + "secure": {secure: true}, + } + + for name, c := range cases { + t.Run(name, func(t *testing.T) { + ctx := suite.Environment().DefaultContext(t) + helmValues := map[string]string{ + "syncCatalog.enabled": "true", + "syncCatalog.ingres.enabled": "true", + "global.tls.enabled": strconv.FormatBool(c.secure), + "global.acls.manageSystemACLs": strconv.FormatBool(c.secure), + } + + releaseName := helpers.RandomName() + consulCluster := consul.NewHelmCluster(t, helmValues, ctx, suite.Config(), releaseName) + + logger.Log(t, "creating ingress resource") + retry.Run(t, func(r *retry.R) { + // Retry the kubectl apply because we've seen sporadic + // "connection refused" errors where the mutating webhook + // endpoint fails initially. + out, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "apply", "-k", "../fixtures/bases/ingress") + require.NoError(r, err, out) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + // Ignore errors here because if the test ran as expected + // the custom resources will have been deleted. + k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-k", "../fixtures/bases/ingress") + }) + }) + + consulCluster.Create(t) + + logger.Log(t, "creating a static-server with a service") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), suite.Config().NoCleanupOnFailure, suite.Config().DebugDirectory, "../fixtures/bases/static-server") + + consulClient, _ := consulCluster.SetupConsulClient(t, c.secure) + + logger.Log(t, "checking that the service has been synced to Consul") + var services map[string][]string + syncedServiceName := fmt.Sprintf("static-server-%s", ctx.KubectlOptions(t).Namespace) + counter := &retry.Counter{Count: 10, Wait: 5 * time.Second} + retry.RunWith(counter, t, func(r *retry.R) { + var err error + services, _, err = consulClient.Catalog().Services(nil) + require.NoError(r, err) + if _, ok := services[syncedServiceName]; !ok { + r.Errorf("service '%s' is not in Consul's list of services %s", syncedServiceName, services) + } + }) + + service, _, err := consulClient.Catalog().Service(syncedServiceName, "", nil) + require.NoError(t, err) + require.Len(t, service, 1) + require.Equal(t, "test.acceptance.com", service[0].Address) + require.Equal(t, []string{"k8s"}, service[0].ServiceTags) + filter := fmt.Sprintf("ServiceID == %q", service[0].ServiceID) + healthChecks, _, err := consulClient.Health().Checks(syncedServiceName, &api.QueryOptions{Filter: filter}) + require.NoError(t, err) + require.Len(t, healthChecks, 1) + require.Equal(t, api.HealthPassing, healthChecks[0].Status) + }) + } +} From c2a149b36242dcfec21fba0dd81655abc91a4a34 Mon Sep 17 00:00:00 2001 From: "hashicorp-copywrite[bot]" <110428419+hashicorp-copywrite[bot]@users.noreply.github.com> Date: Mon, 26 Jun 2023 13:43:08 -0400 Subject: [PATCH 252/340] [COMPLIANCE] Add Copyright and License Headers (#2456) Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com> --- acceptance/ci-inputs/aks_acceptance_test_packages.yaml | 3 +++ acceptance/ci-inputs/eks_acceptance_test_packages.yaml | 3 +++ acceptance/ci-inputs/gke_acceptance_test_packages.yaml | 3 +++ acceptance/ci-inputs/kind_acceptance_test_packages.yaml | 3 +++ acceptance/tests/connect/permissive_mtls_test.go | 3 +++ .../cases/permissive-mtls/mesh-config-permissive-allowed.yaml | 3 +++ .../service-defaults-static-server-permissive.yaml | 3 +++ .../permissive-mtls/service-defaults-static-server-strict.yaml | 3 +++ 8 files changed, 24 insertions(+) diff --git a/acceptance/ci-inputs/aks_acceptance_test_packages.yaml b/acceptance/ci-inputs/aks_acceptance_test_packages.yaml index cef04a3205..c1f1093200 100644 --- a/acceptance/ci-inputs/aks_acceptance_test_packages.yaml +++ b/acceptance/ci-inputs/aks_acceptance_test_packages.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + - {runner: 0, test-packages: "connect peering snapshot-agent wan-federation"} - {runner: 1, test-packages: "consul-dns example partitions metrics sync"} - {runner: 2, test-packages: "basic cli config-entries api-gateway ingress-gateway terminating-gateway vault"} \ No newline at end of file diff --git a/acceptance/ci-inputs/eks_acceptance_test_packages.yaml b/acceptance/ci-inputs/eks_acceptance_test_packages.yaml index cef04a3205..c1f1093200 100644 --- a/acceptance/ci-inputs/eks_acceptance_test_packages.yaml +++ b/acceptance/ci-inputs/eks_acceptance_test_packages.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + - {runner: 0, test-packages: "connect peering snapshot-agent wan-federation"} - {runner: 1, test-packages: "consul-dns example partitions metrics sync"} - {runner: 2, test-packages: "basic cli config-entries api-gateway ingress-gateway terminating-gateway vault"} \ No newline at end of file diff --git a/acceptance/ci-inputs/gke_acceptance_test_packages.yaml b/acceptance/ci-inputs/gke_acceptance_test_packages.yaml index cef04a3205..c1f1093200 100644 --- a/acceptance/ci-inputs/gke_acceptance_test_packages.yaml +++ b/acceptance/ci-inputs/gke_acceptance_test_packages.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + - {runner: 0, test-packages: "connect peering snapshot-agent wan-federation"} - {runner: 1, test-packages: "consul-dns example partitions metrics sync"} - {runner: 2, test-packages: "basic cli config-entries api-gateway ingress-gateway terminating-gateway vault"} \ No newline at end of file diff --git a/acceptance/ci-inputs/kind_acceptance_test_packages.yaml b/acceptance/ci-inputs/kind_acceptance_test_packages.yaml index 74991abd76..8677b83c4e 100644 --- a/acceptance/ci-inputs/kind_acceptance_test_packages.yaml +++ b/acceptance/ci-inputs/kind_acceptance_test_packages.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + - {runner: 0, test-packages: "partitions"} - {runner: 1, test-packages: "peering"} - {runner: 2, test-packages: "connect snapshot-agent wan-federation"} diff --git a/acceptance/tests/connect/permissive_mtls_test.go b/acceptance/tests/connect/permissive_mtls_test.go index f0a55779ee..1dcc6fe911 100644 --- a/acceptance/tests/connect/permissive_mtls_test.go +++ b/acceptance/tests/connect/permissive_mtls_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package connect import ( diff --git a/acceptance/tests/fixtures/cases/permissive-mtls/mesh-config-permissive-allowed.yaml b/acceptance/tests/fixtures/cases/permissive-mtls/mesh-config-permissive-allowed.yaml index 944792588a..c336a621e7 100644 --- a/acceptance/tests/fixtures/cases/permissive-mtls/mesh-config-permissive-allowed.yaml +++ b/acceptance/tests/fixtures/cases/permissive-mtls/mesh-config-permissive-allowed.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: Mesh metadata: diff --git a/acceptance/tests/fixtures/cases/permissive-mtls/service-defaults-static-server-permissive.yaml b/acceptance/tests/fixtures/cases/permissive-mtls/service-defaults-static-server-permissive.yaml index 6fd335b361..4559570544 100644 --- a/acceptance/tests/fixtures/cases/permissive-mtls/service-defaults-static-server-permissive.yaml +++ b/acceptance/tests/fixtures/cases/permissive-mtls/service-defaults-static-server-permissive.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceDefaults metadata: diff --git a/acceptance/tests/fixtures/cases/permissive-mtls/service-defaults-static-server-strict.yaml b/acceptance/tests/fixtures/cases/permissive-mtls/service-defaults-static-server-strict.yaml index e47ae7aa5d..cf84c73407 100644 --- a/acceptance/tests/fixtures/cases/permissive-mtls/service-defaults-static-server-strict.yaml +++ b/acceptance/tests/fixtures/cases/permissive-mtls/service-defaults-static-server-strict.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceDefaults metadata: From c83ce0c51d1a1a65eadb30d1cfb516716681dd76 Mon Sep 17 00:00:00 2001 From: Thomas Eckert Date: Mon, 26 Jun 2023 16:31:35 -0400 Subject: [PATCH 253/340] Fix GatewayClassConfig Test Timing Issue (#2409) * Add retryCheckWithWait func * Fix retry timing on GatewayClassConfig test * remove redundant scale, make scale up number max + 1 * NET-4627, fix acceptance tests flake --------- Co-authored-by: Sarah Alsmiller --- .../api_gateway_gatewayclassconfig_test.go | 103 +++++++++--------- .../api-gateway/api_gateway_tenancy_test.go | 6 +- 2 files changed, 59 insertions(+), 50 deletions(-) diff --git a/acceptance/tests/api-gateway/api_gateway_gatewayclassconfig_test.go b/acceptance/tests/api-gateway/api_gateway_gatewayclassconfig_test.go index add65b89af..444af6af4d 100644 --- a/acceptance/tests/api-gateway/api_gateway_gatewayclassconfig_test.go +++ b/acceptance/tests/api-gateway/api_gateway_gatewayclassconfig_test.go @@ -5,7 +5,9 @@ package apigateway import ( "context" + "fmt" "testing" + "time" "github.com/hashicorp/consul-k8s/acceptance/framework/consul" "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" @@ -29,6 +31,15 @@ import ( // minInstances,maxInstances and defaultInstances parameters, and that changing the parent gateway does not affect // the child gateways. func TestAPIGateway_GatewayClassConfig(t *testing.T) { + var ( + defaultInstances = pointer.Int32(2) + maxInstances = pointer.Int32(3) + minInstances = pointer.Int32(1) + + namespace = "default" + gatewayClassName = "gateway-class" + ) + ctx := suite.Environment().DefaultContext(t) cfg := suite.Config() helmValues := map[string]string{ @@ -38,6 +49,7 @@ func TestAPIGateway_GatewayClassConfig(t *testing.T) { releaseName := helpers.RandomName() consulCluster := consul.NewHelmCluster(t, helmValues, ctx, cfg, releaseName) consulCluster.Create(t) + // Override the default proxy config settings for this test. consulClient, _ := consulCluster.SetupConsulClient(t, false) _, _, err := consulClient.ConfigEntries().Set(&api.ProxyConfigEntry{ @@ -50,28 +62,8 @@ func TestAPIGateway_GatewayClassConfig(t *testing.T) { require.NoError(t, err) k8sClient := ctx.ControllerRuntimeClient(t) - namespace := "gateway-namespace" - //create clean namespace - err = k8sClient.Create(context.Background(), &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: namespace, - }, - }) - require.NoError(t, err) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { - logger.Log(t, "deleting gateway namesapce") - k8sClient.Delete(context.Background(), &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: namespace, - }, - }) - }) - - defaultInstances := pointer.Int32(2) - maxInstances := pointer.Int32(8) - minInstances := pointer.Int32(1) - // create a GatewayClassConfig with configuration set + // Create a GatewayClassConfig. gatewayClassConfigName := "gateway-class-config" gatewayClassConfig := &v1alpha1.GatewayClassConfig{ ObjectMeta: metav1.ObjectMeta{ @@ -99,8 +91,7 @@ func TestAPIGateway_GatewayClassConfig(t *testing.T) { Name: gatewayClassConfigName, } - // create gateway class referencing gateway-class-config - gatewayClassName := "gateway-class" + // Create gateway class referencing gateway-class-config. logger.Log(t, "creating controlled gateway class") createGatewayClass(t, k8sClient, gatewayClassName, gatewayClassControllerName, gatewayParametersRef) helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { @@ -108,7 +99,7 @@ func TestAPIGateway_GatewayClassConfig(t *testing.T) { k8sClient.DeleteAllOf(context.Background(), &gwv1beta1.GatewayClass{}) }) - // Create a certificate to reference in listeners + // Create a certificate to reference in listeners. certificateInfo := generateCertificate(t, nil, "certificate.consul.local") certificateName := "certificate" certificate := &corev1.Secret{ @@ -132,24 +123,24 @@ func TestAPIGateway_GatewayClassConfig(t *testing.T) { k8sClient.Delete(context.Background(), certificate) }) - // Create gateway referencing gateway class config - gatewayName := "gateway" + // Create gateway referencing gateway class. + gatewayName := "gcctestgateway" + namespace logger.Log(t, "creating controlled gateway") gateway := createGateway(t, k8sClient, gatewayName, namespace, gatewayClassName, certificateName) - // make sure it exists - logger.Log(t, "checking that gateway one is synchronized to Consul") - checkConsulExists(t, consulClient, api.APIGateway, gatewayName) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { logger.Log(t, "deleting all gateways") k8sClient.DeleteAllOf(context.Background(), &gwv1beta1.Gateway{}, client.InNamespace(namespace)) }) + // Ensure it exists. + logger.Log(t, "checking that gateway is synchronized to Consul") + checkConsulExists(t, consulClient, api.APIGateway, gatewayName) + // Scenario: Gateway deployment should match the default instances defined on the gateway class config logger.Log(t, "checking that gateway instances match defined gateway class config") checkNumberOfInstances(t, k8sClient, consulClient, gateway.Name, gateway.Namespace, defaultInstances, gateway) - //Scenario: Updating the GatewayClassConfig should not affect gateways that have already been created + // Scenario: Updating the GatewayClassConfig should not affect gateways that have already been created logger.Log(t, "updating gatewayclassconfig values") err = k8sClient.Get(context.Background(), types.NamespacedName{Name: gatewayClassConfigName, Namespace: namespace}, gatewayClassConfig) require.NoError(t, err) @@ -159,10 +150,8 @@ func TestAPIGateway_GatewayClassConfig(t *testing.T) { require.NoError(t, err) checkNumberOfInstances(t, k8sClient, consulClient, gateway.Name, gateway.Namespace, defaultInstances, gateway) - //Scenario: gateways should be able to scale independently and not get overridden by the controller unless it's above the max - scale(t, k8sClient, gateway.Name, gateway.Namespace, maxInstances) - checkNumberOfInstances(t, k8sClient, consulClient, gateway.Name, gateway.Namespace, maxInstances, gateway) - scale(t, k8sClient, gateway.Name, gateway.Namespace, pointer.Int32(10)) + // Scenario: gateways should be able to scale independently and not get overridden by the controller unless it's above the max + scale(t, k8sClient, gateway.Name, gateway.Namespace, pointer.Int32(*maxInstances+1)) checkNumberOfInstances(t, k8sClient, consulClient, gateway.Name, gateway.Namespace, maxInstances, gateway) scale(t, k8sClient, gateway.Name, gateway.Namespace, pointer.Int32(0)) checkNumberOfInstances(t, k8sClient, consulClient, gateway.Name, gateway.Namespace, minInstances, gateway) @@ -172,36 +161,52 @@ func TestAPIGateway_GatewayClassConfig(t *testing.T) { func scale(t *testing.T, client client.Client, name, namespace string, scaleTo *int32) { t.Helper() - retryCheck(t, 30, func(r *retry.R) { - var deployment appsv1.Deployment - err := client.Get(context.Background(), types.NamespacedName{Name: name, Namespace: namespace}, &deployment) - require.NoError(r, err) + var deployment appsv1.Deployment + err := client.Get(context.Background(), types.NamespacedName{Name: name, Namespace: namespace}, &deployment) + require.NoError(t, err) + + logger.Log(t, fmt.Sprintf("scaling gateway from %d to %d", *deployment.Spec.Replicas, *scaleTo)) + + deployment.Spec.Replicas = scaleTo + err = client.Update(context.Background(), &deployment) + require.NoError(t, err) - deployment.Spec.Replicas = scaleTo - err = client.Update(context.Background(), &deployment) - require.NoError(r, err) - }) } func checkNumberOfInstances(t *testing.T, k8client client.Client, consulClient *api.Client, name, namespace string, wantNumber *int32, gateway *gwv1beta1.Gateway) { t.Helper() - retryCheck(t, 30, func(r *retry.R) { - //first check to make sure the number of replicas has been set properly + retryCheckWithWait(t, 30, 10*time.Second, func(r *retry.R) { + logger.Log(t, "checking that gateway instances match defined gateway class config") + logger.Log(t, fmt.Sprintf("want: %d", *wantNumber)) + + // Ensure the number of replicas has been set properly. var deployment appsv1.Deployment err := k8client.Get(context.Background(), types.NamespacedName{Name: name, Namespace: namespace}, &deployment) require.NoError(r, err) - require.EqualValues(r, *wantNumber, *deployment.Spec.Replicas) + logger.Log(t, fmt.Sprintf("deployment replicas: %d", *deployment.Spec.Replicas)) + require.EqualValues(r, *wantNumber, *deployment.Spec.Replicas, "deployment replicas should match the number of instances defined on the gateway class config") - //then check to make sure the number of gateway pods matches the replicas generated + // Ensure the number of gateway pods matches the replicas generated. podList := corev1.PodList{} labels := common.LabelsForGateway(gateway) err = k8client.List(context.Background(), &podList, client.InNamespace(namespace), client.MatchingLabels(labels)) require.NoError(r, err) - require.EqualValues(r, *wantNumber, len(podList.Items)) + logger.Log(t, fmt.Sprintf("number of pods: %d", len(podList.Items))) + require.EqualValues(r, *wantNumber, len(podList.Items), "number of pods should match the number of instances defined on the gateway class config") + // Ensure the number of services matches the replicas generated. services, _, err := consulClient.Catalog().Service(name, "", nil) + seenServices := map[string]interface{}{} require.NoError(r, err) - require.EqualValues(r, *wantNumber, len(services)) + logger.Log(t, fmt.Sprintf("number of services: %d", len(services))) + //we need to double check that we aren't double counting services with the same ID + for _, s := range services { + seenServices[s.ServiceID] = true + logger.Log(t, fmt.Sprintf("service info: id: %s, name: %s, namespace: %s", s.ServiceID, s.ServiceName, s.Namespace)) + } + + logger.Log(t, fmt.Sprintf("number of services: %d", len(services))) + require.EqualValues(r, int(*wantNumber), len(seenServices), "number of services should match the number of instances defined on the gateway class config") }) } diff --git a/acceptance/tests/api-gateway/api_gateway_tenancy_test.go b/acceptance/tests/api-gateway/api_gateway_tenancy_test.go index e7748b9226..716f09bdba 100644 --- a/acceptance/tests/api-gateway/api_gateway_tenancy_test.go +++ b/acceptance/tests/api-gateway/api_gateway_tenancy_test.go @@ -347,9 +347,13 @@ func generateCertificate(t *testing.T, ca *certificateInfo, commonName string) * } func retryCheck(t *testing.T, count int, fn func(r *retry.R)) { + retryCheckWithWait(t, count, 2*time.Second, fn) +} + +func retryCheckWithWait(t *testing.T, count int, wait time.Duration, fn func(r *retry.R)) { t.Helper() - counter := &retry.Counter{Count: count, Wait: 2 * time.Second} + counter := &retry.Counter{Count: count, Wait: wait} retry.RunWith(counter, t, fn) } From 95af4c7b135fae46f7818a49ae7e927649d0b094 Mon Sep 17 00:00:00 2001 From: aahel Date: Tue, 27 Jun 2023 07:58:18 +0530 Subject: [PATCH 254/340] always update acl policy if it exists (#2392) * always update acl policy if it exists * added changelog * added unit test * fix typo * added some additional assertions to test * refactored create_or_update unit test --- .changelog/2392.txt | 3 + .../server-acl-init/create_or_update.go | 57 +++++++--------- .../server-acl-init/create_or_update_test.go | 68 +++++++++++++++++++ 3 files changed, 97 insertions(+), 31 deletions(-) create mode 100644 .changelog/2392.txt diff --git a/.changelog/2392.txt b/.changelog/2392.txt new file mode 100644 index 0000000000..e15ef152b1 --- /dev/null +++ b/.changelog/2392.txt @@ -0,0 +1,3 @@ +```release-note:bug +control-plane: Always update ACL policies upon upgrade +``` \ No newline at end of file diff --git a/control-plane/subcommand/server-acl-init/create_or_update.go b/control-plane/subcommand/server-acl-init/create_or_update.go index d14fbc845c..50f215eacb 100644 --- a/control-plane/subcommand/server-acl-init/create_or_update.go +++ b/control-plane/subcommand/server-acl-init/create_or_update.go @@ -315,42 +315,37 @@ func (c *Command) createOrUpdateACLPolicy(policy api.ACLPolicy, consulClient *ap // Allowing the Consul node name to be configurable also requires any sync // policy to be updated in case the node name has changed. if isPolicyExistsErr(err, policy.Name) { - if c.flagEnableNamespaces || c.flagSyncCatalog { - c.log.Info(fmt.Sprintf("Policy %q already exists, updating", policy.Name)) + c.log.Info(fmt.Sprintf("Policy %q already exists, updating", policy.Name)) - // The policy ID is required in any PolicyUpdate call, so first we need to - // get the existing policy to extract its ID. - existingPolicies, _, err := consulClient.ACL().PolicyList(&api.QueryOptions{}) - if err != nil { - return err - } - - // Find the policy that matches our name and description - // and that's the ID we need - for _, existingPolicy := range existingPolicies { - if existingPolicy.Name == policy.Name && existingPolicy.Description == policy.Description { - policy.ID = existingPolicy.ID - } - } + // The policy ID is required in any PolicyUpdate call, so first we need to + // get the existing policy to extract its ID. + existingPolicies, _, err := consulClient.ACL().PolicyList(&api.QueryOptions{}) + if err != nil { + return err + } - // This shouldn't happen, because we're looking for a policy - // only after we've hit a `Policy already exists` error. - // The only time it might happen is if a user has manually created a policy - // with this name but used a different description. In this case, - // we don't want to overwrite the policy so we just error. - if policy.ID == "" { - return fmt.Errorf("policy found with name %q but not with expected description %q; "+ - "if this policy was created manually it must be renamed to something else because this name is reserved by consul-k8s", - policy.Name, policy.Description) + // Find the policy that matches our name and description + // and that's the ID we need + for _, existingPolicy := range existingPolicies { + if existingPolicy.Name == policy.Name && existingPolicy.Description == policy.Description { + policy.ID = existingPolicy.ID } + } - // Update the policy now that we've found its ID - _, _, err = consulClient.ACL().PolicyUpdate(&policy, &api.WriteOptions{}) - return err - } else { - c.log.Info(fmt.Sprintf("Policy %q already exists, skipping update", policy.Name)) - return nil + // This shouldn't happen, because we're looking for a policy + // only after we've hit a `Policy already exists` error. + // The only time it might happen is if a user has manually created a policy + // with this name but used a different description. In this case, + // we don't want to overwrite the policy so we just error. + if policy.ID == "" { + return fmt.Errorf("policy found with name %q but not with expected description %q; "+ + "if this policy was created manually it must be renamed to something else because this name is reserved by consul-k8s", + policy.Name, policy.Description) } + + // Update the policy now that we've found its ID + _, _, err = consulClient.ACL().PolicyUpdate(&policy, &api.WriteOptions{}) + return err } return err } diff --git a/control-plane/subcommand/server-acl-init/create_or_update_test.go b/control-plane/subcommand/server-acl-init/create_or_update_test.go index 6aff677dda..84ccdc1635 100644 --- a/control-plane/subcommand/server-acl-init/create_or_update_test.go +++ b/control-plane/subcommand/server-acl-init/create_or_update_test.go @@ -70,3 +70,71 @@ func TestCreateOrUpdateACLPolicy_ErrorsIfDescriptionDoesNotMatch(t *testing.T) { require.NoError(err) require.Equal(policyDescription, rereadPolicy.Description) } + +func TestCreateOrUpdateACLPolicy(t *testing.T) { + require := require.New(t) + ui := cli.NewMockUi() + k8s := fake.NewSimpleClientset() + cmd := Command{ + UI: ui, + clientset: k8s, + log: hclog.NewNullLogger(), + } + cmd.init() + // Start Consul. + bootToken := "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" + svr, err := testutil.NewTestServerConfigT(t, func(c *testutil.TestServerConfig) { + c.ACL.Enabled = true + c.ACL.Tokens.InitialManagement = bootToken + }) + require.NoError(err) + defer svr.Stop() + svr.WaitForLeader(t) + + // Get a Consul client. + consul, err := api.NewClient(&api.Config{ + Address: svr.HTTPAddr, + Token: bootToken, + }) + require.NoError(err) + connectInjectRule, err := cmd.injectRules() + require.NoError(err) + aclReplRule, err := cmd.aclReplicationRules() + require.NoError(err) + policyDescription := "policy-description" + policyName := "policy-name" + cases := []struct { + Name string + PolicyDescription string + PolicyName string + Rules string + }{ + { + Name: "create", + PolicyDescription: policyDescription, + PolicyName: policyName, + Rules: connectInjectRule, + }, + { + Name: "update", + PolicyDescription: policyDescription, + PolicyName: policyName, + Rules: aclReplRule, + }, + } + for _, tt := range cases { + t.Run(tt.Name, func(t *testing.T) { + err = cmd.createOrUpdateACLPolicy(api.ACLPolicy{ + Name: tt.PolicyName, + Description: tt.PolicyDescription, + Rules: tt.Rules, + }, consul) + require.Nil(err) + policy, _, err := consul.ACL().PolicyReadByName(tt.PolicyName, nil) + require.Nil(err) + require.Equal(tt.Rules, policy.Rules) + require.Equal(tt.PolicyName, policy.Name) + require.Equal(tt.PolicyDescription, policy.Description) + }) + } +} From e17684617a0f2b8894082513f78a135f3ab0b832 Mon Sep 17 00:00:00 2001 From: Mike Morris Date: Tue, 27 Jun 2023 13:55:10 -0400 Subject: [PATCH 255/340] Proxy Lifecycle helm, connect-inject and acceptance tests (#2233) Proxy Lifecycle helm, connect-inject and acceptance tests (#2233) Co-authored-by: Nitya Dhanushkodi --- .changelog/2233.txt | 3 + acceptance/framework/config/config.go | 15 +- .../framework/connhelper/connect_helper.go | 12 +- acceptance/framework/flags/flags.go | 39 +- .../connect/connect_proxy_lifecycle_test.go | 214 +++++++++++ .../templates/connect-inject-deployment.yaml | 13 + .../test/unit/connect-inject-deployment.bats | 155 +++++++- charts/consul/values.yaml | 26 +- cli/helm/values.go | 11 +- control-plane/connect-inject/common/common.go | 34 ++ .../connect-inject/common/common_test.go | 166 +++++++++ .../constants/annotations_and_labels.go | 7 + .../connect-inject/constants/constants.go | 6 + .../lifecycle/lifecycle_configuration.go | 95 +++++ .../lifecycle/lifecycle_configuration_test.go | 351 ++++++++++++++++++ .../metrics/metrics_configuration.go | 42 +-- .../metrics/metrics_configuration_test.go | 143 ------- .../webhook/consul_dataplane_sidecar.go | 39 ++ .../webhook/consul_dataplane_sidecar_test.go | 191 +++++++++- .../connect-inject/webhook/mesh_webhook.go | 8 + .../subcommand/inject-connect/command.go | 25 ++ 21 files changed, 1353 insertions(+), 242 deletions(-) create mode 100644 .changelog/2233.txt create mode 100644 acceptance/tests/connect/connect_proxy_lifecycle_test.go create mode 100644 control-plane/connect-inject/lifecycle/lifecycle_configuration.go create mode 100644 control-plane/connect-inject/lifecycle/lifecycle_configuration_test.go diff --git a/.changelog/2233.txt b/.changelog/2233.txt new file mode 100644 index 0000000000..bb929501c9 --- /dev/null +++ b/.changelog/2233.txt @@ -0,0 +1,3 @@ +```release-note:feature +Add support for configuring graceful shutdown proxy lifecycle management settings. +``` diff --git a/acceptance/framework/config/config.go b/acceptance/framework/config/config.go index 18673ca260..7151a75908 100644 --- a/acceptance/framework/config/config.go +++ b/acceptance/framework/config/config.go @@ -46,12 +46,14 @@ type TestConfig struct { DisablePeering bool - HelmChartVersion string - ConsulImage string - ConsulK8SImage string - ConsulVersion *version.Version - EnvoyImage string - ConsulCollectorImage string + HelmChartVersion string + ConsulImage string + ConsulK8SImage string + ConsulDataplaneImage string + ConsulVersion *version.Version + ConsulDataplaneVersion *version.Version + EnvoyImage string + ConsulCollectorImage string HCPResourceID string @@ -110,6 +112,7 @@ func (t *TestConfig) HelmValuesFromConfig() (map[string]string, error) { setIfNotEmpty(helmValues, "global.image", t.ConsulImage) setIfNotEmpty(helmValues, "global.imageK8S", t.ConsulK8SImage) setIfNotEmpty(helmValues, "global.imageEnvoy", t.EnvoyImage) + setIfNotEmpty(helmValues, "global.imageConsulDataplane", t.ConsulDataplaneImage) return helmValues, nil } diff --git a/acceptance/framework/connhelper/connect_helper.go b/acceptance/framework/connhelper/connect_helper.go index 8a7f4d3d53..8695e74d56 100644 --- a/acceptance/framework/connhelper/connect_helper.go +++ b/acceptance/framework/connhelper/connect_helper.go @@ -50,8 +50,8 @@ type ConnectHelper struct { // consulCluster is the cluster to use for the test. consulCluster consul.Cluster - // consulClient is the client used to test service mesh connectivity. - consulClient *api.Client + // ConsulClient is the client used to test service mesh connectivity. + ConsulClient *api.Client } // Setup creates a new cluster using the New*Cluster function and assigns it @@ -69,14 +69,14 @@ func (c *ConnectHelper) Setup(t *testing.T) { func (c *ConnectHelper) Install(t *testing.T) { logger.Log(t, "Installing Consul cluster") c.consulCluster.Create(t) - c.consulClient, _ = c.consulCluster.SetupConsulClient(t, c.Secure) + c.ConsulClient, _ = c.consulCluster.SetupConsulClient(t, c.Secure) } // Upgrade uses the existing Consul cluster and upgrades it using Helm values // set by the Secure, AutoEncrypt, and HelmValues fields. func (c *ConnectHelper) Upgrade(t *testing.T) { require.NotNil(t, c.consulCluster, "consulCluster must be set before calling Upgrade (Call Install first).") - require.NotNil(t, c.consulClient, "consulClient must be set before calling Upgrade (Call Install first).") + require.NotNil(t, c.ConsulClient, "ConsulClient must be set before calling Upgrade (Call Install first).") logger.Log(t, "upgrading Consul cluster") c.consulCluster.Upgrade(t, c.helmValues()) @@ -96,7 +96,7 @@ func (c *ConnectHelper) DeployClientAndServer(t *testing.T) { t.Cleanup(func() { retrier := &retry.Timer{Timeout: 30 * time.Second, Wait: 100 * time.Millisecond} retry.RunWith(retrier, t, func(r *retry.R) { - tokens, _, err := c.consulClient.ACL().TokenList(nil) + tokens, _, err := c.ConsulClient.ACL().TokenList(nil) require.NoError(r, err) for _, token := range tokens { require.NotContains(r, token.Description, StaticServerName) @@ -142,7 +142,7 @@ func (c *ConnectHelper) TestConnectionFailureWithoutIntention(t *testing.T) { // the static-client pod. func (c *ConnectHelper) CreateIntention(t *testing.T) { logger.Log(t, "creating intention") - _, _, err := c.consulClient.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{ + _, _, err := c.ConsulClient.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{ Kind: api.ServiceIntentions, Name: StaticServerName, Sources: []*api.SourceIntention{ diff --git a/acceptance/framework/flags/flags.go b/acceptance/framework/flags/flags.go index fd1831c5e6..5df09f853a 100644 --- a/acceptance/framework/flags/flags.go +++ b/acceptance/framework/flags/flags.go @@ -34,14 +34,16 @@ type TestFlags struct { flagEnableTransparentProxy bool - flagHelmChartVersion string - flagConsulImage string - flagConsulK8sImage string - flagConsulVersion string - flagEnvoyImage string - flagConsulCollectorImage string - flagVaultHelmChartVersion string - flagVaultServerVersion string + flagHelmChartVersion string + flagConsulImage string + flagConsulK8sImage string + flagConsulDataplaneImage string + flagConsulVersion string + flagConsulDataplaneVersion string + flagEnvoyImage string + flagConsulCollectorImage string + flagVaultHelmChartVersion string + flagVaultServerVersion string flagHCPResourceID string @@ -75,7 +77,9 @@ func (t *TestFlags) init() { flag.StringVar(&t.flagConsulImage, "consul-image", "", "The Consul image to use for all tests.") flag.StringVar(&t.flagConsulK8sImage, "consul-k8s-image", "", "The consul-k8s image to use for all tests.") + flag.StringVar(&t.flagConsulDataplaneImage, "consul-dataplane-image", "", "The consul-dataplane image to use for all tests.") flag.StringVar(&t.flagConsulVersion, "consul-version", "", "The consul version used for all tests.") + flag.StringVar(&t.flagConsulDataplaneVersion, "consul-dataplane-version", "", "The consul-dataplane version used for all tests.") flag.StringVar(&t.flagHelmChartVersion, "helm-chart-version", config.HelmChartPath, "The helm chart used for all tests.") flag.StringVar(&t.flagEnvoyImage, "envoy-image", "", "The Envoy image to use for all tests.") flag.StringVar(&t.flagConsulCollectorImage, "consul-collector-image", "", "The consul collector image to use for all tests.") @@ -155,6 +159,7 @@ func (t *TestFlags) TestConfigFromFlags() *config.TestConfig { // if the Version is empty consulVersion will be nil consulVersion, _ := version.NewVersion(t.flagConsulVersion) + consulDataplaneVersion, _ := version.NewVersion(t.flagConsulDataplaneVersion) //vaultserverVersion, _ := version.NewVersion(t.flagVaultServerVersion) return &config.TestConfig{ @@ -180,14 +185,16 @@ func (t *TestFlags) TestConfigFromFlags() *config.TestConfig { DisablePeering: t.flagDisablePeering, - HelmChartVersion: t.flagHelmChartVersion, - ConsulImage: t.flagConsulImage, - ConsulK8SImage: t.flagConsulK8sImage, - ConsulVersion: consulVersion, - EnvoyImage: t.flagEnvoyImage, - ConsulCollectorImage: t.flagConsulCollectorImage, - VaultHelmChartVersion: t.flagVaultHelmChartVersion, - VaultServerVersion: t.flagVaultServerVersion, + HelmChartVersion: t.flagHelmChartVersion, + ConsulImage: t.flagConsulImage, + ConsulK8SImage: t.flagConsulK8sImage, + ConsulDataplaneImage: t.flagConsulDataplaneImage, + ConsulVersion: consulVersion, + ConsulDataplaneVersion: consulDataplaneVersion, + EnvoyImage: t.flagEnvoyImage, + ConsulCollectorImage: t.flagConsulCollectorImage, + VaultHelmChartVersion: t.flagVaultHelmChartVersion, + VaultServerVersion: t.flagVaultServerVersion, HCPResourceID: t.flagHCPResourceID, diff --git a/acceptance/tests/connect/connect_proxy_lifecycle_test.go b/acceptance/tests/connect/connect_proxy_lifecycle_test.go new file mode 100644 index 0000000000..ecdc51b547 --- /dev/null +++ b/acceptance/tests/connect/connect_proxy_lifecycle_test.go @@ -0,0 +1,214 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package connect + +import ( + "context" + "fmt" + "strconv" + "strings" + "testing" + "time" + + "github.com/hashicorp/consul-k8s/acceptance/framework/connhelper" + "github.com/hashicorp/consul-k8s/acceptance/framework/consul" + "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" + "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" + "github.com/hashicorp/consul-k8s/acceptance/framework/logger" + "github.com/hashicorp/consul/sdk/testutil/retry" + "github.com/hashicorp/go-version" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type LifecycleShutdownConfig struct { + secure bool + helmValues map[string]string +} + +const ( + helmDrainListenersKey = "connectInject.sidecarProxy.lifecycle.defaultEnableShutdownDrainListeners" + helmGracePeriodSecondsKey = "connectInject.sidecarProxy.lifecycle.defaultShutdownGracePeriodSeconds" +) + +// Test the endpoints controller cleans up force-killed pods. +func TestConnectInject_ProxyLifecycleShutdown(t *testing.T) { + cfg := suite.Config() + + ver, err := version.NewVersion("1.2.0") + require.NoError(t, err) + if cfg.ConsulDataplaneVersion != nil && cfg.ConsulDataplaneVersion.LessThan(ver) { + t.Skipf("skipping this test because proxy lifecycle management is not supported in consul-dataplane version %v", cfg.ConsulDataplaneVersion.String()) + } + + for _, testCfg := range []LifecycleShutdownConfig{ + {secure: false, helmValues: map[string]string{}}, + {secure: true, helmValues: map[string]string{}}, + {secure: false, helmValues: map[string]string{ + helmDrainListenersKey: "true", + helmGracePeriodSecondsKey: "15", + }}, + {secure: true, helmValues: map[string]string{ + helmDrainListenersKey: "true", + helmGracePeriodSecondsKey: "15", + }}, + {secure: false, helmValues: map[string]string{ + helmDrainListenersKey: "false", + helmGracePeriodSecondsKey: "15", + }}, + {secure: true, helmValues: map[string]string{ + helmDrainListenersKey: "false", + helmGracePeriodSecondsKey: "15", + }}, + {secure: false, helmValues: map[string]string{ + helmDrainListenersKey: "false", + helmGracePeriodSecondsKey: "0", + }}, + {secure: true, helmValues: map[string]string{ + helmDrainListenersKey: "false", + helmGracePeriodSecondsKey: "0", + }}, + } { + // Determine if listeners should be expected to drain inbound connections + var drainListenersEnabled bool + val, ok := testCfg.helmValues[helmDrainListenersKey] + if ok { + drainListenersEnabled, err = strconv.ParseBool(val) + require.NoError(t, err) + } + + // Determine expected shutdown grace period + var gracePeriodSeconds int64 + val, ok = testCfg.helmValues[helmGracePeriodSecondsKey] + if ok { + gracePeriodSeconds, err = strconv.ParseInt(val, 10, 64) + require.NoError(t, err) + } else { + // Half of the helm default to speed tests up + gracePeriodSeconds = 15 + } + + name := fmt.Sprintf("secure: %t, drainListeners: %t, gracePeriodSeconds: %d", testCfg.secure, drainListenersEnabled, gracePeriodSeconds) + t.Run(name, func(t *testing.T) { + ctx := suite.Environment().DefaultContext(t) + releaseName := helpers.RandomName() + + connHelper := connhelper.ConnectHelper{ + ClusterKind: consul.Helm, + Secure: testCfg.secure, + ReleaseName: releaseName, + Ctx: ctx, + Cfg: cfg, + HelmValues: testCfg.helmValues, + } + + connHelper.Setup(t) + connHelper.Install(t) + connHelper.DeployClientAndServer(t) + + // TODO: should this move into connhelper.DeployClientAndServer? + logger.Log(t, "waiting for static-client and static-server to be registered with Consul") + retry.Run(t, func(r *retry.R) { + for _, name := range []string{ + "static-client", + "static-client-sidecar-proxy", + "static-server", + "static-server-sidecar-proxy", + } { + logger.Logf(t, "checking for %s service in Consul catalog", name) + instances, _, err := connHelper.ConsulClient.Catalog().Service(name, "", nil) + r.Check(err) + + if len(instances) != 1 { + r.Errorf("expected 1 instance of %s", name) + } + } + }) + + if testCfg.secure { + connHelper.TestConnectionFailureWithoutIntention(t) + connHelper.CreateIntention(t) + } + + connHelper.TestConnectionSuccess(t) + + // Get static-client pod name + ns := ctx.KubectlOptions(t).Namespace + pods, err := ctx.KubernetesClient(t).CoreV1().Pods(ns).List( + context.Background(), + metav1.ListOptions{ + LabelSelector: "app=static-client", + }, + ) + require.NoError(t, err) + require.Len(t, pods.Items, 1) + clientPodName := pods.Items[0].Name + + var terminationGracePeriod int64 = 60 + logger.Logf(t, "killing the %q pod with %dseconds termination grace period", clientPodName, terminationGracePeriod) + err = ctx.KubernetesClient(t).CoreV1().Pods(ns).Delete(context.Background(), clientPodName, metav1.DeleteOptions{GracePeriodSeconds: &terminationGracePeriod}) + require.NoError(t, err) + + // Exec into terminating pod, not just any static-client pod + args := []string{"exec", clientPodName, "-c", connhelper.StaticClientName, "--", "curl", "-vvvsSf"} + + if cfg.EnableTransparentProxy { + args = append(args, "http://static-server") + } else { + args = append(args, "http://localhost:1234") + } + + if gracePeriodSeconds > 0 { + // Ensure outbound requests are still successful during grace + // period. + retry.RunWith(&retry.Timer{Timeout: time.Duration(gracePeriodSeconds) * time.Second, Wait: 2 * time.Second}, t, func(r *retry.R) { + output, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), args...) + require.NoError(r, err) + require.Condition(r, func() bool { + exists := false + if strings.Contains(output, "curl: (7) Failed to connect") { + exists = true + } + return !exists + }) + }) + + // If listener draining is enabled, ensure inbound + // requests are rejected during grace period. + // connHelper.TestConnectionSuccess(t) + } else { + // Ensure outbound requests fail because proxy has terminated + retry.RunWith(&retry.Timer{Timeout: time.Duration(terminationGracePeriod) * time.Second, Wait: 2 * time.Second}, t, func(r *retry.R) { + output, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), args...) + require.Error(r, err) + require.Condition(r, func() bool { + exists := false + if strings.Contains(output, "curl: (7) Failed to connect") { + exists = true + } + return exists + }) + }) + } + + logger.Log(t, "ensuring pod is deregistered after termination") + retry.Run(t, func(r *retry.R) { + for _, name := range []string{ + "static-client", + "static-client-sidecar-proxy", + } { + logger.Logf(t, "checking for %s service in Consul catalog", name) + instances, _, err := connHelper.ConsulClient.Catalog().Service(name, "", nil) + r.Check(err) + + for _, instance := range instances { + if strings.Contains(instance.ServiceID, clientPodName) { + r.Errorf("%s is still registered", instance.ServiceID) + } + } + } + }) + }) + } +} diff --git a/charts/consul/templates/connect-inject-deployment.yaml b/charts/consul/templates/connect-inject-deployment.yaml index 479e05b25a..14c3961b4e 100644 --- a/charts/consul/templates/connect-inject-deployment.yaml +++ b/charts/consul/templates/connect-inject-deployment.yaml @@ -234,6 +234,19 @@ spec: -default-sidecar-proxy-cpu-request={{ $resources.requests.cpu }} \ {{- end }} -default-envoy-proxy-concurrency={{ .Values.connectInject.sidecarProxy.concurrency }} \ + {{- if .Values.connectInject.sidecarProxy.lifecycle.defaultEnabled }} + -default-enable-sidecar-proxy-lifecycle=true \ + {{- else }} + -default-enable-sidecar-proxy-lifecycle=false \ + {{- end }} + {{- if .Values.connectInject.sidecarProxy.lifecycle.defaultEnableShutdownDrainListeners }} + -default-enable-sidecar-proxy-lifecycle-shutdown-drain-listeners=true \ + {{- else }} + -default-enable-sidecar-proxy-lifecycle-shutdown-drain-listeners=false \ + {{- end }} + -default-sidecar-proxy-lifecycle-shutdown-grace-period-seconds={{ .Values.connectInject.sidecarProxy.lifecycle.defaultShutdownGracePeriodSeconds }} \ + -default-sidecar-proxy-lifecycle-graceful-port={{ .Values.connectInject.sidecarProxy.lifecycle.defaultGracefulPort }} \ + -default-sidecar-proxy-lifecycle-graceful-shutdown-path="{{ .Values.connectInject.sidecarProxy.lifecycle.defaultGracefulShutdownPath }}" \ {{- if .Values.connectInject.initContainer }} {{- $initResources := .Values.connectInject.initContainer.resources }} diff --git a/charts/consul/test/unit/connect-inject-deployment.bats b/charts/consul/test/unit/connect-inject-deployment.bats index c1bc63ffc3..ccc6eca68c 100755 --- a/charts/consul/test/unit/connect-inject-deployment.bats +++ b/charts/consul/test/unit/connect-inject-deployment.bats @@ -999,7 +999,7 @@ load _helpers local actual=$(echo "$cmd" | yq 'any(contains("-init-container-memory-limit=150Mi"))' | tee /dev/stderr) [ "${actual}" = "true" ] - + } @test "connectInject/Deployment: can set init container resources" { @@ -1231,6 +1231,144 @@ load _helpers [ "${actual}" = "true" ] } +#-------------------------------------------------------------------- +# sidecarProxy.lifecycle + +@test "connectInject/Deployment: by default sidecar proxy lifecycle management is enabled" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-default-enable-sidecar-proxy-lifecycle"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "connectInject/Deployment: sidecar proxy lifecycle management can be disabled" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + --set 'connectInject.sidecarProxy.lifecycle.defaultEnabled=false' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-default-enable-sidecar-proxy-lifecycle=false"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "connectInject/Deployment: by default sidecar proxy lifecycle management shutdown listener draining is enabled" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-default-enable-sidecar-proxy-lifecycle-shutdown-drain-listeners"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "connectInject/Deployment: sidecar proxy lifecycle management shutdown listener draining can be disabled" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + --set 'connectInject.sidecarProxy.lifecycle.defaultEnableShutdownDrainListeners=false' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-default-enable-sidecar-proxy-lifecycle-shutdown-drain-listeners=false"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "connectInject/Deployment: by default sidecar proxy lifecycle management shutdown grace period is set to 30 seconds" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-default-sidecar-proxy-lifecycle-shutdown-grace-period-seconds=30"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "connectInject/Deployment: sidecar proxy lifecycle management shutdown grace period can be set" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + --set 'connectInject.sidecarProxy.lifecycle.defaultShutdownGracePeriodSeconds=23' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-default-sidecar-proxy-lifecycle-shutdown-grace-period-seconds=23"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "connectInject/Deployment: by default sidecar proxy lifecycle management port is set to 20600" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-default-sidecar-proxy-lifecycle-graceful-port=20600"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "connectInject/Deployment: sidecar proxy lifecycle management port can be set" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + --set 'connectInject.sidecarProxy.lifecycle.defaultGracefulPort=20307' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-default-sidecar-proxy-lifecycle-graceful-port=20307"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "connectInject/Deployment: by default sidecar proxy lifecycle management graceful shutdown path is set to /graceful_shutdown" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-default-sidecar-proxy-lifecycle-graceful-shutdown-path=\"/graceful_shutdown\""))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "connectInject/Deployment: sidecar proxy lifecycle management graceful shutdown path can be set" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + --set 'connectInject.sidecarProxy.lifecycle.defaultGracefulShutdownPath=/exit' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-default-sidecar-proxy-lifecycle-graceful-shutdown-path=\"/exit\""))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + #-------------------------------------------------------------------- # priorityClassName @@ -1418,7 +1556,7 @@ load _helpers } #-------------------------------------------------------------------- -# cni +# cni @test "connectInject/Deployment: cni is disabled by default" { cd `chart_dir` @@ -2300,7 +2438,7 @@ reservedNameTest() { --set 'global.cloud.authUrl.secretName=auth-url-name' \ . [ "$status" -eq 1 ] - + [[ "$output" =~ "When either global.cloud.authUrl.secretName or global.cloud.authUrl.secretKey is defined, both must be set." ]] } @@ -2321,7 +2459,7 @@ reservedNameTest() { --set 'global.cloud.authUrl.secretKey=auth-url-key' \ . [ "$status" -eq 1 ] - + [[ "$output" =~ "When either global.cloud.authUrl.secretName or global.cloud.authUrl.secretKey is defined, both must be set." ]] } @@ -2342,7 +2480,7 @@ reservedNameTest() { --set 'global.cloud.apiHost.secretName=auth-url-name' \ . [ "$status" -eq 1 ] - + [[ "$output" =~ "When either global.cloud.apiHost.secretName or global.cloud.apiHost.secretKey is defined, both must be set." ]] } @@ -2363,7 +2501,7 @@ reservedNameTest() { --set 'global.cloud.apiHost.secretKey=auth-url-key' \ . [ "$status" -eq 1 ] - + [[ "$output" =~ "When either global.cloud.apiHost.secretName or global.cloud.apiHost.secretKey is defined, both must be set." ]] } @@ -2384,7 +2522,7 @@ reservedNameTest() { --set 'global.cloud.scadaAddress.secretName=scada-address-name' \ . [ "$status" -eq 1 ] - + [[ "$output" =~ "When either global.cloud.scadaAddress.secretName or global.cloud.scadaAddress.secretKey is defined, both must be set." ]] } @@ -2405,7 +2543,7 @@ reservedNameTest() { --set 'global.cloud.scadaAddress.secretKey=scada-address-key' \ . [ "$status" -eq 1 ] - + [[ "$output" =~ "When either global.cloud.scadaAddress.secretName or global.cloud.scadaAddress.secretKey is defined, both must be set." ]] } @@ -2449,4 +2587,3 @@ reservedNameTest() { jq -r '. | select( .name == "CONSUL_TLS_SERVER_NAME").value' | tee /dev/stderr) [ "${actual}" = "server.dc1.consul" ] } - diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 89336e319e..ad1f829399 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -584,7 +584,7 @@ global: # The name (and tag) of the consul-dataplane Docker image used for the # connect-injected sidecar proxies and mesh, terminating, and ingress gateways. # @default: hashicorp/consul-dataplane: - imageConsulDataplane: "docker.mirror.hashicorp.services/hashicorppreview/consul-dataplane:1.2-dev" + imageConsulDataplane: "docker.mirror.hashicorp.services/hashicorppreview/consul-dataplane:1.3-dev" # Configuration for running this Helm chart on the Red Hat OpenShift platform. # This Helm chart currently supports OpenShift v4.x+. @@ -2108,7 +2108,7 @@ connectInject: # @type: string nodeSelector: null - # Toleration settings for gateway pods created with the managed gateway class. + # Toleration settings for gateway pods created with the managed gateway class. # This should be a multi-line string matching the # [Tolerations](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) array in a Pod spec. # @@ -2134,7 +2134,7 @@ connectInject: service: null # This value defines the number of pods to deploy for each Gateway as well as a min and max number of pods for all Gateways - deployment: + deployment: defaultInstances: 1 maxInstances: 1 minInstances: 1 @@ -2554,6 +2554,26 @@ connectInject: # Recommended production default: 100m # @type: string cpu: null + # Set default lifecycle management configuration for sidecar proxy. + # These settings can be overridden on a per-pod basis via these annotations: + # + # - `consul.hashicorp.com/enable-sidecar-proxy-lifecycle` + # - `consul.hashicorp.com/enable-sidecar-proxy-shutdown-drain-listeners` + # - `consul.hashicorp.com/sidecar-proxy-lifecycle-shutdown-grace-period-seconds` + # - `consul.hashicorp.com/sidecar-proxy-lifecycle-graceful-port` + # - `consul.hashicorp.com/sidecar-proxy-lifecycle-graceful-shutdown-path` + # @type: map + lifecycle: + # @type: boolean + defaultEnabled: true + # @type: boolean + defaultEnableShutdownDrainListeners: true + # @type: integer + defaultShutdownGracePeriodSeconds: 30 + # @type: integer + defaultGracefulPort: 20600 + # @type: string + defaultGracefulShutdownPath: "/graceful_shutdown" # The resource settings for the Connect injected init container. If null, the resources # won't be set for the initContainer. The defaults are optimized for developer instances of diff --git a/cli/helm/values.go b/cli/helm/values.go index e6951074b1..06671382d1 100644 --- a/cli/helm/values.go +++ b/cli/helm/values.go @@ -411,7 +411,7 @@ type TransparentProxy struct { } type Metrics struct { - DefaultEnabled string `yaml:"defaultEnabled"` + DefaultEnabled bool `yaml:"defaultEnabled"` DefaultEnableMerging bool `yaml:"defaultEnableMerging"` DefaultMergedMetricsPort int `yaml:"defaultMergedMetricsPort"` DefaultPrometheusScrapePort int `yaml:"defaultPrometheusScrapePort"` @@ -425,12 +425,21 @@ type ACLInjectToken struct { type SidecarProxy struct { Resources Resources `yaml:"resources"` + Lifecycle Lifecycle `yaml:"lifecycle"` } type InitContainer struct { Resources Resources `yaml:"resources"` } +type Lifecycle struct { + DefaultEnabled bool `yaml:"defaultEnabled"` + DefaultEnableShutdownDrainListeners bool `yaml:"defaultEnableShutdownDrainListeners"` + DefaultShutdownGracePeriodSeconds int `yaml:"defaultShutdownGracePeriodSeconds"` + DefaultGracefulPort int `yaml:"defaultGracefulPort"` + DefaultGracefulShutdownPath string `yaml:"defaultGracefulShutdownPath"` +} + type ConnectInject struct { Enabled bool `yaml:"enabled"` Replicas int `yaml:"replicas"` diff --git a/control-plane/connect-inject/common/common.go b/control-plane/connect-inject/common/common.go index 67182e6d0a..a99d9fd12e 100644 --- a/control-plane/connect-inject/common/common.go +++ b/control-plane/connect-inject/common/common.go @@ -12,6 +12,40 @@ import ( corev1 "k8s.io/api/core/v1" ) +// DetermineAndValidatePort behaves as follows: +// If the annotation exists, validate the port and return it. +// If the annotation does not exist, return the default port. +// If the privileged flag is true, it will allow the port to be in the +// privileged port range of 1-1023. Otherwise, it will only allow ports in the +// unprivileged range of 1024-65535. +func DetermineAndValidatePort(pod corev1.Pod, annotation string, defaultPort string, privileged bool) (string, error) { + if raw, ok := pod.Annotations[annotation]; ok && raw != "" { + port, err := PortValue(pod, raw) + if err != nil { + return "", fmt.Errorf("%s annotation value of %s is not a valid integer", annotation, raw) + } + + if privileged && (port < 1 || port > 65535) { + return "", fmt.Errorf("%s annotation value of %d is not in the valid port range 1-65535", annotation, port) + } else if !privileged && (port < 1024 || port > 65535) { + return "", fmt.Errorf("%s annotation value of %d is not in the unprivileged port range 1024-65535", annotation, port) + } + + // If the annotation exists, return the validated port. + return fmt.Sprint(port), nil + } + + // If the annotation does not exist, return the default. + if defaultPort != "" { + port, err := PortValue(pod, defaultPort) + if err != nil { + return "", fmt.Errorf("%s is not a valid port on the pod %s", defaultPort, pod.Name) + } + return fmt.Sprint(port), nil + } + return "", nil +} + // PortValue returns the port of the container for the string value passed // in as an argument on the provided pod. func PortValue(pod corev1.Pod, value string) (int32, error) { diff --git a/control-plane/connect-inject/common/common_test.go b/control-plane/connect-inject/common/common_test.go index 3f995e2874..79a9294fe2 100644 --- a/control-plane/connect-inject/common/common_test.go +++ b/control-plane/connect-inject/common/common_test.go @@ -6,10 +6,153 @@ package common import ( "testing" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" + "github.com/hashicorp/consul-k8s/control-plane/namespaces" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +func TestCommonDetermineAndValidatePort(t *testing.T) { + cases := []struct { + Name string + Pod func(*corev1.Pod) *corev1.Pod + Annotation string + Privileged bool + DefaultPort string + Expected string + Err string + }{ + { + Name: "Valid annotation", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations["consul.hashicorp.com/test-annotation-port"] = "1234" + return pod + }, + Annotation: "consul.hashicorp.com/test-annotation-port", + Privileged: false, + Expected: "1234", + Err: "", + }, + { + Name: "Uses default when there's no annotation", + Pod: func(pod *corev1.Pod) *corev1.Pod { + return pod + }, + Annotation: "consul.hashicorp.com/test-annotation-port", + Privileged: false, + DefaultPort: "4321", + Expected: "4321", + Err: "", + }, + { + Name: "Gets the value of the named default port when there's no annotation", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Spec.Containers[0].Ports = []corev1.ContainerPort{ + { + Name: "web-port", + ContainerPort: 2222, + }, + } + return pod + }, + Annotation: "consul.hashicorp.com/test-annotation-port", + Privileged: false, + DefaultPort: "web-port", + Expected: "2222", + Err: "", + }, + { + Name: "Errors if the named default port doesn't exist on the pod", + Pod: func(pod *corev1.Pod) *corev1.Pod { + return pod + }, + Annotation: "consul.hashicorp.com/test-annotation-port", + Privileged: false, + DefaultPort: "web-port", + Expected: "", + Err: "web-port is not a valid port on the pod minimal", + }, + { + Name: "Gets the value of the named port", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations["consul.hashicorp.com/test-annotation-port"] = "web-port" + pod.Spec.Containers[0].Ports = []corev1.ContainerPort{ + { + Name: "web-port", + ContainerPort: 2222, + }, + } + return pod + }, + Annotation: "consul.hashicorp.com/test-annotation-port", + Privileged: false, + DefaultPort: "4321", + Expected: "2222", + Err: "", + }, + { + Name: "Invalid annotation (not an integer)", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations["consul.hashicorp.com/test-annotation-port"] = "not-an-int" + return pod + }, + Annotation: "consul.hashicorp.com/test-annotation-port", + Privileged: false, + Expected: "", + Err: "consul.hashicorp.com/test-annotation-port annotation value of not-an-int is not a valid integer", + }, + { + Name: "Invalid annotation (integer not in port range)", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations["consul.hashicorp.com/test-annotation-port"] = "100000" + return pod + }, + Annotation: "consul.hashicorp.com/test-annotation-port", + Privileged: true, + Expected: "", + Err: "consul.hashicorp.com/test-annotation-port annotation value of 100000 is not in the valid port range 1-65535", + }, + { + Name: "Invalid annotation (integer not in unprivileged port range)", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations["consul.hashicorp.com/test-annotation-port"] = "22" + return pod + }, + Annotation: "consul.hashicorp.com/test-annotation-port", + Privileged: false, + Expected: "", + Err: "consul.hashicorp.com/test-annotation-port annotation value of 22 is not in the unprivileged port range 1024-65535", + }, + { + Name: "Privileged ports allowed", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations["consul.hashicorp.com/test-annotation-port"] = "22" + return pod + }, + Annotation: "consul.hashicorp.com/test-annotation-port", + Privileged: true, + Expected: "22", + Err: "", + }, + } + + for _, tt := range cases { + t.Run(tt.Name, func(t *testing.T) { + require := require.New(t) + + actual, err := DetermineAndValidatePort(*tt.Pod(minimal()), tt.Annotation, tt.DefaultPort, tt.Privileged) + + if tt.Err == "" { + require.NoError(err) + require.Equal(tt.Expected, actual) + } else { + require.EqualError(err, tt.Err) + } + }) + } +} + func TestPortValue(t *testing.T) { cases := []struct { Name string @@ -93,3 +236,26 @@ func TestPortValue(t *testing.T) { }) } } + +func minimal() *corev1.Pod { + return &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespaces.DefaultNamespace, + Name: "minimal", + Annotations: map[string]string{ + constants.AnnotationService: "foo", + }, + }, + + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "web", + }, + { + Name: "web-side", + }, + }, + }, + } +} diff --git a/control-plane/connect-inject/constants/annotations_and_labels.go b/control-plane/connect-inject/constants/annotations_and_labels.go index fa5c7da26c..4efcc24c74 100644 --- a/control-plane/connect-inject/constants/annotations_and_labels.go +++ b/control-plane/connect-inject/constants/annotations_and_labels.go @@ -100,6 +100,13 @@ const ( AnnotationSidecarProxyMemoryLimit = "consul.hashicorp.com/sidecar-proxy-memory-limit" AnnotationSidecarProxyMemoryRequest = "consul.hashicorp.com/sidecar-proxy-memory-request" + // annotations for sidecar proxy lifecycle configuration. + AnnotationEnableSidecarProxyLifecycle = "consul.hashicorp.com/enable-sidecar-proxy-lifecycle" + AnnotationEnableSidecarProxyLifecycleShutdownDrainListeners = "consul.hashicorp.com/enable-sidecar-proxy-lifecycle-shutdown-drain-listeners" + AnnotationSidecarProxyLifecycleShutdownGracePeriodSeconds = "consul.hashicorp.com/sidecar-proxy-lifecycle-shutdown-grace-period-seconds" + AnnotationSidecarProxyLifecycleGracefulPort = "consul.hashicorp.com/sidecar-proxy-lifecycle-graceful-port" + AnnotationSidecarProxyLifecycleGracefulShutdownPath = "consul.hashicorp.com/sidecar-proxy-lifecycle-graceful-shutdown-path" + // annotations for sidecar volumes. AnnotationConsulSidecarUserVolume = "consul.hashicorp.com/consul-sidecar-user-volume" AnnotationConsulSidecarUserVolumeMount = "consul.hashicorp.com/consul-sidecar-user-volume-mount" diff --git a/control-plane/connect-inject/constants/constants.go b/control-plane/connect-inject/constants/constants.go index 0a341cd577..ca6fe23606 100644 --- a/control-plane/connect-inject/constants/constants.go +++ b/control-plane/connect-inject/constants/constants.go @@ -27,4 +27,10 @@ const ( // MetaKeyPodName is the meta key name for Kubernetes pod name used for the Consul services. MetaKeyPodName = "pod-name" + + // DefaultGracefulPort is the default port that consul-dataplane uses for graceful shutdown. + DefaultGracefulPort = 20600 + + // DefaultGracefulShutdownPath is the default path that consul-dataplane uses for graceful shutdown. + DefaultGracefulShutdownPath = "/graceful_shutdown" ) diff --git a/control-plane/connect-inject/lifecycle/lifecycle_configuration.go b/control-plane/connect-inject/lifecycle/lifecycle_configuration.go new file mode 100644 index 0000000000..651d4eecae --- /dev/null +++ b/control-plane/connect-inject/lifecycle/lifecycle_configuration.go @@ -0,0 +1,95 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package lifecycle + +import ( + "fmt" + "strconv" + + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/common" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" + corev1 "k8s.io/api/core/v1" +) + +// Config represents configuration common to connect-inject components related to proxy lifecycle management. +type Config struct { + DefaultEnableProxyLifecycle bool + DefaultEnableShutdownDrainListeners bool + DefaultShutdownGracePeriodSeconds int + DefaultGracefulPort string + DefaultGracefulShutdownPath string +} + +// EnableProxyLifecycle returns whether proxy lifecycle management is enabled either via the default value in the meshWebhook, or if it's been +// overridden via the annotation. +func (lc Config) EnableProxyLifecycle(pod corev1.Pod) (bool, error) { + enabled := lc.DefaultEnableProxyLifecycle + if raw, ok := pod.Annotations[constants.AnnotationEnableSidecarProxyLifecycle]; ok && raw != "" { + enableProxyLifecycle, err := strconv.ParseBool(raw) + if err != nil { + return false, fmt.Errorf("%s annotation value of %s was invalid: %s", constants.AnnotationEnableSidecarProxyLifecycle, raw, err) + } + enabled = enableProxyLifecycle + } + return enabled, nil +} + +// EnableShutdownDrainListeners returns whether proxy listener draining during shutdown is enabled either via the default value in the meshWebhook, or if it's been +// overridden via the annotation. +func (lc Config) EnableShutdownDrainListeners(pod corev1.Pod) (bool, error) { + enabled := lc.DefaultEnableShutdownDrainListeners + if raw, ok := pod.Annotations[constants.AnnotationEnableSidecarProxyLifecycleShutdownDrainListeners]; ok && raw != "" { + enableShutdownDrainListeners, err := strconv.ParseBool(raw) + if err != nil { + return false, fmt.Errorf("%s annotation value of %s was invalid: %s", constants.AnnotationEnableSidecarProxyLifecycleShutdownDrainListeners, raw, err) + } + enabled = enableShutdownDrainListeners + } + return enabled, nil +} + +// ShutdownGracePeriodSeconds returns how long the sidecar proxy should wait before shutdown, either via the default value in the meshWebhook, or if it's been +// overridden via the annotation. +func (lc Config) ShutdownGracePeriodSeconds(pod corev1.Pod) (int, error) { + shutdownGracePeriodSeconds := lc.DefaultShutdownGracePeriodSeconds + if shutdownGracePeriodSecondsAnnotation, ok := pod.Annotations[constants.AnnotationSidecarProxyLifecycleShutdownGracePeriodSeconds]; ok { + val, err := strconv.ParseUint(shutdownGracePeriodSecondsAnnotation, 10, 64) + if err != nil { + return 0, fmt.Errorf("unable to parse annotation %q: %w", constants.AnnotationSidecarProxyLifecycleShutdownGracePeriodSeconds, err) + } + shutdownGracePeriodSeconds = int(val) + } + return shutdownGracePeriodSeconds, nil +} + +// GracefulPort returns the port on which consul-dataplane should serve the proxy lifecycle management HTTP endpoints, either via the default value in the meshWebhook, or +// if it's been overridden via the annotation. It also validates the port is in the unprivileged port range. +func (lc Config) GracefulPort(pod corev1.Pod) (int, error) { + anno, err := common.DetermineAndValidatePort(pod, constants.AnnotationSidecarProxyLifecycleGracefulPort, lc.DefaultGracefulPort, false) + if err != nil { + return 0, err + } + + if anno == "" { + return constants.DefaultGracefulPort, nil + } + + port, _ := strconv.Atoi(anno) + + return port, nil +} + +// GracefulShutdownPath returns the path on which consul-dataplane should serve the graceful shutdown HTTP endpoint, either via the default value in the meshWebhook, or +// if it's been overridden via the annotation. +func (lc Config) GracefulShutdownPath(pod corev1.Pod) string { + if raw, ok := pod.Annotations[constants.AnnotationSidecarProxyLifecycleGracefulShutdownPath]; ok && raw != "" { + return raw + } + + if lc.DefaultGracefulShutdownPath == "" { + return constants.DefaultGracefulShutdownPath + } + + return lc.DefaultGracefulShutdownPath +} diff --git a/control-plane/connect-inject/lifecycle/lifecycle_configuration_test.go b/control-plane/connect-inject/lifecycle/lifecycle_configuration_test.go new file mode 100644 index 0000000000..64157a3d55 --- /dev/null +++ b/control-plane/connect-inject/lifecycle/lifecycle_configuration_test.go @@ -0,0 +1,351 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package lifecycle + +import ( + "testing" + + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" + "github.com/hashicorp/consul-k8s/control-plane/namespaces" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestLifecycleConfig_EnableSidecarProxyLifecycle(t *testing.T) { + cases := []struct { + Name string + Pod func(*corev1.Pod) *corev1.Pod + LifecycleConfig Config + Expected bool + Err string + }{ + { + Name: "Sidecar proxy lifecycle management enabled via meshWebhook", + Pod: func(pod *corev1.Pod) *corev1.Pod { + return pod + }, + LifecycleConfig: Config{ + DefaultEnableProxyLifecycle: true, + }, + Expected: true, + Err: "", + }, + { + Name: "Sidecar proxy lifecycle management enabled via annotation", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[constants.AnnotationEnableSidecarProxyLifecycle] = "true" + return pod + }, + LifecycleConfig: Config{ + DefaultEnableProxyLifecycle: false, + }, + Expected: true, + Err: "", + }, + { + Name: "Sidecar proxy lifecycle management configured via invalid annotation", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[constants.AnnotationEnableSidecarProxyLifecycle] = "not-a-bool" + return pod + }, + LifecycleConfig: Config{ + DefaultEnableProxyLifecycle: false, + }, + Expected: false, + Err: "consul.hashicorp.com/enable-sidecar-proxy-lifecycle annotation value of not-a-bool was invalid: strconv.ParseBool: parsing \"not-a-bool\": invalid syntax", + }, + } + + for _, tt := range cases { + t.Run(tt.Name, func(t *testing.T) { + require := require.New(t) + lc := tt.LifecycleConfig + + actual, err := lc.EnableProxyLifecycle(*tt.Pod(minimal())) + + if tt.Err == "" { + require.Equal(tt.Expected, actual) + require.NoError(err) + } else { + require.EqualError(err, tt.Err) + } + }) + } +} + +func TestLifecycleConfig_ShutdownDrainListeners(t *testing.T) { + cases := []struct { + Name string + Pod func(*corev1.Pod) *corev1.Pod + LifecycleConfig Config + Expected bool + Err string + }{ + { + Name: "Sidecar proxy shutdown listener draining enabled via meshWebhook", + Pod: func(pod *corev1.Pod) *corev1.Pod { + return pod + }, + LifecycleConfig: Config{ + DefaultEnableShutdownDrainListeners: true, + }, + Expected: true, + Err: "", + }, + { + Name: "Sidecar proxy shutdown listener draining enabled via annotation", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[constants.AnnotationEnableSidecarProxyLifecycleShutdownDrainListeners] = "true" + return pod + }, + LifecycleConfig: Config{ + DefaultEnableShutdownDrainListeners: false, + }, + Expected: true, + Err: "", + }, + { + Name: "Sidecar proxy shutdown listener draining configured via invalid annotation", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[constants.AnnotationEnableSidecarProxyLifecycleShutdownDrainListeners] = "not-a-bool" + return pod + }, + Expected: false, + Err: "consul.hashicorp.com/enable-sidecar-proxy-lifecycle-shutdown-drain-listeners annotation value of not-a-bool was invalid: strconv.ParseBool: parsing \"not-a-bool\": invalid syntax", + }, + } + + for _, tt := range cases { + t.Run(tt.Name, func(t *testing.T) { + require := require.New(t) + lc := tt.LifecycleConfig + + actual, err := lc.EnableShutdownDrainListeners(*tt.Pod(minimal())) + + if tt.Err == "" { + require.Equal(tt.Expected, actual) + require.NoError(err) + } else { + require.EqualError(err, tt.Err) + } + }) + } +} + +func TestLifecycleConfig_ShutdownGracePeriodSeconds(t *testing.T) { + cases := []struct { + Name string + Pod func(*corev1.Pod) *corev1.Pod + LifecycleConfig Config + Expected int + Err string + }{ + { + Name: "Sidecar proxy shutdown grace period set via meshWebhook", + Pod: func(pod *corev1.Pod) *corev1.Pod { + return pod + }, + LifecycleConfig: Config{ + DefaultShutdownGracePeriodSeconds: 10, + }, + Expected: 10, + Err: "", + }, + { + Name: "Sidecar proxy shutdown grace period set via annotation", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[constants.AnnotationSidecarProxyLifecycleShutdownGracePeriodSeconds] = "20" + return pod + }, + LifecycleConfig: Config{ + DefaultShutdownGracePeriodSeconds: 10, + }, + Expected: 20, + Err: "", + }, + { + Name: "Sidecar proxy shutdown grace period configured via invalid annotation, negative number", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[constants.AnnotationSidecarProxyLifecycleShutdownGracePeriodSeconds] = "-1" + return pod + }, + Err: "unable to parse annotation \"consul.hashicorp.com/sidecar-proxy-lifecycle-shutdown-grace-period-seconds\": strconv.ParseUint: parsing \"-1\": invalid syntax", + }, + { + Name: "Sidecar proxy shutdown grace period configured via invalid annotation, not-parseable string", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[constants.AnnotationSidecarProxyLifecycleShutdownGracePeriodSeconds] = "not-int" + return pod + }, + Err: "unable to parse annotation \"consul.hashicorp.com/sidecar-proxy-lifecycle-shutdown-grace-period-seconds\": strconv.ParseUint: parsing \"not-int\": invalid syntax", + }, + } + + for _, tt := range cases { + t.Run(tt.Name, func(t *testing.T) { + require := require.New(t) + lc := tt.LifecycleConfig + + actual, err := lc.ShutdownGracePeriodSeconds(*tt.Pod(minimal())) + + if tt.Err == "" { + require.Equal(tt.Expected, actual) + require.NoError(err) + } else { + require.EqualError(err, tt.Err) + } + }) + } +} + +func TestLifecycleConfig_GracefulPort(t *testing.T) { + cases := []struct { + Name string + Pod func(*corev1.Pod) *corev1.Pod + LifecycleConfig Config + Expected int + Err string + }{ + { + Name: "Sidecar proxy lifecycle graceful port set to default", + Pod: func(pod *corev1.Pod) *corev1.Pod { + return pod + }, + Expected: constants.DefaultGracefulPort, + Err: "", + }, + { + Name: "Sidecar proxy lifecycle graceful port set via meshWebhook", + Pod: func(pod *corev1.Pod) *corev1.Pod { + return pod + }, + LifecycleConfig: Config{ + DefaultGracefulPort: "3000", + }, + Expected: 3000, + Err: "", + }, + { + Name: "Sidecar proxy lifecycle graceful port set via annotation", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[constants.AnnotationSidecarProxyLifecycleGracefulPort] = "9000" + return pod + }, + LifecycleConfig: Config{ + DefaultGracefulPort: "3000", + }, + Expected: 9000, + Err: "", + }, + { + Name: "Sidecar proxy lifecycle graceful port configured via invalid annotation, negative number", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[constants.AnnotationSidecarProxyLifecycleGracefulPort] = "-1" + return pod + }, + Err: "consul.hashicorp.com/sidecar-proxy-lifecycle-graceful-port annotation value of -1 is not in the unprivileged port range 1024-65535", + }, + { + Name: "Sidecar proxy lifecycle graceful port configured via invalid annotation, not-parseable string", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[constants.AnnotationSidecarProxyLifecycleGracefulPort] = "not-int" + return pod + }, + Err: "consul.hashicorp.com/sidecar-proxy-lifecycle-graceful-port annotation value of not-int is not a valid integer", + }, + } + + for _, tt := range cases { + t.Run(tt.Name, func(t *testing.T) { + require := require.New(t) + lc := tt.LifecycleConfig + + actual, err := lc.GracefulPort(*tt.Pod(minimal())) + + if tt.Err == "" { + require.Equal(tt.Expected, actual) + require.NoError(err) + } else { + require.EqualError(err, tt.Err) + } + }) + } +} + +func TestLifecycleConfig_GracefulShutdownPath(t *testing.T) { + cases := []struct { + Name string + Pod func(*corev1.Pod) *corev1.Pod + LifecycleConfig Config + Expected string + Err string + }{ + { + Name: "Sidecar proxy lifecycle graceful shutdown path defaults to /graceful_shutdown", + Pod: func(pod *corev1.Pod) *corev1.Pod { + return pod + }, + Expected: "/graceful_shutdown", + Err: "", + }, + { + Name: "Sidecar proxy lifecycle graceful shutdown path set via meshWebhook", + Pod: func(pod *corev1.Pod) *corev1.Pod { + return pod + }, + LifecycleConfig: Config{ + DefaultGracefulShutdownPath: "/quit", + }, + Expected: "/quit", + Err: "", + }, + { + Name: "Sidecar proxy lifecycle graceful port set via annotation", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[constants.AnnotationSidecarProxyLifecycleGracefulShutdownPath] = "/custom-shutdown-path" + return pod + }, + LifecycleConfig: Config{ + DefaultGracefulShutdownPath: "/quit", + }, + Expected: "/custom-shutdown-path", + Err: "", + }, + } + + for _, tt := range cases { + t.Run(tt.Name, func(t *testing.T) { + require := require.New(t) + lc := tt.LifecycleConfig + + actual := lc.GracefulShutdownPath(*tt.Pod(minimal())) + + require.Equal(tt.Expected, actual) + }) + } +} + +func minimal() *corev1.Pod { + return &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespaces.DefaultNamespace, + Name: "minimal", + Annotations: map[string]string{ + constants.AnnotationService: "foo", + }, + }, + + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "web", + }, + { + Name: "web-side", + }, + }, + }, + } +} diff --git a/control-plane/connect-inject/metrics/metrics_configuration.go b/control-plane/connect-inject/metrics/metrics_configuration.go index f5b819af3d..6f9c29c85b 100644 --- a/control-plane/connect-inject/metrics/metrics_configuration.go +++ b/control-plane/connect-inject/metrics/metrics_configuration.go @@ -98,13 +98,13 @@ func (mc Config) EnableMetricsMerging(pod corev1.Pod) (bool, error) { // MergedMetricsPort returns the port to run the merged metrics server on, either via the default value in the meshWebhook, // or if it's been overridden via the annotation. It also validates the port is in the unprivileged port range. func (mc Config) MergedMetricsPort(pod corev1.Pod) (string, error) { - return determineAndValidatePort(pod, constants.AnnotationMergedMetricsPort, mc.DefaultMergedMetricsPort, false) + return common.DetermineAndValidatePort(pod, constants.AnnotationMergedMetricsPort, mc.DefaultMergedMetricsPort, false) } // PrometheusScrapePort returns the port for Prometheus to scrape from, either via the default value in the meshWebhook, or // if it's been overridden via the annotation. It also validates the port is in the unprivileged port range. func (mc Config) PrometheusScrapePort(pod corev1.Pod) (string, error) { - return determineAndValidatePort(pod, constants.AnnotationPrometheusScrapePort, mc.DefaultPrometheusScrapePort, false) + return common.DetermineAndValidatePort(pod, constants.AnnotationPrometheusScrapePort, mc.DefaultPrometheusScrapePort, false) } // PrometheusScrapePath returns the path for Prometheus to scrape from, either via the default value in the meshWebhook, or @@ -133,14 +133,14 @@ func (mc Config) ServiceMetricsPort(pod corev1.Pod) (string, error) { // written their service in such a way that it expects to be able to use // privileged ports. So, the port metrics are exposed on the service can // be privileged. - return determineAndValidatePort(pod, constants.AnnotationServiceMetricsPort, raw, true) + return common.DetermineAndValidatePort(pod, constants.AnnotationServiceMetricsPort, raw, true) } // If the annotationPort is not set, the serviceMetrics port will be 0 // unless overridden by the service-metrics-port annotation. If the service // metrics port is 0, the consul sidecar will not run a merged metrics // server. - return determineAndValidatePort(pod, constants.AnnotationServiceMetricsPort, "0", true) + return common.DetermineAndValidatePort(pod, constants.AnnotationServiceMetricsPort, "0", true) } // ServiceMetricsPath returns a default of /metrics, or overrides @@ -180,37 +180,3 @@ func (mc Config) ShouldRunMergedMetricsServer(pod corev1.Pod) (bool, error) { } return false, nil } - -// determineAndValidatePort behaves as follows: -// If the annotation exists, validate the port and return it. -// If the annotation does not exist, return the default port. -// If the privileged flag is true, it will allow the port to be in the -// privileged port range of 1-1023. Otherwise, it will only allow ports in the -// unprivileged range of 1024-65535. -func determineAndValidatePort(pod corev1.Pod, annotation string, defaultPort string, privileged bool) (string, error) { - if raw, ok := pod.Annotations[annotation]; ok && raw != "" { - port, err := common.PortValue(pod, raw) - if err != nil { - return "", fmt.Errorf("%s annotation value of %s is not a valid integer", annotation, raw) - } - - if privileged && (port < 1 || port > 65535) { - return "", fmt.Errorf("%s annotation value of %d is not in the valid port range 1-65535", annotation, port) - } else if !privileged && (port < 1024 || port > 65535) { - return "", fmt.Errorf("%s annotation value of %d is not in the unprivileged port range 1024-65535", annotation, port) - } - - // If the annotation exists, return the validated port. - return fmt.Sprint(port), nil - } - - // If the annotation does not exist, return the default. - if defaultPort != "" { - port, err := common.PortValue(pod, defaultPort) - if err != nil { - return "", fmt.Errorf("%s is not a valid port on the pod %s", defaultPort, pod.Name) - } - return fmt.Sprint(port), nil - } - return "", nil -} diff --git a/control-plane/connect-inject/metrics/metrics_configuration_test.go b/control-plane/connect-inject/metrics/metrics_configuration_test.go index ec19d4f55a..12045e28d1 100644 --- a/control-plane/connect-inject/metrics/metrics_configuration_test.go +++ b/control-plane/connect-inject/metrics/metrics_configuration_test.go @@ -307,149 +307,6 @@ func TestMetricsConfigShouldRunMergedMetricsServer(t *testing.T) { } } -// Tests determineAndValidatePort, which in turn tests the -// PrometheusScrapePort() and MergedMetricsPort() functions because their logic -// is just to call out to determineAndValidatePort(). -func TestMetricsConfigDetermineAndValidatePort(t *testing.T) { - cases := []struct { - Name string - Pod func(*corev1.Pod) *corev1.Pod - Annotation string - Privileged bool - DefaultPort string - Expected string - Err string - }{ - { - Name: "Valid annotation", - Pod: func(pod *corev1.Pod) *corev1.Pod { - pod.Annotations["consul.hashicorp.com/test-annotation-port"] = "1234" - return pod - }, - Annotation: "consul.hashicorp.com/test-annotation-port", - Privileged: false, - Expected: "1234", - Err: "", - }, - { - Name: "Uses default when there's no annotation", - Pod: func(pod *corev1.Pod) *corev1.Pod { - return pod - }, - Annotation: "consul.hashicorp.com/test-annotation-port", - Privileged: false, - DefaultPort: "4321", - Expected: "4321", - Err: "", - }, - { - Name: "Gets the value of the named default port when there's no annotation", - Pod: func(pod *corev1.Pod) *corev1.Pod { - pod.Spec.Containers[0].Ports = []corev1.ContainerPort{ - { - Name: "web-port", - ContainerPort: 2222, - }, - } - return pod - }, - Annotation: "consul.hashicorp.com/test-annotation-port", - Privileged: false, - DefaultPort: "web-port", - Expected: "2222", - Err: "", - }, - { - Name: "Errors if the named default port doesn't exist on the pod", - Pod: func(pod *corev1.Pod) *corev1.Pod { - return pod - }, - Annotation: "consul.hashicorp.com/test-annotation-port", - Privileged: false, - DefaultPort: "web-port", - Expected: "", - Err: "web-port is not a valid port on the pod minimal", - }, - { - Name: "Gets the value of the named port", - Pod: func(pod *corev1.Pod) *corev1.Pod { - pod.Annotations["consul.hashicorp.com/test-annotation-port"] = "web-port" - pod.Spec.Containers[0].Ports = []corev1.ContainerPort{ - { - Name: "web-port", - ContainerPort: 2222, - }, - } - return pod - }, - Annotation: "consul.hashicorp.com/test-annotation-port", - Privileged: false, - DefaultPort: "4321", - Expected: "2222", - Err: "", - }, - { - Name: "Invalid annotation (not an integer)", - Pod: func(pod *corev1.Pod) *corev1.Pod { - pod.Annotations["consul.hashicorp.com/test-annotation-port"] = "not-an-int" - return pod - }, - Annotation: "consul.hashicorp.com/test-annotation-port", - Privileged: false, - Expected: "", - Err: "consul.hashicorp.com/test-annotation-port annotation value of not-an-int is not a valid integer", - }, - { - Name: "Invalid annotation (integer not in port range)", - Pod: func(pod *corev1.Pod) *corev1.Pod { - pod.Annotations["consul.hashicorp.com/test-annotation-port"] = "100000" - return pod - }, - Annotation: "consul.hashicorp.com/test-annotation-port", - Privileged: true, - Expected: "", - Err: "consul.hashicorp.com/test-annotation-port annotation value of 100000 is not in the valid port range 1-65535", - }, - { - Name: "Invalid annotation (integer not in unprivileged port range)", - Pod: func(pod *corev1.Pod) *corev1.Pod { - pod.Annotations["consul.hashicorp.com/test-annotation-port"] = "22" - return pod - }, - Annotation: "consul.hashicorp.com/test-annotation-port", - Privileged: false, - Expected: "", - Err: "consul.hashicorp.com/test-annotation-port annotation value of 22 is not in the unprivileged port range 1024-65535", - }, - { - Name: "Privileged ports allowed", - Pod: func(pod *corev1.Pod) *corev1.Pod { - pod.Annotations["consul.hashicorp.com/test-annotation-port"] = "22" - return pod - }, - Annotation: "consul.hashicorp.com/test-annotation-port", - Privileged: true, - Expected: "22", - Err: "", - }, - } - - for _, tt := range cases { - t.Run(tt.Name, func(t *testing.T) { - require := require.New(t) - - actual, err := determineAndValidatePort(*tt.Pod(minimal()), tt.Annotation, tt.DefaultPort, tt.Privileged) - - if tt.Err == "" { - require.NoError(err) - require.Equal(tt.Expected, actual) - } else { - require.EqualError(err, tt.Err) - } - }) - } -} - // Tests MergedMetricsServerConfiguration happy path and error case not covered by other Config tests. func TestMetricsConfigMergedMetricsServerConfiguration(t *testing.T) { cases := []struct { diff --git a/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go b/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go index fe37720b7d..68f57ed061 100644 --- a/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go +++ b/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go @@ -247,6 +247,45 @@ func (w *MeshWebhook) getContainerSidecarArgs(namespace corev1.Namespace, mpi mu args = append(args, fmt.Sprintf("-envoy-admin-bind-port=%d", 19000+mpi.serviceIndex)) } + // The consul-dataplane HTTP listener always starts for graceful shutdown. To avoid port conflicts, the + // graceful port always needs to be set + gracefulPort, err := w.LifecycleConfig.GracefulPort(pod) + if err != nil { + return nil, fmt.Errorf("unable to determine proxy lifecycle graceful port: %w", err) + } + + // To avoid conflicts + if mpi.serviceName != "" { + gracefulPort = gracefulPort + mpi.serviceIndex + } + args = append(args, fmt.Sprintf("-graceful-port=%d", gracefulPort)) + + enableProxyLifecycle, err := w.LifecycleConfig.EnableProxyLifecycle(pod) + if err != nil { + return nil, fmt.Errorf("unable to determine if proxy lifecycle management is enabled: %w", err) + } + if enableProxyLifecycle { + shutdownDrainListeners, err := w.LifecycleConfig.EnableShutdownDrainListeners(pod) + if err != nil { + return nil, fmt.Errorf("unable to determine if proxy lifecycle shutdown listener draining is enabled: %w", err) + } + if shutdownDrainListeners { + args = append(args, "-shutdown-drain-listeners") + } + + shutdownGracePeriodSeconds, err := w.LifecycleConfig.ShutdownGracePeriodSeconds(pod) + if err != nil { + return nil, fmt.Errorf("unable to determine proxy lifecycle shutdown grace period: %w", err) + } + args = append(args, fmt.Sprintf("-shutdown-grace-period-seconds=%d", shutdownGracePeriodSeconds)) + + gracefulShutdownPath := w.LifecycleConfig.GracefulShutdownPath(pod) + if err != nil { + return nil, fmt.Errorf("unable to determine proxy lifecycle graceful shutdown path: %w", err) + } + args = append(args, fmt.Sprintf("-graceful-shutdown-path=%s", gracefulShutdownPath)) + } + // Set a default scrape path that can be overwritten by the annotation. prometheusScrapePath := w.MetricsConfig.PrometheusScrapePath(pod) args = append(args, "-telemetry-prom-scrape-path="+prometheusScrapePath) diff --git a/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go b/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go index 0860293352..d83b094d99 100644 --- a/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go +++ b/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go @@ -10,6 +10,7 @@ import ( "testing" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/lifecycle" "github.com/hashicorp/consul-k8s/control-plane/consul" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" @@ -28,20 +29,20 @@ func TestHandlerConsulDataplaneSidecar(t *testing.T) { }{ "default": { webhookSetupFunc: nil, - additionalExpCmdArgs: " -tls-disabled -telemetry-prom-scrape-path=/metrics", + additionalExpCmdArgs: " -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with custom gRPC port": { webhookSetupFunc: func(w *MeshWebhook) { w.ConsulConfig.GRPCPort = 8602 }, - additionalExpCmdArgs: " -tls-disabled -telemetry-prom-scrape-path=/metrics", + additionalExpCmdArgs: " -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with ACLs": { webhookSetupFunc: func(w *MeshWebhook) { w.AuthMethod = "test-auth-method" }, additionalExpCmdArgs: " -credential-type=login -login-auth-method=test-auth-method -login-bearer-token-path=/var/run/secrets/kubernetes.io/serviceaccount/token " + - "-login-meta=pod=k8snamespace/test-pod -tls-disabled -telemetry-prom-scrape-path=/metrics", + "-login-meta=pod=k8snamespace/test-pod -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with ACLs and namespace mirroring": { webhookSetupFunc: func(w *MeshWebhook) { @@ -50,7 +51,7 @@ func TestHandlerConsulDataplaneSidecar(t *testing.T) { w.EnableK8SNSMirroring = true }, additionalExpCmdArgs: " -credential-type=login -login-auth-method=test-auth-method -login-bearer-token-path=/var/run/secrets/kubernetes.io/serviceaccount/token " + - "-login-meta=pod=k8snamespace/test-pod -login-namespace=default -service-namespace=k8snamespace -tls-disabled -telemetry-prom-scrape-path=/metrics", + "-login-meta=pod=k8snamespace/test-pod -login-namespace=default -service-namespace=k8snamespace -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with ACLs and single destination namespace": { webhookSetupFunc: func(w *MeshWebhook) { @@ -59,7 +60,7 @@ func TestHandlerConsulDataplaneSidecar(t *testing.T) { w.ConsulDestinationNamespace = "test-ns" }, additionalExpCmdArgs: " -credential-type=login -login-auth-method=test-auth-method -login-bearer-token-path=/var/run/secrets/kubernetes.io/serviceaccount/token " + - "-login-meta=pod=k8snamespace/test-pod -login-namespace=test-ns -service-namespace=test-ns -tls-disabled -telemetry-prom-scrape-path=/metrics", + "-login-meta=pod=k8snamespace/test-pod -login-namespace=test-ns -service-namespace=test-ns -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with ACLs and partitions": { webhookSetupFunc: func(w *MeshWebhook) { @@ -67,7 +68,7 @@ func TestHandlerConsulDataplaneSidecar(t *testing.T) { w.ConsulPartition = "test-part" }, additionalExpCmdArgs: " -credential-type=login -login-auth-method=test-auth-method -login-bearer-token-path=/var/run/secrets/kubernetes.io/serviceaccount/token " + - "-login-meta=pod=k8snamespace/test-pod -login-partition=test-part -service-partition=test-part -tls-disabled -telemetry-prom-scrape-path=/metrics", + "-login-meta=pod=k8snamespace/test-pod -login-partition=test-part -service-partition=test-part -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with TLS and CA cert provided": { webhookSetupFunc: func(w *MeshWebhook) { @@ -75,28 +76,28 @@ func TestHandlerConsulDataplaneSidecar(t *testing.T) { w.ConsulTLSServerName = "server.dc1.consul" w.ConsulCACert = "consul-ca-cert" }, - additionalExpCmdArgs: " -tls-server-name=server.dc1.consul -ca-certs=/consul/connect-inject/consul-ca.pem -telemetry-prom-scrape-path=/metrics", + additionalExpCmdArgs: " -tls-server-name=server.dc1.consul -ca-certs=/consul/connect-inject/consul-ca.pem -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with TLS and no CA cert provided": { webhookSetupFunc: func(w *MeshWebhook) { w.TLSEnabled = true w.ConsulTLSServerName = "server.dc1.consul" }, - additionalExpCmdArgs: " -tls-server-name=server.dc1.consul -telemetry-prom-scrape-path=/metrics", + additionalExpCmdArgs: " -tls-server-name=server.dc1.consul -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with single destination namespace": { webhookSetupFunc: func(w *MeshWebhook) { w.EnableNamespaces = true w.ConsulDestinationNamespace = "consul-namespace" }, - additionalExpCmdArgs: " -service-namespace=consul-namespace -tls-disabled -telemetry-prom-scrape-path=/metrics", + additionalExpCmdArgs: " -service-namespace=consul-namespace -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with namespace mirroring": { webhookSetupFunc: func(w *MeshWebhook) { w.EnableNamespaces = true w.EnableK8SNSMirroring = true }, - additionalExpCmdArgs: " -service-namespace=k8snamespace -tls-disabled -telemetry-prom-scrape-path=/metrics", + additionalExpCmdArgs: " -service-namespace=k8snamespace -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with namespace mirroring prefix": { webhookSetupFunc: func(w *MeshWebhook) { @@ -104,38 +105,38 @@ func TestHandlerConsulDataplaneSidecar(t *testing.T) { w.EnableK8SNSMirroring = true w.K8SNSMirroringPrefix = "foo-" }, - additionalExpCmdArgs: " -service-namespace=foo-k8snamespace -tls-disabled -telemetry-prom-scrape-path=/metrics", + additionalExpCmdArgs: " -service-namespace=foo-k8snamespace -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with partitions": { webhookSetupFunc: func(w *MeshWebhook) { w.ConsulPartition = "partition-1" }, - additionalExpCmdArgs: " -service-partition=partition-1 -tls-disabled -telemetry-prom-scrape-path=/metrics", + additionalExpCmdArgs: " -service-partition=partition-1 -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with different log level": { webhookSetupFunc: func(w *MeshWebhook) { w.LogLevel = "debug" }, - additionalExpCmdArgs: " -tls-disabled -telemetry-prom-scrape-path=/metrics", + additionalExpCmdArgs: " -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with different log level and log json": { webhookSetupFunc: func(w *MeshWebhook) { w.LogLevel = "debug" w.LogJSON = true }, - additionalExpCmdArgs: " -tls-disabled -telemetry-prom-scrape-path=/metrics", + additionalExpCmdArgs: " -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "skip server watch enabled": { webhookSetupFunc: func(w *MeshWebhook) { w.SkipServerWatch = true }, - additionalExpCmdArgs: " -server-watch-disabled=true -tls-disabled -telemetry-prom-scrape-path=/metrics", + additionalExpCmdArgs: " -server-watch-disabled=true -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "custom prometheus scrape path": { webhookSetupFunc: func(w *MeshWebhook) { w.MetricsConfig.DefaultPrometheusScrapePath = "/scrape-path" // Simulate what would be passed as a flag }, - additionalExpCmdArgs: " -tls-disabled -telemetry-prom-scrape-path=/scrape-path", + additionalExpCmdArgs: " -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/scrape-path", }, } @@ -622,18 +623,18 @@ func TestHandlerConsulDataplaneSidecar_Multiport(t *testing.T) { } expArgs := []string{ "-addresses 1.1.1.1 -grpc-port=8502 -proxy-service-id-path=/consul/connect-inject/proxyid-web " + - "-log-level=info -log-json=false -envoy-concurrency=0 -tls-disabled -envoy-admin-bind-port=19000 -telemetry-prom-scrape-path=/metrics -- --base-id 0", + "-log-level=info -log-json=false -envoy-concurrency=0 -tls-disabled -envoy-admin-bind-port=19000 -graceful-port=20600 -telemetry-prom-scrape-path=/metrics -- --base-id 0", "-addresses 1.1.1.1 -grpc-port=8502 -proxy-service-id-path=/consul/connect-inject/proxyid-web-admin " + - "-log-level=info -log-json=false -envoy-concurrency=0 -tls-disabled -envoy-admin-bind-port=19001 -telemetry-prom-scrape-path=/metrics -- --base-id 1", + "-log-level=info -log-json=false -envoy-concurrency=0 -tls-disabled -envoy-admin-bind-port=19001 -graceful-port=20601 -telemetry-prom-scrape-path=/metrics -- --base-id 1", } if aclsEnabled { expArgs = []string{ "-addresses 1.1.1.1 -grpc-port=8502 -proxy-service-id-path=/consul/connect-inject/proxyid-web " + "-log-level=info -log-json=false -envoy-concurrency=0 -credential-type=login -login-auth-method=test-auth-method " + - "-login-bearer-token-path=/var/run/secrets/kubernetes.io/serviceaccount/token -login-meta=pod=k8snamespace/test-pod -tls-disabled -envoy-admin-bind-port=19000 -telemetry-prom-scrape-path=/metrics -- --base-id 0", + "-login-bearer-token-path=/var/run/secrets/kubernetes.io/serviceaccount/token -login-meta=pod=k8snamespace/test-pod -tls-disabled -envoy-admin-bind-port=19000 -graceful-port=20600 -telemetry-prom-scrape-path=/metrics -- --base-id 0", "-addresses 1.1.1.1 -grpc-port=8502 -proxy-service-id-path=/consul/connect-inject/proxyid-web-admin " + "-log-level=info -log-json=false -envoy-concurrency=0 -credential-type=login -login-auth-method=test-auth-method " + - "-login-bearer-token-path=/consul/serviceaccount-web-admin/token -login-meta=pod=k8snamespace/test-pod -tls-disabled -envoy-admin-bind-port=19001 -telemetry-prom-scrape-path=/metrics -- --base-id 1", + "-login-bearer-token-path=/consul/serviceaccount-web-admin/token -login-meta=pod=k8snamespace/test-pod -tls-disabled -envoy-admin-bind-port=19001 -graceful-port=20601 -telemetry-prom-scrape-path=/metrics -- --base-id 1", } } expSAVolumeMounts := []corev1.VolumeMount{ @@ -1299,6 +1300,156 @@ func TestHandlerConsulDataplaneSidecar_Metrics(t *testing.T) { } } +func TestHandlerConsulDataplaneSidecar_Lifecycle(t *testing.T) { + gracefulShutdownSeconds := 10 + gracefulPort := "20307" + gracefulShutdownPath := "/exit" + + cases := []struct { + name string + webhook MeshWebhook + annotations map[string]string + expCmdArgs string + expErr string + }{ + { + name: "no defaults, no annotations", + webhook: MeshWebhook{}, + annotations: nil, + expCmdArgs: "", + }, + { + name: "all defaults, no annotations", + webhook: MeshWebhook{ + LifecycleConfig: lifecycle.Config{ + DefaultEnableProxyLifecycle: true, + DefaultEnableShutdownDrainListeners: true, + DefaultShutdownGracePeriodSeconds: gracefulShutdownSeconds, + DefaultGracefulPort: gracefulPort, + DefaultGracefulShutdownPath: gracefulShutdownPath, + }, + }, + annotations: nil, + expCmdArgs: "graceful-port=20307 -shutdown-drain-listeners -shutdown-grace-period-seconds=10 -graceful-shutdown-path=/exit", + }, + { + name: "no defaults, all annotations", + webhook: MeshWebhook{}, + annotations: map[string]string{ + constants.AnnotationEnableSidecarProxyLifecycle: "true", + constants.AnnotationEnableSidecarProxyLifecycleShutdownDrainListeners: "true", + constants.AnnotationSidecarProxyLifecycleShutdownGracePeriodSeconds: fmt.Sprint(gracefulShutdownSeconds), + constants.AnnotationSidecarProxyLifecycleGracefulPort: gracefulPort, + constants.AnnotationSidecarProxyLifecycleGracefulShutdownPath: gracefulShutdownPath, + }, + expCmdArgs: "-graceful-port=20307 -shutdown-drain-listeners -shutdown-grace-period-seconds=10 -graceful-shutdown-path=/exit", + }, + { + name: "annotations override defaults", + webhook: MeshWebhook{ + LifecycleConfig: lifecycle.Config{ + DefaultEnableProxyLifecycle: false, + DefaultEnableShutdownDrainListeners: true, + DefaultShutdownGracePeriodSeconds: gracefulShutdownSeconds, + DefaultGracefulPort: gracefulPort, + DefaultGracefulShutdownPath: gracefulShutdownPath, + }, + }, + annotations: map[string]string{ + constants.AnnotationEnableSidecarProxyLifecycle: "true", + constants.AnnotationEnableSidecarProxyLifecycleShutdownDrainListeners: "false", + constants.AnnotationSidecarProxyLifecycleShutdownGracePeriodSeconds: fmt.Sprint(gracefulShutdownSeconds + 5), + constants.AnnotationSidecarProxyLifecycleGracefulPort: "20317", + constants.AnnotationSidecarProxyLifecycleGracefulShutdownPath: "/foo", + }, + expCmdArgs: "-graceful-port=20317 -shutdown-grace-period-seconds=15 -graceful-shutdown-path=/foo", + }, + { + name: "lifecycle disabled, no annotations", + webhook: MeshWebhook{ + LifecycleConfig: lifecycle.Config{ + DefaultEnableProxyLifecycle: false, + DefaultEnableShutdownDrainListeners: true, + DefaultShutdownGracePeriodSeconds: gracefulShutdownSeconds, + DefaultGracefulPort: gracefulPort, + DefaultGracefulShutdownPath: gracefulShutdownPath, + }, + }, + annotations: nil, + expCmdArgs: "-graceful-port=20307", + }, + { + name: "lifecycle enabled, defaults omited, no annotations", + webhook: MeshWebhook{ + LifecycleConfig: lifecycle.Config{ + DefaultEnableProxyLifecycle: true, + }, + }, + annotations: nil, + expCmdArgs: "", + }, + { + name: "annotations disable lifecycle default", + webhook: MeshWebhook{ + LifecycleConfig: lifecycle.Config{ + DefaultEnableProxyLifecycle: true, + DefaultEnableShutdownDrainListeners: true, + DefaultShutdownGracePeriodSeconds: gracefulShutdownSeconds, + DefaultGracefulPort: gracefulPort, + DefaultGracefulShutdownPath: gracefulShutdownPath, + }, + }, + annotations: map[string]string{ + constants.AnnotationEnableSidecarProxyLifecycle: "false", + }, + expCmdArgs: "-graceful-port=20307", + }, + { + name: "annotations skip graceful shutdown", + webhook: MeshWebhook{ + LifecycleConfig: lifecycle.Config{ + DefaultEnableProxyLifecycle: false, + DefaultEnableShutdownDrainListeners: true, + DefaultShutdownGracePeriodSeconds: gracefulShutdownSeconds, + }, + }, + annotations: map[string]string{ + constants.AnnotationEnableSidecarProxyLifecycle: "false", + constants.AnnotationEnableSidecarProxyLifecycleShutdownDrainListeners: "false", + constants.AnnotationSidecarProxyLifecycleShutdownGracePeriodSeconds: "0", + }, + expCmdArgs: "", + }, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + c.webhook.ConsulConfig = &consul.Config{HTTPPort: 8500, GRPCPort: 8502} + require := require.New(t) + pod := corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: c.annotations, + }, + + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "web", + }, + }, + }, + } + container, err := c.webhook.consulDataplaneSidecar(testNS, pod, multiPortInfo{}) + if c.expErr != "" { + require.NotNil(err) + require.Contains(err.Error(), c.expErr) + } else { + require.NoError(err) + require.Contains(strings.Join(container.Args, " "), c.expCmdArgs) + } + }) + } +} + // boolPtr returns pointer to b. func boolPtr(b bool) *bool { return &b diff --git a/control-plane/connect-inject/webhook/mesh_webhook.go b/control-plane/connect-inject/webhook/mesh_webhook.go index 96c73d93d4..d97bca6646 100644 --- a/control-plane/connect-inject/webhook/mesh_webhook.go +++ b/control-plane/connect-inject/webhook/mesh_webhook.go @@ -17,6 +17,7 @@ import ( "github.com/go-logr/logr" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/common" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/lifecycle" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/metrics" "github.com/hashicorp/consul-k8s/control-plane/consul" "github.com/hashicorp/consul-k8s/control-plane/namespaces" @@ -151,6 +152,10 @@ type MeshWebhook struct { DefaultProxyMemoryRequest resource.Quantity DefaultProxyMemoryLimit resource.Quantity + // LifecycleConfig contains proxy lifecycle management configuration from the inject-connect command and has methods to determine whether + // configuration should come from the default flags or annotations. The meshWebhook uses this to configure container sidecar proxy args. + LifecycleConfig lifecycle.Config + // Default Envoy concurrency flag, this is the number of worker threads to be used by the proxy. DefaultEnvoyProxyConcurrency int @@ -306,6 +311,7 @@ func (w *MeshWebhook) Handle(ctx context.Context, req admission.Request) admissi w.Log.Error(err, "error configuring injection sidecar container", "request name", req.Name) return admission.Errored(http.StatusInternalServerError, fmt.Errorf("error configuring injection sidecar container: %s", err)) } + // TODO: invert to start the Envoy sidecar before the application container pod.Spec.Containers = append(pod.Spec.Containers, envoySidecar) } else { // For multi port pods, check for unsupported cases, mount all relevant service account tokens, and mount an init @@ -376,6 +382,8 @@ func (w *MeshWebhook) Handle(ctx context.Context, req admission.Request) admissi w.Log.Error(err, "error configuring injection sidecar container", "request name", req.Name) return admission.Errored(http.StatusInternalServerError, fmt.Errorf("error configuring injection sidecar container: %s", err)) } + // TODO: invert to start the Envoy sidecar container before the + // application container pod.Spec.Containers = append(pod.Spec.Containers, envoySidecar) } } diff --git a/control-plane/subcommand/inject-connect/command.go b/control-plane/subcommand/inject-connect/command.go index 8bada415db..6767e60130 100644 --- a/control-plane/subcommand/inject-connect/command.go +++ b/control-plane/subcommand/inject-connect/command.go @@ -19,8 +19,10 @@ import ( gatewaycontrollers "github.com/hashicorp/consul-k8s/control-plane/api-gateway/controllers" apicommon "github.com/hashicorp/consul-k8s/control-plane/api/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/controllers/endpoints" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/controllers/peering" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/lifecycle" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/metrics" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/webhook" "github.com/hashicorp/consul-k8s/control-plane/controllers" @@ -86,6 +88,13 @@ type Command struct { flagDefaultSidecarProxyMemoryRequest string flagDefaultEnvoyProxyConcurrency int + // Proxy lifecycle settings. + flagDefaultEnableSidecarProxyLifecycle bool + flagDefaultEnableSidecarProxyLifecycleShutdownDrainListeners bool + flagDefaultSidecarProxyLifecycleShutdownGracePeriodSeconds int + flagDefaultSidecarProxyLifecycleGracefulPort string + flagDefaultSidecarProxyLifecycleGracefulShutdownPath string + // Metrics settings. flagDefaultEnableMetrics bool flagEnableGatewayMetrics bool @@ -220,6 +229,13 @@ func (c *Command) init() { c.flagSet.StringVar(&c.flagDefaultSidecarProxyMemoryRequest, "default-sidecar-proxy-memory-request", "", "Default sidecar proxy memory request.") c.flagSet.StringVar(&c.flagDefaultSidecarProxyMemoryLimit, "default-sidecar-proxy-memory-limit", "", "Default sidecar proxy memory limit.") + // Proxy lifecycle setting flags. + c.flagSet.BoolVar(&c.flagDefaultEnableSidecarProxyLifecycle, "default-enable-sidecar-proxy-lifecycle", false, "Default for enabling sidecar proxy lifecycle management.") + c.flagSet.BoolVar(&c.flagDefaultEnableSidecarProxyLifecycleShutdownDrainListeners, "default-enable-sidecar-proxy-lifecycle-shutdown-drain-listeners", false, "Default for enabling sidecar proxy listener draining of inbound connections during shutdown.") + c.flagSet.IntVar(&c.flagDefaultSidecarProxyLifecycleShutdownGracePeriodSeconds, "default-sidecar-proxy-lifecycle-shutdown-grace-period-seconds", 0, "Default sidecar proxy shutdown grace period in seconds.") + c.flagSet.StringVar(&c.flagDefaultSidecarProxyLifecycleGracefulPort, "default-sidecar-proxy-lifecycle-graceful-port", strconv.Itoa(constants.DefaultGracefulPort), "Default port for sidecar proxy lifecycle management HTTP endpoints.") + c.flagSet.StringVar(&c.flagDefaultSidecarProxyLifecycleGracefulShutdownPath, "default-sidecar-proxy-lifecycle-graceful-shutdown-path", "/graceful_shutdown", "Default sidecar proxy lifecycle management graceful shutdown path.") + // Metrics setting flags. c.flagSet.BoolVar(&c.flagDefaultEnableMetrics, "default-enable-metrics", false, "Default for enabling connect service metrics.") c.flagSet.BoolVar(&c.flagEnableGatewayMetrics, "enable-gateway-metrics", false, "Allows enabling Consul gateway metrics.") @@ -422,6 +438,14 @@ func (c *Command) Run(args []string) int { return 1 } + lifecycleConfig := lifecycle.Config{ + DefaultEnableProxyLifecycle: c.flagDefaultEnableSidecarProxyLifecycle, + DefaultEnableShutdownDrainListeners: c.flagDefaultEnableSidecarProxyLifecycleShutdownDrainListeners, + DefaultShutdownGracePeriodSeconds: c.flagDefaultSidecarProxyLifecycleShutdownGracePeriodSeconds, + DefaultGracefulPort: c.flagDefaultSidecarProxyLifecycleGracefulPort, + DefaultGracefulShutdownPath: c.flagDefaultSidecarProxyLifecycleGracefulShutdownPath, + } + metricsConfig := metrics.Config{ DefaultEnableMetrics: c.flagDefaultEnableMetrics, EnableGatewayMetrics: c.flagEnableGatewayMetrics, @@ -725,6 +749,7 @@ func (c *Command) Run(args []string) int { DefaultProxyMemoryRequest: sidecarProxyMemoryRequest, DefaultProxyMemoryLimit: sidecarProxyMemoryLimit, DefaultEnvoyProxyConcurrency: c.flagDefaultEnvoyProxyConcurrency, + LifecycleConfig: lifecycleConfig, MetricsConfig: metricsConfig, InitContainerResources: initResources, ConsulPartition: c.consul.Partition, From d3f9b670ab8055f0fc8ea4061c2d3c40abeb047f Mon Sep 17 00:00:00 2001 From: David Yu Date: Tue, 27 Jun 2023 19:15:51 -0700 Subject: [PATCH 256/340] PR breaking change release note change (#2469) * Add breaking change to release notes --- .changelog/2392.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.changelog/2392.txt b/.changelog/2392.txt index e15ef152b1..e268c796ff 100644 --- a/.changelog/2392.txt +++ b/.changelog/2392.txt @@ -1,3 +1,6 @@ +```release-note:breaking-change +control-plane: All policies managed by consul-k8s will now be updated on upgrade. If you previously edited the policies after install, your changes will be overwritten. +``` ```release-note:bug -control-plane: Always update ACL policies upon upgrade -``` \ No newline at end of file +control-plane: Always update ACL policies upon upgrade. +``` From 920ee3233d41792134a02f7a0e9e1d2d5ed4482d Mon Sep 17 00:00:00 2001 From: Melisa Griffin Date: Wed, 28 Jun 2023 12:53:41 -0400 Subject: [PATCH 257/340] Adds back gateway controller halting integration test (#2412) Co-authored-by: John Maguire --- .../api-gateway/binding/route_binding.go | 2 +- .../gateway_controller_integration_test.go | 1320 +++++++++++++++++ 2 files changed, 1321 insertions(+), 1 deletion(-) create mode 100644 control-plane/api-gateway/controllers/gateway_controller_integration_test.go diff --git a/control-plane/api-gateway/binding/route_binding.go b/control-plane/api-gateway/binding/route_binding.go index 93e68241a0..36fcda0204 100644 --- a/control-plane/api-gateway/binding/route_binding.go +++ b/control-plane/api-gateway/binding/route_binding.go @@ -457,7 +457,7 @@ func consulCondition(generation int64, status api.ConfigEntryStatus) *metav1.Con for _, c := range status.Conditions { // we only care about the top-level status that isn't in reference // to a resource. - if c.Type == "Accepted" && c.Resource.Name == "" { + if c.Type == "Accepted" && (c.Resource == nil || c.Resource.Name == "") { return &metav1.Condition{ Type: "ConsulAccepted", Reason: c.Reason, diff --git a/control-plane/api-gateway/controllers/gateway_controller_integration_test.go b/control-plane/api-gateway/controllers/gateway_controller_integration_test.go new file mode 100644 index 0000000000..b423ae54e7 --- /dev/null +++ b/control-plane/api-gateway/controllers/gateway_controller_integration_test.go @@ -0,0 +1,1320 @@ +package controllers + +import ( + "context" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "math/big" + "sync" + "testing" + "time" + + mapset "github.com/deckarep/golang-set" + logrtest "github.com/go-logr/logr/testr" + "github.com/stretchr/testify/require" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/cache" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul-k8s/control-plane/helper/test" + "github.com/hashicorp/consul/api" +) + +func TestControllerDoesNotInfinitelyReconcile(t *testing.T) { + s := runtime.NewScheme() + require.NoError(t, clientgoscheme.AddToScheme(s)) + require.NoError(t, gwv1alpha2.Install(s)) + require.NoError(t, gwv1beta1.Install(s)) + require.NoError(t, v1alpha1.AddToScheme(s)) + + testCases := map[string]struct { + namespace string + certFn func(*testing.T, context.Context, client.WithWatch, string) *corev1.Secret + gwFn func(*testing.T, context.Context, client.WithWatch, string) *gwv1beta1.Gateway + httpRouteFn func(*testing.T, context.Context, client.WithWatch, *gwv1beta1.Gateway) *gwv1beta1.HTTPRoute + tcpRouteFn func(*testing.T, context.Context, client.WithWatch, *gwv1beta1.Gateway) *v1alpha2.TCPRoute + }{ + "all fields set": { + namespace: "consul", + certFn: createCert, + gwFn: createAllFieldsSetAPIGW, + httpRouteFn: createAllFieldsSetHTTPRoute, + tcpRouteFn: createAllFieldsSetTCPRoute, + }, + "minimal fields set": { + namespace: "", + certFn: createCert, + gwFn: minimalFieldsSetAPIGW, + httpRouteFn: minimalFieldsSetHTTPRoute, + tcpRouteFn: minimalFieldsSetTCPRoute, + }, + "funky casing to test normalization doesnt cause infinite reconciliation": { + namespace: "", + certFn: createCert, + gwFn: createFunkyCasingFieldsAPIGW, + httpRouteFn: createFunkyCasingFieldsHTTPRoute, + tcpRouteFn: createFunkyCasingFieldsTCPRoute, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + k8sClient := registerFieldIndexersForTest(fake.NewClientBuilder().WithScheme(s)).Build() + consulTestServerClient := test.TestServerWithMockConnMgrWatcher(t, nil) + ctx, cancel := context.WithCancel(context.Background()) + + t.Cleanup(func() { + cancel() + }) + logger := logrtest.New(t) + + cacheCfg := cache.Config{ + ConsulClientConfig: consulTestServerClient.Cfg, + ConsulServerConnMgr: consulTestServerClient.Watcher, + Logger: logger, + } + resourceCache := cache.New(cacheCfg) + + gwCache := cache.NewGatewayCache(ctx, cacheCfg) + + gwCtrl := GatewayController{ + HelmConfig: common.HelmConfig{}, + Log: logger, + Translator: common.ResourceTranslator{}, + cache: resourceCache, + gatewayCache: gwCache, + Client: k8sClient, + allowK8sNamespacesSet: mapset.NewSet(), + denyK8sNamespacesSet: mapset.NewSet(), + } + + go func() { + resourceCache.Run(ctx) + }() + + resourceCache.WaitSynced(ctx) + + gwSub := resourceCache.Subscribe(ctx, api.APIGateway, gwCtrl.transformConsulGateway) + httpRouteSub := resourceCache.Subscribe(ctx, api.HTTPRoute, gwCtrl.transformConsulHTTPRoute(ctx)) + tcpRouteSub := resourceCache.Subscribe(ctx, api.TCPRoute, gwCtrl.transformConsulTCPRoute(ctx)) + inlineCertSub := resourceCache.Subscribe(ctx, api.InlineCertificate, gwCtrl.transformConsulInlineCertificate(ctx)) + + cert := tc.certFn(t, ctx, k8sClient, tc.namespace) + k8sGWObj := tc.gwFn(t, ctx, k8sClient, tc.namespace) + + // reconcile so we add the finalizer + _, err := gwCtrl.Reconcile(ctx, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: k8sGWObj.Namespace, + Name: k8sGWObj.Name, + }, + }) + require.NoError(t, err) + + // reconcile again so that we get the creation with the finalizer + _, err = gwCtrl.Reconcile(ctx, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: k8sGWObj.Namespace, + Name: k8sGWObj.Name, + }, + }) + require.NoError(t, err) + + httpRouteObj := tc.httpRouteFn(t, ctx, k8sClient, k8sGWObj) + tcpRouteObj := tc.tcpRouteFn(t, ctx, k8sClient, k8sGWObj) + + // reconcile again so that we get the route bound to the gateway + _, err = gwCtrl.Reconcile(ctx, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: k8sGWObj.Namespace, + Name: k8sGWObj.Name, + }, + }) + require.NoError(t, err) + + // reconcile again so that we get the route bound to the gateway + _, err = gwCtrl.Reconcile(ctx, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: k8sGWObj.Namespace, + Name: k8sGWObj.Name, + }, + }) + require.NoError(t, err) + + wg := &sync.WaitGroup{} + // we never get the event from the cert because when it's created there are no gateways that reference it + wg.Add(3) + go func(w *sync.WaitGroup) { + gwDone := false + httpRouteDone := false + tcpRouteDone := false + for { + // get the creation events from the upsert and then continually read from channel so we dont block other subs + select { + case <-ctx.Done(): + return + case <-gwSub.Events(): + if !gwDone { + gwDone = true + wg.Done() + } + case <-httpRouteSub.Events(): + if !httpRouteDone { + httpRouteDone = true + wg.Done() + } + case <-tcpRouteSub.Events(): + if !tcpRouteDone { + tcpRouteDone = true + wg.Done() + } + case <-inlineCertSub.Events(): + } + } + }(wg) + + wg.Wait() + + gwNamespaceName := types.NamespacedName{ + Name: k8sGWObj.Name, + Namespace: k8sGWObj.Namespace, + } + + httpRouteNamespaceName := types.NamespacedName{ + Name: httpRouteObj.Name, + Namespace: httpRouteObj.Namespace, + } + + tcpRouteNamespaceName := types.NamespacedName{ + Name: tcpRouteObj.Name, + Namespace: tcpRouteObj.Namespace, + } + + certNamespaceName := types.NamespacedName{ + Name: cert.Name, + Namespace: cert.Namespace, + } + + gwRef := gwCtrl.Translator.ConfigEntryReference(api.APIGateway, gwNamespaceName) + httpRouteRef := gwCtrl.Translator.ConfigEntryReference(api.HTTPRoute, httpRouteNamespaceName) + tcpRouteRef := gwCtrl.Translator.ConfigEntryReference(api.TCPRoute, tcpRouteNamespaceName) + certRef := gwCtrl.Translator.ConfigEntryReference(api.InlineCertificate, certNamespaceName) + + curGWModifyIndex := resourceCache.Get(gwRef).GetModifyIndex() + curHTTPRouteModifyIndex := resourceCache.Get(httpRouteRef).GetModifyIndex() + curTCPRouteModifyIndex := resourceCache.Get(tcpRouteRef).GetModifyIndex() + curCertModifyIndex := resourceCache.Get(certRef).GetModifyIndex() + + err = k8sClient.Get(ctx, gwNamespaceName, k8sGWObj) + require.NoError(t, err) + curGWResourceVersion := k8sGWObj.ResourceVersion + + err = k8sClient.Get(ctx, httpRouteNamespaceName, httpRouteObj) + require.NoError(t, err) + curHTTPRouteResourceVersion := httpRouteObj.ResourceVersion + + err = k8sClient.Get(ctx, tcpRouteNamespaceName, tcpRouteObj) + require.NoError(t, err) + curTCPRouteResourceVersion := tcpRouteObj.ResourceVersion + + err = k8sClient.Get(ctx, certNamespaceName, cert) + require.NoError(t, err) + curCertResourceVersion := cert.ResourceVersion + + go func() { + // reconcile multiple times with no changes to be sure + for i := 0; i < 5; i++ { + _, err = gwCtrl.Reconcile(ctx, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: k8sGWObj.Namespace, + }, + }) + require.NoError(t, err) + } + }() + + require.Never(t, func() bool { + err = k8sClient.Get(ctx, gwNamespaceName, k8sGWObj) + require.NoError(t, err) + newGWResourceVersion := k8sGWObj.ResourceVersion + + err = k8sClient.Get(ctx, httpRouteNamespaceName, httpRouteObj) + require.NoError(t, err) + newHTTPRouteResourceVersion := httpRouteObj.ResourceVersion + + err = k8sClient.Get(ctx, tcpRouteNamespaceName, tcpRouteObj) + require.NoError(t, err) + newTCPRouteResourceVersion := tcpRouteObj.ResourceVersion + + err = k8sClient.Get(ctx, certNamespaceName, cert) + require.NoError(t, err) + newCertResourceVersion := cert.ResourceVersion + + return curGWModifyIndex == resourceCache.Get(gwRef).GetModifyIndex() && + curGWResourceVersion == newGWResourceVersion && + curHTTPRouteModifyIndex == resourceCache.Get(httpRouteRef).GetModifyIndex() && + curHTTPRouteResourceVersion == newHTTPRouteResourceVersion && + curTCPRouteModifyIndex == resourceCache.Get(tcpRouteRef).GetModifyIndex() && + curTCPRouteResourceVersion == newTCPRouteResourceVersion && + curCertModifyIndex == resourceCache.Get(certRef).GetModifyIndex() && + curCertResourceVersion == newCertResourceVersion + }, time.Duration(2*time.Second), time.Duration(500*time.Millisecond), fmt.Sprintf("curGWModifyIndex: %d, newIndx: %d", curGWModifyIndex, resourceCache.Get(gwRef).GetModifyIndex()), + ) + }) + } +} + +func createAllFieldsSetAPIGW(t *testing.T, ctx context.Context, k8sClient client.WithWatch, namespace string) *gwv1beta1.Gateway { + // listener one configuration + listenerOneName := "listener-one" + listenerOneHostname := "*.consul.io" + listenerOnePort := 3366 + listenerOneProtocol := "https" + + // listener two configuration + listenerTwoName := "listener-two" + listenerTwoHostname := "*.consul.io" + listenerTwoPort := 5432 + listenerTwoProtocol := "http" + + // listener three configuration + listenerThreeName := "listener-three" + listenerThreePort := 8081 + listenerThreeProtocol := "tcp" + + // listener four configuration + listenerFourName := "listener-four" + listenerFourHostname := "*.consul.io" + listenerFourPort := 5433 + listenerFourProtocol := "http" + + // Write gw to k8s + gwClassCfg := &v1alpha1.GatewayClassConfig{ + TypeMeta: metav1.TypeMeta{ + Kind: "GatewayClassConfig", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway-class-config", + }, + Spec: v1alpha1.GatewayClassConfigSpec{}, + } + gwClass := &gwv1beta1.GatewayClass{ + TypeMeta: metav1.TypeMeta{ + Kind: "GatewayClass", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "gatewayclass", + }, + Spec: gwv1beta1.GatewayClassSpec{ + ControllerName: "consul.hashicorp.com/gateway-controller", + ParametersRef: &gwv1beta1.ParametersReference{ + Group: "consul.hashicorp.com", + Kind: "GatewayClassConfig", + Name: "gateway-class-config", + }, + Description: new(string), + }, + } + gw := &gwv1beta1.Gateway{ + TypeMeta: metav1.TypeMeta{ + Kind: "Gateway", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "gw", + Namespace: namespace, + Annotations: make(map[string]string), + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gwv1beta1.ObjectName(gwClass.Name), + Listeners: []gwv1beta1.Listener{ + { + Name: gwv1beta1.SectionName(listenerOneName), + Hostname: common.PointerTo(gwv1beta1.Hostname(listenerOneHostname)), + Port: gwv1beta1.PortNumber(listenerOnePort), + Protocol: gwv1beta1.ProtocolType(listenerOneProtocol), + TLS: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{ + { + Kind: common.PointerTo(gwv1beta1.Kind("Secret")), + Name: gwv1beta1.ObjectName("one-cert"), + Namespace: common.PointerTo(gwv1beta1.Namespace(namespace)), + }, + }, + }, + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: common.PointerTo(gwv1beta1.FromNamespaces("All")), + }, + }, + }, + { + Name: gwv1beta1.SectionName(listenerTwoName), + Hostname: common.PointerTo(gwv1beta1.Hostname(listenerTwoHostname)), + Port: gwv1beta1.PortNumber(listenerTwoPort), + Protocol: gwv1beta1.ProtocolType(listenerTwoProtocol), + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: common.PointerTo(gwv1beta1.FromNamespaces("Same")), + }, + }, + }, + { + Name: gwv1beta1.SectionName(listenerThreeName), + Port: gwv1beta1.PortNumber(listenerThreePort), + Protocol: gwv1beta1.ProtocolType(listenerThreeProtocol), + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: common.PointerTo(gwv1beta1.FromNamespaces("All")), + }, + }, + }, + { + Name: gwv1beta1.SectionName(listenerFourName), + Hostname: common.PointerTo(gwv1beta1.Hostname(listenerFourHostname)), + Port: gwv1beta1.PortNumber(listenerFourPort), + Protocol: gwv1beta1.ProtocolType(listenerFourProtocol), + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: common.PointerTo(gwv1beta1.FromNamespaces("Selector")), + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + common.NamespaceNameLabel: "consul", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{}, + }, + }, + }, + }, + }, + }, + } + + err := k8sClient.Create(ctx, gwClassCfg) + require.NoError(t, err) + + err = k8sClient.Create(ctx, gwClass) + require.NoError(t, err) + + err = k8sClient.Create(ctx, gw) + require.NoError(t, err) + + return gw +} + +func createAllFieldsSetHTTPRoute(t *testing.T, ctx context.Context, k8sClient client.WithWatch, gw *gwv1beta1.Gateway) *gwv1beta1.HTTPRoute { + svcDefault := &v1alpha1.ServiceDefaults{ + TypeMeta: metav1.TypeMeta{ + Kind: "ServiceDefaults", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "Service", + }, + Spec: v1alpha1.ServiceDefaultsSpec{ + Protocol: "http", + }, + } + + svc := &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "Service", + Labels: map[string]string{"app": "Service"}, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "high", + Protocol: "TCP", + Port: 8080, + }, + }, + Selector: map[string]string{"app": "Service"}, + }, + } + + serviceAccount := &corev1.ServiceAccount{ + TypeMeta: metav1.TypeMeta{ + Kind: "ServiceAccount", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "Service", + }, + } + + deployment := &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + Kind: "Deployment", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "Service", + Labels: map[string]string{"app": "Service"}, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: common.PointerTo(int32(1)), + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "Service"}, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{}, + Spec: corev1.PodSpec{}, + }, + }, + } + + err := k8sClient.Create(ctx, svcDefault) + require.NoError(t, err) + + err = k8sClient.Create(ctx, svc) + require.NoError(t, err) + + err = k8sClient.Create(ctx, serviceAccount) + require.NoError(t, err) + + err = k8sClient.Create(ctx, deployment) + require.NoError(t, err) + + route := &gwv1beta1.HTTPRoute{ + TypeMeta: metav1.TypeMeta{ + Kind: "HTTPRoute", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "http-route", + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + { + Kind: (*gwv1beta1.Kind)(&gw.Kind), + Namespace: (*gwv1beta1.Namespace)(&gw.Namespace), + Name: gwv1beta1.ObjectName(gw.Name), + SectionName: &gw.Spec.Listeners[0].Name, + Port: &gw.Spec.Listeners[0].Port, + }, + }, + }, + Hostnames: []gwv1beta1.Hostname{"route.consul.io"}, + Rules: []gwv1beta1.HTTPRouteRule{ + { + Matches: []gwv1beta1.HTTPRouteMatch{ + { + Path: &gwv1beta1.HTTPPathMatch{ + Type: common.PointerTo(gwv1beta1.PathMatchType("PathPrefix")), + Value: common.PointerTo("/v1"), + }, + Headers: []gwv1beta1.HTTPHeaderMatch{ + { + Type: common.PointerTo(gwv1beta1.HeaderMatchExact), + Name: "version", + Value: "version", + }, + }, + QueryParams: []gwv1beta1.HTTPQueryParamMatch{ + { + Type: common.PointerTo(gwv1beta1.QueryParamMatchExact), + Name: "search", + Value: "q", + }, + }, + Method: common.PointerTo(gwv1beta1.HTTPMethod("GET")), + }, + }, + Filters: []gwv1beta1.HTTPRouteFilter{ + { + Type: gwv1beta1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ + Set: []gwv1beta1.HTTPHeader{ + { + Name: "foo", + Value: "bax", + }, + }, + Add: []gwv1beta1.HTTPHeader{ + { + Name: "arc", + Value: "reactor", + }, + }, + Remove: []string{"remove"}, + }, + }, + { + Type: gwv1beta1.HTTPRouteFilterURLRewrite, + URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ + Hostname: common.PointerTo(gwv1beta1.PreciseHostname("host.com")), + Path: &gwv1beta1.HTTPPathModifier{ + Type: gwv1beta1.FullPathHTTPPathModifier, + ReplaceFullPath: common.PointerTo("/foobar"), + }, + }, + }, + + { + Type: gwv1beta1.HTTPRouteFilterURLRewrite, + URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ + Hostname: common.PointerTo(gwv1beta1.PreciseHostname("host.com")), + Path: &gwv1beta1.HTTPPathModifier{ + Type: gwv1beta1.PrefixMatchHTTPPathModifier, + ReplacePrefixMatch: common.PointerTo("/foo"), + }, + }, + }, + }, + BackendRefs: []gwv1beta1.HTTPBackendRef{ + { + BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "Service", + Port: common.PointerTo(gwv1beta1.PortNumber(8080)), + }, + Weight: common.PointerTo(int32(50)), + }, + }, + }, + }, + }, + }, + } + + err = k8sClient.Create(ctx, route) + require.NoError(t, err) + + return route +} + +func createAllFieldsSetTCPRoute(t *testing.T, ctx context.Context, k8sClient client.WithWatch, gw *gwv1beta1.Gateway) *v1alpha2.TCPRoute { + route := &v1alpha2.TCPRoute{ + TypeMeta: metav1.TypeMeta{ + Kind: "TCPRoute", + APIVersion: "gateway.networking.k8s.io/v1alpha2", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "tcp-route", + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + { + Kind: (*gwv1beta1.Kind)(&gw.Kind), + Namespace: (*gwv1beta1.Namespace)(&gw.Namespace), + Name: gwv1beta1.ObjectName(gw.Name), + SectionName: &gw.Spec.Listeners[2].Name, + Port: &gw.Spec.Listeners[2].Port, + }, + }, + }, + Rules: []gwv1alpha2.TCPRouteRule{ + { + BackendRefs: []gwv1beta1.BackendRef{ + { + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "Service", + Port: common.PointerTo(gwv1beta1.PortNumber(25000)), + }, + Weight: common.PointerTo(int32(50)), + }, + }, + }, + }, + }, + } + + err := k8sClient.Create(ctx, route) + require.NoError(t, err) + + return route +} + +func createCert(t *testing.T, ctx context.Context, k8sClient client.WithWatch, certNS string) *corev1.Secret { + // listener one tls config + certName := "one-cert" + + privateKey, err := rsa.GenerateKey(rand.Reader, 1024) + require.NoError(t, err) + + usage := x509.KeyUsageCertSign + expiration := time.Now().AddDate(10, 0, 0) + + cert := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: "consul.test", + }, + IsCA: true, + NotBefore: time.Now().Add(-10 * time.Minute), + NotAfter: expiration, + SubjectKeyId: []byte{1, 2, 3, 4, 6}, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: usage, + BasicConstraintsValid: true, + } + caCert := cert + caPrivateKey := privateKey + + data, err := x509.CreateCertificate(rand.Reader, cert, caCert, &privateKey.PublicKey, caPrivateKey) + require.NoError(t, err) + + certBytes := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: data, + }) + + privateKeyBytes := pem.EncodeToMemory(&pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(privateKey), + }) + + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: certNS, + Name: certName, + }, + Data: map[string][]byte{ + corev1.TLSCertKey: certBytes, + corev1.TLSPrivateKeyKey: privateKeyBytes, + }, + } + + err = k8sClient.Create(ctx, secret) + require.NoError(t, err) + + return secret +} + +func minimalFieldsSetAPIGW(t *testing.T, ctx context.Context, k8sClient client.WithWatch, namespace string) *gwv1beta1.Gateway { + // listener one configuration + listenerOneName := "listener-one" + listenerOneHostname := "*.consul.io" + listenerOnePort := 3366 + listenerOneProtocol := "https" + + // listener three configuration + listenerThreeName := "listener-three" + listenerThreePort := 8081 + listenerThreeProtocol := "tcp" + + // Write gw to k8s + gwClassCfg := &v1alpha1.GatewayClassConfig{ + TypeMeta: metav1.TypeMeta{ + Kind: "GatewayClassConfig", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway-class-config", + }, + Spec: v1alpha1.GatewayClassConfigSpec{}, + } + gwClass := &gwv1beta1.GatewayClass{ + TypeMeta: metav1.TypeMeta{ + Kind: "GatewayClass", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "gatewayclass", + }, + Spec: gwv1beta1.GatewayClassSpec{ + ControllerName: "consul.hashicorp.com/gateway-controller", + ParametersRef: &gwv1beta1.ParametersReference{ + Group: "consul.hashicorp.com", + Kind: "GatewayClassConfig", + Name: "gateway-class-config", + }, + Description: new(string), + }, + } + gw := &gwv1beta1.Gateway{ + TypeMeta: metav1.TypeMeta{ + Kind: "Gateway", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "gw", + Annotations: make(map[string]string), + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gwv1beta1.ObjectName(gwClass.Name), + Listeners: []gwv1beta1.Listener{ + { + Name: gwv1beta1.SectionName(listenerOneName), + Hostname: common.PointerTo(gwv1beta1.Hostname(listenerOneHostname)), + Port: gwv1beta1.PortNumber(listenerOnePort), + Protocol: gwv1beta1.ProtocolType(listenerOneProtocol), + TLS: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{ + { + Kind: common.PointerTo(gwv1beta1.Kind("Secret")), + Name: gwv1beta1.ObjectName("one-cert"), + Namespace: common.PointerTo(gwv1beta1.Namespace(namespace)), + }, + }, + }, + }, + { + Name: gwv1beta1.SectionName(listenerThreeName), + Port: gwv1beta1.PortNumber(listenerThreePort), + Protocol: gwv1beta1.ProtocolType(listenerThreeProtocol), + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: common.PointerTo(gwv1beta1.FromNamespaces("All")), + }, + }, + }, + }, + }, + } + + err := k8sClient.Create(ctx, gwClassCfg) + require.NoError(t, err) + + err = k8sClient.Create(ctx, gwClass) + require.NoError(t, err) + + err = k8sClient.Create(ctx, gw) + require.NoError(t, err) + + return gw +} + +func minimalFieldsSetHTTPRoute(t *testing.T, ctx context.Context, k8sClient client.WithWatch, gw *gwv1beta1.Gateway) *gwv1beta1.HTTPRoute { + svcDefault := &v1alpha1.ServiceDefaults{ + TypeMeta: metav1.TypeMeta{ + Kind: "ServiceDefaults", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "Service", + }, + Spec: v1alpha1.ServiceDefaultsSpec{ + Protocol: "http", + }, + } + + svc := &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "Service", + Labels: map[string]string{"app": "Service"}, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "high", + Protocol: "TCP", + Port: 8080, + }, + }, + Selector: map[string]string{"app": "Service"}, + }, + } + + serviceAccount := &corev1.ServiceAccount{ + TypeMeta: metav1.TypeMeta{ + Kind: "ServiceAccount", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "Service", + }, + } + + deployment := &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + Kind: "Deployment", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "Service", + Labels: map[string]string{"app": "Service"}, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: common.PointerTo(int32(1)), + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "Service"}, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{}, + Spec: corev1.PodSpec{}, + }, + }, + } + + err := k8sClient.Create(ctx, svcDefault) + require.NoError(t, err) + + err = k8sClient.Create(ctx, svc) + require.NoError(t, err) + + err = k8sClient.Create(ctx, serviceAccount) + require.NoError(t, err) + + err = k8sClient.Create(ctx, deployment) + require.NoError(t, err) + + route := &gwv1beta1.HTTPRoute{ + TypeMeta: metav1.TypeMeta{ + Kind: "HTTPRoute", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "http-route", + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + { + Kind: (*gwv1beta1.Kind)(&gw.Kind), + Namespace: (*gwv1beta1.Namespace)(&gw.Namespace), + Name: gwv1beta1.ObjectName(gw.Name), + SectionName: &gw.Spec.Listeners[0].Name, + Port: &gw.Spec.Listeners[0].Port, + }, + }, + }, + Hostnames: []gwv1beta1.Hostname{"route.consul.io"}, + Rules: []gwv1beta1.HTTPRouteRule{ + { + BackendRefs: []gwv1beta1.HTTPBackendRef{ + { + BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "Service", + Port: common.PointerTo(gwv1beta1.PortNumber(8080)), + }, + }, + }, + }, + }, + }, + }, + } + + err = k8sClient.Create(ctx, route) + require.NoError(t, err) + + return route +} + +func minimalFieldsSetTCPRoute(t *testing.T, ctx context.Context, k8sClient client.WithWatch, gw *gwv1beta1.Gateway) *v1alpha2.TCPRoute { + route := &v1alpha2.TCPRoute{ + TypeMeta: metav1.TypeMeta{ + Kind: "TCPRoute", + APIVersion: "gateway.networking.k8s.io/v1alpha2", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "tcp-route", + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + { + Kind: (*gwv1beta1.Kind)(&gw.Kind), + Namespace: (*gwv1beta1.Namespace)(&gw.Namespace), + Name: gwv1beta1.ObjectName(gw.Name), + SectionName: &gw.Spec.Listeners[1].Name, + Port: &gw.Spec.Listeners[1].Port, + }, + }, + }, + Rules: []gwv1alpha2.TCPRouteRule{ + { + BackendRefs: []gwv1beta1.BackendRef{ + { + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "Service", + Port: common.PointerTo(gwv1beta1.PortNumber(25000)), + }, + }, + }, + }, + }, + }, + } + + err := k8sClient.Create(ctx, route) + require.NoError(t, err) + + return route +} + +func createFunkyCasingFieldsAPIGW(t *testing.T, ctx context.Context, k8sClient client.WithWatch, namespace string) *gwv1beta1.Gateway { + // listener one configuration + listenerOneName := "listener-one" + listenerOneHostname := "*.consul.io" + listenerOnePort := 3366 + listenerOneProtocol := "hTtPs" + + // listener two configuration + listenerTwoName := "listener-two" + listenerTwoHostname := "*.consul.io" + listenerTwoPort := 5432 + listenerTwoProtocol := "HTTP" + + // listener three configuration + listenerThreeName := "listener-three" + listenerThreePort := 8081 + listenerThreeProtocol := "tCp" + + // listener four configuration + listenerFourName := "listener-four" + listenerFourHostname := "*.consul.io" + listenerFourPort := 5433 + listenerFourProtocol := "hTTp" + + // Write gw to k8s + gwClassCfg := &v1alpha1.GatewayClassConfig{ + TypeMeta: metav1.TypeMeta{ + Kind: "GatewayClassConfig", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway-class-config", + }, + Spec: v1alpha1.GatewayClassConfigSpec{}, + } + gwClass := &gwv1beta1.GatewayClass{ + TypeMeta: metav1.TypeMeta{ + Kind: "GatewayClass", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "gatewayclass", + }, + Spec: gwv1beta1.GatewayClassSpec{ + ControllerName: "consul.hashicorp.com/gateway-controller", + ParametersRef: &gwv1beta1.ParametersReference{ + Group: "consul.hashicorp.com", + Kind: "GatewayClassConfig", + Name: "gateway-class-config", + }, + Description: new(string), + }, + } + gw := &gwv1beta1.Gateway{ + TypeMeta: metav1.TypeMeta{ + Kind: "Gateway", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "gw", + Namespace: namespace, + Annotations: make(map[string]string), + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gwv1beta1.ObjectName(gwClass.Name), + Listeners: []gwv1beta1.Listener{ + { + Name: gwv1beta1.SectionName(listenerOneName), + Hostname: common.PointerTo(gwv1beta1.Hostname(listenerOneHostname)), + Port: gwv1beta1.PortNumber(listenerOnePort), + Protocol: gwv1beta1.ProtocolType(listenerOneProtocol), + TLS: &gwv1beta1.GatewayTLSConfig{ + CertificateRefs: []gwv1beta1.SecretObjectReference{ + { + Kind: common.PointerTo(gwv1beta1.Kind("Secret")), + Name: gwv1beta1.ObjectName("one-cert"), + Namespace: common.PointerTo(gwv1beta1.Namespace(namespace)), + }, + }, + }, + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: common.PointerTo(gwv1beta1.FromNamespaces("All")), + }, + }, + }, + { + Name: gwv1beta1.SectionName(listenerTwoName), + Hostname: common.PointerTo(gwv1beta1.Hostname(listenerTwoHostname)), + Port: gwv1beta1.PortNumber(listenerTwoPort), + Protocol: gwv1beta1.ProtocolType(listenerTwoProtocol), + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: common.PointerTo(gwv1beta1.FromNamespaces("Same")), + }, + }, + }, + { + Name: gwv1beta1.SectionName(listenerThreeName), + Port: gwv1beta1.PortNumber(listenerThreePort), + Protocol: gwv1beta1.ProtocolType(listenerThreeProtocol), + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: common.PointerTo(gwv1beta1.FromNamespaces("All")), + }, + }, + }, + { + Name: gwv1beta1.SectionName(listenerFourName), + Hostname: common.PointerTo(gwv1beta1.Hostname(listenerFourHostname)), + Port: gwv1beta1.PortNumber(listenerFourPort), + Protocol: gwv1beta1.ProtocolType(listenerFourProtocol), + AllowedRoutes: &gwv1beta1.AllowedRoutes{ + Namespaces: &gwv1beta1.RouteNamespaces{ + From: common.PointerTo(gwv1beta1.FromNamespaces("Selector")), + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + common.NamespaceNameLabel: "consul", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{}, + }, + }, + }, + }, + }, + }, + } + + err := k8sClient.Create(ctx, gwClassCfg) + require.NoError(t, err) + + err = k8sClient.Create(ctx, gwClass) + require.NoError(t, err) + + err = k8sClient.Create(ctx, gw) + require.NoError(t, err) + + return gw +} + +func createFunkyCasingFieldsHTTPRoute(t *testing.T, ctx context.Context, k8sClient client.WithWatch, gw *gwv1beta1.Gateway) *gwv1beta1.HTTPRoute { + svcDefault := &v1alpha1.ServiceDefaults{ + TypeMeta: metav1.TypeMeta{ + Kind: "ServiceDefaults", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "Service", + }, + Spec: v1alpha1.ServiceDefaultsSpec{ + Protocol: "hTtp", + }, + } + + svc := &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "Service", + Labels: map[string]string{"app": "Service"}, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "high", + Protocol: "TCP", + Port: 8080, + }, + }, + Selector: map[string]string{"app": "Service"}, + }, + } + + serviceAccount := &corev1.ServiceAccount{ + TypeMeta: metav1.TypeMeta{ + Kind: "ServiceAccount", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "Service", + }, + } + + deployment := &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + Kind: "Deployment", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "Service", + Labels: map[string]string{"app": "Service"}, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: common.PointerTo(int32(1)), + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "Service"}, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{}, + Spec: corev1.PodSpec{}, + }, + }, + } + + err := k8sClient.Create(ctx, svcDefault) + require.NoError(t, err) + + err = k8sClient.Create(ctx, svc) + require.NoError(t, err) + + err = k8sClient.Create(ctx, serviceAccount) + require.NoError(t, err) + + err = k8sClient.Create(ctx, deployment) + require.NoError(t, err) + + route := &gwv1beta1.HTTPRoute{ + TypeMeta: metav1.TypeMeta{ + Kind: "HTTPRoute", + APIVersion: "gateway.networking.k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "http-route", + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + { + Namespace: (*gwv1beta1.Namespace)(&gw.Namespace), + Name: gwv1beta1.ObjectName(gw.Name), + SectionName: &gw.Spec.Listeners[0].Name, + Port: &gw.Spec.Listeners[0].Port, + }, + }, + }, + Hostnames: []gwv1beta1.Hostname{"route.consul.io"}, + Rules: []gwv1beta1.HTTPRouteRule{ + { + Matches: []gwv1beta1.HTTPRouteMatch{ + { + Path: &gwv1beta1.HTTPPathMatch{ + Type: common.PointerTo(gwv1beta1.PathMatchPathPrefix), + }, + Headers: []gwv1beta1.HTTPHeaderMatch{ + { + Type: common.PointerTo(gwv1beta1.HeaderMatchExact), + Name: "version", + Value: "version", + }, + }, + QueryParams: []gwv1beta1.HTTPQueryParamMatch{ + { + Type: common.PointerTo(gwv1beta1.QueryParamMatchExact), + Name: "search", + Value: "q", + }, + }, + Method: common.PointerTo(gwv1beta1.HTTPMethod("geT")), + }, + }, + Filters: []gwv1beta1.HTTPRouteFilter{ + { + Type: gwv1beta1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ + Set: []gwv1beta1.HTTPHeader{ + { + Name: "foo", + Value: "bax", + }, + }, + Add: []gwv1beta1.HTTPHeader{ + { + Name: "arc", + Value: "reactor", + }, + }, + Remove: []string{"remove"}, + }, + }, + { + Type: gwv1beta1.HTTPRouteFilterURLRewrite, + URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ + Hostname: common.PointerTo(gwv1beta1.PreciseHostname("host.com")), + Path: &gwv1beta1.HTTPPathModifier{ + Type: gwv1beta1.FullPathHTTPPathModifier, + ReplaceFullPath: common.PointerTo("/foobar"), + }, + }, + }, + + { + Type: gwv1beta1.HTTPRouteFilterURLRewrite, + URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ + Hostname: common.PointerTo(gwv1beta1.PreciseHostname("host.com")), + Path: &gwv1beta1.HTTPPathModifier{ + Type: gwv1beta1.PrefixMatchHTTPPathModifier, + ReplacePrefixMatch: common.PointerTo("/foo"), + }, + }, + }, + }, + BackendRefs: []gwv1beta1.HTTPBackendRef{ + { + BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "Service", + Port: common.PointerTo(gwv1beta1.PortNumber(8080)), + }, + Weight: common.PointerTo(int32(-50)), + }, + }, + }, + }, + }, + }, + } + + err = k8sClient.Create(ctx, route) + require.NoError(t, err) + + return route +} + +func createFunkyCasingFieldsTCPRoute(t *testing.T, ctx context.Context, k8sClient client.WithWatch, gw *gwv1beta1.Gateway) *v1alpha2.TCPRoute { + route := &v1alpha2.TCPRoute{ + TypeMeta: metav1.TypeMeta{ + Kind: "TCPRoute", + APIVersion: "gateway.networking.k8s.io/v1alpha2", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "tcp-route", + }, + Spec: gwv1alpha2.TCPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + { + Namespace: (*gwv1beta1.Namespace)(&gw.Namespace), + Name: gwv1beta1.ObjectName(gw.Name), + SectionName: &gw.Spec.Listeners[2].Name, + Port: &gw.Spec.Listeners[2].Port, + }, + }, + }, + Rules: []gwv1alpha2.TCPRouteRule{ + { + BackendRefs: []gwv1beta1.BackendRef{ + { + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "Service", + Port: common.PointerTo(gwv1beta1.PortNumber(25000)), + }, + Weight: common.PointerTo(int32(-50)), + }, + }, + }, + }, + }, + } + + err := k8sClient.Create(ctx, route) + require.NoError(t, err) + + return route +} From e976b88a26c2e0147daf9582b5e95ccf68383317 Mon Sep 17 00:00:00 2001 From: sarahalsmiller <100602640+sarahalsmiller@users.noreply.github.com> Date: Thu, 29 Jun 2023 09:54:04 -0500 Subject: [PATCH 258/340] api-gateway: Fix nil pointer exception panic (#2487) * fix nil pointer exception * add unit test * added changelog * delete changelog --- .../api-gateway/common/translation.go | 14 ++--- .../api-gateway/common/translation_test.go | 54 +++++++++++++++++++ .../gateway_controller_integration_test.go | 2 + 3 files changed, 64 insertions(+), 6 deletions(-) diff --git a/control-plane/api-gateway/common/translation.go b/control-plane/api-gateway/common/translation.go index fd69c8601f..55bfb29a2f 100644 --- a/control-plane/api-gateway/common/translation.go +++ b/control-plane/api-gateway/common/translation.go @@ -254,14 +254,16 @@ func (t ResourceTranslator) translateHTTPFilters(filters []gwv1beta1.HTTPRouteFi } for _, filter := range filters { - consulFilter.Remove = append(consulFilter.Remove, filter.RequestHeaderModifier.Remove...) + if filter.RequestHeaderModifier != nil { + consulFilter.Remove = append(consulFilter.Remove, filter.RequestHeaderModifier.Remove...) - for _, toAdd := range filter.RequestHeaderModifier.Add { - consulFilter.Add[string(toAdd.Name)] = toAdd.Value - } + for _, toAdd := range filter.RequestHeaderModifier.Add { + consulFilter.Add[string(toAdd.Name)] = toAdd.Value + } - for _, toSet := range filter.RequestHeaderModifier.Set { - consulFilter.Set[string(toSet.Name)] = toSet.Value + for _, toSet := range filter.RequestHeaderModifier.Set { + consulFilter.Set[string(toSet.Name)] = toSet.Value + } } // we drop any path rewrites that are not prefix matches as we don't support those diff --git a/control-plane/api-gateway/common/translation_test.go b/control-plane/api-gateway/common/translation_test.go index caa3efbcac..20917151f3 100644 --- a/control-plane/api-gateway/common/translation_test.go +++ b/control-plane/api-gateway/common/translation_test.go @@ -1372,3 +1372,57 @@ func generateTestCertificate(t *testing.T, namespace, name string) corev1.Secret }, } } + +func TestResourceTranslator_translateHTTPFilters(t1 *testing.T) { + type fields struct { + EnableConsulNamespaces bool + ConsulDestNamespace string + EnableK8sMirroring bool + MirroringPrefix string + ConsulPartition string + Datacenter string + } + type args struct { + filters []gwv1beta1.HTTPRouteFilter + } + tests := []struct { + name string + fields fields + args args + want api.HTTPFilters + }{ + { + name: "no httproutemodifier set", + fields: fields{}, + args: args{ + filters: []gwv1beta1.HTTPRouteFilter{ + { + URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{}, + }, + }, + }, + want: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{ + { + Add: map[string]string{}, + Set: map[string]string{}, + }, + }, + URLRewrite: nil, + }, + }, + } + for _, tt := range tests { + t1.Run(tt.name, func(t1 *testing.T) { + t := ResourceTranslator{ + EnableConsulNamespaces: tt.fields.EnableConsulNamespaces, + ConsulDestNamespace: tt.fields.ConsulDestNamespace, + EnableK8sMirroring: tt.fields.EnableK8sMirroring, + MirroringPrefix: tt.fields.MirroringPrefix, + ConsulPartition: tt.fields.ConsulPartition, + Datacenter: tt.fields.Datacenter, + } + assert.Equalf(t1, tt.want, t.translateHTTPFilters(tt.args.filters), "translateHTTPFilters(%v)", tt.args.filters) + }) + } +} diff --git a/control-plane/api-gateway/controllers/gateway_controller_integration_test.go b/control-plane/api-gateway/controllers/gateway_controller_integration_test.go index b423ae54e7..43f3d7d41d 100644 --- a/control-plane/api-gateway/controllers/gateway_controller_integration_test.go +++ b/control-plane/api-gateway/controllers/gateway_controller_integration_test.go @@ -37,6 +37,8 @@ import ( ) func TestControllerDoesNotInfinitelyReconcile(t *testing.T) { + //TODO @apigatewayteam test is consistently failing on main after merge, fix in a follow up PR + t.Skip() s := runtime.NewScheme() require.NoError(t, clientgoscheme.AddToScheme(s)) require.NoError(t, gwv1alpha2.Install(s)) From 83f050be498c3b93d5248c2a7020c7e45837b352 Mon Sep 17 00:00:00 2001 From: John Maguire Date: Thu, 29 Jun 2023 13:57:20 -0400 Subject: [PATCH 259/340] Use correct length for certificate RSA key for tests (#2490) * Use correct length for certificate RSA key * api-gateway: Fix nil pointer exception panic (#2487) * fix nil pointer exception * add unit test * added changelog * delete changelog * Remove skip for fixed test --------- Co-authored-by: sarahalsmiller <100602640+sarahalsmiller@users.noreply.github.com> --- .../controllers/gateway_controller_integration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/control-plane/api-gateway/controllers/gateway_controller_integration_test.go b/control-plane/api-gateway/controllers/gateway_controller_integration_test.go index 43f3d7d41d..35b3c64652 100644 --- a/control-plane/api-gateway/controllers/gateway_controller_integration_test.go +++ b/control-plane/api-gateway/controllers/gateway_controller_integration_test.go @@ -652,7 +652,7 @@ func createCert(t *testing.T, ctx context.Context, k8sClient client.WithWatch, c // listener one tls config certName := "one-cert" - privateKey, err := rsa.GenerateKey(rand.Reader, 1024) + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) require.NoError(t, err) usage := x509.KeyUsageCertSign From 8fe4fb6aa8f5217a9060f6d52182d5d5272c3300 Mon Sep 17 00:00:00 2001 From: John Maguire Date: Thu, 29 Jun 2023 15:30:27 -0400 Subject: [PATCH 260/340] APIGW: Validate length of RSA Keys (#2478) * Validate length of RSA key for inline certs * Bring key length check functions over from consul * move validation of key length from certificate parsing into validation of cert * Update to use sentinel errors * Add changelog * Addressing PR comments: fixing text in changelog, fixing import blocks, slight refactor of cert validation for readability * Ensure cert is removed from consul if an invalid one is presented * Fix linting issues, added tests for validating keys --- .changelog/2478.txt | 5 + .../api-gateway/binding/binder_test.go | 4 +- control-plane/api-gateway/binding/result.go | 22 ++-- .../api-gateway/binding/validation.go | 22 +++- control-plane/api-gateway/common/secrets.go | 57 +++++++++- .../api-gateway/common/secrets_test.go | 105 ++++++++++++++++++ .../api-gateway/common/translation.go | 14 ++- 7 files changed, 207 insertions(+), 22 deletions(-) create mode 100644 .changelog/2478.txt create mode 100644 control-plane/api-gateway/common/secrets_test.go diff --git a/.changelog/2478.txt b/.changelog/2478.txt new file mode 100644 index 0000000000..ccbbb71ec8 --- /dev/null +++ b/.changelog/2478.txt @@ -0,0 +1,5 @@ +```release-note:bug +api-gateway: fixes bug where envoy will silently reject RSA keys less than 2048 bits in length when not in FIPS mode, and +will reject keys that are not 2048, 3072, or 4096 bits in length in FIPS mode. We now validate +and reject invalid certs earlier. +``` diff --git a/control-plane/api-gateway/binding/binder_test.go b/control-plane/api-gateway/binding/binder_test.go index bb30b98f52..7366d1a164 100644 --- a/control-plane/api-gateway/binding/binder_test.go +++ b/control-plane/api-gateway/binding/binder_test.go @@ -15,7 +15,6 @@ import ( logrtest "github.com/go-logr/logr/testing" "github.com/google/go-cmp/cmp" - "github.com/hashicorp/consul/api" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -26,6 +25,7 @@ import ( "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul/api" ) func init() { @@ -2331,7 +2331,7 @@ func controlledBinder(config BinderConfig) BinderConfig { } func generateTestCertificate(t *testing.T, namespace, name string) (*api.InlineCertificateConfigEntry, corev1.Secret) { - privateKey, err := rsa.GenerateKey(rand.Reader, 1024) + privateKey, err := rsa.GenerateKey(rand.Reader, common.MinKeyLength) require.NoError(t, err) usage := x509.KeyUsageCertSign diff --git a/control-plane/api-gateway/binding/result.go b/control-plane/api-gateway/binding/result.go index 3ccb645c32..fd2eaca829 100644 --- a/control-plane/api-gateway/binding/result.go +++ b/control-plane/api-gateway/binding/result.go @@ -218,15 +218,17 @@ var ( // to the ListenerConditionReason given in the spec. If a reason is overloaded and can // be used with two different types of things (i.e. something is not found or it's not supported) // then we distinguish those two usages with errListener*_Usage. - errListenerUnsupportedProtocol = errors.New("listener protocol is unsupported") - errListenerPortUnavailable = errors.New("listener port is unavailable") - errListenerHostnameConflict = errors.New("listener hostname conflicts with another listener") - errListenerProtocolConflict = errors.New("listener protocol conflicts with another listener") - errListenerInvalidCertificateRef_NotFound = errors.New("certificate not found") - errListenerInvalidCertificateRef_NotSupported = errors.New("certificate type is not supported") - errListenerInvalidCertificateRef_InvalidData = errors.New("certificate is invalid or does not contain a supported server name") - errListenerInvalidRouteKinds = errors.New("allowed route kind is invalid") - errListenerProgrammed_Invalid = errors.New("listener cannot be programmed because it is invalid") + errListenerUnsupportedProtocol = errors.New("listener protocol is unsupported") + errListenerPortUnavailable = errors.New("listener port is unavailable") + errListenerHostnameConflict = errors.New("listener hostname conflicts with another listener") + errListenerProtocolConflict = errors.New("listener protocol conflicts with another listener") + errListenerInvalidCertificateRef_NotFound = errors.New("certificate not found") + errListenerInvalidCertificateRef_NotSupported = errors.New("certificate type is not supported") + errListenerInvalidCertificateRef_InvalidData = errors.New("certificate is invalid or does not contain a supported server name") + errListenerInvalidCertificateRef_NonFIPSRSAKeyLen = errors.New("certificate has an invalid length: RSA Keys must be at least 2048-bit") + errListenerInvalidCertificateRef_FIPSRSAKeyLen = errors.New("certificate has an invalid length: RSA keys must be either 2048-bit, 3072-bit, or 4096-bit in FIPS mode") + errListenerInvalidRouteKinds = errors.New("allowed route kind is invalid") + errListenerProgrammed_Invalid = errors.New("listener cannot be programmed because it is invalid") // Below is where any custom generic listener validation errors should go. // We map anything under here to a custom ListenerConditionReason of Invalid on @@ -372,7 +374,7 @@ func (l listenerValidationResult) resolvedRefsCondition(generation int64) metav1 } switch l.refErr { - case errListenerInvalidCertificateRef_NotFound, errListenerInvalidCertificateRef_NotSupported, errListenerInvalidCertificateRef_InvalidData: + case errListenerInvalidCertificateRef_NotFound, errListenerInvalidCertificateRef_NotSupported, errListenerInvalidCertificateRef_InvalidData, errListenerInvalidCertificateRef_NonFIPSRSAKeyLen, errListenerInvalidCertificateRef_FIPSRSAKeyLen: return metav1.Condition{ Type: "ResolvedRefs", Status: metav1.ConditionFalse, diff --git a/control-plane/api-gateway/binding/validation.go b/control-plane/api-gateway/binding/validation.go index 0e17e2b306..a57cf598a4 100644 --- a/control-plane/api-gateway/binding/validation.go +++ b/control-plane/api-gateway/binding/validation.go @@ -6,9 +6,6 @@ package binding import ( "strings" - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul/api" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" klabels "k8s.io/apimachinery/pkg/labels" @@ -17,6 +14,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul-k8s/control-plane/version" + "github.com/hashicorp/consul/api" ) var ( @@ -205,10 +207,20 @@ func validateTLS(gateway gwv1beta1.Gateway, tls *gwv1beta1.GatewayTLSConfig, res } func validateCertificateData(secret corev1.Secret) error { - _, _, err := common.ParseCertificateData(secret) + _, privateKey, err := common.ParseCertificateData(secret) if err != nil { return errListenerInvalidCertificateRef_InvalidData } + + err = common.ValidateKeyLength(privateKey) + if err != nil { + if version.IsFIPS() { + return errListenerInvalidCertificateRef_FIPSRSAKeyLen + } + + return errListenerInvalidCertificateRef_NonFIPSRSAKeyLen + } + return nil } @@ -235,7 +247,7 @@ func validateListeners(gateway gwv1beta1.Gateway, listeners []gwv1beta1.Listener _, supported := supportedKindsForProtocol[listener.Protocol] if !supported { result.acceptedErr = errListenerUnsupportedProtocol - } else if listener.Port == 20000 { //admin port + } else if listener.Port == 20000 { // admin port result.acceptedErr = errListenerPortUnavailable } diff --git a/control-plane/api-gateway/common/secrets.go b/control-plane/api-gateway/common/secrets.go index f7e6064d9f..1b7d8dec33 100644 --- a/control-plane/api-gateway/common/secrets.go +++ b/control-plane/api-gateway/common/secrets.go @@ -12,6 +12,14 @@ import ( "github.com/miekg/dns" corev1 "k8s.io/api/core/v1" + + "github.com/hashicorp/consul-k8s/control-plane/version" +) + +var ( + errFailedToParsePrivateKeyPem = errors.New("failed to parse private key PEM") + errKeyLengthTooShort = errors.New("RSA key length must be at least 2048-bit") + errKeyLengthTooShortFIPS = errors.New("RSA key length must be at either 2048-bit, 3072-bit, or 4096-bit in FIPS mode") ) func ParseCertificateData(secret corev1.Secret) (cert string, privateKey string, err error) { @@ -20,7 +28,7 @@ func ParseCertificateData(secret corev1.Secret) (cert string, privateKey string, privateKeyBlock, _ := pem.Decode(decodedPrivateKey) if privateKeyBlock == nil { - return "", "", errors.New("failed to parse private key PEM") + return "", "", errFailedToParsePrivateKeyPem } certificateBlock, _ := pem.Decode(decodedCertificate) @@ -66,3 +74,50 @@ func validateCertificateHosts(certificate *x509.Certificate) error { return nil } + +// Envoy will silently reject any keys that are less than 2048 bytes long +// https://github.com/envoyproxy/envoy/blob/main/source/extensions/transport_sockets/tls/context_impl.cc#L238 +const MinKeyLength = 2048 + +// ValidateKeyLength ensures that the key length for a certificate is of a valid length +// for envoy dependent on if consul is running in FIPS mode or not. +func ValidateKeyLength(privateKey string) error { + privateKeyBlock, _ := pem.Decode([]byte(privateKey)) + + if privateKeyBlock == nil { + return errFailedToParsePrivateKeyPem + } + + if privateKeyBlock.Type != "RSA PRIVATE KEY" { + return nil + } + + key, err := x509.ParsePKCS1PrivateKey(privateKeyBlock.Bytes) + if err != nil { + return err + } + + keyBitLen := key.N.BitLen() + + if version.IsFIPS() { + return fipsLenCheck(keyBitLen) + } + + return nonFipsLenCheck(keyBitLen) +} + +func nonFipsLenCheck(keyLen int) error { + // ensure private key is of the correct length + if keyLen < MinKeyLength { + return errKeyLengthTooShort + } + + return nil +} + +func fipsLenCheck(keyLen int) error { + if keyLen != 2048 && keyLen != 3072 && keyLen != 4096 { + return errKeyLengthTooShortFIPS + } + return nil +} diff --git a/control-plane/api-gateway/common/secrets_test.go b/control-plane/api-gateway/common/secrets_test.go new file mode 100644 index 0000000000..d5a2578b5e --- /dev/null +++ b/control-plane/api-gateway/common/secrets_test.go @@ -0,0 +1,105 @@ +package common + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestValidateKeyLength(t *testing.T) { + tooShortPrivateKey := `-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQCtmK1VjmXJ7vm4CZkkOSjc+kjGNMlyce5rXxwlDRz9LcGGc3Tg +kwUJesyBpDtxLLVHXQIPr5mWYbX/W/ezQ9sntxrATbDek8pBgoOlARebwkD2ivVW +BWfVhlryVihWlXApKiJ2n3i0m+OVtdrceC9Bv2hEMhYVOwzxtb3O0YFkbwIDAQAB +AoGAIxgnipFUEKPIRiVimUkY8ruCdNd9Fi7kNT6wEOl6v9A9PHIg4bm3Hfh+WYMb +JUEVkMzDuuoUEavFQE+WXt5L8oE1lEBmN2++FQsvllN+MRBTRg2sfw4mUWDI6S4r +h8+XNTzTIg2sUd2J3o2qNmQoOheYb+iuYDj76IFoEdwwZ0kCQQDYKKs5HAbnrLj1 +UrOp8TyHdFf0YNw5tGdbNTbffq4rlBD6SW70+Sj624i2UqdnYwRiWzdXv3zN08aI +Vfoh2cGlAkEAzZe5B6BhiX/PcIYutMtuT3K+mysFNlowrutXWoQOpR7gGAkgEt6e +oCDgx1QJRjsp6NFQxKc6l034Hzs17gqJgwJAcu9U873aUg9+HTuHOoKB28haCCAE +mU46cr3d2oKCW7uUN3EaZXmid5iJneBfENMOfrnfuHGiC9NiShXlNWCS3QJAO5Ne +w83+1ahaxUGs4SkeExmuECrcPM7P0rBRxOIFmGWlDHIAgFdQYhiE6l34vghA8b1O +CV5oRRYL84jl7M/S3wJBALDfL5YXcc8P6scLJJ1biqhLYppvGN5CUwbsJsluvHCW +XCTVIbPOaS42A0xUfpoiTcdbNSFRvdCzPR5nsGy8Y7g= +-----END RSA PRIVATE KEY-----` + validPrivateKey := `-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAzVKRcYlTHHPjPbCieOFIUT2hCouRYe4N8ZhNrSpZf/BAAn4M +d/LWn/9OrLagbxrRF6cWdWGNEI2COnBRLgNVxyPXneaHaYFqOBRi9GWhuD3sw1jn +7gf4/m/AVO8cu2JYjEX+s9RjSRzpjx+4nhit46bGNUyb9qUeQwoBidAzOSmU8nHY +y3LpuuzkjS3FEyNXHxqgpTJnV4ytx8YGkPnG92GBAlrZnr4Eclv0/Sq6OViTpeuh +z8noNkbugYWHMXGlTZ4lPnELJW2fx/HIpD2ovOO3X8XYBo5KDzs9qyKzDgIOMZLF +i/qLCLHgfosb4TMaXCeVu4fA7Y47jtGOO4mbiwIDAQABAoIBAFhicDibIDtRyaLv +K+l0NPC/4liLPwCUfM0gvmNKJS/VSICqKQzjbK+ANCpWDVb2iMaxRxItdY+IEuS8 +H736cozgaXtP1r+8lXBhmj1RmJ2ajpaC6YgGR5GjonwNWGVzjuGHaf6YcUryVrol +MhBgWE50psMf4M16Q74hCwt7o+k5Lz55xKasgc9dtSnvyCupPBwrOT+d55C1P2Wn +2oebWM4WKtCZIgvlvZrt4xQkGWy9qloxL6V1F67ZbizAyFMZUMmJv+4/whF8tmXi +aydleL64K23ZSK1pM/x0JI+7qo0GpEoA4k+2fdmh5dAOM0TrXhV5Kv01efLIaITT +s7lYjG0CgYEA4qGIM7qO3e9fHgSK/9UdxnpL/1OvfYATBMhEtR46sAxmKQGC8fTM +iTBkmLAKn3zBgDghCbygPIQjex+W+Ra7JkQIcGB6KLR8rr5GkOuF6vkqHV93RQRT +lT/1quqq3fVH6V4ymifKJCDNg0IEPcmo+M8RnXBgpFsCN4b5UyjXNScCgYEA5+4h +LITPJxGytlWzwtsy44U2PvafJYJCktW+LYqhk3xzz4qWX5ubmPz18LrEyybgcy/W +Dm4JCu+TOS2gvf2WbJKR/tKdgRN7dkU/dbgMtRL8QW5ir+5qqRITYOhiSZPIOpbP +5zg+c/ZvmK/t5h35/8l7b0bu/E1FOEF27ADpzP0CgYEArqch2gup0muI+A80N9i7 +q5vQOaL6mVM8VPEp0hLL06Sajnt1uJWZkxhSTkFMzoBMd03KWECflEOZPGep56iW +7fR8NG6Fdh0yAVDt/P0lJWKEDELoHa4p49l4sBFNQOSoWLaZdKe5ZoJJHyCfOCbT +K3wY7SYPtFnWqYhBWM8emv0CgYBdrNqNRp78orNR3c+bNjmZl6ZPTAD/f1swP1Bu +yH12Ol/0RX9y4kC4TANx1Z3Ch9ND8uA8N8lDN3x5Laqs0g29kH2TNLIU/i9xl4qI +G2xWfnKQYutNL7i4zOoyy+lW2m+W6m7Sbu8am0B7pSMrPJRK8a//Q+Em2nbIv/gu +XjgQaQKBgHKZUKkMv597vpAjgTNsKIl5RDFONBq3omnAwlK9EDLVeAxIrvrvMHBW +H/ZMFpSGp1eQgKyu1xkEqGdkYXx7BKtdTHK+Thqif2ZGWczy5rVSAIsBYDo1DGE2 +wbocWxkWNb5o2ZZtis5lTB6nr9EWo0zyaPqIh0pfjqVEES2YDEx6 +-----END RSA PRIVATE KEY-----` + nonTraditionalRSAKey := `-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCcrB9oNKLtzA3Q +02KDgtsnrxns7vJ5aCkjJCm/h0Ju7a2mel5YHSN5iLlU5oTMJVIMpWlW9E8P76/a +GLGMNfSBRVJdfW71iks/ddp4SjpDe9Bo+aY2snrR2/AP7eQepVNjFbg4YLQqvENh +05k1FuuP1/AgGVNn0kGEwzKxz35shmhRKBCvaRaHLz/fdkDIeIrVLON4FnmAmpOZ +AztZCwAZc6HZfj8Nh9Wlaw6Dg2boIgxTU160pwpX+nUxcJ9M5sUP9DBuNL0Mdrqi +U+R49uqG/5ssSk+xVik3q+WF+XySJ6H21fttWDJS2OTm/Nx/wHlBC73mthbA0emB +rkiBy9SBAgMBAAECggEAOhybz6aKcmKYE0d8yGPejwMjPh9JH+ATNh4hQBHXAdc1 +7ESCPvOb52XfvE5+nkwPeXJXNrIKq1IPq3kyTdvrc5F3Ygb3A6tGiuTXYnvBzasc +m/tRfANKjBGkovvte7J90ghJ2tt/qERJR/1Y2/jC6glB314VcjJqK+jNImfgsDa7 +1r47efKG7B5eUGvhQDTpL5ENXKxIdvCghHrLqj19QGUZ5MbXsEYrso0lxKw2Xk39 +uM8p3WTxIy0LQGyCm+FYlJ7r61tm7tUOGuNT0YiptVavIw1QPgIbRWdS2gnJu3+J +kHS0vu6AW1fJav48TA9hXcIQR70alrJA2VVqsvQouwKBgQDNs96l8BfWD6s/urIw +yzC3/VZPLFJ3BlxvkdP1UDC0S+7pgQ6qdEmJg0z5IfYzDB1PK2X/DS/70JA1LRSS +MRmjQGHCYIp9g8EqmABwfKf4YnN53KPRyR8Yq1pwaq7wKowtW+5GH95qQPINZsNO +J21AENEzq7IoB4gpM3tIaX73YwKBgQDC+yl5JvoV7e6FIpFrwL62aKrWmpidML/G +stdrg9ylCSM9SIVFINMhmFPicW1+DrkQ5HRV7DG//ZcOZNbbNmSu32PVcQI1MJgQ +rkMZ3ukUURnlvQYOEmZY4zHzTJ+jcw6kEH/+b47Bv13PpD7ZqA4/28dpU9wi9gt3 ++GiSnkKDywKBgHqjr63dPEjapK3lQFHJAu3fM7MWaMAf4cJ+/hD202LbFsDOuhC0 +Lhe3WY/7SI7cvSizZicvFJmcmi2qB+a1MWTcgKxj5I26nNMpNrHaEEcNY22XN3Be +6ZRKrSvy3wO/Sj3M3n2eiHtu5yFIUE7rQL5+iEu3JQuqmep+kBT3GMSjAoGAP77B +VlyJ0nWRT3F3vZSsRRJ/F94/GtT/PcTmbL4Vetc78CMvfuQ2YntcoWGX/Ghv1Lf7 +2MN5mF0d75TEMbLcw9dA2l0x7ZXPgVSXl3OrG/tPzi44No2JbHIKuJJKdrN9C+Jh +Fhv+vhUEZIg8DAjHb9U4opTKGZv7L+PEvHqFIHUCgYBTB2TxTgEMNZSsRwrhQRMh +tsz5rS2MoTgzk4BlSsv6xVC4GnBJ2HlNAjYEsBEg50zCCTPlZXcsNjrAxFrwWhLJ +DjN2iMsYFz4WHS94W5UYl6/35ye25KsHuS9vnNeidhFAvYgC1nIkh4mFhLoSeSCG +GODy2KwC2ssLuUHb6WoJ6A== +-----END PRIVATE KEY-----` + + testCases := map[string]struct { + key string + expectedError error + }{ + "key is RSA and of the correct length": { + key: validPrivateKey, + expectedError: nil, + }, + "key is RSA and too short": { + key: tooShortPrivateKey, + expectedError: errKeyLengthTooShort, + }, + "key is non-traditional RSA key": { + key: nonTraditionalRSAKey, + expectedError: nil, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + err := ValidateKeyLength(tc.key) + require.ErrorIs(t, err, tc.expectedError) + }) + } +} diff --git a/control-plane/api-gateway/common/translation.go b/control-plane/api-gateway/common/translation.go index 55bfb29a2f..94241eed22 100644 --- a/control-plane/api-gateway/common/translation.go +++ b/control-plane/api-gateway/common/translation.go @@ -6,14 +6,15 @@ package common import ( "strings" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" - "github.com/hashicorp/consul-k8s/control-plane/namespaces" - "github.com/hashicorp/consul/api" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" + "github.com/hashicorp/consul-k8s/control-plane/namespaces" + "github.com/hashicorp/consul/api" ) // ResourceTranslator handles translating K8s resources into Consul config entries. @@ -340,6 +341,11 @@ func (t ResourceTranslator) ToInlineCertificate(secret corev1.Secret) (*api.Inli return nil, err } + err = ValidateKeyLength(privateKey) + if err != nil { + return nil, err + } + namespace := t.Namespace(secret.Namespace) return &api.InlineCertificateConfigEntry{ From ced0ae836a6588c0c5b09f4016f07bc8704bb2fd Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Thu, 29 Jun 2023 13:07:23 -0700 Subject: [PATCH 261/340] add changelog for 1.2.0 dataplane and consul 1.16.0 (#2496) * add changelog for Consul 1.16.0 * add changelog for dataplane 1.2.0 --- .changelog/2476.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changelog/2476.txt diff --git a/.changelog/2476.txt b/.changelog/2476.txt new file mode 100644 index 0000000000..e57889cabe --- /dev/null +++ b/.changelog/2476.txt @@ -0,0 +1,7 @@ +```release-note:improvement +helm: update `imageConsulDataplane` value to `hashicorp/consul-dataplane:1.2.0` +``` + +```release-note:improvement +helm: update `image` value to `hashicorp/consul:1.16.0` +``` \ No newline at end of file From 736649d9d81334df1bddbfd8e4d04d66b719577b Mon Sep 17 00:00:00 2001 From: Melisa Griffin Date: Fri, 30 Jun 2023 14:02:16 -0400 Subject: [PATCH 262/340] Adds chanelog values for 0.49.7 (#2501) --- CHANGELOG.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cf1d2084f..445e0f1816 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,27 @@ +## 0.49.7 (June 28, 2023) +BREAKING CHANGES: + +* control-plane: All policies managed by consul-k8s will now be updated on upgrade. If you previously edited the policies after install, your changes will be overwritten. [[GH-2392](https://github.com/hashicorp/consul-k8s/issues/2392)] + +SECURITY: + +* Bump Dockerfile base image for RedHat UBI `consul-k8s-control-plane` image to `ubi-minimal:9.2`. [[GH-2204](https://github.com/hashicorp/consul-k8s/issues/2204)] +* Bump Dockerfile base image to `alpine:3.18`. Resolves [CVE-2023-2650](https://github.com/advisories/GHSA-gqxg-9vfr-p9cg) vulnerability in openssl@3.0.8-r4 [[GH-2284](https://github.com/hashicorp/consul-k8s/issues/2284)] + +FEATURES: + +* helm: Adds `acls.resources` field which can be configured to override the `resource` settings for the `server-acl-init` and `server-acl-init-cleanup` Jobs. [[GH-2416](https://github.com/hashicorp/consul-k8s/issues/2416)] + +IMPROVEMENTS: + +* (Consul Enterprise) Add support to provide inputs via helm for audit log related configuration [[GH-2265](https://github.com/hashicorp/consul-k8s/issues/2265)] +* helm: Update the default amount of memory used by the connect-inject controller so that its less likely to get OOM killed. [[GH-2249](https://github.com/hashicorp/consul-k8s/issues/2249)] + +BUG FIXES: + +* control-plane: Always update ACL policies upon upgrade. [[GH-2392](https://github.com/hashicorp/consul-k8s/issues/2392)] +* crd: fix bug on service intentions CRD causing some updates to be ignored. [[GH-2194](https://github.com/hashicorp/consul-k8s/issues/2194)] + ## 1.1.2 (June 5, 2023) SECURITY: From 30e9f55ff36085dfa35df822b1055f6a9d6f6d36 Mon Sep 17 00:00:00 2001 From: Nitya Dhanushkodi Date: Mon, 3 Jul 2023 10:36:25 -0700 Subject: [PATCH 263/340] ci: fix eks terraform quota error by cleaning up oidc providers (#2470) cleans up oidc providers older than 8 hours. --- hack/aws-acceptance-test-cleanup/main.go | 125 +++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/hack/aws-acceptance-test-cleanup/main.go b/hack/aws-acceptance-test-cleanup/main.go index d62e5e4405..e4094ec47e 100644 --- a/hack/aws-acceptance-test-cleanup/main.go +++ b/hack/aws-acceptance-test-cleanup/main.go @@ -25,6 +25,7 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/eks" "github.com/aws/aws-sdk-go/service/elb" + "github.com/aws/aws-sdk-go/service/iam" "github.com/cenkalti/backoff/v4" ) @@ -38,6 +39,11 @@ var ( errNotDestroyed = errors.New("not yet destroyed") ) +type oidc struct { + arn string + buildUrl string +} + func main() { flag.BoolVar(&flagAutoApprove, "auto-approve", false, "Skip interactive approval before destroying.") flag.Parse() @@ -68,6 +74,106 @@ func realMain(ctx context.Context) error { eksClient := eks.New(clientSession, awsCfg) ec2Client := ec2.New(clientSession, awsCfg) elbClient := elb.New(clientSession, awsCfg) + iamClient := iam.New(clientSession, awsCfg) + + // Find OIDC providers to delete. + oidcProvidersOutput, err := iamClient.ListOpenIDConnectProvidersWithContext(ctx, &iam.ListOpenIDConnectProvidersInput{}) + if err != nil { + return err + } else if oidcProvidersOutput == nil { + return fmt.Errorf("nil output for OIDC Providers") + } + + toDeleteOidcArns := []*oidc{} + for _, providerEntry := range oidcProvidersOutput.OpenIDConnectProviderList { + arnString := "" + if providerEntry.Arn != nil { + arnString = *providerEntry.Arn + } + // Check if it's older than 8 hours. + older, err := oidcOlderThanEightHours(ctx, iamClient, providerEntry.Arn) + if err != nil { + return err + } + // Only add to delete list if it's older than 8 hours and has a buildURL tag. + if older { + output, err := iamClient.ListOpenIDConnectProviderTags(&iam.ListOpenIDConnectProviderTagsInput{OpenIDConnectProviderArn: providerEntry.Arn}) + if err != nil { + return err + } + for _, tag := range output.Tags { + if tag.Key != nil && *tag.Key == buildURLTag { + var buildUrl string + if tag.Value != nil { + buildUrl = *tag.Value + } + toDeleteOidcArns = append(toDeleteOidcArns, &oidc{arn: arnString, buildUrl: buildUrl}) + } + } + } else { + fmt.Printf("Skipping OIDC provider: %s because it's not over 8 hours old\n", arnString) + } + } + + oidcProvidersExist := true + if len(toDeleteOidcArns) == 0 { + fmt.Println("Found no OIDC Providers to clean up") + oidcProvidersExist = false + } else { + // Print out the OIDC Provider arns and the build tags. + var oidcPrint string + for _, oidcProvider := range toDeleteOidcArns { + oidcPrint += fmt.Sprintf("- %s (%s)\n", oidcProvider.arn, oidcProvider.buildUrl) + } + + fmt.Printf("Found OIDC Providers:\n%s", oidcPrint) + } + + // Check for approval. + if !flagAutoApprove && oidcProvidersExist { + type input struct { + text string + err error + } + inputCh := make(chan input) + + // Read input in a goroutine so we can also exit if we get a Ctrl-C + // (see select{} below). + go func() { + reader := bufio.NewReader(os.Stdin) + fmt.Println("\nDo you want to delete these OIDC Providers (y/n)?") + inputStr, err := reader.ReadString('\n') + if err != nil { + inputCh <- input{err: err} + return + } + inputCh <- input{text: inputStr} + }() + + select { + case in := <-inputCh: + if in.err != nil { + return in.err + } + inputTrimmed := strings.TrimSpace(in.text) + if inputTrimmed != "y" && inputTrimmed != "yes" { + return errors.New("exiting after negative") + } + case <-ctx.Done(): + return errors.New("context cancelled") + } + } + + // Actually delete the OIDC providers. + for _, oidcArn := range toDeleteOidcArns { + fmt.Printf("Deleting OIDC provider: %s\n", oidcArn.arn) + _, err := iamClient.DeleteOpenIDConnectProviderWithContext(ctx, &iam.DeleteOpenIDConnectProviderInput{ + OpenIDConnectProviderArn: &oidcArn.arn, + }) + if err != nil { + return err + } + } // Find VPCs to delete. Most resources we create belong to a VPC, except // for IAM resources, and so if there are no VPCs, that means all leftover resources have been deleted. @@ -537,6 +643,25 @@ func realMain(ctx context.Context) error { return nil } +// oidcOlderThanEightHours checks if the oidc provider is older than 8 hours. +func oidcOlderThanEightHours(ctx context.Context, iamClient *iam.IAM, oidcArn *string) (bool, error) { + fullOidc, err := iamClient.GetOpenIDConnectProviderWithContext(ctx, &iam.GetOpenIDConnectProviderInput{ + OpenIDConnectProviderArn: oidcArn, + }) + if err != nil { + return false, err + } + if fullOidc != nil { + if fullOidc.CreateDate != nil { + d := time.Since(*fullOidc.CreateDate) + if d.Hours() > 8 { + return true, nil + } + } + } + return false, nil +} + func vpcNameAndBuildURL(vpc *ec2.Vpc) (string, string) { var vpcName string var buildURL string From 1161322e494509ca2642811d88c92b898bb7a4d4 Mon Sep 17 00:00:00 2001 From: Dan Stough Date: Thu, 6 Jul 2023 12:18:33 -0400 Subject: [PATCH 264/340] build: update versions to 1.3.0-dev (#2511) --- charts/consul/Chart.yaml | 12 ++++++------ charts/consul/values.yaml | 4 ++-- cli/version/version.go | 2 +- control-plane/version/version.go | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/charts/consul/Chart.yaml b/charts/consul/Chart.yaml index 64d7ed4ed0..de8e6e9df6 100644 --- a/charts/consul/Chart.yaml +++ b/charts/consul/Chart.yaml @@ -3,8 +3,8 @@ apiVersion: v2 name: consul -version: 1.2.0-dev -appVersion: 1.16-dev +version: 1.3.0-dev +appVersion: 1.17-dev kubeVersion: ">=1.22.0-0" description: Official HashiCorp Consul Chart home: https://www.consul.io @@ -16,13 +16,13 @@ annotations: artifacthub.io/prerelease: true artifacthub.io/images: | - name: consul - image: docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.16-dev + image: docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.17-dev - name: consul-k8s-control-plane - image: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.2.0-dev + image: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.3.0-dev - name: consul-dataplane - image: docker.mirror.hashicorp.services/hashicorppreview/consul-dataplane:1.2-dev + image: docker.mirror.hashicorp.services/hashicorppreview/consul-dataplane:1.3-dev - name: envoy - image: envoyproxy/envoy:v1.25.1 + image: envoyproxy/envoy:v1.26.2 artifacthub.io/license: MPL-2.0 artifacthub.io/links: | - name: Documentation diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index ad1f829399..6d8eca9d0d 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -66,7 +66,7 @@ global: # image: "hashicorp/consul-enterprise:1.10.0-ent" # ``` # @default: hashicorp/consul: - image: docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.16-dev + image: docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.17-dev # Array of objects containing image pull secret names that will be applied to each service account. # This can be used to reference image pull secrets if using a custom consul or consul-k8s-control-plane Docker image. @@ -86,7 +86,7 @@ global: # image that is used for functionality such as catalog sync. # This can be overridden per component. # @default: hashicorp/consul-k8s-control-plane: - imageK8S: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.2.0-dev + imageK8S: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.3.0-dev # The name of the datacenter that the agents should # register as. This can't be changed once the Consul cluster is up and running diff --git a/cli/version/version.go b/cli/version/version.go index 320600c30b..9cde4a2d66 100644 --- a/cli/version/version.go +++ b/cli/version/version.go @@ -17,7 +17,7 @@ var ( // // Version must conform to the format expected by // github.com/hashicorp/go-version for tests to work. - Version = "1.2.0" + Version = "1.3.0" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release diff --git a/control-plane/version/version.go b/control-plane/version/version.go index 320600c30b..9cde4a2d66 100644 --- a/control-plane/version/version.go +++ b/control-plane/version/version.go @@ -17,7 +17,7 @@ var ( // // Version must conform to the format expected by // github.com/hashicorp/go-version for tests to work. - Version = "1.2.0" + Version = "1.3.0" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release From cbcbdc591901a7e5d4183fe1676a0bb363edbb9b Mon Sep 17 00:00:00 2001 From: "hashicorp-copywrite[bot]" <110428419+hashicorp-copywrite[bot]@users.noreply.github.com> Date: Fri, 7 Jul 2023 16:09:44 -0700 Subject: [PATCH 265/340] [COMPLIANCE] Add Copyright and License Headers (#2507) Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com> --- control-plane/api-gateway/common/secrets_test.go | 3 +++ .../controllers/gateway_controller_integration_test.go | 3 +++ 2 files changed, 6 insertions(+) diff --git a/control-plane/api-gateway/common/secrets_test.go b/control-plane/api-gateway/common/secrets_test.go index d5a2578b5e..223e8aa24e 100644 --- a/control-plane/api-gateway/common/secrets_test.go +++ b/control-plane/api-gateway/common/secrets_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package common import ( diff --git a/control-plane/api-gateway/controllers/gateway_controller_integration_test.go b/control-plane/api-gateway/controllers/gateway_controller_integration_test.go index 35b3c64652..2605991238 100644 --- a/control-plane/api-gateway/controllers/gateway_controller_integration_test.go +++ b/control-plane/api-gateway/controllers/gateway_controller_integration_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package controllers import ( From 0cb24d70e70b370599ba4e28addf003240ee2d70 Mon Sep 17 00:00:00 2001 From: David Yu Date: Mon, 10 Jul 2023 08:27:27 -0700 Subject: [PATCH 266/340] values.yaml - replace connect with service mesh for some instances (#2516) * fix connect/service mesh * Update values.yaml --- charts/consul/values.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 6d8eca9d0d..16b641d5cb 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -805,11 +805,11 @@ server: # @type: string storageClass: null - # This will enable/disable [Connect](https://developer.hashicorp.com/consul/docs/connect). Setting this to true + # This will enable/disable [service mesh](https://developer.hashicorp.com/consul/docs/connect). Setting this to true # _will not_ automatically secure pod communication, this # setting will only enable usage of the feature. Consul will automatically initialize - # a new CA and set of certificates. Additional Connect settings can be configured - # by setting the `server.extraConfig` value. + # a new CA and set of certificates. Additional service mesh settings can be configured + # by setting the `server.extraConfig` value or by applying [configuration entries](https://developer.hashicorp.com/consul/docs/connect/config-entries). connect: true serviceAccount: @@ -1611,7 +1611,7 @@ dns: # @type: boolean enabled: "-" - # If true, services using Consul Connect will use Consul DNS + # If true, services using Consul service mesh will use Consul DNS # for default DNS resolution. The DNS lookups fall back to the nameserver IPs # listed in /etc/resolv.conf if not found in Consul. # @type: boolean @@ -2264,7 +2264,7 @@ connectInject: # @type: map meta: null - # Configures metrics for Consul Connect services. All values are overridable + # Configures metrics for Consul service mesh services. All values are overridable # via annotations on a per-pod basis. metrics: # If true, the connect-injector will automatically @@ -2414,7 +2414,7 @@ connectInject: # annotated. Use `["*"]` to automatically allow all k8s namespaces. # # For example, `["namespace1", "namespace2"]` will only allow pods in the k8s - # namespaces `namespace1` and `namespace2` to have Connect sidecars injected + # namespaces `namespace1` and `namespace2` to have Consul service mesh sidecars injected # and registered with Consul. All other k8s namespaces will be ignored. # # To deny all namespaces, set this to `[]`. @@ -2599,7 +2599,7 @@ connectInject: # [Mesh Gateways](https://developer.hashicorp.com/consul/docs/connect/gateways/mesh-gateway) enable Consul Connect to work across Consul datacenters. meshGateway: # If [mesh gateways](https://developer.hashicorp.com/consul/docs/connect/gateways/mesh-gateway) are enabled, a Deployment will be created that runs - # gateways and Consul Connect will be configured to use gateways. + # gateways and Consul service mesh will be configured to use gateways. # This setting is required for [Cluster Peering](https://developer.hashicorp.com/consul/docs/connect/cluster-peering/k8s). # Requirements: consul 1.6.0+ if using `global.acls.manageSystemACLs``. enabled: false From 6624d34b9f1a4d0f12cef156c5f35152ee3e08b5 Mon Sep 17 00:00:00 2001 From: Dan Stough Date: Mon, 10 Jul 2023 12:18:38 -0400 Subject: [PATCH 267/340] docs: self service changelog instructions (#2526) --- .github/pull_request_template.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index b615e69dd1..f5d211b137 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -9,7 +9,6 @@ How I expect reviewers to test this PR: Checklist: - [ ] Tests added -- [ ] CHANGELOG entry added - > HashiCorp engineers only, community PRs should not add a changelog entry. - > Entries should use present tense (e.g. Add support for...) +- [ ] [CHANGELOG entry added](https://github.com/hashicorp/consul-k8s/blob/main/CONTRIBUTING.md#adding-a-changelog-entry) + From 11a18515e988187dfb905a878a53eca04819b60a Mon Sep 17 00:00:00 2001 From: Dan Stough Date: Mon, 10 Jul 2023 13:56:46 -0400 Subject: [PATCH 268/340] feat: adding security context and annotations to tls and acl init/cleanup jobs (#2525) * feat: adding security context and annotations to tls and acl init/cleanup jobs * changelog --------- Co-authored-by: Chinikins --- .changelog/2525.txt | 3 ++ .../consul/templates/gateway-cleanup-job.yaml | 3 ++ .../templates/gateway-resources-job.yaml | 3 ++ .../server-acl-init-cleanup-job.yaml | 7 +++ .../consul/templates/server-acl-init-job.yaml | 7 +++ .../templates/tls-init-cleanup-job.yaml | 7 +++ charts/consul/templates/tls-init-job.yaml | 7 +++ .../consul/test/unit/gateway-cleanup-job.bats | 24 ++++++++- .../test/unit/gateway-resources-job.bats | 25 ++++++++- .../unit/server-acl-init-cleanup-job.bats | 39 ++++++++++++++ .../consul/test/unit/server-acl-init-job.bats | 52 ++++++++++++++++--- .../test/unit/tls-init-cleanup-job.bats | 40 ++++++++++++++ charts/consul/test/unit/tls-init-job.bats | 39 ++++++++++++++ charts/consul/values.yaml | 32 ++++++++++++ 14 files changed, 280 insertions(+), 8 deletions(-) create mode 100644 .changelog/2525.txt diff --git a/.changelog/2525.txt b/.changelog/2525.txt new file mode 100644 index 0000000000..74a2cd596e --- /dev/null +++ b/.changelog/2525.txt @@ -0,0 +1,3 @@ +```release-note:improvement +helm: adds values for `securityContext` and `annotations` on TLS and ACL init/cleanup jobs. +``` diff --git a/charts/consul/templates/gateway-cleanup-job.yaml b/charts/consul/templates/gateway-cleanup-job.yaml index 44f032b5fd..8731aaed81 100644 --- a/charts/consul/templates/gateway-cleanup-job.yaml +++ b/charts/consul/templates/gateway-cleanup-job.yaml @@ -31,6 +31,9 @@ spec: {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" + {{- if .Values.global.acls.annotations }} + {{- tpl .Values.global.acls.annotations . | nindent 8 }} + {{- end }} spec: restartPolicy: Never serviceAccountName: {{ template "consul.fullname" . }}-gateway-cleanup diff --git a/charts/consul/templates/gateway-resources-job.yaml b/charts/consul/templates/gateway-resources-job.yaml index 441e64eb14..5fcd96cad3 100644 --- a/charts/consul/templates/gateway-resources-job.yaml +++ b/charts/consul/templates/gateway-resources-job.yaml @@ -31,6 +31,9 @@ spec: {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" + {{- if .Values.global.acls.annotations }} + {{- tpl .Values.global.acls.annotations . | nindent 8 }} + {{- end }} spec: restartPolicy: Never serviceAccountName: {{ template "consul.fullname" . }}-gateway-resources diff --git a/charts/consul/templates/server-acl-init-cleanup-job.yaml b/charts/consul/templates/server-acl-init-cleanup-job.yaml index 38ecfcf1b0..4d0aa8f05d 100644 --- a/charts/consul/templates/server-acl-init-cleanup-job.yaml +++ b/charts/consul/templates/server-acl-init-cleanup-job.yaml @@ -47,9 +47,16 @@ spec: {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" + {{- if .Values.global.acls.annotations }} + {{- tpl .Values.global.acls.annotations . | nindent 8 }} + {{- end }} spec: restartPolicy: Never serviceAccountName: {{ template "consul.fullname" . }}-server-acl-init-cleanup + {{- if .Values.server.containerSecurityContext.aclInit }} + securityContext: + {{- toYaml .Values.server.containerSecurityContext.aclInit | nindent 8 }} + {{- end }} containers: - name: server-acl-init-cleanup image: {{ .Values.global.imageK8S }} diff --git a/charts/consul/templates/server-acl-init-job.yaml b/charts/consul/templates/server-acl-init-job.yaml index 27272d0f76..6625e23b38 100644 --- a/charts/consul/templates/server-acl-init-job.yaml +++ b/charts/consul/templates/server-acl-init-job.yaml @@ -46,6 +46,9 @@ spec: {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" + {{- if .Values.global.acls.annotations }} + {{- tpl .Values.global.acls.annotations . | nindent 8 }} + {{- end }} {{- if .Values.global.secretsBackend.vault.enabled }} {{- /* Run the Vault agent as both an init container and sidecar. @@ -94,6 +97,10 @@ spec: spec: restartPolicy: Never serviceAccountName: {{ template "consul.fullname" . }}-server-acl-init + {{- if .Values.server.containerSecurityContext.aclInit }} + securityContext: + {{- toYaml .Values.server.containerSecurityContext.aclInit | nindent 8 }} + {{- end }} {{- if (or .Values.global.tls.enabled .Values.global.acls.replicationToken.secretName .Values.global.acls.bootstrapToken.secretName) }} volumes: {{- if and .Values.global.tls.enabled (not .Values.global.secretsBackend.vault.enabled) }} diff --git a/charts/consul/templates/tls-init-cleanup-job.yaml b/charts/consul/templates/tls-init-cleanup-job.yaml index ba29bb84ae..69b5a30849 100644 --- a/charts/consul/templates/tls-init-cleanup-job.yaml +++ b/charts/consul/templates/tls-init-cleanup-job.yaml @@ -35,9 +35,16 @@ spec: {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" + {{- if .Values.global.tls.annotations }} + {{- tpl .Values.global.tls.annotations . | nindent 8 }} + {{- end }} spec: restartPolicy: Never serviceAccountName: {{ template "consul.fullname" . }}-tls-init-cleanup + {{- if .Values.server.containerSecurityContext.tlsInit }} + securityContext: + {{- toYaml .Values.server.containerSecurityContext.tlsInit | nindent 8 }} + {{- end }} containers: - name: tls-init-cleanup image: "{{ .Values.global.image }}" diff --git a/charts/consul/templates/tls-init-job.yaml b/charts/consul/templates/tls-init-job.yaml index d002ae7a75..5839f07dbf 100644 --- a/charts/consul/templates/tls-init-job.yaml +++ b/charts/consul/templates/tls-init-job.yaml @@ -35,9 +35,16 @@ spec: {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" + {{- if .Values.global.tls.annotations }} + {{- tpl .Values.global.tls.annotations . | nindent 8 }} + {{- end }} spec: restartPolicy: Never serviceAccountName: {{ template "consul.fullname" . }}-tls-init + {{- if .Values.server.containerSecurityContext.tlsInit }} + securityContext: + {{- toYaml .Values.server.containerSecurityContext.tlsInit | nindent 8 }} + {{- end }} {{- if (and .Values.global.tls.caCert.secretName .Values.global.tls.caKey.secretName) }} volumes: - name: consul-ca-cert diff --git a/charts/consul/test/unit/gateway-cleanup-job.bats b/charts/consul/test/unit/gateway-cleanup-job.bats index ff59768c75..657bf4a791 100644 --- a/charts/consul/test/unit/gateway-cleanup-job.bats +++ b/charts/consul/test/unit/gateway-cleanup-job.bats @@ -18,6 +18,28 @@ target=templates/gateway-cleanup-job.yaml assert_empty helm template \ -s $target \ --set 'connectInject.enabled=false' \ - . + . } + +#-------------------------------------------------------------------- +# annotations + +@test "gatewaycleanup/Job: no annotations defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s $target \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations | del(."consul.hashicorp.com/connect-inject") | del(."consul.hashicorp.com/config-checksum")' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "gatewaycleanup/Job: annotations can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s $target \ + --set 'global.acls.annotations=foo: bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} diff --git a/charts/consul/test/unit/gateway-resources-job.bats b/charts/consul/test/unit/gateway-resources-job.bats index 0d3bfa2e4d..27bba00ed5 100644 --- a/charts/consul/test/unit/gateway-resources-job.bats +++ b/charts/consul/test/unit/gateway-resources-job.bats @@ -18,7 +18,7 @@ target=templates/gateway-resources-job.yaml assert_empty helm template \ -s $target \ --set 'connectInject.enabled=false' \ - . + . } @test "gatewayresources/Job: imageK8S set properly" { @@ -116,3 +116,26 @@ target=templates/gateway-resources-job.yaml local actual=$(echo "$spec" | jq '.[14]') [ "${actual}" = "\"-service-annotations=- bingo\"" ] } + + +#-------------------------------------------------------------------- +# annotations + +@test "gatewayresources/Job: no annotations defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s $target \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations | del(."consul.hashicorp.com/connect-inject") | del(."consul.hashicorp.com/config-checksum")' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "gatewayresources/Job: annotations can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s $target \ + --set 'global.acls.annotations=foo: bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} diff --git a/charts/consul/test/unit/server-acl-init-cleanup-job.bats b/charts/consul/test/unit/server-acl-init-cleanup-job.bats index bf6a455d0e..c886b2ec51 100644 --- a/charts/consul/test/unit/server-acl-init-cleanup-job.bats +++ b/charts/consul/test/unit/server-acl-init-cleanup-job.bats @@ -183,3 +183,42 @@ load _helpers yq -r '.spec.template.spec.containers[0].resources.foo' | tee /dev/stderr) [ "${actual}" = "bar" ] } + +#-------------------------------------------------------------------- +# server.containerSecurityContext.aclInit + +@test "serverACLInitCleanup/Job: securityContext is set when server.containerSecurityContext.aclInit is set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-cleanup-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'server.containerSecurityContext.aclInit.runAsUser=100' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.runAsUser' | tee /dev/stderr) + + [ "${actual}" = "100" ] +} + +#-------------------------------------------------------------------- +# annotations + +@test "serverACLInitCleanup/Job: no annotations defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-cleanup-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations | del(."consul.hashicorp.com/connect-inject") | del(."consul.hashicorp.com/config-checksum")' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "serverACLInitCleanup/Job: annotations can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-cleanup-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.acls.annotations=foo: bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} diff --git a/charts/consul/test/unit/server-acl-init-job.bats b/charts/consul/test/unit/server-acl-init-job.bats index a0d0950e89..17c3e63935 100644 --- a/charts/consul/test/unit/server-acl-init-job.bats +++ b/charts/consul/test/unit/server-acl-init-job.bats @@ -590,6 +590,22 @@ load _helpers [ "${actual}" = "key" ] } +#-------------------------------------------------------------------- +# server.containerSecurityContext.aclInit + +@test "serverACLInit/Job: securityContext is set when server.containerSecurityContext.aclInit is set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'server.containerSecurityContext.aclInit.runAsUser=100' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.runAsUser' | tee /dev/stderr) + + [ "${actual}" = "100" ] + +} + #-------------------------------------------------------------------- # Vault @@ -2030,7 +2046,7 @@ load _helpers --set 'global.cloud.authUrl.secretName=auth-url-name' \ . [ "$status" -eq 1 ] - + [[ "$output" =~ "When either global.cloud.authUrl.secretName or global.cloud.authUrl.secretKey is defined, both must be set." ]] } @@ -2050,7 +2066,7 @@ load _helpers --set 'global.cloud.authUrl.secretKey=auth-url-key' \ . [ "$status" -eq 1 ] - + [[ "$output" =~ "When either global.cloud.authUrl.secretName or global.cloud.authUrl.secretKey is defined, both must be set." ]] } @@ -2070,7 +2086,7 @@ load _helpers --set 'global.cloud.apiHost.secretName=auth-url-name' \ . [ "$status" -eq 1 ] - + [[ "$output" =~ "When either global.cloud.apiHost.secretName or global.cloud.apiHost.secretKey is defined, both must be set." ]] } @@ -2090,7 +2106,7 @@ load _helpers --set 'global.cloud.apiHost.secretKey=auth-url-key' \ . [ "$status" -eq 1 ] - + [[ "$output" =~ "When either global.cloud.apiHost.secretName or global.cloud.apiHost.secretKey is defined, both must be set." ]] } @@ -2110,7 +2126,7 @@ load _helpers --set 'global.cloud.scadaAddress.secretName=scada-address-name' \ . [ "$status" -eq 1 ] - + [[ "$output" =~ "When either global.cloud.scadaAddress.secretName or global.cloud.scadaAddress.secretKey is defined, both must be set." ]] } @@ -2130,7 +2146,7 @@ load _helpers --set 'global.cloud.scadaAddress.secretKey=scada-address-key' \ . [ "$status" -eq 1 ] - + [[ "$output" =~ "When either global.cloud.scadaAddress.secretName or global.cloud.scadaAddress.secretKey is defined, both must be set." ]] } @@ -2226,3 +2242,27 @@ load _helpers yq -r '.spec.template.spec.containers[0].resources.foo' | tee /dev/stderr) [ "${actual}" = "bar" ] } + +#-------------------------------------------------------------------- +# annotations + +@test "serverACLInit/Job: no annotations defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations | del(."consul.hashicorp.com/connect-inject") | del(."consul.hashicorp.com/config-checksum")' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "serverACLInit/Job: annotations can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.acls.annotations=foo: bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} diff --git a/charts/consul/test/unit/tls-init-cleanup-job.bats b/charts/consul/test/unit/tls-init-cleanup-job.bats index 04b4a2df31..735d991780 100644 --- a/charts/consul/test/unit/tls-init-cleanup-job.bats +++ b/charts/consul/test/unit/tls-init-cleanup-job.bats @@ -119,3 +119,43 @@ load _helpers [ "${actualTemplateFoo}" = "bar" ] [ "${actualTemplateBaz}" = "qux" ] } + +#-------------------------------------------------------------------- +# server.containerSecurityContext.tlsInit + +@test "tlsInitCleanup/Job: securityContext is set when server.containerSecurityContext.tlsInit is set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/tls-init-cleanup-job.yaml \ + --set 'global.tls.enabled=true' \ + --set 'server.containerSecurityContext.tlsInit.runAsUser=100' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.runAsUser' | tee /dev/stderr) + + [ "${actual}" = "100" ] +} + + +#-------------------------------------------------------------------- +# annotations + +@test "tlsInitCleanup/Job: no annotations defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/tls-init-cleanup-job.yaml \ + --set 'global.tls.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations | del(."consul.hashicorp.com/connect-inject") | del(."consul.hashicorp.com/config-checksum")' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "tlsInitCleanup/Job: annotations can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/tls-init-cleanup-job.yaml \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.annotations=foo: bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} diff --git a/charts/consul/test/unit/tls-init-job.bats b/charts/consul/test/unit/tls-init-job.bats index f9294915a5..bf1f84a0a6 100644 --- a/charts/consul/test/unit/tls-init-job.bats +++ b/charts/consul/test/unit/tls-init-job.bats @@ -207,3 +207,42 @@ load _helpers [ "${actualTemplateFoo}" = "bar" ] [ "${actualTemplateBaz}" = "qux" ] } + +#-------------------------------------------------------------------- +# server.containerSecurityContext.tlsInit + +@test "tlsInit/Job: securityContext is set when server.containerSecurityContext.tlsInit is set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/tls-init-job.yaml \ + --set 'global.tls.enabled=true' \ + --set 'server.containerSecurityContext.tlsInit.runAsUser=100' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.runAsUser' | tee /dev/stderr) + + [ "${actual}" = "100" ] +} + +#-------------------------------------------------------------------- +# annotations + +@test "tlsInit/Job: no annotations defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/tls-init-job.yaml \ + --set 'global.tls.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations | del(."consul.hashicorp.com/connect-inject") | del(."consul.hashicorp.com/config-checksum")' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "tlsInit/Job: annotations can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/tls-init-job.yaml \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.annotations=foo: bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 16b641d5cb..b3f842e429 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -379,6 +379,18 @@ global: # @type: string secretKey: null + # This value defines additional annotations for + # tls init jobs. This should be formatted as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + # [Enterprise Only] `enableConsulNamespaces` indicates that you are running # Consul Enterprise v1.7+ with a valid Consul Enterprise license and would # like to make use of configuration beyond registering everything into @@ -489,6 +501,18 @@ global: # @type: string nodeSelector: null + # This value defines additional annotations for + # acl init jobs. This should be formatted as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + # [Enterprise Only] This value refers to a Kubernetes or Vault secret that you have created # that contains your enterprise license. It is required if you are using an # enterprise binary. Defining it here applies it to your cluster once a leader @@ -877,6 +901,14 @@ server: # @type: map # @recurse: false server: null + # The acl-init job + # @type: map + # @recurse: false + aclInit: null + # The tls-init job + # @type: map + # @recurse: false + tlsInit: null # This value is used to carefully # control a rolling update of Consul server agents. This value specifies the From fb02159cc1b96fab69e702a30f42724b0bd966fe Mon Sep 17 00:00:00 2001 From: Derek Menteer <105233703+hashi-derek@users.noreply.github.com> Date: Mon, 10 Jul 2023 16:53:01 -0500 Subject: [PATCH 269/340] NET-4813: Fix issue where virtual IP saving had insufficient ACLs. (#2520) Fix issue where virtual IP saving had insufficient ACLs. --- .changelog/2520.txt | 4 +++ .../framework/connhelper/connect_helper.go | 13 +++++++++ .../tests/connect/connect_inject_test.go | 27 +++++++++++++++++++ .../bases/resolver-redirect/intention.yaml | 24 +++++++++++++++++ .../resolver-redirect/kustomization.yaml | 8 ++++++ .../bases/resolver-redirect/resolver.yaml | 10 +++++++ .../bases/resolver-redirect/service.yaml | 15 +++++++++++ .../resolver-redirect/serviceaccount.yaml | 7 +++++ .../kustomization.yaml | 5 ++++ .../subcommand/server-acl-init/rules.go | 1 + .../subcommand/server-acl-init/rules_test.go | 3 +++ 11 files changed, 117 insertions(+) create mode 100644 .changelog/2520.txt create mode 100644 acceptance/tests/fixtures/bases/resolver-redirect/intention.yaml create mode 100644 acceptance/tests/fixtures/bases/resolver-redirect/kustomization.yaml create mode 100644 acceptance/tests/fixtures/bases/resolver-redirect/resolver.yaml create mode 100644 acceptance/tests/fixtures/bases/resolver-redirect/service.yaml create mode 100644 acceptance/tests/fixtures/bases/resolver-redirect/serviceaccount.yaml create mode 100644 acceptance/tests/fixtures/cases/resolver-redirect-virtualip/kustomization.yaml diff --git a/.changelog/2520.txt b/.changelog/2520.txt new file mode 100644 index 0000000000..96d03dc093 --- /dev/null +++ b/.changelog/2520.txt @@ -0,0 +1,4 @@ +```release-note:bug +transparent-proxy: Fix issue where connect-inject lacked sufficient `mesh:write` privileges in some deployments, +which prevented virtual IPs from persisting properly. +``` diff --git a/acceptance/framework/connhelper/connect_helper.go b/acceptance/framework/connhelper/connect_helper.go index 8695e74d56..d10bf43b1a 100644 --- a/acceptance/framework/connhelper/connect_helper.go +++ b/acceptance/framework/connhelper/connect_helper.go @@ -127,6 +127,19 @@ func (c *ConnectHelper) DeployClientAndServer(t *testing.T) { } } +// CreateResolverRedirect creates a resolver that redirects to a static-server, a corresponding k8s service, +// and intentions. This helper is primarly used to ensure that the virtual-ips are persisted to consul properly. +func (c *ConnectHelper) CreateResolverRedirect(t *testing.T) { + logger.Log(t, "creating resolver redirect") + options := c.Ctx.KubectlOptions(t) + kustomizeDir := "../fixtures/cases/resolver-redirect-virtualip" + k8s.KubectlApplyK(t, options, kustomizeDir) + + helpers.Cleanup(t, c.Cfg.NoCleanupOnFailure, func() { + k8s.KubectlDeleteK(t, options, kustomizeDir) + }) +} + // TestConnectionFailureWithoutIntention ensures the connection to the static // server fails when no intentions are configured. func (c *ConnectHelper) TestConnectionFailureWithoutIntention(t *testing.T) { diff --git a/acceptance/tests/connect/connect_inject_test.go b/acceptance/tests/connect/connect_inject_test.go index 3f1660319f..199bbf0f1b 100644 --- a/acceptance/tests/connect/connect_inject_test.go +++ b/acceptance/tests/connect/connect_inject_test.go @@ -60,6 +60,33 @@ func TestConnectInject(t *testing.T) { } } +// TestConnectInject_VirtualIPFailover ensures that KubeDNS entries are saved to the virtual IP address table in Consul. +func TestConnectInject_VirtualIPFailover(t *testing.T) { + cfg := suite.Config() + if !cfg.EnableTransparentProxy { + // This can only be tested in transparent proxy mode. + t.SkipNow() + } + ctx := suite.Environment().DefaultContext(t) + + releaseName := helpers.RandomName() + connHelper := connhelper.ConnectHelper{ + ClusterKind: consul.Helm, + Secure: true, + ReleaseName: releaseName, + Ctx: ctx, + Cfg: cfg, + } + + connHelper.Setup(t) + + connHelper.Install(t) + connHelper.CreateResolverRedirect(t) + connHelper.DeployClientAndServer(t) + + k8s.CheckStaticServerConnectionSuccessful(t, connHelper.Ctx.KubectlOptions(t), "static-client", "http://resolver-redirect") +} + // Test the endpoints controller cleans up force-killed pods. func TestConnectInject_CleanupKilledPods(t *testing.T) { for _, secure := range []bool{false, true} { diff --git a/acceptance/tests/fixtures/bases/resolver-redirect/intention.yaml b/acceptance/tests/fixtures/bases/resolver-redirect/intention.yaml new file mode 100644 index 0000000000..faff0cd251 --- /dev/null +++ b/acceptance/tests/fixtures/bases/resolver-redirect/intention.yaml @@ -0,0 +1,24 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ServiceIntentions +metadata: + name: client-to-server +spec: + destination: + name: static-server + sources: + - name: static-client + action: allow +--- +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ServiceIntentions +metadata: + name: client-to-redirect +spec: + destination: + name: resolver-redirect + sources: + - name: static-client + action: allow \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/resolver-redirect/kustomization.yaml b/acceptance/tests/fixtures/bases/resolver-redirect/kustomization.yaml new file mode 100644 index 0000000000..323957ad53 --- /dev/null +++ b/acceptance/tests/fixtures/bases/resolver-redirect/kustomization.yaml @@ -0,0 +1,8 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - intention.yaml + - service.yaml + - serviceaccount.yaml + - resolver.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/resolver-redirect/resolver.yaml b/acceptance/tests/fixtures/bases/resolver-redirect/resolver.yaml new file mode 100644 index 0000000000..9adbcc9fb4 --- /dev/null +++ b/acceptance/tests/fixtures/bases/resolver-redirect/resolver.yaml @@ -0,0 +1,10 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ServiceResolver +metadata: + name: resolver-redirect +spec: + redirect: + service: static-server \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/resolver-redirect/service.yaml b/acceptance/tests/fixtures/bases/resolver-redirect/service.yaml new file mode 100644 index 0000000000..e63ae97cca --- /dev/null +++ b/acceptance/tests/fixtures/bases/resolver-redirect/service.yaml @@ -0,0 +1,15 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: v1 +kind: Service +metadata: + name: resolver-redirect +spec: + selector: + # Nothing needs to be selected. We only utilize this service so that KubeDNS has a ClusterIP to resolve. + app: idonotexist + ports: + - name: http + port: 80 + targetPort: 8080 diff --git a/acceptance/tests/fixtures/bases/resolver-redirect/serviceaccount.yaml b/acceptance/tests/fixtures/bases/resolver-redirect/serviceaccount.yaml new file mode 100644 index 0000000000..c74ecd667b --- /dev/null +++ b/acceptance/tests/fixtures/bases/resolver-redirect/serviceaccount.yaml @@ -0,0 +1,7 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: v1 +kind: ServiceAccount +metadata: + name: resolver-redirect diff --git a/acceptance/tests/fixtures/cases/resolver-redirect-virtualip/kustomization.yaml b/acceptance/tests/fixtures/cases/resolver-redirect-virtualip/kustomization.yaml new file mode 100644 index 0000000000..09790e05c6 --- /dev/null +++ b/acceptance/tests/fixtures/cases/resolver-redirect-virtualip/kustomization.yaml @@ -0,0 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ../../bases/resolver-redirect \ No newline at end of file diff --git a/control-plane/subcommand/server-acl-init/rules.go b/control-plane/subcommand/server-acl-init/rules.go index d86dd38a0a..5f65b6c75c 100644 --- a/control-plane/subcommand/server-acl-init/rules.go +++ b/control-plane/subcommand/server-acl-init/rules.go @@ -330,6 +330,7 @@ partition "{{ .PartitionName }}" { mesh = "write" acl = "write" {{- else }} + mesh = "write" operator = "write" acl = "write" {{- end }} diff --git a/control-plane/subcommand/server-acl-init/rules_test.go b/control-plane/subcommand/server-acl-init/rules_test.go index 1e629d68f7..a45af33c11 100644 --- a/control-plane/subcommand/server-acl-init/rules_test.go +++ b/control-plane/subcommand/server-acl-init/rules_test.go @@ -953,6 +953,7 @@ func TestInjectRules(t *testing.T) { EnablePartitions: false, EnablePeering: false, Expected: ` + mesh = "write" operator = "write" acl = "write" node_prefix "" { @@ -969,6 +970,7 @@ func TestInjectRules(t *testing.T) { EnablePartitions: false, EnablePeering: false, Expected: ` + mesh = "write" operator = "write" acl = "write" node_prefix "" { @@ -987,6 +989,7 @@ func TestInjectRules(t *testing.T) { EnablePartitions: false, EnablePeering: true, Expected: ` + mesh = "write" operator = "write" acl = "write" peering = "write" From 6adb9a2f5ca3c6b86d34334bde49f82e0bca0cd0 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Mon, 10 Jul 2023 16:18:02 -0700 Subject: [PATCH 270/340] reactivate proxy-lifecycle tests (#2532) --- .../tests/connect/connect_proxy_lifecycle_test.go | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/acceptance/tests/connect/connect_proxy_lifecycle_test.go b/acceptance/tests/connect/connect_proxy_lifecycle_test.go index ecdc51b547..321c002a4c 100644 --- a/acceptance/tests/connect/connect_proxy_lifecycle_test.go +++ b/acceptance/tests/connect/connect_proxy_lifecycle_test.go @@ -17,7 +17,6 @@ import ( "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/logger" "github.com/hashicorp/consul/sdk/testutil/retry" - "github.com/hashicorp/go-version" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -36,15 +35,7 @@ const ( func TestConnectInject_ProxyLifecycleShutdown(t *testing.T) { cfg := suite.Config() - ver, err := version.NewVersion("1.2.0") - require.NoError(t, err) - if cfg.ConsulDataplaneVersion != nil && cfg.ConsulDataplaneVersion.LessThan(ver) { - t.Skipf("skipping this test because proxy lifecycle management is not supported in consul-dataplane version %v", cfg.ConsulDataplaneVersion.String()) - } - for _, testCfg := range []LifecycleShutdownConfig{ - {secure: false, helmValues: map[string]string{}}, - {secure: true, helmValues: map[string]string{}}, {secure: false, helmValues: map[string]string{ helmDrainListenersKey: "true", helmGracePeriodSecondsKey: "15", @@ -72,6 +63,7 @@ func TestConnectInject_ProxyLifecycleShutdown(t *testing.T) { } { // Determine if listeners should be expected to drain inbound connections var drainListenersEnabled bool + var err error val, ok := testCfg.helmValues[helmDrainListenersKey] if ok { drainListenersEnabled, err = strconv.ParseBool(val) From 46766525bed97353e902acc42d338a100a9a47b1 Mon Sep 17 00:00:00 2001 From: Derek Menteer <105233703+hashi-derek@users.noreply.github.com> Date: Mon, 10 Jul 2023 18:27:04 -0500 Subject: [PATCH 271/340] Fix test flakes. (#2483) --- .../framework/connhelper/connect_helper.go | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/acceptance/framework/connhelper/connect_helper.go b/acceptance/framework/connhelper/connect_helper.go index d10bf43b1a..670307da88 100644 --- a/acceptance/framework/connhelper/connect_helper.go +++ b/acceptance/framework/connhelper/connect_helper.go @@ -117,14 +117,19 @@ func (c *ConnectHelper) DeployClientAndServer(t *testing.T) { // Check that both static-server and static-client have been injected and // now have 2 containers. - for _, labelSelector := range []string{"app=static-server", "app=static-client"} { - podList, err := c.Ctx.KubernetesClient(t).CoreV1().Pods(c.Ctx.KubectlOptions(t).Namespace).List(context.Background(), metav1.ListOptions{ - LabelSelector: labelSelector, + retry.RunWith( + &retry.Timer{Timeout: 30 * time.Second, Wait: 100 * time.Millisecond}, t, + func(r *retry.R) { + for _, labelSelector := range []string{"app=static-server", "app=static-client"} { + podList, err := c.Ctx.KubernetesClient(t).CoreV1().Pods(c.Ctx.KubectlOptions(t).Namespace).List(context.Background(), metav1.ListOptions{ + LabelSelector: labelSelector, + FieldSelector: `status.phase=Running`, + }) + require.NoError(r, err) + require.Len(r, podList.Items, 1) + require.Len(r, podList.Items[0].Spec.Containers, 2) + } }) - require.NoError(t, err) - require.Len(t, podList.Items, 1) - require.Len(t, podList.Items[0].Spec.Containers, 2) - } } // CreateResolverRedirect creates a resolver that redirects to a static-server, a corresponding k8s service, From 486061a751d34be9565ff6be0fb9995bd3ae2e64 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Tue, 11 Jul 2023 08:18:21 -0400 Subject: [PATCH 272/340] Update chart to use OSS image (#2528) --- charts/consul/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index b3f842e429..b5f8437d83 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -66,7 +66,7 @@ global: # image: "hashicorp/consul-enterprise:1.10.0-ent" # ``` # @default: hashicorp/consul: - image: docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.17-dev + image: docker.mirror.hashicorp.services/hashicorppreview/consul:1.17-dev # Array of objects containing image pull secret names that will be applied to each service account. # This can be used to reference image pull secrets if using a custom consul or consul-k8s-control-plane Docker image. From 6b45156cc14689372ef4eef437aeac281a245b17 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Tue, 11 Jul 2023 10:06:31 -0400 Subject: [PATCH 273/340] Remove todo.txt (#2548) --- charts/consul/todo.txt | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 charts/consul/todo.txt diff --git a/charts/consul/todo.txt b/charts/consul/todo.txt deleted file mode 100644 index c79bef389b..0000000000 --- a/charts/consul/todo.txt +++ /dev/null @@ -1,3 +0,0 @@ - -- [x] Remove gatewayclass gatewayclassconfig bats -- [ ] Add test for each of the CRDs From fd201c57caa59875492914392b6e45a368e3d749 Mon Sep 17 00:00:00 2001 From: Melisa Griffin Date: Tue, 11 Jul 2023 16:02:26 -0400 Subject: [PATCH 274/340] makes gateway controllers less chatty (#2524) --- .changelog/2524.txt | 3 +++ .../controllers/gateway_controller.go | 25 +++++++++++++++++-- .../controllers/gatewayclass_controller.go | 20 ++++++++++++--- .../gatewayclassconfig_controller.go | 21 +++++++++++----- .../api-gateway/gatekeeper/deployment.go | 8 +++--- .../api-gateway/gatekeeper/gatekeeper.go | 4 +-- .../api-gateway/gatekeeper/service.go | 6 ++--- 7 files changed, 67 insertions(+), 20 deletions(-) create mode 100644 .changelog/2524.txt diff --git a/.changelog/2524.txt b/.changelog/2524.txt new file mode 100644 index 0000000000..5d634e68e1 --- /dev/null +++ b/.changelog/2524.txt @@ -0,0 +1,3 @@ +```release-note:improvement +(api-gateway) make API gateway controller less verbose +``` \ No newline at end of file diff --git a/control-plane/api-gateway/controllers/gateway_controller.go b/control-plane/api-gateway/controllers/gateway_controller.go index 8569508769..66347adea4 100644 --- a/control-plane/api-gateway/controllers/gateway_controller.go +++ b/control-plane/api-gateway/controllers/gateway_controller.go @@ -72,7 +72,7 @@ func (r *GatewayController) Reconcile(ctx context.Context, req ctrl.Request) (ct var gateway gwv1beta1.Gateway - log := r.Log.WithValues("gateway", req.NamespacedName) + log := r.Log.V(1).WithValues("gateway", req.NamespacedName) log.Info("Reconciling Gateway") // get the gateway @@ -199,6 +199,11 @@ func (r *GatewayController) Reconcile(ctx context.Context, req ctrl.Request) (ct err := r.updateGatekeeperResources(ctx, log, &gateway, updates.GatewayClassConfig) if err != nil { + if k8serrors.IsConflict(err) { + log.Info("error updating object when updating gateway resources, will try to re-reconcile") + + return ctrl.Result{Requeue: true}, nil + } log.Error(err, "unable to update gateway resources") return ctrl.Result{}, err } @@ -206,11 +211,16 @@ func (r *GatewayController) Reconcile(ctx context.Context, req ctrl.Request) (ct } else { err := r.deleteGatekeeperResources(ctx, log, &gateway) if err != nil { + if k8serrors.IsConflict(err) { + log.Info("error updating object when deleting gateway resources, will try to re-reconcile") + + return ctrl.Result{Requeue: true}, nil + } log.Error(err, "unable to delete gateway resources") return ctrl.Result{}, err } r.gatewayCache.RemoveSubscription(nonNormalizedConsulKey) - // make sure we have deregister all services even if they haven't + // make sure we have deregistered all services even if they haven't // hit cache yet if err := r.deregisterAllServices(ctx, nonNormalizedConsulKey); err != nil { log.Error(err, "error deregistering services") @@ -266,6 +276,11 @@ func (r *GatewayController) Reconcile(ctx context.Context, req ctrl.Request) (ct for _, update := range updates.Kubernetes.Updates.Operations() { log.Info("update in Kubernetes", "kind", update.GetObjectKind().GroupVersionKind().Kind, "namespace", update.GetNamespace(), "name", update.GetName()) if err := r.updateAndResetStatus(ctx, update); err != nil { + if k8serrors.IsConflict(err) { + log.Info("error updating object for gateway, will try to re-reconcile") + + return ctrl.Result{Requeue: true}, nil + } log.Error(err, "error updating object") return ctrl.Result{}, err } @@ -274,6 +289,11 @@ func (r *GatewayController) Reconcile(ctx context.Context, req ctrl.Request) (ct for _, update := range updates.Kubernetes.StatusUpdates.Operations() { log.Info("update status in Kubernetes", "kind", update.GetObjectKind().GroupVersionKind().Kind, "namespace", update.GetNamespace(), "name", update.GetName()) if err := r.Client.Status().Update(ctx, update); err != nil { + if k8serrors.IsConflict(err) { + log.Info("error updating status for gateway, will try to re-reconcile") + + return ctrl.Result{Requeue: true}, nil + } log.Error(err, "error updating status") return ctrl.Result{}, err } @@ -305,6 +325,7 @@ func (r *GatewayController) updateAndResetStatus(ctx context.Context, o client.O if err := r.Client.Update(ctx, o); err != nil { return err } + // reset the status in case it needs to be updated below reflect.ValueOf(o).Elem().FieldByName("Status").Set(status) return nil diff --git a/control-plane/api-gateway/controllers/gatewayclass_controller.go b/control-plane/api-gateway/controllers/gatewayclass_controller.go index e37d1b3bcd..3bde2d6ab1 100644 --- a/control-plane/api-gateway/controllers/gatewayclass_controller.go +++ b/control-plane/api-gateway/controllers/gatewayclass_controller.go @@ -6,7 +6,6 @@ package controllers import ( "context" "fmt" - "github.com/go-logr/logr" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" k8serrors "k8s.io/apimachinery/pkg/api/errors" @@ -42,7 +41,7 @@ type GatewayClassController struct { // Reconcile handles the reconciliation loop for GatewayClass objects. func (r *GatewayClassController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := r.Log.WithValues("gatewayClass", req.NamespacedName.Name) - log.Info("Reconciling GatewayClass") + log.V(1).Info("Reconciling GatewayClass") gc := &gwv1beta1.GatewayClass{} @@ -78,6 +77,11 @@ func (r *GatewayClassController) Reconcile(ctx context.Context, req ctrl.Request } // Remove our finalizer. if _, err := RemoveFinalizer(ctx, r.Client, gc, gatewayClassFinalizer); err != nil { + if k8serrors.IsConflict(err) { + log.V(1).Info("error removing finalizer for gatewayClass, will try to re-reconcile") + + return ctrl.Result{Requeue: true}, nil + } log.Error(err, "unable to remove finalizer") return ctrl.Result{}, err } @@ -87,6 +91,11 @@ func (r *GatewayClassController) Reconcile(ctx context.Context, req ctrl.Request // We are creating or updating the GatewayClass. didUpdate, err := EnsureFinalizer(ctx, r.Client, gc, gatewayClassFinalizer) if err != nil { + if k8serrors.IsConflict(err) { + log.V(1).Info("error adding finalizer for gatewayClass, will try to re-reconcile") + + return ctrl.Result{Requeue: true}, nil + } log.Error(err, "unable to add finalizer") return ctrl.Result{}, err } @@ -98,7 +107,12 @@ func (r *GatewayClassController) Reconcile(ctx context.Context, req ctrl.Request didUpdate, err = r.validateParametersRef(ctx, gc, log) if didUpdate { if err := r.Client.Status().Update(ctx, gc); err != nil { - log.Error(err, "unable to update GatewayClass") + if k8serrors.IsConflict(err) { + log.V(1).Info("error updating status for gatewayClass, will try to re-reconcile") + + return ctrl.Result{Requeue: true}, nil + } + log.Error(err, "unable to update status for GatewayClass") return ctrl.Result{}, err } return ctrl.Result{}, nil diff --git a/control-plane/api-gateway/controllers/gatewayclassconfig_controller.go b/control-plane/api-gateway/controllers/gatewayclassconfig_controller.go index 3889778348..878d6549f9 100644 --- a/control-plane/api-gateway/controllers/gatewayclassconfig_controller.go +++ b/control-plane/api-gateway/controllers/gatewayclassconfig_controller.go @@ -37,14 +37,14 @@ type GatewayClassConfigController struct { // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.8.3/pkg/reconcile func (r *GatewayClassConfigController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := r.Log.WithValues("gatewayClassConfig", req.NamespacedName.Name) - log.Info("Reconciling GatewayClassConfig ") + log.V(1).Info("Reconciling GatewayClassConfig ") gcc := &v1alpha1.GatewayClassConfig{} if err := r.Client.Get(ctx, req.NamespacedName, gcc); err != nil { if k8serrors.IsNotFound(err) { return ctrl.Result{}, nil } - r.Log.Error(err, "failed to get gateway class config") + log.Error(err, "failed to get gateway class config") return ctrl.Result{}, err } @@ -52,24 +52,33 @@ func (r *GatewayClassConfigController) Reconcile(ctx context.Context, req ctrl.R // We have a deletion, ensure we're not in use. used, err := gatewayClassConfigInUse(ctx, r.Client, gcc) if err != nil { - r.Log.Error(err, "failed to check if the gateway class config is still in use") + log.Error(err, "failed to check if the gateway class config is still in use") return ctrl.Result{}, err } if used { - r.Log.Info("gateway class config still in use") + log.Info("gateway class config still in use") // Requeue as to not block the reconciliation loop. return ctrl.Result{RequeueAfter: 10 * time.Second}, nil } // gcc is no longer in use. if _, err := RemoveFinalizer(ctx, r.Client, gcc, gatewayClassConfigFinalizer); err != nil { - r.Log.Error(err, "error removing gateway class config finalizer") + if k8serrors.IsConflict(err) { + log.V(1).Info("error removing gateway class config finalizer, will try to re-reconcile") + return ctrl.Result{Requeue: true}, nil + } + log.Error(err, "error removing gateway class config finalizer") return ctrl.Result{}, err } return ctrl.Result{}, nil } if _, err := EnsureFinalizer(ctx, r.Client, gcc, gatewayClassConfigFinalizer); err != nil { - r.Log.Error(err, "error adding gateway class config finalizer") + if k8serrors.IsConflict(err) { + log.V(1).Info("error adding gateway class config finalizer, will try to re-reconcile") + + return ctrl.Result{Requeue: true}, nil + } + log.Error(err, "error adding gateway class config finalizer") return ctrl.Result{}, err } diff --git a/control-plane/api-gateway/gatekeeper/deployment.go b/control-plane/api-gateway/gatekeeper/deployment.go index cc08e1bbef..3590caaf52 100644 --- a/control-plane/api-gateway/gatekeeper/deployment.go +++ b/control-plane/api-gateway/gatekeeper/deployment.go @@ -49,7 +49,7 @@ func (g *Gatekeeper) upsertDeployment(ctx context.Context, gateway gwv1beta1.Gat } if exists { - g.Log.Info("Existing Gateway Deployment found.") + g.Log.V(1).Info("Existing Gateway Deployment found.") // If the user has set the number of replicas, let's respect that. deployment.Spec.Replicas = existingDeployment.Spec.Replicas @@ -65,11 +65,11 @@ func (g *Gatekeeper) upsertDeployment(ctx context.Context, gateway gwv1beta1.Gat switch result { case controllerutil.OperationResultCreated: - g.Log.Info("Created Deployment") + g.Log.V(1).Info("Created Deployment") case controllerutil.OperationResultUpdated: - g.Log.Info("Updated Deployment") + g.Log.V(1).Info("Updated Deployment") case controllerutil.OperationResultNone: - g.Log.Info("No change to deployment") + g.Log.V(1).Info("No change to deployment") } return nil diff --git a/control-plane/api-gateway/gatekeeper/gatekeeper.go b/control-plane/api-gateway/gatekeeper/gatekeeper.go index 46243ff9a1..19444831ee 100644 --- a/control-plane/api-gateway/gatekeeper/gatekeeper.go +++ b/control-plane/api-gateway/gatekeeper/gatekeeper.go @@ -32,7 +32,7 @@ func New(log logr.Logger, client client.Client) *Gatekeeper { // Upsert creates or updates the resources for handling routing of network traffic. // This is done in order based on dependencies between resources. func (g *Gatekeeper) Upsert(ctx context.Context, gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config common.HelmConfig) error { - g.Log.Info(fmt.Sprintf("Upsert Gateway Deployment %s/%s", gateway.Namespace, gateway.Name)) + g.Log.V(1).Info(fmt.Sprintf("Upsert Gateway Deployment %s/%s", gateway.Namespace, gateway.Name)) if err := g.upsertRole(ctx, gateway, gcc, config); err != nil { return err @@ -60,7 +60,7 @@ func (g *Gatekeeper) Upsert(ctx context.Context, gateway gwv1beta1.Gateway, gcc // Delete removes the resources for handling routing of network traffic. // This is done in the reverse order of Upsert due to dependencies between resources. func (g *Gatekeeper) Delete(ctx context.Context, gatewayName types.NamespacedName) error { - g.Log.Info(fmt.Sprintf("Delete Gateway Deployment %s/%s", gatewayName.Namespace, gatewayName.Name)) + g.Log.V(1).Info(fmt.Sprintf("Delete Gateway Deployment %s/%s", gatewayName.Namespace, gatewayName.Name)) if err := g.deleteDeployment(ctx, gatewayName); err != nil { return err diff --git a/control-plane/api-gateway/gatekeeper/service.go b/control-plane/api-gateway/gatekeeper/service.go index 80272b7495..3b0e848899 100644 --- a/control-plane/api-gateway/gatekeeper/service.go +++ b/control-plane/api-gateway/gatekeeper/service.go @@ -43,11 +43,11 @@ func (g *Gatekeeper) upsertService(ctx context.Context, gateway gwv1beta1.Gatewa switch result { case controllerutil.OperationResultCreated: - g.Log.Info("Created Service") + g.Log.V(1).Info("Created Service") case controllerutil.OperationResultUpdated: - g.Log.Info("Updated Service") + g.Log.V(1).Info("Updated Service") case controllerutil.OperationResultNone: - g.Log.Info("No change to service") + g.Log.V(1).Info("No change to service") } return nil From 592e45702f3680f713d35511b41b39d3cc421b5e Mon Sep 17 00:00:00 2001 From: chappie <6537530+chapmanc@users.noreply.github.com> Date: Tue, 11 Jul 2023 14:33:21 -0600 Subject: [PATCH 275/340] HCP Observability acceptance test (#2254) --- acceptance/go.mod | 21 +- acceptance/go.sum | 128 ++++++++- acceptance/tests/cloud/basic_test.go | 1 + acceptance/tests/cloud/remote_dev_test.go | 264 ++++++++++++++++++ .../bases/cloud/service-intentions/acl.yaml | 15 + .../service-intentions/kustomization.yaml | 5 + .../consul/templates/server-statefulset.yaml | 1 + .../telemetry-collector-deployment.yaml | 2 +- 8 files changed, 431 insertions(+), 6 deletions(-) create mode 100644 acceptance/tests/cloud/remote_dev_test.go create mode 100644 acceptance/tests/fixtures/bases/cloud/service-intentions/acl.yaml create mode 100644 acceptance/tests/fixtures/bases/cloud/service-intentions/kustomization.yaml diff --git a/acceptance/go.mod b/acceptance/go.mod index 59cbbab79f..02ef978cf6 100644 --- a/acceptance/go.mod +++ b/acceptance/go.mod @@ -9,6 +9,7 @@ require ( github.com/hashicorp/consul/sdk v0.14.0-rc1 github.com/hashicorp/go-uuid v1.0.3 github.com/hashicorp/go-version v1.6.0 + github.com/hashicorp/hcp-sdk-go v0.50.0 github.com/hashicorp/serf v0.10.1 github.com/hashicorp/vault/api v1.8.3 github.com/stretchr/testify v1.8.3 @@ -24,6 +25,7 @@ require ( require ( github.com/armon/go-metrics v0.4.1 // indirect github.com/armon/go-radix v1.0.0 // indirect + github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect github.com/aws/aws-sdk-go v1.44.262 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect @@ -39,9 +41,18 @@ require ( github.com/ghodss/yaml v1.0.0 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-openapi/analysis v0.21.4 // indirect + github.com/go-openapi/errors v0.20.3 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.1 // indirect + github.com/go-openapi/loads v0.21.2 // indirect + github.com/go-openapi/runtime v0.25.0 // indirect + github.com/go-openapi/spec v0.20.8 // indirect + github.com/go-openapi/strfmt v0.21.3 // indirect github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-openapi/validate v0.22.1 // indirect + github.com/go-ozzo/ozzo-validation v3.6.0+incompatible // indirect github.com/go-sql-driver/mysql v1.5.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // indirect @@ -77,6 +88,7 @@ require ( github.com/mattn/go-isatty v0.0.17 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect github.com/miekg/dns v1.1.50 // indirect + github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.0 // indirect @@ -88,6 +100,8 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oklog/run v1.0.0 // indirect + github.com/oklog/ulid v1.3.1 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pierrec/lz4 v2.5.2+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect @@ -98,14 +112,18 @@ require ( github.com/prometheus/procfs v0.8.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect + github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/urfave/cli v1.22.2 // indirect + go.mongodb.org/mongo-driver v1.11.0 // indirect + go.opentelemetry.io/otel v1.11.1 // indirect + go.opentelemetry.io/otel/trace v1.11.1 // indirect go.uber.org/atomic v1.9.0 // indirect golang.org/x/crypto v0.1.0 // indirect golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect golang.org/x/mod v0.9.0 // indirect golang.org/x/net v0.10.0 // indirect - golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect + golang.org/x/oauth2 v0.6.0 // indirect golang.org/x/sys v0.8.0 // indirect golang.org/x/term v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect @@ -119,7 +137,6 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/square/go-jose.v2 v2.5.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/component-base v0.26.3 // indirect k8s.io/klog/v2 v2.100.1 // indirect k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect diff --git a/acceptance/go.sum b/acceptance/go.sum index 578a95b1dd..b3aaefe655 100644 --- a/acceptance/go.sum +++ b/acceptance/go.sum @@ -91,6 +91,9 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= +github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.16.26/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= @@ -214,31 +217,88 @@ github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KE github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= +github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= +github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc= +github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo= +github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.20.3 h1:rz6kiC84sqNQoqrtulzaL/VERgkoCyB6WdEkc2ujzUc= +github.com/go-openapi/errors v0.20.3/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g= +github.com/go-openapi/loads v0.21.2 h1:r2a/xFIYeZ4Qd2TnGpWDIQNcP80dIaZgf704za8enro= +github.com/go-openapi/loads v0.21.2/go.mod h1:Jq58Os6SSGz0rzh62ptiu8Z31I+OTHqmULx5e/gJbNw= +github.com/go-openapi/runtime v0.25.0 h1:7yQTCdRbWhX8vnIjdzU8S00tBYf7Sg71EBeorlPHvhc= +github.com/go-openapi/runtime v0.25.0/go.mod h1:Ux6fikcHXyyob6LNWxtE96hWwjBPYF0DXgVFuMTneOs= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= +github.com/go-openapi/spec v0.20.8 h1:ubHmXNY3FCIOinT8RNrrPfGc9t7I1qhPtdOGoG2AxRU= +github.com/go-openapi/spec v0.20.8/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= +github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= +github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= +github.com/go-openapi/strfmt v0.21.3 h1:xwhj5X6CjXEZZHMWy1zKJxvW9AfHC9pkyUjLvHtKG7o= +github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9Fqs9NU= +github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= +github.com/go-ozzo/ozzo-validation v3.6.0+incompatible h1:msy24VGS42fKO9K1vLz82/GeYW1cILu7Nuuj1N3BBkE= +github.com/go-ozzo/ozzo-validation v3.6.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= @@ -277,6 +337,7 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -291,6 +352,7 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/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.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -393,6 +455,8 @@ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+l github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/hcp-sdk-go v0.50.0 h1:vOUpVf4MQF/gtoBukuoYKs/i6KinTSpP5jhKCvsZ2bc= +github.com/hashicorp/hcp-sdk-go v0.50.0/go.mod h1:hZqky4HEzsKwvLOt4QJlZUrjeQmb4UCZUhDP2HyQFfc= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= @@ -421,6 +485,7 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= @@ -438,10 +503,13 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -460,8 +528,11 @@ github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -492,6 +563,8 @@ github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -501,6 +574,7 @@ github.com/mitchellh/go-testing-interface v1.14.0/go.mod h1:gfgS7OtZj6MA4U1UrDRp github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= @@ -518,6 +592,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= @@ -525,8 +600,11 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -541,11 +619,14 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/oracle/oci-go-sdk v7.1.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -595,6 +676,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uY github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto= @@ -612,9 +695,12 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUt github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= +github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= @@ -640,6 +726,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= @@ -647,6 +734,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.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= @@ -656,8 +745,14 @@ github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vdemeester/k8s-pkg-credentialprovider v0.0.0-20200107171650-7c61ffa44238/go.mod h1:JwQJCMWpUDqjZrB5jpw0f5VbN7U95zxFy1ZDpoEarGo= github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= +github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -666,11 +761,21 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= +go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= +go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= +go.mongodb.org/mongo-driver v1.11.0 h1:FZKhBSTydeuffHj9CBjXlR8vQLee1cQyTWYPA6/tqiE= +go.mongodb.org/mongo-driver v1.11.0/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opentelemetry.io/otel v1.11.1 h1:4WLLAmcfkmDk2ukNXJyq3/kiz/3UzCaYq6PskJsaou4= +go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE= +go.opentelemetry.io/otel/sdk v1.11.1 h1:F7KmQgoHljhUuJyA+9BiU+EkJfyX5nVVF4wyzWZpKxs= +go.opentelemetry.io/otel/trace v1.11.1 h1:ofxdnzsNrGBYXbP7t7zpUK281+go5rF7dvdIZXF8gdQ= +go.opentelemetry.io/otel/trace v1.11.1/go.mod h1:f/Q9G7vzk5u91PhbmKbg1Qn0rzH1LJ4vbPHFGkTPtOk= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= @@ -683,6 +788,7 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -690,8 +796,10 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -771,8 +879,10 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -785,12 +895,14 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= +golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= 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= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -812,10 +924,13 @@ golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -854,6 +969,7 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -903,9 +1019,13 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= @@ -1056,6 +1176,7 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= @@ -1082,7 +1203,9 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= @@ -1110,8 +1233,7 @@ k8s.io/client-go v0.26.3/go.mod h1:ZPNu9lm8/dbRIPAgteN30RSXea6vrCpFvq+MateTUuQ= k8s.io/cloud-provider v0.17.0/go.mod h1:Ze4c3w2C0bRsjkBUoHpFi+qWe3ob1wI2/7cUn+YQIDE= k8s.io/code-generator v0.0.0-20191121015212-c4c8f8345c7e/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= k8s.io/component-base v0.17.0/go.mod h1:rKuRAokNMY2nn2A6LP/MiwpoaMRHpfRnrPaUJJj1Yoc= -k8s.io/component-base v0.26.3 h1:oC0WMK/ggcbGDTkdcqefI4wIZRYdK3JySx9/HADpV0g= -k8s.io/component-base v0.26.3/go.mod h1:5kj1kZYwSC6ZstHJN7oHBqcJC6yyn41eR+Sqa/mQc8E= +k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4= k8s.io/csi-translation-lib v0.17.0/go.mod h1:HEF7MEz7pOLJCnxabi45IPkhSsE/KmxPQksuCrHKWls= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= diff --git a/acceptance/tests/cloud/basic_test.go b/acceptance/tests/cloud/basic_test.go index 8278309ff3..7a0a31430a 100644 --- a/acceptance/tests/cloud/basic_test.go +++ b/acceptance/tests/cloud/basic_test.go @@ -150,6 +150,7 @@ func TestBasicCloud(t *testing.T) { releaseName := helpers.RandomName() helmValues := map[string]string{ + "global.imagePullPolicy": "IfNotPresent", "global.cloud.enabled": "true", "global.cloud.resourceId.secretName": resourceSecretName, "global.cloud.resourceId.secretKey": resourceSecretKey, diff --git a/acceptance/tests/cloud/remote_dev_test.go b/acceptance/tests/cloud/remote_dev_test.go new file mode 100644 index 0000000000..aa7dbe70c7 --- /dev/null +++ b/acceptance/tests/cloud/remote_dev_test.go @@ -0,0 +1,264 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package cloud + +import ( + "crypto/tls" + "encoding/json" + "os" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/hashicorp/consul-k8s/acceptance/framework/consul" + "github.com/hashicorp/consul-k8s/acceptance/framework/environment" + "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" + "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" + "github.com/hashicorp/consul-k8s/acceptance/framework/logger" + + hcpgnm "github.com/hashicorp/hcp-sdk-go/clients/cloud-global-network-manager-service/preview/2022-02-15/client/global_network_manager_service" + "github.com/hashicorp/hcp-sdk-go/clients/cloud-global-network-manager-service/preview/2022-02-15/models" + hcpcfg "github.com/hashicorp/hcp-sdk-go/config" + "github.com/hashicorp/hcp-sdk-go/httpclient" + "github.com/hashicorp/hcp-sdk-go/resource" +) + +type DevTokenResponse struct { + Token string `json:"token"` +} + +type hcp struct { + ResourceID string + ClientID string + ClientSecret string + AuthURL string + APIHostname string + ScadaAddress string +} + +func TestRemoteDevCloud(t *testing.T) { + _, rIDok := os.LookupEnv("HCP_RESOURCE_ID") + _, cIDok := os.LookupEnv("HCP_CLIENT_ID") + _, cSECok := os.LookupEnv("HCP_CLIENT_SECRET") + + if !rIDok || !cIDok || !cSECok { + t.Log("Must set HCP_RESOURCE_ID, HCP_CLIENT_ID and HCP_CLIENT_SECRET") + t.FailNow() + } + + apiHost := os.Getenv("HCP_AUTH_URL") + if apiHost == "" { + apiHost = "https://api.hcp.dev" + } + authURL := os.Getenv("HCP_API_HOST") + if authURL == "" { + authURL = "https://auth.idp.hcp.dev" + } + scadaAddr := os.Getenv("HCP_SCADA_ADDRESS") + if scadaAddr == "" { + scadaAddr = "scada.internal.hcp.dev:7224" + } + + ctx := suite.Environment().DefaultContext(t) + + kubectlOptions := ctx.KubectlOptions(t) + ns := kubectlOptions.Namespace + k8sClient := environment.KubernetesClientFromOptions(t, kubectlOptions) + + var ( + resourceSecretName = "resource-sec-name" + resourceSecretKey = "resource-sec-key" + resourceSecretKeyValue = os.Getenv("HCP_RESOURCE_ID") + + clientIDSecretName = "clientid-sec-name" + clientIDSecretKey = "clientid-sec-key" + clientIDSecretKeyValue = os.Getenv("HCP_CLIENT_ID") + + clientSecretName = "client-sec-name" + clientSecretKey = "client-sec-key" + clientSecretKeyValue = os.Getenv("HCP_CLIENT_SECRET") + + apiHostSecretName = "apihost-sec-name" + apiHostSecretKey = "apihost-sec-key" + apiHostSecretKeyValue = apiHost + + authUrlSecretName = "authurl-sec-name" + authUrlSecretKey = "authurl-sec-key" + authUrlSecretKeyValue = authURL + + scadaAddressSecretName = "scadaaddress-sec-name" + scadaAddressSecretKey = "scadaaddress-sec-key" + scadaAddressSecretKeyValue = scadaAddr + + bootstrapTokenSecretName = "bootstrap-token" + bootstrapTokenSecretKey = "token" + ) + + hcpCfg := hcp{ + ResourceID: resourceSecretKeyValue, + ClientID: clientIDSecretKeyValue, + ClientSecret: clientSecretKeyValue, + AuthURL: authUrlSecretKeyValue, + APIHostname: apiHostSecretKeyValue, + ScadaAddress: scadaAddressSecretKeyValue, + } + + aclToken := hcpCfg.fetchAgentBootstrapConfig(t) + + cfg := suite.Config() + consul.CreateK8sSecret(t, k8sClient, cfg, ns, resourceSecretName, resourceSecretKey, resourceSecretKeyValue) + consul.CreateK8sSecret(t, k8sClient, cfg, ns, clientIDSecretName, clientIDSecretKey, clientIDSecretKeyValue) + consul.CreateK8sSecret(t, k8sClient, cfg, ns, clientSecretName, clientSecretKey, clientSecretKeyValue) + consul.CreateK8sSecret(t, k8sClient, cfg, ns, apiHostSecretName, apiHostSecretKey, apiHostSecretKeyValue) + consul.CreateK8sSecret(t, k8sClient, cfg, ns, authUrlSecretName, authUrlSecretKey, authUrlSecretKeyValue) + consul.CreateK8sSecret(t, k8sClient, cfg, ns, scadaAddressSecretName, scadaAddressSecretKey, scadaAddressSecretKeyValue) + consul.CreateK8sSecret(t, k8sClient, cfg, ns, bootstrapTokenSecretName, bootstrapTokenSecretKey, aclToken) + + releaseName := helpers.RandomName() + + helmValues := map[string]string{ + "global.imagePullPolicy": "IfNotPresent", + "global.cloud.enabled": "true", + "global.cloud.resourceId.secretName": resourceSecretName, + "global.cloud.resourceId.secretKey": resourceSecretKey, + + "global.cloud.clientId.secretName": clientIDSecretName, + "global.cloud.clientId.secretKey": clientIDSecretKey, + + "global.cloud.clientSecret.secretName": clientSecretName, + "global.cloud.clientSecret.secretKey": clientSecretKey, + + "global.cloud.apiHost.secretName": apiHostSecretName, + "global.cloud.apiHost.secretKey": apiHostSecretKey, + + "global.cloud.authUrl.secretName": authUrlSecretName, + "global.cloud.authUrl.secretKey": authUrlSecretKey, + + "global.cloud.scadaAddress.secretName": scadaAddressSecretName, + "global.cloud.scadaAddress.secretKey": scadaAddressSecretKey, + "connectInject.default": "true", + + "global.acls.manageSystemACLs": "true", + "global.acls.bootstrapToken.secretName": bootstrapTokenSecretName, + "global.acls.bootstrapToken.secretKey": bootstrapTokenSecretKey, + + "global.gossipEncryption.autoGenerate": "false", + "global.tls.enabled": "true", + "global.tls.enableAutoEncrypt": "true", + + "telemetryCollector.enabled": "true", + "telemetryCollector.cloud.clientId.secretName": clientIDSecretName, + "telemetryCollector.cloud.clientId.secretKey": clientIDSecretKey, + + "telemetryCollector.cloud.clientSecret.secretName": clientSecretName, + "telemetryCollector.cloud.clientSecret.secretKey": clientSecretKey, + // Either we set the global.trustedCAs (make sure it's idented exactly) or we + // set TLS to insecure + + "telemetryCollector.extraEnvironmentVars.HCP_API_ADDRESS": apiHostSecretKeyValue, + } + + if cfg.ConsulImage != "" { + helmValues["global.image"] = cfg.ConsulImage + } + if cfg.ConsulCollectorImage != "" { + helmValues["telemetryCollector.image"] = cfg.ConsulCollectorImage + } + + consulCluster := consul.NewHelmCluster(t, helmValues, suite.Environment().DefaultContext(t), cfg, releaseName) + consulCluster.Create(t) + + logger.Log(t, "setting acl permissions for collector and services") + aclDir := "../fixtures/bases/cloud/service-intentions" + k8s.KubectlApplyK(t, ctx.KubectlOptions(t), aclDir) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.KubectlDeleteK(t, ctx.KubectlOptions(t), aclDir) + }) + + logger.Log(t, "creating static-server deployment") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-server") + time.Sleep(1 * time.Hour) + + // TODO: add in test assertions here + +} + +// fetchAgentBootstrapConfig use the resource-id, client-id, and client-secret +// to call to the agent bootstrap config endpoint and parse the response into a +// CloudBootstrapConfig struct. +func (c *hcp) fetchAgentBootstrapConfig(t *testing.T) string { + cfg, err := c.HCPConfig() + require.NoError(t, err) + logger.Log(t, "Fetching Consul cluster configuration from HCP") + httpClientCfg := httpclient.Config{ + HCPConfig: cfg, + } + clientRuntime, err := httpclient.New(httpClientCfg) + require.NoError(t, err) + + hcpgnmClient := hcpgnm.New(clientRuntime, nil) + clusterResource, err := resource.FromString(c.ResourceID) + require.NoError(t, err) + + params := hcpgnm.NewAgentBootstrapConfigParams(). + WithID(clusterResource.ID). + WithLocationOrganizationID(clusterResource.Organization). + WithLocationProjectID(clusterResource.Project) + + resp, err := hcpgnmClient.AgentBootstrapConfig(params, nil) + require.NoError(t, err) + + bootstrapConfig := resp.GetPayload() + logger.Log(t, "HCP configuration successfully fetched.") + + return c.parseBootstrapConfigResponse(t, bootstrapConfig) +} + +// ConsulConfig represents 'cluster.consul_config' in the response +// fetched from the agent bootstrap config endpoint in HCP. +type ConsulConfig struct { + ACL ACL `json:"acl"` +} + +// ACL represents 'cluster.consul_config.acl' in the response +// fetched from the agent bootstrap config endpoint in HCP. +type ACL struct { + Tokens Tokens `json:"tokens"` +} + +// Tokens represents 'cluster.consul_config.acl.tokens' in the +// response fetched from the agent bootstrap config endpoint in HCP. +type Tokens struct { + Agent string `json:"agent"` + InitialManagement string `json:"initial_management"` +} + +// parseBootstrapConfigResponse unmarshals the boostrap parseBootstrapConfigResponse +// and also sets the HCPConfig values to return CloudBootstrapConfig struct. +func (c *hcp) parseBootstrapConfigResponse(t *testing.T, bootstrapRepsonse *models.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse) string { + + var consulConfig ConsulConfig + err := json.Unmarshal([]byte(bootstrapRepsonse.Bootstrap.ConsulConfig), &consulConfig) + require.NoError(t, err) + + return consulConfig.ACL.Tokens.InitialManagement +} + +func (c *hcp) HCPConfig(opts ...hcpcfg.HCPConfigOption) (hcpcfg.HCPConfig, error) { + if c.ClientID != "" && c.ClientSecret != "" { + opts = append(opts, hcpcfg.WithClientCredentials(c.ClientID, c.ClientSecret)) + } + if c.AuthURL != "" { + opts = append(opts, hcpcfg.WithAuth(c.AuthURL, &tls.Config{})) + } + if c.APIHostname != "" { + opts = append(opts, hcpcfg.WithAPI(c.APIHostname, &tls.Config{})) + } + if c.ScadaAddress != "" { + opts = append(opts, hcpcfg.WithSCADA(c.ScadaAddress, &tls.Config{})) + } + opts = append(opts, hcpcfg.FromEnv(), hcpcfg.WithoutBrowserLogin()) + return hcpcfg.NewHCPConfig(opts...) +} diff --git a/acceptance/tests/fixtures/bases/cloud/service-intentions/acl.yaml b/acceptance/tests/fixtures/bases/cloud/service-intentions/acl.yaml new file mode 100644 index 0000000000..fb3f77f496 --- /dev/null +++ b/acceptance/tests/fixtures/bases/cloud/service-intentions/acl.yaml @@ -0,0 +1,15 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ServiceIntentions +metadata: + name: consul-telemetry-collector +spec: + destination: + name: 'consul-telemetry-collector' + sources: + - name: '*' + action: allow + + + \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/cloud/service-intentions/kustomization.yaml b/acceptance/tests/fixtures/bases/cloud/service-intentions/kustomization.yaml new file mode 100644 index 0000000000..9c19bf4ca3 --- /dev/null +++ b/acceptance/tests/fixtures/bases/cloud/service-intentions/kustomization.yaml @@ -0,0 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - acl.yaml \ No newline at end of file diff --git a/charts/consul/templates/server-statefulset.yaml b/charts/consul/templates/server-statefulset.yaml index 0bde9b881a..9efbcb8085 100644 --- a/charts/consul/templates/server-statefulset.yaml +++ b/charts/consul/templates/server-statefulset.yaml @@ -241,6 +241,7 @@ spec: containers: - name: consul image: "{{ default .Values.global.image .Values.server.image }}" + imagePullPolicy: {{ .Values.global.imagePullPolicy }} env: - name: ADVERTISE_IP valueFrom: diff --git a/charts/consul/templates/telemetry-collector-deployment.yaml b/charts/consul/templates/telemetry-collector-deployment.yaml index 62b8868f1f..b729273dd8 100644 --- a/charts/consul/templates/telemetry-collector-deployment.yaml +++ b/charts/consul/templates/telemetry-collector-deployment.yaml @@ -150,7 +150,7 @@ spec: containers: - name: consul-telemetry-collector image: {{ .Values.telemetryCollector.image }} - imagePullPolicy: Always + imagePullPolicy: {{ .Values.global.imagePullPolicy }} ports: - containerPort: 9090 name: metrics From 858228683c05d9d0e0727ad5f120b75f30ac6480 Mon Sep 17 00:00:00 2001 From: chappie <6537530+chapmanc@users.noreply.github.com> Date: Tue, 11 Jul 2023 14:59:26 -0600 Subject: [PATCH 276/340] HCP bootstrap preset to always downcase datacenter (#2551) * Lowercase datacenter name from HCP bootstrap response * Add test cases to cloud bootstrap --- cli/preset/cloud_preset.go | 3 ++- cli/preset/cloud_preset_test.go | 34 +++++++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/cli/preset/cloud_preset.go b/cli/preset/cloud_preset.go index 732bad1b14..cbc335ae17 100644 --- a/cli/preset/cloud_preset.go +++ b/cli/preset/cloud_preset.go @@ -8,6 +8,7 @@ import ( "encoding/json" "fmt" "net/http" + "strings" "github.com/hashicorp/consul-k8s/cli/common" "github.com/hashicorp/consul-k8s/cli/common/terminal" @@ -235,7 +236,7 @@ connectInject: enabled: true controller: enabled: true -`, cfg.BootstrapResponse.Cluster.ID, secretNameServerCA, corev1.TLSCertKey, +`, strings.ToLower(cfg.BootstrapResponse.Cluster.ID), secretNameServerCA, corev1.TLSCertKey, secretNameGossipKey, secretKeyGossipKey, secretNameBootstrapToken, secretKeyBootstrapToken, secretNameHCPResourceID, secretKeyHCPResourceID, diff --git a/cli/preset/cloud_preset_test.go b/cli/preset/cloud_preset_test.go index 770b47ba5c..d905cb4088 100644 --- a/cli/preset/cloud_preset_test.go +++ b/cli/preset/cloud_preset_test.go @@ -43,7 +43,7 @@ const ( { "cluster": { - "id": "dc1", + "id": "Dc1", "bootstrap_expect" : 3 }, "bootstrap": @@ -63,7 +63,7 @@ const ( var validBootstrapReponse *models.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse = &models.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse{ Bootstrap: &models.HashicorpCloudGlobalNetworkManager20220215ClusterBootstrap{ - ID: "dc1", + ID: "Dc1", GossipKey: "Wa6/XFAnYy0f9iqVH2iiG+yore3CqHSemUy4AIVTa/w=", BootstrapExpect: 3, ServerTLS: &models.HashicorpCloudGlobalNetworkManager20220215ServerTLS{ @@ -558,12 +558,30 @@ telemetryCollector: cloudPreset := &CloudPreset{} - testCases := []struct { - description string + testCases := map[string]struct { config *CloudBootstrapConfig expectedYaml string }{ - {"Config including optional parameters", + "Config_including_optional_parameters_with_mixedcase_DC": { + &CloudBootstrapConfig{ + BootstrapResponse: &models.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse{ + Cluster: &models.HashicorpCloudGlobalNetworkManager20220215Cluster{ + BootstrapExpect: 3, + ID: "Dc1", + }, + }, + HCPConfig: HCPConfig{ + ResourceID: "consul-hcp-resource-id", + ClientID: "consul-hcp-client-id", + ClientSecret: "consul-hcp-client-secret", + AuthURL: "consul-hcp-auth-url", + APIHostname: "consul-hcp-api-host", + ScadaAddress: "consul-hcp-scada-address", + }, + }, + expectedFull, + }, + "Config_including_optional_parameters": { &CloudBootstrapConfig{ BootstrapResponse: &models.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse{ Cluster: &models.HashicorpCloudGlobalNetworkManager20220215Cluster{ @@ -582,7 +600,7 @@ telemetryCollector: }, expectedFull, }, - {"Config without optional parameters", + "Config_without_optional_parameters": { &CloudBootstrapConfig{ BootstrapResponse: &models.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse{ Cluster: &models.HashicorpCloudGlobalNetworkManager20220215Cluster{ @@ -599,8 +617,8 @@ telemetryCollector: expectedWithoutOptional, }, } - for _, tc := range testCases { - t.Run(tc.description, func(t *testing.T) { + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { cloudHelmValues := cloudPreset.getHelmConfigWithMapSecretNames(tc.config) require.NotNil(t, cloudHelmValues) valuesYaml, err := yaml.Marshal(cloudHelmValues) From 4f064795810b2db052109bcbff9c0f18206d44c5 Mon Sep 17 00:00:00 2001 From: Nathan Coleman Date: Tue, 11 Jul 2023 16:53:11 -0500 Subject: [PATCH 277/340] api-gateway: when multiple listeners have the same port, only add to K8s Service once (#2413) * Modify unit tests to include multiple listeners w/ same port Running the tests on this commit will demonstrate the bug * When multiple listeners have the same port, only add to K8s Service once * Add changelog entry --- .changelog/2413.txt | 3 +++ control-plane/api-gateway/gatekeeper/gatekeeper_test.go | 7 +++++++ control-plane/api-gateway/gatekeeper/service.go | 8 ++++++++ 3 files changed, 18 insertions(+) create mode 100644 .changelog/2413.txt diff --git a/.changelog/2413.txt b/.changelog/2413.txt new file mode 100644 index 0000000000..89755b23a7 --- /dev/null +++ b/.changelog/2413.txt @@ -0,0 +1,3 @@ +```release-note:bug +api-gateway: Fix creation of invalid Kubernetes Service when multiple Gateway listeners have the same port. +``` diff --git a/control-plane/api-gateway/gatekeeper/gatekeeper_test.go b/control-plane/api-gateway/gatekeeper/gatekeeper_test.go index ba58cb441f..069643e301 100644 --- a/control-plane/api-gateway/gatekeeper/gatekeeper_test.go +++ b/control-plane/api-gateway/gatekeeper/gatekeeper_test.go @@ -40,12 +40,19 @@ var ( Name: "Listener 1", Port: 8080, Protocol: "TCP", + Hostname: common.PointerTo(gwv1beta1.Hostname("example.com")), }, { Name: "Listener 2", Port: 8081, Protocol: "TCP", }, + { + Name: "Listener 3", + Port: 8080, + Protocol: "TCP", + Hostname: common.PointerTo(gwv1beta1.Hostname("example.net")), + }, } ) diff --git a/control-plane/api-gateway/gatekeeper/service.go b/control-plane/api-gateway/gatekeeper/service.go index 3b0e848899..d534ad50d7 100644 --- a/control-plane/api-gateway/gatekeeper/service.go +++ b/control-plane/api-gateway/gatekeeper/service.go @@ -65,14 +65,22 @@ func (g *Gatekeeper) deleteService(ctx context.Context, gwName types.NamespacedN } func (g *Gatekeeper) service(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig) *corev1.Service { + seenPorts := map[gwv1beta1.PortNumber]struct{}{} ports := []corev1.ServicePort{} for _, listener := range gateway.Spec.Listeners { + if _, seen := seenPorts[listener.Port]; seen { + // We've already added this listener's port to the Service + continue + } + ports = append(ports, corev1.ServicePort{ Name: string(listener.Name), // only TCP-based services are supported for now Protocol: corev1.ProtocolTCP, Port: int32(listener.Port), }) + + seenPorts[listener.Port] = struct{}{} } // Copy annotations from the Gateway, filtered by those allowed by the GatewayClassConfig. From b8be6a0d9ab9ea2ab941c74207cd68d0af2454cb Mon Sep 17 00:00:00 2001 From: Nathan Coleman Date: Tue, 11 Jul 2023 17:35:12 -0500 Subject: [PATCH 278/340] NET-4482: set route condition appropriately when parent ref includes non-existent section (#2420) * Set route accepted condition appropriately when no listener with section name matching parent * Adjust error message for bind errors that aren't specific to one listener * Include section name in message for NoMatchingParent when available * Add unit test coverage for conditions derived from binding results * Add changelog entry --- .changelog/2420.txt | 3 + control-plane/api-gateway/binding/result.go | 22 ++++-- .../api-gateway/binding/result_test.go | 67 +++++++++++++++++++ .../api-gateway/binding/route_binding.go | 23 +++++-- 4 files changed, 104 insertions(+), 11 deletions(-) create mode 100644 .changelog/2420.txt create mode 100644 control-plane/api-gateway/binding/result_test.go diff --git a/.changelog/2420.txt b/.changelog/2420.txt new file mode 100644 index 0000000000..86776497c4 --- /dev/null +++ b/.changelog/2420.txt @@ -0,0 +1,3 @@ +```release-note:bug +api-gateway: set route condition appropriately when parent ref includes non-existent section name +``` diff --git a/control-plane/api-gateway/binding/result.go b/control-plane/api-gateway/binding/result.go index fd2eaca829..b148e441e2 100644 --- a/control-plane/api-gateway/binding/result.go +++ b/control-plane/api-gateway/binding/result.go @@ -34,6 +34,7 @@ var ( errRouteNoMatchingListenerHostname = errors.New("listener cannot bind route with a non-aligned hostname") errRouteInvalidKind = errors.New("invalid backend kind") errRouteBackendNotFound = errors.New("backend not found") + errRouteNoMatchingParent = errors.New("no matching parent") ) // routeValidationResult holds the result of validating a route globally, in other @@ -128,13 +129,17 @@ type bindResult struct { type bindResults []bindResult // Error constructs a human readable error for bindResults, containing any errors that a route -// had in binding to a gateway, note that this is only used if a route failed to bind to every +// had in binding to a gateway. Note that this is only used if a route failed to bind to every // listener it attempted to bind to. func (b bindResults) Error() string { messages := []string{} for _, result := range b { if result.err != nil { - messages = append(messages, fmt.Sprintf("%s: %s", result.section, result.err.Error())) + message := result.err.Error() + if result.section != "" { + message = fmt.Sprintf("%s: %s", result.section, result.err.Error()) + } + messages = append(messages, message) } } @@ -171,13 +176,16 @@ func (b bindResults) Condition() metav1.Condition { // if we only have a single binding error, we can get more specific if len(b) == 1 { for _, result := range b { - // if we have a hostname mismatch error, then use the more specific reason - if result.err == errRouteNoMatchingListenerHostname { + switch result.err { + case errRouteNoMatchingListenerHostname: + // if we have a hostname mismatch error, then use the more specific reason reason = "NoMatchingListenerHostname" - } - // or if we have a ref not permitted, then use that - if result.err == errRefNotPermitted { + case errRefNotPermitted: + // or if we have a ref not permitted, then use that reason = "RefNotPermitted" + case errRouteNoMatchingParent: + // or if the route declares a parent that we can't find + reason = "NoMatchingParent" } } } diff --git a/control-plane/api-gateway/binding/result_test.go b/control-plane/api-gateway/binding/result_test.go new file mode 100644 index 0000000000..c6987cdaeb --- /dev/null +++ b/control-plane/api-gateway/binding/result_test.go @@ -0,0 +1,67 @@ +package binding + +import ( + "errors" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestBindResults_Condition(t *testing.T) { + testCases := []struct { + Name string + Results bindResults + Expected metav1.Condition + }{ + { + Name: "route successfully bound", + Results: bindResults{{section: "", err: nil}}, + Expected: metav1.Condition{Type: "Accepted", Status: "True", Reason: "Accepted", Message: "route accepted"}, + }, + { + Name: "multiple bind results", + Results: bindResults{ + {section: "abc", err: errRouteNoMatchingListenerHostname}, + {section: "def", err: errRouteNoMatchingParent}, + }, + Expected: metav1.Condition{Type: "Accepted", Status: "False", Reason: "NotAllowedByListeners", Message: "abc: listener cannot bind route with a non-aligned hostname; def: no matching parent"}, + }, + { + Name: "no matching listener hostname error", + Results: bindResults{{section: "abc", err: errRouteNoMatchingListenerHostname}}, + Expected: metav1.Condition{Type: "Accepted", Status: "False", Reason: "NoMatchingListenerHostname", Message: "abc: listener cannot bind route with a non-aligned hostname"}, + }, + { + Name: "ref not permitted error", + Results: bindResults{{section: "abc", err: errRefNotPermitted}}, + Expected: metav1.Condition{Type: "Accepted", Status: "False", Reason: "RefNotPermitted", Message: "abc: reference not permitted due to lack of ReferenceGrant"}, + }, + { + Name: "no matching parent error", + Results: bindResults{{section: "hello1", err: errRouteNoMatchingParent}}, + Expected: metav1.Condition{Type: "Accepted", Status: "False", Reason: "NoMatchingParent", Message: "hello1: no matching parent"}, + }, + { + Name: "bind result without section name", + Results: bindResults{{section: "", err: errRouteNoMatchingParent}}, + Expected: metav1.Condition{Type: "Accepted", Status: "False", Reason: "NoMatchingParent", Message: "no matching parent"}, + }, + { + Name: "unhandled error type", + Results: bindResults{{section: "abc", err: errors.New("you don't know me")}}, + Expected: metav1.Condition{Type: "Accepted", Status: "False", Reason: "NotAllowedByListeners", Message: "abc: you don't know me"}, + }, + } + + for _, tc := range testCases { + t.Run(fmt.Sprintf("%s_%s", t.Name(), tc.Name), func(t *testing.T) { + actual := tc.Results.Condition() + assert.Equalf(t, tc.Expected.Type, actual.Type, "expected condition with type %q but got %q", tc.Expected.Type, actual.Type) + assert.Equalf(t, tc.Expected.Status, actual.Status, "expected condition with status %q but got %q", tc.Expected.Status, actual.Status) + assert.Equalf(t, tc.Expected.Reason, actual.Reason, "expected condition with reason %q but got %q", tc.Expected.Reason, actual.Reason) + assert.Equalf(t, tc.Expected.Message, actual.Message, "expected condition with message %q but got %q", tc.Expected.Message, actual.Message) + }) + } +} diff --git a/control-plane/api-gateway/binding/route_binding.go b/control-plane/api-gateway/binding/route_binding.go index 36fcda0204..8b2e66e761 100644 --- a/control-plane/api-gateway/binding/route_binding.go +++ b/control-plane/api-gateway/binding/route_binding.go @@ -104,7 +104,22 @@ func (r *Binder) bindRoute(route client.Object, boundCount map[gwv1beta1.Section for _, ref := range filteredParents { var result bindResults - for _, listener := range listenersFor(&r.config.Gateway, ref.SectionName) { + listeners := listenersFor(&r.config.Gateway, ref.SectionName) + + // If there are no matching listeners, then we failed to find the parent + if len(listeners) == 0 { + var sectionName gwv1beta1.SectionName + if ref.SectionName != nil { + sectionName = *ref.SectionName + } + + result = append(result, bindResult{ + section: sectionName, + err: errRouteNoMatchingParent, + }) + } + + for _, listener := range listeners { if !routeKindIsAllowedForListener(supportedKindsForProtocol[listener.Protocol], groupKind) { result = append(result, bindResult{ section: listener.Name, @@ -179,9 +194,9 @@ func filterParentRefs(gateway types.NamespacedName, namespace string, refs []gwv return references } -// listenersFor returns the listeners corresponding the given section name. If the section -// name is actually specified, the returned set should just have one listener, if it is -// unspecified, the all gatweway listeners should be returned. +// listenersFor returns the listeners corresponding to the given section name. If the section +// name is actually specified, the returned set will only contain the named listener. If it is +// unspecified, then all gateway listeners will be returned. func listenersFor(gateway *gwv1beta1.Gateway, name *gwv1beta1.SectionName) []gwv1beta1.Listener { listeners := []gwv1beta1.Listener{} for _, listener := range gateway.Spec.Listeners { From 73959e7acc0f0d6995bf749df2ec3c845d29b033 Mon Sep 17 00:00:00 2001 From: Dan Stough Date: Wed, 12 Jul 2023 10:04:36 -0400 Subject: [PATCH 279/340] test: update nightly tests to consul 1.17-dev (#2556) --- .github/workflows/merge.yml | 2 +- .github/workflows/nightly-acceptance.yml | 2 +- .github/workflows/nightly-api-gateway-conformance.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml index b6037e0af3..201df1dadd 100644 --- a/.github/workflows/merge.yml +++ b/.github/workflows/merge.yml @@ -11,7 +11,7 @@ on: # these should be the only settings that you will ever need to change env: - CONSUL_IMAGE: hashicorppreview/consul-enterprise:1.16-dev # Consul's enterprise version to use in tests. We use this consul image on release branches too + CONSUL_IMAGE: hashicorppreview/consul-enterprise:1.17-dev # Consul's enterprise version to use in tests. We use this consul image on release branches too BRANCH: ${{ github.head_ref || github.ref_name }} CONTEXT: "merge" SHA: ${{ github.event.pull_request.head.sha || github.sha }} diff --git a/.github/workflows/nightly-acceptance.yml b/.github/workflows/nightly-acceptance.yml index 6414d6a611..4d437b4990 100644 --- a/.github/workflows/nightly-acceptance.yml +++ b/.github/workflows/nightly-acceptance.yml @@ -8,7 +8,7 @@ on: # these should be the only settings that you will ever need to change env: - CONSUL_IMAGE: hashicorppreview/consul-enterprise:1.16-dev # Consul's enterprise version to use in tests + CONSUL_IMAGE: hashicorppreview/consul-enterprise:1.17-dev # Consul's enterprise version to use in tests BRANCH: ${{ github.ref_name }} CONTEXT: "nightly" diff --git a/.github/workflows/nightly-api-gateway-conformance.yml b/.github/workflows/nightly-api-gateway-conformance.yml index cd63ee8467..5038ffdb93 100644 --- a/.github/workflows/nightly-api-gateway-conformance.yml +++ b/.github/workflows/nightly-api-gateway-conformance.yml @@ -9,7 +9,7 @@ on: # these should be the only settings that you will ever need to change env: - CONSUL_IMAGE: hashicorppreview/consul-enterprise:1.16-dev # Consul's enterprise version to use in tests + CONSUL_IMAGE: hashicorppreview/consul-enterprise:1.17-dev # Consul's enterprise version to use in tests BRANCH: ${{ github.ref_name }} CONTEXT: "nightly" From 65c4e7431013a1eb5416a596674a54c6a44a4499 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Wed, 12 Jul 2023 14:29:11 -0700 Subject: [PATCH 280/340] Update Release Scripts (#2558) * update environment variables with CONSUL_K8s prefix - This will let us check that we have all the environment variables set more easily with `printenv | grep "CONSUL_K8S"` * update imageConsulDataplane without quotes - this makes it consistent with the other images - allows scripting to work similarly to other images * updated utils script - handle replace case where consul-enterprise is in the values.yaml file and charts.yaml file - handle adding pre-release tag in changelog - handle updating consul-dataplane --- Makefile | 42 ++++++----- charts/consul/values.yaml | 2 +- .../build-support/functions/10-util.sh | 71 ++++++++++++++----- 3 files changed, 78 insertions(+), 37 deletions(-) diff --git a/Makefile b/Makefile index de35275868..1b7e4d8d7b 100644 --- a/Makefile +++ b/Makefile @@ -209,38 +209,42 @@ eks-test-packages: aks-test-packages: @./control-plane/build-support/scripts/set_test_package_matrix.sh "acceptance/ci-inputs/aks_acceptance_test_packages.yaml" - # ===========> Release Targets +check-env: + @printenv | grep "CONSUL_K8S" prepare-release: ## Sets the versions, updates changelog to prepare this repository to release -ifndef RELEASE_VERSION - $(error RELEASE_VERSION is required) +ifndef CONSUL_K8S_RELEASE_VERSION + $(error CONSUL_K8S_RELEASE_VERSION is required) endif -ifndef RELEASE_DATE - $(error RELEASE_DATE is required, use format , (ex. October 4, 2022)) +ifndef CONSUL_K8S_RELEASE_DATE + $(error CONSUL_K8S_RELEASE_DATE is required, use format , (ex. October 4, 2022)) endif -ifndef LAST_RELEASE_GIT_TAG - $(error LAST_RELEASE_GIT_TAG is required) +ifndef CONSUL_K8S_LAST_RELEASE_GIT_TAG + $(error CONSUL_K8S_LAST_RELEASE_GIT_TAG is required) endif -ifndef CONSUL_VERSION - $(error CONSUL_VERSION is required) +ifndef CONSUL_K8S_CONSUL_VERSION + $(error CONSUL_K8S_CONSUL_VERSION is required) endif - source $(CURDIR)/control-plane/build-support/scripts/functions.sh; prepare_release $(CURDIR) $(RELEASE_VERSION) "$(RELEASE_DATE)" $(LAST_RELEASE_GIT_TAG) $(CONSUL_VERSION) $(PRERELEASE_VERSION) + source $(CURDIR)/control-plane/build-support/scripts/functions.sh; prepare_release $(CURDIR) $(CONSUL_K8S_RELEASE_VERSION) "$(CONSUL_K8S_RELEASE_DATE)" $(CONSUL_K8S_LAST_RELEASE_GIT_TAG) $(CONSUL_K8S_CONSUL_VERSION) $(CONSUL_K8S_CONSUL_DATAPLANE_VERSION) $(CONSUL_K8S_PRERELEASE_VERSION) prepare-dev: -ifndef RELEASE_VERSION - $(error RELEASE_VERSION is required) +ifndef CONSUL_K8S_RELEASE_VERSION + $(error CONSUL_K8S_RELEASE_VERSION is required) +endif +ifndef CONSUL_K8S_RELEASE_DATE + $(error CONSUL_K8S_RELEASE_DATE is required, use format , (ex. October 4, 2022)) endif -ifndef RELEASE_DATE - $(error RELEASE_DATE is required, use format , (ex. October 4, 2022)) +ifndef CONSUL_K8S_NEXT_RELEASE_VERSION + $(error CONSUL_K8S_NEXT_RELEASE_VERSION is required) endif -ifndef NEXT_RELEASE_VERSION - $(error NEXT_RELEASE_VERSION is required) +ifndef CONSUL_K8S_NEXT_CONSUL_VERSION + $(error CONSUL_K8S_NEXT_CONSUL_VERSION is required) endif -ifndef NEXT_CONSUL_VERSION - $(error NEXT_CONSUL_VERSION is required) +ifndef CONSUL_K8S_NEXT_CONSUL_DATAPLANE_VERSION + $(error CONSUL_K8S_NEXT_CONSUL_DATAPLANE_VERSION is required) endif - source $(CURDIR)/control-plane/build-support/scripts/functions.sh; prepare_dev $(CURDIR) $(RELEASE_VERSION) "$(RELEASE_DATE)" "" $(NEXT_RELEASE_VERSION) $(NEXT_CONSUL_VERSION) + source $(CURDIR)/control-plane/build-support/scripts/functions.sh; prepare_dev $(CURDIR) $(CONSUL_K8S_RELEASE_VERSION) "$(CONSUL_K8S_RELEASE_DATE)" "" $(CONSUL_K8S_NEXT_RELEASE_VERSION) $(CONSUL_K8S_NEXT_CONSUL_VERSION) $(CONSUL_K8S_NEXT_CONSUL_DATAPLANE_VERSION) # ===========> Makefile config diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index b5f8437d83..12c45ac958 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -608,7 +608,7 @@ global: # The name (and tag) of the consul-dataplane Docker image used for the # connect-injected sidecar proxies and mesh, terminating, and ingress gateways. # @default: hashicorp/consul-dataplane: - imageConsulDataplane: "docker.mirror.hashicorp.services/hashicorppreview/consul-dataplane:1.3-dev" + imageConsulDataplane: docker.mirror.hashicorp.services/hashicorppreview/consul-dataplane:1.3-dev # Configuration for running this Helm chart on the Red Hat OpenShift platform. # This Helm chart currently supports OpenShift v4.x+. diff --git a/control-plane/build-support/functions/10-util.sh b/control-plane/build-support/functions/10-util.sh index 72ce91720c..3bc87124d9 100644 --- a/control-plane/build-support/functions/10-util.sh +++ b/control-plane/build-support/functions/10-util.sh @@ -599,6 +599,8 @@ function update_version_helm { # $4 - Image base path # $5 - Consul version string # $6 - Consul image base path + # $7 - Consul-Dataplane version string + # $8 - Consul-Dataplane base path # # Returns: # 0 - success @@ -620,19 +622,32 @@ function update_version_helm { local prerelease="$3" local full_version="$2" local full_consul_version="$5" - if ! test -z "$3"; then + local full_consul_dataplane_version="$7" + local consul_dataplane_base_path="$8" + if ! test -z "$3" && test "$3" != "dev"; then + full_version="$2-$3" + full_consul_version="$5-$3" + full_consul_dataplane_version="$7-$3" + elif test "$3" == "dev"; then full_version="$2-$3" # strip off the last minor patch version so that the consul image can be set to something like 1.16-dev. The image # is produced by Consul every night full_consul_version="${5%.*}-$3" + full_consul_dataplane_version="${7%.*}-$3" fi sed_i ${SED_EXT} -e "s/(imageK8S:.*\/consul-k8s-control-plane:)[^\"]*/imageK8S: $4${full_version}/g" "${vfile}" sed_i ${SED_EXT} -e "s/(version:[[:space:]]*)[^\"]*/\1${full_version}/g" "${cfile}" sed_i ${SED_EXT} -e "s/(appVersion:[[:space:]]*)[^\"]*/\1${full_consul_version}/g" "${cfile}" sed_i ${SED_EXT} -e "s/(image:.*\/consul-k8s-control-plane:)[^\"]*/image: $4${full_version}/g" "${cfile}" - sed_i ${SED_EXT} -e "s/(image:.*\/consul:)[^\"]*/image: $6:${full_consul_version}/g" "${cfile}" - sed_i ${SED_EXT} -e "s/(image:.*\/consul:)[^\"]*/image: $6:${full_consul_version}/g" "${vfile}" + + sed_i ${SED_EXT} -e "s,^( *image:)(.*/consul:)[^\"]*\$,\1 $6:${full_consul_version},g" ${cfile} + sed_i ${SED_EXT} -e "s,^( *image:)(.*/consul:)[^\"]*\$,\1 $6:${full_consul_version},g" ${vfile} + sed_i ${SED_EXT} -e "s,^( *image:)(.*/consul-enterprise:)[^\"]*\$,\1 $6:${full_consul_version},g" ${cfile} + sed_i ${SED_EXT} -e "s,^( *image:)(.*/consul-enterprise:)[^\"]*\$,\1 $6:${full_consul_version},g" ${vfile} + + sed_i ${SED_EXT} -e "s/(imageConsulDataplane:.*\/consul-dataplane:)[^\"]*/imageConsulDataplane: ${consul_dataplane_base_path}:${full_consul_dataplane_version}/g" "${vfile}" + sed_i ${SED_EXT} -e "s,^( *image:)(.*/consul-dataplane:)[^\"]*\$,\1 ${consul_dataplane_base_path}:${full_consul_dataplane_version},g" ${cfile} if test -z "$3"; then sed_i ${SED_EXT} -e "s/(artifacthub.io\/prerelease:[[:space:]]*)[^\"]*/\1false/g" "${cfile}" @@ -651,6 +666,8 @@ function set_version { # $5 - The consul-k8s helm docker image base path # $6 - The consul version # $7 - The consul helm docker image base path + # $8 - The consul dataplane version + # $9 - The consul-dataplane helm docker image base path # # # Returns: @@ -670,6 +687,7 @@ function set_version { local sdir="$1" local vers="$2" local consul_vers="$6" + local consul_dataplane_vers="$8" status_stage "==> Updating control-plane version/version.go with version info: ${vers} "$4"" if ! update_version "${sdir}/control-plane/version/version.go" "${vers}" "$4"; then @@ -681,8 +699,8 @@ function set_version { return 1 fi - status_stage "==> Updating Helm chart version, consul-k8s: ${vers} "$4" consul: ${consul_vers} "$4"" - if ! update_version_helm "${sdir}/charts/consul" "${vers}" "$4" "$5" "${consul_vers}" "$7"; then + status_stage "==> Updating Helm chart version, consul-k8s: ${vers} "$4" consul: ${consul_vers} "$4" consul-dataplane: ${consul_dataplane_vers} "$4"" + if ! update_version_helm "${sdir}/charts/consul" "${vers}" "$4" "$5" "${consul_vers}" "$7" "${consul_dataplane_vers}" "$9"; then return 1 fi @@ -695,6 +713,7 @@ function set_changelog { # $2 - Version # $3 - Release Date # $4 - The last git release tag + # $5 - Pre-release version # # # Returns: @@ -714,20 +733,21 @@ function set_changelog { rel_date="$3" fi local last_release_date_git_tag=$4 + local preReleaseVersion="-$5" if test -z "${version}"; then err "ERROR: Must specify a version to put into the changelog" return 1 fi - if [ -z "$LAST_RELEASE_GIT_TAG" ]; then - echo "Error: LAST_RELEASE_GIT_TAG not specified." + if [ -z "$CONSUL_K8S_LAST_RELEASE_GIT_TAG" ]; then + echo "Error: CONSUL_K8S_LAST_RELEASE_GIT_TAG not specified." exit 1 fi cat <tmp && mv tmp "${curdir}"/CHANGELOG.MD -## ${version} (${rel_date}) -$(changelog-build -last-release ${LAST_RELEASE_GIT_TAG} \ +## ${version}${preReleaseVersion} (${rel_date}) +$(changelog-build -last-release ${CONSUL_K8S_LAST_RELEASE_GIT_TAG} \ -entries-dir .changelog/ \ -changelog-template .changelog/changelog.tmpl \ -note-template .changelog/note.tmpl \ @@ -742,17 +762,26 @@ function prepare_release { # $2 - The version of the release # $3 - The release date # $4 - The last release git tag for this branch (eg. v1.1.0) - # $5 - The pre-release version - # $6 - The consul version + # $5 - The consul version + # $6 - The consul-dataplane version + # $7 - The pre-release version # # # Returns: # 0 - success # * - error - echo "prepare_release: dir:$1 consul-k8s:$2 consul:$5 date:"$3" git tag:$4" - set_version "$1" "$2" "$3" "$6" "hashicorp\/consul-k8s-control-plane:" "$5" "hashicorp\/consul" - set_changelog "$1" "$2" "$3" "$4" + local curDir=$1 + local version=$2 + local releaseDate=$3 + local lastGitTag=$4 + local consulVersion=$5 + local consulDataplaneVersion=$6 + local prereleaseVersion=$7 + + echo "prepare_release: dir:${curDir} consul-k8s:${version} consul:${consulVersion} consul-dataplane:${consulDataplaneVersion} date:"${releaseDate}" git tag:${lastGitTag}" + set_version "${curDir}" "${version}" "${releaseDate}" "${prereleaseVersion}" "hashicorp\/consul-k8s-control-plane:" "${consulVersion}" "hashicorp\/consul" "${consulDataplaneVersion}" "hashicorp\/consul-dataplane" + set_changelog "${curDir}" "${version}" "${releaseDate}" "${lastGitTag}" "${prereleaseVersion}" } function prepare_dev { @@ -763,13 +792,21 @@ function prepare_dev { # $4 - The last release git tag for this branch (eg. v1.1.0) (Unused) # $5 - The version of the next release # $6 - The version of the next consul release + # $7 - The next consul-dataplane version # # Returns: # 0 - success # * - error - echo "prepare_dev: dir:$1 consul-k8s:$5 consul:$6 date:"$3" mode:dev" - set_version "$1" "$5" "$3" "dev" "docker.mirror.hashicorp.services\/hashicorppreview\/consul-k8s-control-plane:" "$6" "docker.mirror.hashicorp.services\/hashicorppreview\/consul" + local curDir=$1 + local version=$2 + local releaseDate=$3 + local nextReleaseVersion=$5 + local nextConsulVersion=$6 + local nextConsulDataplaneVersion=$7 + + echo "prepare_dev: dir:${curDir} consul-k8s:${nextReleaseVersion} consul:${nextConsulVersion} date:"${releaseDate}" mode:dev" + set_version "${curDir}" "${nextReleaseVersion}" "${releaseDate}" "dev" "docker.mirror.hashicorp.services\/hashicorppreview\/consul-k8s-control-plane:" "${nextConsulVersion}" "docker.mirror.hashicorp.services\/hashicorppreview\/consul" "${nextConsulDataplaneVersion}" "docker.mirror.hashicorp.services\/hashicorppreview\/consul-dataplane" return 0 } @@ -896,7 +933,7 @@ function ui_version { return 1 fi - local ui_version=$(sed -n ${SED_EXT} -e 's/.*CONSUL_VERSION%22%3A%22([^%]*)%22%2C%22.*/\1/p' <"$1") || return 1 + local ui_version=$(sed -n ${SED_EXT} -e 's/.*CONSUL_K8S_CONSUL_VERSION%22%3A%22([^%]*)%22%2C%22.*/\1/p' <"$1") || return 1 echo "$ui_version" return 0 } From df0e649e95caf9bd9ccf911d7e810c1dd781fa81 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Wed, 12 Jul 2023 14:42:51 -0700 Subject: [PATCH 281/340] added missing changelogs (#2565) * added missing changelogs * Update CHANGELOG.md for 0.49.8 --------- Co-authored-by: Curt Bushko --- CHANGELOG.md | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 445e0f1816..54bdba549b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,116 @@ +## 0.49.8 (July 12, 2023) + +IMPROVEMENTS: + +* helm: Add `connectInject.prepareDataplanesUpgrade` setting for help upgrading to dataplanes. This setting is required if upgrading from non-dataplanes to dataplanes when ACLs are enabled. See https://developer.hashicorp.com/consul/docs/k8s/upgrade#upgrading-to-consul-dataplane for more information. [[GH-2514](https://github.com/hashicorp/consul-k8s/issues/2514)] + +## 1.2.0 (June 28, 2023) + +FEATURES: + +* Add support for configuring Consul server-side rate limiting [[GH-2166](https://github.com/hashicorp/consul-k8s/issues/2166)] +* api-gateway: Add API Gateway for Consul on Kubernetes leveraging Consul native API Gateway configuration. [[GH-2152](https://github.com/hashicorp/consul-k8s/issues/2152)] +* crd: Add `mutualTLSMode` to the ProxyDefaults and ServiceDefaults CRDs and `allowEnablingPermissiveMutualTLS` to the Mesh CRD to support configuring permissive mutual TLS. [[GH-2100](https://github.com/hashicorp/consul-k8s/issues/2100)] +* helm: Add `JWTProvider` CRD for configuring the `jwt-provider` config entry. [[GH-2209](https://github.com/hashicorp/consul-k8s/issues/2209)] +* helm: Update the ServiceIntentions CRD to support `JWT` fields. [[GH-2213](https://github.com/hashicorp/consul-k8s/issues/2213)] + +IMPROVEMENTS: + +* cli: update minimum go version for project to 1.20. [[GH-2102](https://github.com/hashicorp/consul-k8s/issues/2102)] +* control-plane: add FIPS support [[GH-2165](https://github.com/hashicorp/consul-k8s/issues/2165)] +* control-plane: server ACL Init always appends both, the secrets from the serviceAccount's secretRefs and the one created by the Helm chart, to support Openshift secret handling. [[GH-1770](https://github.com/hashicorp/consul-k8s/issues/1770)] +* control-plane: set agent localities on Consul servers to the server node's `topology.kubernetes.io/region` label. [[GH-2093](https://github.com/hashicorp/consul-k8s/issues/2093)] +* control-plane: update alpine to 3.17 in the Docker image. [[GH-1934](https://github.com/hashicorp/consul-k8s/issues/1934)] +* control-plane: update minimum go version for project to 1.20. [[GH-2102](https://github.com/hashicorp/consul-k8s/issues/2102)] +* helm: Kubernetes v1.27 is now supported. Minimum tested version of Kubernetes is now v1.24. [[GH-2304](https://github.com/hashicorp/consul-k8s/issues/2304)] +* helm: Update the default amount of memory used by the connect-inject controller so that its less likely to get OOM killed. [[GH-2249](https://github.com/hashicorp/consul-k8s/issues/2249)] +* helm: add failover policy field to service resolver and proxy default CRDs [[GH-2030](https://github.com/hashicorp/consul-k8s/issues/2030)] +* helm: add samenessGroup CRD [[GH-2048](https://github.com/hashicorp/consul-k8s/issues/2048)] +* helm: add samenessGroup field to exported services CRD [[GH-2075](https://github.com/hashicorp/consul-k8s/issues/2075)] +* helm: add samenessGroup field to service resolver CRD [[GH-2086](https://github.com/hashicorp/consul-k8s/issues/2086)] +* helm: add samenessGroup field to source intention CRD [[GH-2097](https://github.com/hashicorp/consul-k8s/issues/2097)] +* helm: update `imageConsulDataplane` value to `hashicorp/consul-dataplane:1.1.0`. [[GH-1953](https://github.com/hashicorp/consul-k8s/issues/1953)] + +SECURITY: + +* Update [Go-Discover](https://github.com/hashicorp/go-discover) in the container has been updated to address [CVE-2020-14040](https://github.com/advisories/GHSA-5rcv-m4m3-hfh7) [[GH-2390](https://github.com/hashicorp/consul-k8s/issues/2390)] +* Bump Dockerfile base image to `alpine:3.18`. Resolves [CVE-2023-2650](https://github.com/advisories/GHSA-gqxg-9vfr-p9cg) vulnerability in openssl@3.0.8-r4 [[GH-2284](https://github.com/hashicorp/consul-k8s/issues/2284)] +* Fix Prometheus CVEs by bumping controller-runtime. [[GH-2183](https://github.com/hashicorp/consul-k8s/issues/2183)] +* Upgrade to use Go 1.20.4. + This resolves vulnerabilities [CVE-2023-24537](https://github.com/advisories/GHSA-9f7g-gqwh-jpf5)(`go/scanner`), + [CVE-2023-24538](https://github.com/advisories/GHSA-v4m2-x4rp-hv22)(`html/template`), + [CVE-2023-24534](https://github.com/advisories/GHSA-8v5j-pwr7-w5f8)(`net/textproto`) and + [CVE-2023-24536](https://github.com/advisories/GHSA-9f7g-gqwh-jpf5)(`mime/multipart`). + Also, `golang.org/x/net` has been updated to v0.7.0 to resolve CVEs [CVE-2022-41721 + ](https://github.com/advisories/GHSA-fxg5-wq6x-vr4w + ), [CVE-2022-27664](https://github.com/advisories/GHSA-69cg-p879-7622) and [CVE-2022-41723 + ](https://github.com/advisories/GHSA-vvpx-j8f3-3w6h + .) [[GH-2102](https://github.com/hashicorp/consul-k8s/issues/2102)] + +BUG FIXES: + +* control-plane: Fix casing of the Enforce Consecutive 5xx field on Service Defaults and acceptance test fixtures. [[GH-2266](https://github.com/hashicorp/consul-k8s/issues/2266)] +* control-plane: fix issue where consul-connect-injector acl token was unintentionally being deleted and not recreated when a container was restarted due to a livenessProbe failure. [[GH-1914](https://github.com/hashicorp/consul-k8s/issues/1914)] + +## 1.1.3 (June 28, 2023) +BREAKING CHANGES: + +* control-plane: All policies managed by consul-k8s will now be updated on upgrade. If you previously edited the policies after install, your changes will be overwritten. [[GH-2392](https://github.com/hashicorp/consul-k8s/issues/2392)] + +SECURITY: + +* Bump Dockerfile base image to `alpine:3.18`. Resolves [CVE-2023-2650](https://github.com/advisories/GHSA-gqxg-9vfr-p9cg) vulnerability in openssl@3.0.8-r4 [[GH-2284](https://github.com/hashicorp/consul-k8s/issues/2284)] +* Update [Go-Discover](https://github.com/hashicorp/go-discover) in the container has been updated to address [CVE-2020-14040](https://github.com/advisories/GHSA-5rcv-m4m3-hfh7) [[GH-2390](https://github.com/hashicorp/consul-k8s/issues/2390)] + +FEATURES: + +* Add support for configuring graceful shutdown proxy lifecycle management settings. [[GH-2233](https://github.com/hashicorp/consul-k8s/issues/2233)] +* helm: Adds `acls.resources` field which can be configured to override the `resource` settings for the `server-acl-init` and `server-acl-init-cleanup` Jobs. [[GH-2416](https://github.com/hashicorp/consul-k8s/issues/2416)] +* sync-catalog: add ability to support weighted loadbalancing by service annotation `consul.hashicorp.com/service-weight: ` [[GH-2293](https://github.com/hashicorp/consul-k8s/issues/2293)] + +IMPROVEMENTS: + +* (Consul Enterprise) Add support to provide inputs via helm for audit log related configuration [[GH-2369](https://github.com/hashicorp/consul-k8s/issues/2369)] +* helm: Update the default amount of memory used by the connect-inject controller so that its less likely to get OOM killed. [[GH-2249](https://github.com/hashicorp/consul-k8s/issues/2249)] + +BUG FIXES: + +* control-plane: Always update ACL policies upon upgrade. [[GH-2392](https://github.com/hashicorp/consul-k8s/issues/2392)] +* control-plane: Fix casing of the Enforce Consecutive 5xx field on Service Defaults and acceptance test fixtures. [[GH-2266](https://github.com/hashicorp/consul-k8s/issues/2266)] + +## 1.0.8 (June 28, 2023) +BREAKING CHANGES: + +* control-plane: All policies managed by consul-k8s will now be updated on upgrade. If you previously edited the policies after install, your changes will be overwritten. [[GH-2392](https://github.com/hashicorp/consul-k8s/issues/2392)] + +SECURITY: + +* Bump Dockerfile base image for RedHat UBI `consul-k8s-control-plane` image to `ubi-minimal:9.2`. [[GH-2204](https://github.com/hashicorp/consul-k8s/issues/2204)] +* Bump Dockerfile base image to `alpine:3.18`. Resolves [CVE-2023-2650](https://github.com/advisories/GHSA-gqxg-9vfr-p9cg) vulnerability in openssl@3.0.8-r4 [[GH-2284](https://github.com/hashicorp/consul-k8s/issues/2284)] +* Bump `controller-runtime` to address CVEs in dependencies. [[GH-2225](https://github.com/hashicorp/consul-k8s/issues/2225)] +* Update [Go-Discover](https://github.com/hashicorp/go-discover) in the container has been updated to address [CVE-2020-14040](https://github.com/advisories/GHSA-5rcv-m4m3-hfh7) [[GH-2390](https://github.com/hashicorp/consul-k8s/issues/2390)] + +FEATURES: + +* Add support for configuring graceful shutdown proxy lifecycle management settings. [[GH-2233](https://github.com/hashicorp/consul-k8s/issues/2233)] +* helm: Adds `acls.resources` field which can be configured to override the `resource` settings for the `server-acl-init` and `server-acl-init-cleanup` Jobs. [[GH-2416](https://github.com/hashicorp/consul-k8s/issues/2416)] +* sync-catalog: add ability to support weighted loadbalancing by service annotation `consul.hashicorp.com/service-weight: ` [[GH-2293](https://github.com/hashicorp/consul-k8s/issues/2293)] + +IMPROVEMENTS: + +* (Consul Enterprise) Add support to provide inputs via helm for audit log related configuration [[GH-2265](https://github.com/hashicorp/consul-k8s/issues/2265)] +* helm: Update the default amount of memory used by the connect-inject controller so that its less likely to get OOM killed. [[GH-2249](https://github.com/hashicorp/consul-k8s/issues/2249)] + +BUG FIXES: + +* control-plane: Always update ACL policies upon upgrade. [[GH-2392](https://github.com/hashicorp/consul-k8s/issues/2392)] +* control-plane: Fix casing of the Enforce Consecutive 5xx field on Service Defaults and acceptance test fixtures. [[GH-2266](https://github.com/hashicorp/consul-k8s/issues/2266)] +* control-plane: add support for idleTimeout in the Service Router config [[GH-2156](https://github.com/hashicorp/consul-k8s/issues/2156)] +* control-plane: fix issue with json tags of service defaults fields EnforcingConsecutive5xx, MaxEjectionPercent and BaseEjectionTime. [[GH-2159](https://github.com/hashicorp/consul-k8s/issues/2159)] +* control-plane: fix issue with multiport pods crashlooping due to dataplane port conflicts by ensuring dns redirection is disabled for non-tproxy pods [[GH-2176](https://github.com/hashicorp/consul-k8s/issues/2176)] +* crd: fix bug on service intentions CRD causing some updates to be ignored. [[GH-2194](https://github.com/hashicorp/consul-k8s/issues/2194)] + + ## 0.49.7 (June 28, 2023) BREAKING CHANGES: From 29b6ed36923498afc8f377455d4275653960230f Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Fri, 14 Jul 2023 13:42:05 -0700 Subject: [PATCH 282/340] Refactor test framework to allow for more than two kube contexts (#2534) * updated contributing example with new configuration lists add new make target "kind" to makefile * This lets us setup our standard kind environment for testing refactor framework to take config list flags * removed primary/secondary kube flags as this limited us to only two clusters * added flags for kube configs, contexts and namespaces. This way we can support n clusters where n is the length of the longest list. The flags are then combined into a list of objects for use in testing added tests for new helper methods refactored tests * now TestMain for multicluster check that the test arguments contain the expected number of clusters * use helper method `env.GetSecondaryContextKey(t)` which grabs the second context in the list instead of using the defunct environment.SecondaryContextName refactored flag test to use new config lists refactored cli cluster to use get primary helper added multicluster check for vault acceptance * vault tests are multi-cluster but we weren't performing the necessary checks --- CONTRIBUTING.md | 21 ++-- Makefile | 29 +++-- acceptance/framework/config/config.go | 64 +++++++++-- acceptance/framework/config/config_test.go | 103 ++++++++++++++++++ acceptance/framework/consul/cli_cluster.go | 7 +- .../framework/environment/environment.go | 47 +++----- acceptance/framework/flags/flags.go | 82 ++++++++------ acceptance/framework/flags/flags_test.go | 97 ++++++++++++----- acceptance/tests/partitions/main_test.go | 6 +- .../partitions/partitions_connect_test.go | 3 +- .../partitions/partitions_gateway_test.go | 3 +- .../tests/partitions/partitions_sync_test.go | 3 +- acceptance/tests/peering/main_test.go | 6 +- .../peering_connect_namespaces_test.go | 3 +- .../tests/peering/peering_connect_test.go | 3 +- .../tests/peering/peering_gateway_test.go | 3 +- acceptance/tests/vault/main_test.go | 11 +- .../tests/vault/vault_partitions_test.go | 3 +- acceptance/tests/vault/vault_wan_fed_test.go | 2 +- acceptance/tests/wan-federation/main_test.go | 6 +- .../wan_federation_gateway_test.go | 2 +- .../wan-federation/wan_federation_test.go | 3 +- 22 files changed, 357 insertions(+), 150 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f0deb97ce9..c1e3446e8d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -642,8 +642,7 @@ you may use the following command: go test ./... -p 1 -timeout 20m \ -enable-multi-cluster \ - -kubecontext= \ - -secondary-kubecontext= + -kube-contexts=",, etc.>" Below is the list of available flags: @@ -667,20 +666,14 @@ Below is the list of available flags: This applies only to tests that enable connectInject. -enterprise-license The enterprise license for Consul. --kubeconfig string - The path to a kubeconfig file. If this is blank, the default kubeconfig path (~/.kube/config) will be used. --kubecontext string - The name of the Kubernetes context to use. If this is blank, the context set as the current context will be used by default. --namespace string - The Kubernetes namespace to use for tests. (default "default") +-kubeconfigs string + The comma separated list of Kubernetes configs to use (eg. "~/.kube/config,~/.kube/config2"). The first in the list will be treated as the primary config, followed by the secondary, etc. If the list is empty, or items are blank, then the default kubeconfig path (~/.kube/config) will be used. +-kube-contexts string + The comma separated list of Kubernetes contexts to use (eg. "kind-dc1,kind-dc2"). The first in the list will be treated as the primary context, followed by the secondary, etc. If the list is empty, or items are blank, then the current context will be used. +-kube-namespaces string + The comma separated list of Kubernetes namespaces to use (eg. "consul,consul-secondary"). The first in the list will be treated as the primary namespace, followed by the secondary, etc. If the list is empty, or fields are blank, then the current namespace will be used. -no-cleanup-on-failure If true, the tests will not cleanup Kubernetes resources they create when they finish running.Note this flag must be run with -failfast flag, otherwise subsequent tests will fail. --secondary-kubeconfig string - The path to a kubeconfig file of the secondary k8s cluster. If this is blank, the default kubeconfig path (~/.kube/config) will be used. --secondary-kubecontext string - The name of the Kubernetes context for the secondary cluster to use. If this is blank, the context set as the current context will be used by default. --secondary-namespace string - The Kubernetes namespace to use in the secondary k8s cluster. (default "default") ``` **Note:** There is a Terraform configuration in the diff --git a/Makefile b/Makefile index 1b7e4d8d7b..1d62035dbb 100644 --- a/Makefile +++ b/Makefile @@ -87,15 +87,6 @@ cni-plugin-lint: ctrl-generate: get-controller-gen ## Run CRD code generation. cd control-plane; $(CONTROLLER_GEN) object paths="./..." -# Helper target for doing local cni acceptance testing -kind-cni: - kind delete cluster --name dc1 - kind delete cluster --name dc2 - kind create cluster --config=$(CURDIR)/acceptance/framework/environment/cni-kind/kind.config --name dc1 --image kindest/node:v1.23.6 - make kind-cni-calico - kind create cluster --config=$(CURDIR)/acceptance/framework/environment/cni-kind/kind.config --name dc2 --image kindest/node:v1.23.6 - make kind-cni-calico - # Perform a terraform fmt check but don't change anything terraform-fmt-check: @$(CURDIR)/control-plane/build-support/scripts/terraformfmtcheck.sh $(TERRAFORM_DIR) @@ -133,7 +124,24 @@ kind-cni-calico: # Sleeps are needed as installs can happen too quickly for Kind to handle it @sleep 30 kubectl create -f $(CURDIR)/acceptance/framework/environment/cni-kind/custom-resources.yaml - @sleep 20 + @sleep 20 + +# Helper target for doing local cni acceptance testing +kind-cni: + kind delete cluster --name dc1 + kind delete cluster --name dc2 + kind create cluster --config=$(CURDIR)/acceptance/framework/environment/cni-kind/kind.config --name dc1 --image $(KIND_NODE_IMAGE) + make kind-cni-calico + kind create cluster --config=$(CURDIR)/acceptance/framework/environment/cni-kind/kind.config --name dc2 --image $(KIND_NODE_IMAGE) + make kind-cni-calico + +# Helper target for doing local acceptance testing +kind: + kind delete cluster --name dc1 + kind delete cluster --name dc2 + kind create cluster --name dc1 --image $(KIND_NODE_IMAGE) + kind create cluster --name dc2 --image $(KIND_NODE_IMAGE) + # ===========> Shared Targets @@ -247,7 +255,6 @@ endif source $(CURDIR)/control-plane/build-support/scripts/functions.sh; prepare_dev $(CURDIR) $(CONSUL_K8S_RELEASE_VERSION) "$(CONSUL_K8S_RELEASE_DATE)" "" $(CONSUL_K8S_NEXT_RELEASE_VERSION) $(CONSUL_K8S_NEXT_CONSUL_VERSION) $(CONSUL_K8S_NEXT_CONSUL_DATAPLANE_VERSION) # ===========> Makefile config - .DEFAULT_GOAL := help .PHONY: gen-helm-docs copy-crds-to-chart generate-external-crds bats-tests help ci.aws-acceptance-test-cleanup version cli-dev prepare-dev prepare-release SHELL = bash diff --git a/acceptance/framework/config/config.go b/acceptance/framework/config/config.go index 7151a75908..eada42af20 100644 --- a/acceptance/framework/config/config.go +++ b/acceptance/framework/config/config.go @@ -5,6 +5,7 @@ package config import ( "fmt" + "math" "os" "path/filepath" "strconv" @@ -22,16 +23,48 @@ const ( LicenseSecretKey = "key" ) -// TestConfig holds configuration for the test suite. -type TestConfig struct { - Kubeconfig string +type KubeTestConfig struct { + KubeConfig string KubeContext string KubeNamespace string +} + +// NewKubeTestConfigList takes lists of kubernetes configs, contexts and namespaces and constructs KubeTestConfig +// We validate ahead of time that the lists are either 0 or the same length as we expect that if the length of a list +// is greater than 0, then the indexes should match. For example: []kubeContexts{"ctx1", "ctx2"} indexes 0, 1 match with []kubeNamespaces{"ns1", "ns2"}. +func NewKubeTestConfigList(kubeConfigs, kubeContexts, kubeNamespaces []string) []KubeTestConfig { + // Grab the longest length. + l := math.Max(float64(len(kubeConfigs)), + math.Max(float64(len(kubeContexts)), float64(len(kubeNamespaces)))) + + // If all are empty, then return a single empty entry + if l == 0 { + return []KubeTestConfig{{}} + } + + // Add each non-zero length list to the new structs, we should have + // n structs where n == l. + out := make([]KubeTestConfig, int(l)) + for i := range out { + kenv := KubeTestConfig{} + if len(kubeConfigs) != 0 { + kenv.KubeConfig = kubeConfigs[i] + } + if len(kubeContexts) != 0 { + kenv.KubeContext = kubeContexts[i] + } + if len(kubeNamespaces) != 0 { + kenv.KubeNamespace = kubeNamespaces[i] + } + out[i] = kenv + } + return out +} - EnableMultiCluster bool - SecondaryKubeconfig string - SecondaryKubeContext string - SecondaryKubeNamespace string +// TestConfig holds configuration for the test suite. +type TestConfig struct { + KubeEnvs []KubeTestConfig + EnableMultiCluster bool EnableEnterprise bool EnterpriseLicense string @@ -117,6 +150,23 @@ func (t *TestConfig) HelmValuesFromConfig() (map[string]string, error) { return helmValues, nil } +// IsExpectedClusterCount check that we have at least the required number of clusters to +// run a test. +func (t *TestConfig) IsExpectedClusterCount(count int) bool { + return len(t.KubeEnvs) >= count +} + +// GetPrimaryKubeEnv returns the primary Kubernetes environment. +func (t *TestConfig) GetPrimaryKubeEnv() KubeTestConfig { + // Return the first in the list as this is always the primary + // kube environment. If empty return an empty kubeEnv + if len(t.KubeEnvs) < 1 { + return KubeTestConfig{} + } else { + return t.KubeEnvs[0] + } +} + type values struct { Global globalValues `yaml:"global"` } diff --git a/acceptance/framework/config/config_test.go b/acceptance/framework/config/config_test.go index f5992cdd99..96f0f0e7eb 100644 --- a/acceptance/framework/config/config_test.go +++ b/acceptance/framework/config/config_test.go @@ -181,3 +181,106 @@ func TestConfig_HelmValuesFromConfig_EntImage(t *testing.T) { }) } } + +func Test_KubeEnvListFromStringList(t *testing.T) { + tests := []struct { + name string + kubeContexts []string + KubeConfigs []string + kubeNamespaces []string + expKubeEnvList []KubeTestConfig + }{ + { + name: "empty-lists", + kubeContexts: []string{}, + KubeConfigs: []string{}, + kubeNamespaces: []string{}, + expKubeEnvList: []KubeTestConfig{{}}, + }, + { + name: "kubeContext set", + kubeContexts: []string{"ctx1", "ctx2"}, + KubeConfigs: []string{}, + kubeNamespaces: []string{}, + expKubeEnvList: []KubeTestConfig{{KubeContext: "ctx1"}, {KubeContext: "ctx2"}}, + }, + { + name: "kubeNamespace set", + kubeContexts: []string{}, + KubeConfigs: []string{"/path/config1", "/path/config2"}, + kubeNamespaces: []string{}, + expKubeEnvList: []KubeTestConfig{{KubeConfig: "/path/config1"}, {KubeConfig: "/path/config2"}}, + }, + { + name: "kubeConfigs set", + kubeContexts: []string{}, + KubeConfigs: []string{}, + kubeNamespaces: []string{"ns1", "ns2"}, + expKubeEnvList: []KubeTestConfig{{KubeNamespace: "ns1"}, {KubeNamespace: "ns2"}}, + }, + { + name: "multiple everything", + kubeContexts: []string{"ctx1", "ctx2"}, + KubeConfigs: []string{"/path/config1", "/path/config2"}, + kubeNamespaces: []string{"ns1", "ns2"}, + expKubeEnvList: []KubeTestConfig{{KubeContext: "ctx1", KubeNamespace: "ns1", KubeConfig: "/path/config1"}, {KubeContext: "ctx2", KubeNamespace: "ns2", KubeConfig: "/path/config2"}}, + }, + { + name: "multiple context and configs", + kubeContexts: []string{"ctx1", "ctx2"}, + KubeConfigs: []string{"/path/config1", "/path/config2"}, + kubeNamespaces: []string{}, + expKubeEnvList: []KubeTestConfig{{KubeContext: "ctx1", KubeConfig: "/path/config1"}, {KubeContext: "ctx2", KubeConfig: "/path/config2"}}, + }, + { + name: "multiple namespace and configs", + kubeContexts: []string{}, + KubeConfigs: []string{"/path/config1", "/path/config2"}, + kubeNamespaces: []string{"ns1", "ns2"}, + expKubeEnvList: []KubeTestConfig{{KubeNamespace: "ns1", KubeConfig: "/path/config1"}, {KubeNamespace: "ns2", KubeConfig: "/path/config2"}}, + }, + { + name: "multiple context and namespace", + kubeContexts: []string{"ctx1", "ctx2"}, + KubeConfigs: []string{}, + kubeNamespaces: []string{"ns1", "ns2"}, + expKubeEnvList: []KubeTestConfig{{KubeContext: "ctx1", KubeNamespace: "ns1"}, {KubeContext: "ctx2", KubeNamespace: "ns2"}}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual := NewKubeTestConfigList(tt.KubeConfigs, tt.kubeContexts, tt.kubeNamespaces) + require.Equal(t, tt.expKubeEnvList, actual) + }) + } +} + +func Test_GetPrimaryKubeEnv(t *testing.T) { + tests := []struct { + name string + kubeEnvList []KubeTestConfig + expPrimaryKubeEnv KubeTestConfig + }{ + { + name: "context config multiple namespace single", + kubeEnvList: []KubeTestConfig{{KubeContext: "ctx1", KubeNamespace: "ns1", KubeConfig: "/path/config1"}, {KubeContext: "ctx2", KubeConfig: "/path/config2"}}, + expPrimaryKubeEnv: KubeTestConfig{KubeContext: "ctx1", KubeNamespace: "ns1", KubeConfig: "/path/config1"}, + }, + { + name: "context config multiple namespace single", + kubeEnvList: []KubeTestConfig{}, + expPrimaryKubeEnv: KubeTestConfig{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := TestConfig{ + KubeEnvs: tt.kubeEnvList, + } + actual := cfg.GetPrimaryKubeEnv() + require.Equal(t, tt.expPrimaryKubeEnv, actual) + }) + } +} diff --git a/acceptance/framework/consul/cli_cluster.go b/acceptance/framework/consul/cli_cluster.go index ba4cfc93ab..9e119af76d 100644 --- a/acceptance/framework/consul/cli_cluster.go +++ b/acceptance/framework/consul/cli_cluster.go @@ -97,16 +97,17 @@ func NewCLICluster( cli, err := cli.NewCLI() require.NoError(t, err) + require.Greater(t, len(cfg.KubeEnvs), 0) return &CLICluster{ ctx: ctx, helmOptions: hopts, kubectlOptions: kopts, - namespace: cfg.KubeNamespace, + namespace: cfg.GetPrimaryKubeEnv().KubeNamespace, values: values, releaseName: releaseName, kubernetesClient: ctx.KubernetesClient(t), - kubeConfig: cfg.Kubeconfig, - kubeContext: cfg.KubeContext, + kubeConfig: cfg.GetPrimaryKubeEnv().KubeConfig, + kubeContext: cfg.GetPrimaryKubeEnv().KubeContext, noCleanupOnFailure: cfg.NoCleanupOnFailure, debugDirectory: cfg.DebugDirectory, logger: logger, diff --git a/acceptance/framework/environment/environment.go b/acceptance/framework/environment/environment.go index 58e4e83a5b..9150f4ff03 100644 --- a/acceptance/framework/environment/environment.go +++ b/acceptance/framework/environment/environment.go @@ -21,15 +21,14 @@ import ( ) const ( - DefaultContextName = "default" - SecondaryContextName = "secondary" + DefaultContextIndex = 0 ) // TestEnvironment represents the infrastructure environment of the test, // such as the kubernetes cluster(s) the test is running against. type TestEnvironment interface { DefaultContext(t *testing.T) TestContext - Context(t *testing.T, name string) TestContext + Context(t *testing.T, index int) TestContext } // TestContext represents a specific context a test needs, @@ -41,50 +40,40 @@ type TestContext interface { } type KubernetesEnvironment struct { - contexts map[string]*kubernetesContext + contexts []*kubernetesContext } func NewKubernetesEnvironmentFromConfig(config *config.TestConfig) *KubernetesEnvironment { - defaultContext := NewContext(config.KubeNamespace, config.Kubeconfig, config.KubeContext) + // First kubeEnv is the default + defaultContext := NewContext(config.GetPrimaryKubeEnv().KubeNamespace, config.GetPrimaryKubeEnv().KubeConfig, config.GetPrimaryKubeEnv().KubeContext) // Create a kubernetes environment with default context. kenv := &KubernetesEnvironment{ - contexts: map[string]*kubernetesContext{ - DefaultContextName: defaultContext, + contexts: []*kubernetesContext{ + defaultContext, }, } - // Add secondary context if multi cluster tests are enabled. + // Add additional contexts if multi cluster tests are enabled. if config.EnableMultiCluster { - kenv.contexts[SecondaryContextName] = NewContext(config.SecondaryKubeNamespace, config.SecondaryKubeconfig, config.SecondaryKubeContext) - } - - return kenv -} - -func NewKubernetesEnvironmentFromContext(context *kubernetesContext) *KubernetesEnvironment { - // Create a kubernetes environment with default context. - kenv := &KubernetesEnvironment{ - contexts: map[string]*kubernetesContext{ - DefaultContextName: context, - }, + for _, v := range config.KubeEnvs[1:] { + kenv.contexts = append(kenv.contexts, NewContext(v.KubeNamespace, v.KubeConfig, v.KubeContext)) + } } return kenv } -func (k *KubernetesEnvironment) Context(t *testing.T, name string) TestContext { - ctx, ok := k.contexts[name] - require.Truef(t, ok, fmt.Sprintf("requested context %s not found", name)) - - return ctx +func (k *KubernetesEnvironment) Context(t *testing.T, index int) TestContext { + lenContexts := len(k.contexts) + require.Greater(t, lenContexts, index, fmt.Sprintf("context list does not contain an index %d, length is %d", index, lenContexts)) + return k.contexts[index] } func (k *KubernetesEnvironment) DefaultContext(t *testing.T) TestContext { - ctx, ok := k.contexts[DefaultContextName] - require.Truef(t, ok, "default context not found") - - return ctx + lenContexts := len(k.contexts) + require.Greater(t, lenContexts, DefaultContextIndex, fmt.Sprintf("context list does not contain an index %d, length is %d", DefaultContextIndex, lenContexts)) + return k.contexts[DefaultContextIndex] } type kubernetesContext struct { diff --git a/acceptance/framework/flags/flags.go b/acceptance/framework/flags/flags.go index 5df09f853a..ad36099b37 100644 --- a/acceptance/framework/flags/flags.go +++ b/acceptance/framework/flags/flags.go @@ -7,6 +7,7 @@ import ( "errors" "flag" "os" + "strings" "sync" "github.com/hashicorp/consul-k8s/acceptance/framework/config" @@ -14,14 +15,10 @@ import ( ) type TestFlags struct { - flagKubeconfig string - flagKubecontext string - flagNamespace string - - flagEnableMultiCluster bool - flagSecondaryKubeconfig string - flagSecondaryKubecontext string - flagSecondaryNamespace string + flagKubeconfigs listFlag + flagKubecontexts listFlag + flagKubeNamespaces listFlag + flagEnableMultiCluster bool flagEnableEnterprise bool flagEnterpriseLicense string @@ -68,13 +65,19 @@ func NewTestFlags() *TestFlags { return t } -func (t *TestFlags) init() { - flag.StringVar(&t.flagKubeconfig, "kubeconfig", "", "The path to a kubeconfig file. If this is blank, "+ - "the default kubeconfig path (~/.kube/config) will be used.") - flag.StringVar(&t.flagKubecontext, "kubecontext", "", "The name of the Kubernetes context to use. If this is blank, "+ - "the context set as the current context will be used by default.") - flag.StringVar(&t.flagNamespace, "namespace", "", "The Kubernetes namespace to use for tests.") +type listFlag []string +// String() returns a comma separated list in the form "var1,var2,var3". +func (f *listFlag) String() string { + return strings.Join(*f, ",") +} + +func (f *listFlag) Set(value string) error { + *f = strings.Split(value, ",") + return nil +} + +func (t *TestFlags) init() { flag.StringVar(&t.flagConsulImage, "consul-image", "", "The Consul image to use for all tests.") flag.StringVar(&t.flagConsulK8sImage, "consul-k8s-image", "", "The consul-k8s image to use for all tests.") flag.StringVar(&t.flagConsulDataplaneImage, "consul-dataplane-image", "", "The consul-dataplane image to use for all tests.") @@ -86,16 +89,16 @@ func (t *TestFlags) init() { flag.StringVar(&t.flagVaultServerVersion, "vault-server-version", "", "The vault serverversion used for all tests.") flag.StringVar(&t.flagVaultHelmChartVersion, "vault-helm-chart-version", "", "The Vault helm chart used for all tests.") + flag.Var(&t.flagKubeconfigs, "kubeconfigs", "The list of paths to a kubeconfig files. If this is blank, "+ + "the default kubeconfig path (~/.kube/config) will be used.") + flag.Var(&t.flagKubecontexts, "kube-contexts", "The list of names of the Kubernetes contexts to use. If this is blank, "+ + "the context set as the current context will be used by default.") + flag.Var(&t.flagKubeNamespaces, "kube-namespaces", "The list of Kubernetes namespaces to use for tests.") flag.StringVar(&t.flagHCPResourceID, "hcp-resource-id", "", "The hcp resource id to use for all tests.") flag.BoolVar(&t.flagEnableMultiCluster, "enable-multi-cluster", false, "If true, the tests that require multiple Kubernetes clusters will be run. "+ - "At least one of -secondary-kubeconfig or -secondary-kubecontext is required when this flag is used.") - flag.StringVar(&t.flagSecondaryKubeconfig, "secondary-kubeconfig", "", "The path to a kubeconfig file of the secondary k8s cluster. "+ - "If this is blank, the default kubeconfig path (~/.kube/config) will be used.") - flag.StringVar(&t.flagSecondaryKubecontext, "secondary-kubecontext", "", "The name of the Kubernetes context for the secondary cluster to use. "+ - "If this is blank, the context set as the current context will be used by default.") - flag.StringVar(&t.flagSecondaryNamespace, "secondary-namespace", "", "The Kubernetes namespace to use in the secondary k8s cluster.") + "The lists -kubeconfig or -kube-context must contain more than one entry when this flag is used.") flag.BoolVar(&t.flagEnableEnterprise, "enable-enterprise", false, "If true, the test suite will run tests for enterprise features. "+ @@ -143,8 +146,26 @@ func (t *TestFlags) init() { func (t *TestFlags) Validate() error { if t.flagEnableMultiCluster { - if t.flagSecondaryKubecontext == "" && t.flagSecondaryKubeconfig == "" { - return errors.New("at least one of -secondary-kubecontext or -secondary-kubeconfig flags must be provided if -enable-multi-cluster is set") + if len(t.flagKubecontexts) <= 1 && len(t.flagKubeconfigs) <= 1 { + return errors.New("at least two contexts must be included in -kube-contexts or -kubeconfigs if -enable-multi-cluster is set") + } + } + + if len(t.flagKubecontexts) != 0 && len(t.flagKubeconfigs) != 0 { + if len(t.flagKubecontexts) != len(t.flagKubeconfigs) { + return errors.New("-kube-contexts and -kubeconfigs are both set but are not of equal length") + } + } + + if len(t.flagKubecontexts) != 0 && len(t.flagKubeNamespaces) != 0 { + if len(t.flagKubecontexts) != len(t.flagKubeNamespaces) { + return errors.New("-kube-contexts and -kube-namespaces are both set but are not of equal length") + } + } + + if len(t.flagKubeNamespaces) != 0 && len(t.flagKubeconfigs) != 0 { + if len(t.flagKubeNamespaces) != len(t.flagKubeconfigs) { + return errors.New("-kube-namespaces and -kubeconfigs are both set but are not of equal length") } } @@ -161,20 +182,15 @@ func (t *TestFlags) TestConfigFromFlags() *config.TestConfig { consulVersion, _ := version.NewVersion(t.flagConsulVersion) consulDataplaneVersion, _ := version.NewVersion(t.flagConsulDataplaneVersion) //vaultserverVersion, _ := version.NewVersion(t.flagVaultServerVersion) + kubeEnvs := config.NewKubeTestConfigList(t.flagKubeconfigs, t.flagKubecontexts, t.flagKubeNamespaces) - return &config.TestConfig{ - Kubeconfig: t.flagKubeconfig, - KubeContext: t.flagKubecontext, - KubeNamespace: t.flagNamespace, - - EnableMultiCluster: t.flagEnableMultiCluster, - SecondaryKubeconfig: t.flagSecondaryKubeconfig, - SecondaryKubeContext: t.flagSecondaryKubecontext, - SecondaryKubeNamespace: t.flagSecondaryNamespace, - + c := &config.TestConfig{ EnableEnterprise: t.flagEnableEnterprise, EnterpriseLicense: t.flagEnterpriseLicense, + KubeEnvs: kubeEnvs, + EnableMultiCluster: t.flagEnableMultiCluster, + EnableOpenshift: t.flagEnableOpenshift, EnablePodSecurityPolicies: t.flagEnablePodSecurityPolicies, @@ -205,4 +221,6 @@ func (t *TestFlags) TestConfigFromFlags() *config.TestConfig { UseGKE: t.flagUseGKE, UseKind: t.flagUseKind, } + + return c } diff --git a/acceptance/framework/flags/flags_test.go b/acceptance/framework/flags/flags_test.go index 7546ae911c..1e2bf0a039 100644 --- a/acceptance/framework/flags/flags_test.go +++ b/acceptance/framework/flags/flags_test.go @@ -11,9 +11,10 @@ import ( func TestFlags_validate(t *testing.T) { type fields struct { - flagEnableMultiCluster bool - flagSecondaryKubeconfig string - flagSecondaryKubecontext string + flagEnableMultiCluster bool + flagKubeConfigs listFlag + flagKubeContexts listFlag + flagNamespaces listFlag flagEnableEnt bool flagEntLicense string @@ -26,20 +27,16 @@ func TestFlags_validate(t *testing.T) { }{ { "no error by default", - fields{ - flagEnableMultiCluster: false, - flagSecondaryKubeconfig: "", - flagSecondaryKubecontext: "", - }, + fields{}, false, "", }, { "enable multi cluster: no error when multi cluster is disabled", fields{ - flagEnableMultiCluster: false, - flagSecondaryKubeconfig: "", - flagSecondaryKubecontext: "", + flagEnableMultiCluster: false, + flagKubeConfigs: listFlag{}, + flagKubeContexts: listFlag{}, }, false, "", @@ -47,19 +44,19 @@ func TestFlags_validate(t *testing.T) { { "enable multi cluster: errors when both secondary kubeconfig and kubecontext are empty", fields{ - flagEnableMultiCluster: true, - flagSecondaryKubeconfig: "", - flagSecondaryKubecontext: "", + flagEnableMultiCluster: true, + flagKubeConfigs: listFlag{}, + flagKubeContexts: listFlag{}, }, true, - "at least one of -secondary-kubecontext or -secondary-kubeconfig flags must be provided if -enable-multi-cluster is set", + "at least two contexts must be included in -kube-contexts or -kubeconfigs if -enable-multi-cluster is set", }, { "enable multi cluster: no error when secondary kubeconfig but not kubecontext is provided", fields{ - flagEnableMultiCluster: true, - flagSecondaryKubeconfig: "foo", - flagSecondaryKubecontext: "", + flagEnableMultiCluster: true, + flagKubeConfigs: listFlag{"foo", "bar"}, + flagKubeContexts: listFlag{}, }, false, "", @@ -67,9 +64,9 @@ func TestFlags_validate(t *testing.T) { { "enable multi cluster: no error when secondary kubecontext but not kubeconfig is provided", fields{ - flagEnableMultiCluster: true, - flagSecondaryKubeconfig: "", - flagSecondaryKubecontext: "foo", + flagEnableMultiCluster: true, + flagKubeConfigs: listFlag{}, + flagKubeContexts: listFlag{"foo", "bar"}, }, false, "", @@ -77,13 +74,54 @@ func TestFlags_validate(t *testing.T) { { "enable multi cluster: no error when both secondary kubecontext and kubeconfig are provided", fields{ - flagEnableMultiCluster: true, - flagSecondaryKubeconfig: "foo", - flagSecondaryKubecontext: "bar", + flagEnableMultiCluster: true, + flagKubeConfigs: listFlag{"foo", "bar"}, + flagKubeContexts: listFlag{"foo", "bar"}, + }, + false, + "", + }, + { + "enable multi cluster: no error when all of secondary kubecontext, kubeconfigs and namespaces are provided", + fields{ + flagEnableMultiCluster: true, + flagKubeConfigs: listFlag{"foo", "bar"}, + flagKubeContexts: listFlag{"foo", "bar"}, + flagNamespaces: listFlag{"foo", "bar"}, }, false, "", }, + { + "enable multi cluster: error when the list of kubeconfigs and kubecontexts do not match", + fields{ + flagEnableMultiCluster: true, + flagKubeConfigs: listFlag{"foo", "bar"}, + flagKubeContexts: listFlag{"foo"}, + }, + true, + "-kube-contexts and -kubeconfigs are both set but are not of equal length", + }, + { + "enable multi cluster: error when the list of kubeconfigs and namespaces do not match", + fields{ + flagEnableMultiCluster: true, + flagKubeConfigs: listFlag{"foo", "bar"}, + flagNamespaces: listFlag{"foo"}, + }, + true, + "-kube-namespaces and -kubeconfigs are both set but are not of equal length", + }, + { + "enable multi cluster: error when the list of kubecontexts and namespaces do not match", + fields{ + flagEnableMultiCluster: true, + flagKubeContexts: listFlag{"foo", "bar"}, + flagNamespaces: listFlag{"foo"}, + }, + true, + "-kube-contexts and -kube-namespaces are both set but are not of equal length", + }, { "enterprise license: error when only -enable-enterprise is true but env CONSUL_ENT_LICENSE is not provided", fields{ @@ -105,11 +143,12 @@ func TestFlags_validate(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tf := &TestFlags{ - flagEnableMultiCluster: tt.fields.flagEnableMultiCluster, - flagSecondaryKubeconfig: tt.fields.flagSecondaryKubeconfig, - flagSecondaryKubecontext: tt.fields.flagSecondaryKubecontext, - flagEnableEnterprise: tt.fields.flagEnableEnt, - flagEnterpriseLicense: tt.fields.flagEntLicense, + flagEnableMultiCluster: tt.fields.flagEnableMultiCluster, + flagKubeconfigs: tt.fields.flagKubeConfigs, + flagKubecontexts: tt.fields.flagKubeContexts, + flagKubeNamespaces: tt.fields.flagNamespaces, + flagEnableEnterprise: tt.fields.flagEnableEnt, + flagEnterpriseLicense: tt.fields.flagEntLicense, } err := tf.Validate() if tt.wantErr { diff --git a/acceptance/tests/partitions/main_test.go b/acceptance/tests/partitions/main_test.go index 9368c12f00..89833ec2cc 100644 --- a/acceptance/tests/partitions/main_test.go +++ b/acceptance/tests/partitions/main_test.go @@ -16,10 +16,12 @@ var suite testsuite.Suite func TestMain(m *testing.M) { suite = testsuite.NewSuite(m) - if suite.Config().EnableMultiCluster { + expectedNumberOfClusters := 2 + if suite.Config().EnableMultiCluster && suite.Config().IsExpectedClusterCount(expectedNumberOfClusters) { os.Exit(suite.Run()) } else { - fmt.Println("Skipping partitions tests because -enable-multi-cluster is not set") + fmt.Println(fmt.Sprintf("Skipping partitions tests because either -enable-multi-cluster is "+ + "not set or the number of clusters did not match the expected count of %d", expectedNumberOfClusters)) os.Exit(0) } } diff --git a/acceptance/tests/partitions/partitions_connect_test.go b/acceptance/tests/partitions/partitions_connect_test.go index b14f079a68..aa73c17047 100644 --- a/acceptance/tests/partitions/partitions_connect_test.go +++ b/acceptance/tests/partitions/partitions_connect_test.go @@ -11,7 +11,6 @@ import ( terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/environment" "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/logger" @@ -85,7 +84,7 @@ func TestPartitions_Connect(t *testing.T) { for _, c := range cases { t.Run(c.name, func(t *testing.T) { defaultPartitionClusterContext := env.DefaultContext(t) - secondaryPartitionClusterContext := env.Context(t, environment.SecondaryContextName) + secondaryPartitionClusterContext := env.Context(t, 1) commonHelmValues := map[string]string{ "global.adminPartitions.enabled": "true", diff --git a/acceptance/tests/partitions/partitions_gateway_test.go b/acceptance/tests/partitions/partitions_gateway_test.go index 06bc933ce8..5c85e6725b 100644 --- a/acceptance/tests/partitions/partitions_gateway_test.go +++ b/acceptance/tests/partitions/partitions_gateway_test.go @@ -12,7 +12,6 @@ import ( terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/environment" "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/logger" @@ -37,7 +36,7 @@ func TestPartitions_Gateway(t *testing.T) { const secondaryPartition = "secondary" defaultPartitionClusterContext := env.DefaultContext(t) - secondaryPartitionClusterContext := env.Context(t, environment.SecondaryContextName) + secondaryPartitionClusterContext := env.Context(t, 1) commonHelmValues := map[string]string{ "global.adminPartitions.enabled": "true", diff --git a/acceptance/tests/partitions/partitions_sync_test.go b/acceptance/tests/partitions/partitions_sync_test.go index cf32c97ae3..8eaaff099e 100644 --- a/acceptance/tests/partitions/partitions_sync_test.go +++ b/acceptance/tests/partitions/partitions_sync_test.go @@ -11,7 +11,6 @@ import ( terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/environment" "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/logger" @@ -82,7 +81,7 @@ func TestPartitions_Sync(t *testing.T) { for _, c := range cases { t.Run(c.name, func(t *testing.T) { primaryClusterContext := env.DefaultContext(t) - secondaryClusterContext := env.Context(t, environment.SecondaryContextName) + secondaryClusterContext := env.Context(t, 1) commonHelmValues := map[string]string{ "global.adminPartitions.enabled": "true", diff --git a/acceptance/tests/peering/main_test.go b/acceptance/tests/peering/main_test.go index 64c5f8ed03..075051861d 100644 --- a/acceptance/tests/peering/main_test.go +++ b/acceptance/tests/peering/main_test.go @@ -16,10 +16,12 @@ var suite testsuite.Suite func TestMain(m *testing.M) { suite = testsuite.NewSuite(m) - if suite.Config().EnableMultiCluster && !suite.Config().DisablePeering { + expectedNumberOfClusters := 2 + if suite.Config().EnableMultiCluster && suite.Config().IsExpectedClusterCount(expectedNumberOfClusters) && !suite.Config().DisablePeering { os.Exit(suite.Run()) } else { - fmt.Println("Skipping peering tests because either -enable-multi-cluster is not set or -disable-peering is set") + fmt.Println(fmt.Sprintf("Skipping peerings tests because either -enable-multi-cluster is "+ + "not set, -disable-peering is set, or the number of clusters did not match the expected count of %d", expectedNumberOfClusters)) os.Exit(0) } } diff --git a/acceptance/tests/peering/peering_connect_namespaces_test.go b/acceptance/tests/peering/peering_connect_namespaces_test.go index 9276582db3..622e547091 100644 --- a/acceptance/tests/peering/peering_connect_namespaces_test.go +++ b/acceptance/tests/peering/peering_connect_namespaces_test.go @@ -12,7 +12,6 @@ import ( terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/environment" "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/logger" @@ -93,7 +92,7 @@ func TestPeering_ConnectNamespaces(t *testing.T) { for _, c := range cases { t.Run(c.name, func(t *testing.T) { staticServerPeerClusterContext := env.DefaultContext(t) - staticClientPeerClusterContext := env.Context(t, environment.SecondaryContextName) + staticClientPeerClusterContext := env.Context(t, 1) commonHelmValues := map[string]string{ "global.peering.enabled": "true", diff --git a/acceptance/tests/peering/peering_connect_test.go b/acceptance/tests/peering/peering_connect_test.go index ad62ca6926..a14cf3a805 100644 --- a/acceptance/tests/peering/peering_connect_test.go +++ b/acceptance/tests/peering/peering_connect_test.go @@ -12,7 +12,6 @@ import ( terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/environment" "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/logger" @@ -53,7 +52,7 @@ func TestPeering_Connect(t *testing.T) { for _, c := range cases { t.Run(c.name, func(t *testing.T) { staticServerPeerClusterContext := env.DefaultContext(t) - staticClientPeerClusterContext := env.Context(t, environment.SecondaryContextName) + staticClientPeerClusterContext := env.Context(t, 1) commonHelmValues := map[string]string{ "global.peering.enabled": "true", diff --git a/acceptance/tests/peering/peering_gateway_test.go b/acceptance/tests/peering/peering_gateway_test.go index 76698102e6..17824b8e69 100644 --- a/acceptance/tests/peering/peering_gateway_test.go +++ b/acceptance/tests/peering/peering_gateway_test.go @@ -11,7 +11,6 @@ import ( terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/environment" "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/logger" @@ -42,7 +41,7 @@ func TestPeering_Gateway(t *testing.T) { const staticClientPeer = "client" staticServerPeerClusterContext := env.DefaultContext(t) - staticClientPeerClusterContext := env.Context(t, environment.SecondaryContextName) + staticClientPeerClusterContext := env.Context(t, 1) commonHelmValues := map[string]string{ "global.peering.enabled": "true", diff --git a/acceptance/tests/vault/main_test.go b/acceptance/tests/vault/main_test.go index e20892bf1c..02a22c2b79 100644 --- a/acceptance/tests/vault/main_test.go +++ b/acceptance/tests/vault/main_test.go @@ -4,6 +4,7 @@ package vault import ( + "fmt" "os" "testing" @@ -14,5 +15,13 @@ var suite testsuite.Suite func TestMain(m *testing.M) { suite = testsuite.NewSuite(m) - os.Exit(suite.Run()) + + expectedNumberOfClusters := 2 + if suite.Config().EnableMultiCluster && suite.Config().IsExpectedClusterCount(expectedNumberOfClusters) { + os.Exit(suite.Run()) + } else { + fmt.Println(fmt.Sprintf("Skipping vault tests because either -enable-multi-cluster is "+ + "not set or the number of clusters did not match the expected count of %d", expectedNumberOfClusters)) + os.Exit(0) + } } diff --git a/acceptance/tests/vault/vault_partitions_test.go b/acceptance/tests/vault/vault_partitions_test.go index 53bdc23e97..63002993a6 100644 --- a/acceptance/tests/vault/vault_partitions_test.go +++ b/acceptance/tests/vault/vault_partitions_test.go @@ -9,7 +9,6 @@ import ( "testing" "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/environment" "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/logger" @@ -26,7 +25,7 @@ func TestVault_Partitions(t *testing.T) { env := suite.Environment() cfg := suite.Config() serverClusterCtx := env.DefaultContext(t) - clientClusterCtx := env.Context(t, environment.SecondaryContextName) + clientClusterCtx := env.Context(t, 1) ns := serverClusterCtx.KubectlOptions(t).Namespace const secondaryPartition = "secondary" diff --git a/acceptance/tests/vault/vault_wan_fed_test.go b/acceptance/tests/vault/vault_wan_fed_test.go index 21a86937f4..d8c00b732a 100644 --- a/acceptance/tests/vault/vault_wan_fed_test.go +++ b/acceptance/tests/vault/vault_wan_fed_test.go @@ -44,7 +44,7 @@ func TestVault_WANFederationViaGateways(t *testing.T) { } primaryCtx := suite.Environment().DefaultContext(t) - secondaryCtx := suite.Environment().Context(t, environment.SecondaryContextName) + secondaryCtx := suite.Environment().Context(t, 1) ns := primaryCtx.KubectlOptions(t).Namespace diff --git a/acceptance/tests/wan-federation/main_test.go b/acceptance/tests/wan-federation/main_test.go index ced18d5cc7..4a47a8a00f 100644 --- a/acceptance/tests/wan-federation/main_test.go +++ b/acceptance/tests/wan-federation/main_test.go @@ -16,10 +16,12 @@ var suite testsuite.Suite func TestMain(m *testing.M) { suite = testsuite.NewSuite(m) - if suite.Config().EnableMultiCluster { + expectedNumberOfClusters := 2 + if suite.Config().EnableMultiCluster && suite.Config().IsExpectedClusterCount(expectedNumberOfClusters) { os.Exit(suite.Run()) } else { - fmt.Println("Skipping wan federation tests because -enable-multi-cluster is not set") + fmt.Println(fmt.Sprintf("Skipping wan-federation tests because either -enable-multi-cluster is "+ + "not set or the number of clusters did not match the expected count of %d", expectedNumberOfClusters)) os.Exit(0) } } diff --git a/acceptance/tests/wan-federation/wan_federation_gateway_test.go b/acceptance/tests/wan-federation/wan_federation_gateway_test.go index 0ef48b9920..c87ee7197b 100644 --- a/acceptance/tests/wan-federation/wan_federation_gateway_test.go +++ b/acceptance/tests/wan-federation/wan_federation_gateway_test.go @@ -33,7 +33,7 @@ func TestWANFederation_Gateway(t *testing.T) { } primaryContext := env.DefaultContext(t) - secondaryContext := env.Context(t, environment.SecondaryContextName) + secondaryContext := env.Context(t, 1) primaryHelmValues := map[string]string{ "global.datacenter": "dc1", diff --git a/acceptance/tests/wan-federation/wan_federation_test.go b/acceptance/tests/wan-federation/wan_federation_test.go index ced126af42..e7a128887e 100644 --- a/acceptance/tests/wan-federation/wan_federation_test.go +++ b/acceptance/tests/wan-federation/wan_federation_test.go @@ -10,7 +10,6 @@ import ( "testing" "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/environment" "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/logger" @@ -49,7 +48,7 @@ func TestWANFederation(t *testing.T) { } primaryContext := env.DefaultContext(t) - secondaryContext := env.Context(t, environment.SecondaryContextName) + secondaryContext := env.Context(t, 1) primaryHelmValues := map[string]string{ "global.datacenter": "dc1", From 59228dd4cb064e892a6ef80668c447eb3689ca54 Mon Sep 17 00:00:00 2001 From: "hashicorp-copywrite[bot]" <110428419+hashicorp-copywrite[bot]@users.noreply.github.com> Date: Mon, 17 Jul 2023 15:04:09 -0400 Subject: [PATCH 283/340] [COMPLIANCE] Add Copyright and License Headers (#2577) Add copyright and license headers --- control-plane/api-gateway/binding/result_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/control-plane/api-gateway/binding/result_test.go b/control-plane/api-gateway/binding/result_test.go index c6987cdaeb..6989bb09ab 100644 --- a/control-plane/api-gateway/binding/result_test.go +++ b/control-plane/api-gateway/binding/result_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package binding import ( From ab462d0a483c6b31c6d6846f61fc6b66c45c0540 Mon Sep 17 00:00:00 2001 From: Nathan Coleman Date: Tue, 18 Jul 2023 11:01:55 -0500 Subject: [PATCH 284/340] Consume gateway-api v0.7.1 for acceptance testing (#2578) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes proposed in this PR: - Consume the same version of gateway-api for acceptance testing that we're consuming in the control plane: https://github.com/hashicorp/consul-k8s/blob/29b6ed36923498afc8f377455d4275653960230f/control-plane/go.mod#L42 How I've tested this PR: - 👀 - 🤖 tests pass How I expect reviewers to test this PR: - See above Checklist: - [ ] Tests added - [ ] [CHANGELOG entry added](https://github.com/hashicorp/consul-k8s/blob/main/CONTRIBUTING.md#adding-a-changelog-entry) --- acceptance/go.mod | 2 +- acceptance/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/acceptance/go.mod b/acceptance/go.mod index 02ef978cf6..382d9f8225 100644 --- a/acceptance/go.mod +++ b/acceptance/go.mod @@ -19,7 +19,7 @@ require ( k8s.io/client-go v0.26.3 k8s.io/utils v0.0.0-20230209194617-a36077c30491 sigs.k8s.io/controller-runtime v0.14.6 - sigs.k8s.io/gateway-api v0.7.0 + sigs.k8s.io/gateway-api v0.7.1 ) require ( diff --git a/acceptance/go.sum b/acceptance/go.sum index b3aaefe655..53b9400c42 100644 --- a/acceptance/go.sum +++ b/acceptance/go.sum @@ -1264,8 +1264,8 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/controller-runtime v0.14.6 h1:oxstGVvXGNnMvY7TAESYk+lzr6S3V5VFxQ6d92KcwQA= sigs.k8s.io/controller-runtime v0.14.6/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= -sigs.k8s.io/gateway-api v0.7.0 h1:/mG8yyJNBifqvuVLW5gwlI4CQs0NR/5q4BKUlf1bVdY= -sigs.k8s.io/gateway-api v0.7.0/go.mod h1:Xv0+ZMxX0lu1nSSDIIPEfbVztgNZ+3cfiYrJsa2Ooso= +sigs.k8s.io/gateway-api v0.7.1 h1:Tts2jeepVkPA5rVG/iO+S43s9n7Vp7jCDhZDQYtPigQ= +sigs.k8s.io/gateway-api v0.7.1/go.mod h1:Xv0+ZMxX0lu1nSSDIIPEfbVztgNZ+3cfiYrJsa2Ooso= 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/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= From c790951382235e9a5f10365d1b46fba16c5f2e5a Mon Sep 17 00:00:00 2001 From: chappie <6537530+chapmanc@users.noreply.github.com> Date: Tue, 18 Jul 2023 12:10:23 -0600 Subject: [PATCH 285/340] Update to handle validation endpoints (#2580) Changes proposed in this PR: - add in new validation call in endpoint How I've tested this PR: Ran it locally and tested the changes How I expect reviewers to test this PR: Read the code and run the command themselves to verify: ``` ./consul-k8s/acceptance/tests/cloud && go test -run TestBasicCloud -v -p 1 -timeout 20m \ -use-kind \ -kubecontext="kind-dc1" \ -consul-image hashicorppreview/consul-enterprise:1.17-dev -consul-k8s-image hashicorppreview/consul-k8s-control-plane:1.3.0-dev -consul-collector-image hashicorp/consul-telemetry-collector:0.0.1 \ -enable-enterprise ``` Checklist: - [X] Tests added - [n/a] [CHANGELOG entry added](https://github.com/hashicorp/consul-k8s/blob/main/CONTRIBUTING.md#adding-a-changelog-entry) --- acceptance/tests/cloud/basic_test.go | 59 ++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/acceptance/tests/cloud/basic_test.go b/acceptance/tests/cloud/basic_test.go index 7a0a31430a..0b5e3da719 100644 --- a/acceptance/tests/cloud/basic_test.go +++ b/acceptance/tests/cloud/basic_test.go @@ -129,7 +129,7 @@ func TestBasicCloud(t *testing.T) { localPort, 443, logger.TestLogger{}) - + defer tunnel.Close() // Retry creating the port forward since it can fail occasionally. retry.RunWith(&retry.Counter{Wait: 5 * time.Second, Count: 60}, t, func(r *retry.R) { // NOTE: It's okay to pass in `t` to ForwardPortE despite being in a retry @@ -144,7 +144,7 @@ func TestBasicCloud(t *testing.T) { logger.Log(t, "error finding consul token") return } - tunnel.Close() + logger.Log(t, "consul test token :"+consulToken) releaseName := helpers.RandomName() @@ -229,6 +229,57 @@ func TestBasicCloud(t *testing.T) { logger.Log(t, "creating static-server deployment") k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-server") - // time.Sleep(1 * time.Hour) - // TODO: add in test assertions here + t.Log("Finished deployment. Validating expected conditions now") + // Give some time for collector send metrics + time.Sleep(5 * time.Second) + err = validate(tunnel.Endpoint()) + logger.Log(t, fmt.Sprintf("result: %v", err)) + require.NoError(t, err) + +} + +func validate(endpoint string) error { + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + + client := &http.Client{Transport: tr} + url := fmt.Sprintf("https://%s/validation", endpoint) + req, err := http.NewRequest("GET", url, nil) + if err != nil { + fmt.Println("Error creating request:", err) + return errors.New("error creating validation request") + } + + // Perform the request + resp, err := client.Do(req) + if err != nil { + fmt.Println("Error sending request:", err) + return errors.New("error making validation request") + } + if resp.StatusCode == http.StatusExpectationFailed { + // Read the response body + body, err := io.ReadAll(resp.Body) + if err != nil { + fmt.Println("Error reading response:", err) + return errors.New("error reading body") + } + var message errMsg + err = json.Unmarshal(body, &message) + if err != nil { + fmt.Println("Error parsing response:", err) + return errors.New("error parsing body") + } + + return fmt.Errorf("Failed validation: %s", message) + } else if resp.StatusCode != http.StatusOK { + return errors.New("unexpected status code response from failure") + } + + return nil + +} + +type errMsg struct { + Error string `json:"error"` } From 07cc5cd57733f90c0ac7a1defe35ebef57dec437 Mon Sep 17 00:00:00 2001 From: Dan Stough Date: Wed, 19 Jul 2023 09:50:08 -0400 Subject: [PATCH 286/340] test(eks): fix deprecated CSI driver terraform (#2584) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes proposed in this PR: - Replacing the deprecated [`resolve_conflicts`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_addon#resolve_conflicts) with the new attributes. I don't know if we really need this setting since it is optional and the addon has no user-defined config, but I'm keeping this to keep the behavior consistent. How I've tested this PR: I did not. How I expect reviewers to test this PR: 👀 Checklist: - [ ] ~Tests added~ - [ ] ~[CHANGELOG entry added](https://github.com/hashicorp/consul-k8s/blob/main/CONTRIBUTING.md#adding-a-changelog-entry)~ --- charts/consul/test/terraform/eks/main.tf | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/charts/consul/test/terraform/eks/main.tf b/charts/consul/test/terraform/eks/main.tf index efbab0e833..3bc8b40451 100644 --- a/charts/consul/test/terraform/eks/main.tf +++ b/charts/consul/test/terraform/eks/main.tf @@ -124,12 +124,13 @@ resource "aws_iam_role_policy_attachment" "csi" { } resource "aws_eks_addon" "csi-driver" { - count = var.cluster_count - cluster_name = module.eks[count.index].cluster_id - addon_name = "aws-ebs-csi-driver" - addon_version = "v1.15.0-eksbuild.1" - service_account_role_arn = aws_iam_role.csi-driver-role[count.index].arn - resolve_conflicts = "OVERWRITE" + count = var.cluster_count + cluster_name = module.eks[count.index].cluster_id + addon_name = "aws-ebs-csi-driver" + addon_version = "v1.15.0-eksbuild.1" + service_account_role_arn = aws_iam_role.csi-driver-role[count.index].arn + resolve_conflicts_on_create = "OVERWRITE" + resolve_conflicts_on_update = "OVERWRITE" } data "aws_eks_cluster" "cluster" { From f0530d9a5ac9c0e8ee5de910975bd722a4ade2c0 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Wed, 19 Jul 2023 13:53:00 -0400 Subject: [PATCH 287/340] Add a check to prevent a nil-pointer dereference on Ingress LB (#2592) --- control-plane/catalog/to-consul/resource.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/control-plane/catalog/to-consul/resource.go b/control-plane/catalog/to-consul/resource.go index 08aeec8821..2d29d6c15a 100644 --- a/control-plane/catalog/to-consul/resource.go +++ b/control-plane/catalog/to-consul/resource.go @@ -938,7 +938,7 @@ func (t *serviceIngressResource) Upsert(key string, raw interface{}) error { continue } if t.SyncLoadBalancerIPs { - if ingress.Status.LoadBalancer.Ingress[0].IP == "" { + if len(ingress.Status.LoadBalancer.Ingress) > 0 && ingress.Status.LoadBalancer.Ingress[0].IP == "" { continue } hostName = ingress.Status.LoadBalancer.Ingress[0].IP From b3769b1cb19004112f83badd5028a3fce1030bb4 Mon Sep 17 00:00:00 2001 From: Dan Stough Date: Wed, 19 Jul 2023 14:13:27 -0400 Subject: [PATCH 288/340] test: remove unused workflow inputs (#2589) Changes proposed in this PR: - Removed unused workflow inputs. --- .github/workflows/merge.yml | 3 +-- .github/workflows/nightly-acceptance.yml | 3 +-- .github/workflows/nightly-api-gateway-conformance.yml | 1 - .github/workflows/nightly-cleanup.yml | 3 +-- .github/workflows/weekly-acceptance-0-49-x.yml | 3 +-- .github/workflows/weekly-acceptance-1-0-x.yml | 3 +-- .github/workflows/weekly-acceptance-1-1-x.yml | 3 +-- 7 files changed, 6 insertions(+), 13 deletions(-) diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml index 201df1dadd..e95af6cdcc 100644 --- a/.github/workflows/merge.yml +++ b/.github/workflows/merge.yml @@ -11,7 +11,6 @@ on: # these should be the only settings that you will ever need to change env: - CONSUL_IMAGE: hashicorppreview/consul-enterprise:1.17-dev # Consul's enterprise version to use in tests. We use this consul image on release branches too BRANCH: ${{ github.head_ref || github.ref_name }} CONTEXT: "merge" SHA: ${{ github.event.pull_request.head.sha || github.sha }} @@ -28,4 +27,4 @@ jobs: repo: hashicorp/consul-k8s-workflows ref: main token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} - inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ env.SHA }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}", "consul-image":"${{ env.CONSUL_IMAGE }}" }' + inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ env.SHA }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}" }' diff --git a/.github/workflows/nightly-acceptance.yml b/.github/workflows/nightly-acceptance.yml index 4d437b4990..6db7684bb8 100644 --- a/.github/workflows/nightly-acceptance.yml +++ b/.github/workflows/nightly-acceptance.yml @@ -8,7 +8,6 @@ on: # these should be the only settings that you will ever need to change env: - CONSUL_IMAGE: hashicorppreview/consul-enterprise:1.17-dev # Consul's enterprise version to use in tests BRANCH: ${{ github.ref_name }} CONTEXT: "nightly" @@ -24,4 +23,4 @@ jobs: repo: hashicorp/consul-k8s-workflows ref: main token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} - inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}", "consul-image":"${{ env.CONSUL_IMAGE }}" }' + inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}" }' diff --git a/.github/workflows/nightly-api-gateway-conformance.yml b/.github/workflows/nightly-api-gateway-conformance.yml index 5038ffdb93..abeec34659 100644 --- a/.github/workflows/nightly-api-gateway-conformance.yml +++ b/.github/workflows/nightly-api-gateway-conformance.yml @@ -9,7 +9,6 @@ on: # these should be the only settings that you will ever need to change env: - CONSUL_IMAGE: hashicorppreview/consul-enterprise:1.17-dev # Consul's enterprise version to use in tests BRANCH: ${{ github.ref_name }} CONTEXT: "nightly" diff --git a/.github/workflows/nightly-cleanup.yml b/.github/workflows/nightly-cleanup.yml index 4a304549df..83d6688ac5 100644 --- a/.github/workflows/nightly-cleanup.yml +++ b/.github/workflows/nightly-cleanup.yml @@ -8,7 +8,6 @@ on: # these should be the only settings that you will ever need to change env: - CONSUL_IMAGE: "not used" BRANCH: ${{ github.ref_name }} CONTEXT: "nightly" @@ -24,4 +23,4 @@ jobs: repo: hashicorp/consul-k8s-workflows ref: main token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} - inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}", "consul-image":"${{ env.CONSUL_IMAGE }}" }' + inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}" }' diff --git a/.github/workflows/weekly-acceptance-0-49-x.yml b/.github/workflows/weekly-acceptance-0-49-x.yml index adba13846a..5e1c17f3c7 100644 --- a/.github/workflows/weekly-acceptance-0-49-x.yml +++ b/.github/workflows/weekly-acceptance-0-49-x.yml @@ -10,7 +10,6 @@ on: # these should be the only settings that you will ever need to change env: - CONSUL_IMAGE: hashicorppreview/consul-enterprise:1.13-dev # Consul's enterprise version to use in tests BRANCH: "release/0.49.x" CONTEXT: "weekly" @@ -26,4 +25,4 @@ jobs: repo: hashicorp/consul-k8s-workflows ref: main token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} - inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}", "consul-image":"${{ env.CONSUL_IMAGE }}" }' + inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}" }' diff --git a/.github/workflows/weekly-acceptance-1-0-x.yml b/.github/workflows/weekly-acceptance-1-0-x.yml index 72769f0ca1..11dda52bed 100644 --- a/.github/workflows/weekly-acceptance-1-0-x.yml +++ b/.github/workflows/weekly-acceptance-1-0-x.yml @@ -11,7 +11,6 @@ on: # these should be the only settings that you will ever need to change env: - CONSUL_IMAGE: hashicorppreview/consul-enterprise:1.14-dev # Consul's enterprise version to use in tests BRANCH: "release/1.0.x" CONTEXT: "weekly" @@ -27,4 +26,4 @@ jobs: repo: hashicorp/consul-k8s-workflows ref: main token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} - inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}", "consul-image":"${{ env.CONSUL_IMAGE }}" }' + inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}" }' diff --git a/.github/workflows/weekly-acceptance-1-1-x.yml b/.github/workflows/weekly-acceptance-1-1-x.yml index b77da7eff0..86153587b0 100644 --- a/.github/workflows/weekly-acceptance-1-1-x.yml +++ b/.github/workflows/weekly-acceptance-1-1-x.yml @@ -11,7 +11,6 @@ on: # these should be the only settings that you will ever need to change env: - CONSUL_IMAGE: hashicorppreview/consul-enterprise:1.15-dev # Consul's enterprise version to use in tests BRANCH: "release/1.1.x" CONTEXT: "weekly" @@ -27,4 +26,4 @@ jobs: repo: hashicorp/consul-k8s-workflows ref: main token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} - inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}", "consul-image":"${{ env.CONSUL_IMAGE }}" }' + inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}" }' From 4d4c35a832fdebc87364c7ca895533ca2838b963 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Wed, 19 Jul 2023 22:57:49 -0400 Subject: [PATCH 289/340] chore: Update actions for security (#2601) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes proposed in this PR: - Update actions that are out of date How I've tested this PR: 👀 How I expect reviewers to test this PR: 👀 Checklist: - [ ] Tests added - [ ] [CHANGELOG entry added](https://github.com/hashicorp/consul-k8s/blob/main/CONTRIBUTING.md#adding-a-changelog-entry) --- .github/workflows/build.yml | 18 +++++++++--------- .github/workflows/changelog-checker.yml | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 917456e9d0..1d7942617d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,7 +21,7 @@ jobs: outputs: go-version: ${{ steps.get-go-version.outputs.go-version }} steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Determine Go version id: get-go-version # We use .go-version as our source of truth for current Go @@ -35,7 +35,7 @@ jobs: outputs: product-version: ${{ steps.get-product-version.outputs.product-version }} steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: get product version id: get-product-version run: | @@ -49,7 +49,7 @@ jobs: filepath: ${{ steps.generate-metadata-file.outputs.filepath }} steps: - name: "Checkout directory" - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Generate metadata file id: generate-metadata-file uses: hashicorp/actions-generate-metadata@v1 @@ -121,7 +121,7 @@ jobs: name: Go ${{ matrix.go }} ${{ matrix.goos }} ${{ matrix.goarch }} ${{ matrix.component }} ${{ matrix.fips }} build steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Setup go uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1 @@ -193,7 +193,7 @@ jobs: - name: Test rpm package if: ${{ matrix.goos == 'linux' && matrix.component == 'cli' && matrix.goarch == 'amd64'}} - uses: addnab/docker-run-action@v3 # TSCCR: no entry for repository "addnab/docker-run-action" + uses: addnab/docker-run-action@v3 # TSCCR: no entry for repository "addnab/docker-run-action" with: image: registry.access.redhat.com/ubi9/ubi:latest options: -v ${{ github.workspace }}:/work @@ -218,7 +218,7 @@ jobs: - name: Test debian package if: ${{ matrix.goos == 'linux' && matrix.component == 'cli' && matrix.goarch == 'amd64'}} - uses: addnab/docker-run-action@v3 # TSCCR: no entry for repository "addnab/docker-run-action" + uses: addnab/docker-run-action@v3 # TSCCR: no entry for repository "addnab/docker-run-action" with: image: ubuntu:latest options: -v ${{ github.workspace }}:/work @@ -258,7 +258,7 @@ jobs: repo: ${{ github.event.repository.name }} version: ${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }} steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: name: consul-cni_${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }}_${{ matrix.goos}}_${{ matrix.goarch }}.zip @@ -328,7 +328,7 @@ jobs: repo: ${{ github.event.repository.name }} version: ${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }} steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: name: consul-cni_${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }}_linux_${{ matrix.arch }}.zip @@ -391,7 +391,7 @@ jobs: repo: ${{ github.event.repository.name }} version: ${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }} steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: name: consul-cni_${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }}_linux_${{ matrix.arch }}.zip diff --git a/.github/workflows/changelog-checker.yml b/.github/workflows/changelog-checker.yml index 1eea4196e7..40c9b17c68 100644 --- a/.github/workflows/changelog-checker.yml +++ b/.github/workflows/changelog-checker.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 # by default the checkout action doesn't checkout all branches From a4d9487df5613afc518f2275ca541c3d297c7dca Mon Sep 17 00:00:00 2001 From: Michael Zalimeni Date: Thu, 20 Jul 2023 15:28:59 -0400 Subject: [PATCH 290/340] [NET-4122] Doc guidance for federation with externalServers (#2583) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add guidance for proper configuration when joining to a secondary cluster using WAN fed with external servers also enabled. Also clarify federation requirements and fix formatting for an unrelated value. Changes proposed in this PR: - Update base content for generating Helm chart docs to clarify the use case encountered in https://github.com/hashicorp/consul-k8s/issues/2138 - Minor additional fixes - _Follow-up: propagate generated doc changes to `consul` and additionally update https://developer.hashicorp.com/consul/docs/k8s/deployment-configurations/servers-outside-kubernetes there_ How I've tested this PR: N/A (docs only) How I expect reviewers to test this PR: 👀 Checklist: - [ ] Tests added - [ ] [CHANGELOG entry added](https://github.com/hashicorp/consul-k8s/blob/main/CONTRIBUTING.md#adding-a-changelog-entry) --- charts/consul/values.yaml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 12c45ac958..81065d4bb2 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -535,8 +535,9 @@ global: # If enabled, this datacenter will be federation-capable. Only federation # via mesh gateways is supported. # Mesh gateways and servers will be configured to allow federation. - # Requires `global.tls.enabled`, `meshGateway.enabled` and `connectInject.enabled` - # to be true. Requires Consul 1.8+. + # Requires `global.tls.enabled`, `connectInject.enabled`, and one of + # `meshGateway.enabled` or `externalServers.enabled` to be true. + # Requires Consul 1.8+. enabled: false # If true, the chart will create a Kubernetes secret that can be imported @@ -552,8 +553,8 @@ global: # @type: string primaryDatacenter: null - # A list of addresses of the primary mesh gateways in the form `:`. - # (e.g. ["1.1.1.1:443", "2.3.4.5:443"] + # A list of addresses of the primary mesh gateways in the form `:` + # (e.g. `["1.1.1.1:443", "2.3.4.5:443"]`). # @type: array primaryGateways: [] @@ -564,6 +565,9 @@ global: # from the one used by the Consul Service Mesh. # Please refer to the [Kubernetes Auth Method documentation](https://developer.hashicorp.com/consul/docs/security/acl/auth-methods/kubernetes). # + # If `externalServers.enabled` is set to true, `global.federation.k8sAuthMethodHost` and + # `externalServers.k8sAuthMethodHost` should be set to the same value. + # # You can retrieve this value from your `kubeconfig` by running: # # ```shell-session @@ -1339,6 +1343,9 @@ externalServers: # This address must be reachable from the Consul servers. # Please refer to the [Kubernetes Auth Method documentation](https://developer.hashicorp.com/consul/docs/security/acl/auth-methods/kubernetes). # + # If `global.federation.enabled` is set to true, `global.federation.k8sAuthMethodHost` and + # `externalServers.k8sAuthMethodHost` should be set to the same value. + # # You could retrieve this value from your `kubeconfig` by running: # # ```shell-session From 414554c056d5b66131bc0f6a58d6c7ef1b3d29ce Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Thu, 20 Jul 2023 16:53:47 -0400 Subject: [PATCH 291/340] Handle errors properly when services are de-registered from the catalog (#2571) - In the past, kubernetes nodes were used as the source of truth to determine the list of services that should exist in Consul. - In most cases this was ok but becomes a problem when nodes are quickly deleted from kubernetes such as the case when using spot instances. - Instead, use consul synthetic-nodes to get the list of services and deregister the services that do not have endpoint addresses. --------- Co-authored-by: mr-miles --- .changelog/2571.txt | 3 + .../endpoints/endpoints_controller.go | 62 ++++++---- .../endpoints/endpoints_controller_test.go | 117 ++++++++++++++++++ 3 files changed, 159 insertions(+), 23 deletions(-) create mode 100644 .changelog/2571.txt diff --git a/.changelog/2571.txt b/.changelog/2571.txt new file mode 100644 index 0000000000..91b3f2943b --- /dev/null +++ b/.changelog/2571.txt @@ -0,0 +1,3 @@ +```release-note:bug +control-plane: fix bug in endpoints controller when deregistering services from consul when a node is deleted. +``` diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go index 7b236792ab..cdf56b187f 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go @@ -159,7 +159,6 @@ func (r *Controller) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu } err = r.Client.Get(ctx, req.NamespacedName, &serviceEndpoints) - // endpointPods holds a set of all pods this endpoints object is currently pointing to. // We use this later when we reconcile ACL tokens to decide whether an ACL token in Consul // is for a pod that no longer exists. @@ -183,7 +182,7 @@ func (r *Controller) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu // It is possible that the endpoints object has never been registered, in which case deregistration is a no-op. if isLabeledIgnore(serviceEndpoints.Labels) { // We always deregister the service to handle the case where a user has registered the service, then added the label later. - r.Log.Info("Ignoring endpoint labeled with `consul.hashicorp.com/service-ignore: \"true\"`", "name", req.Name, "namespace", req.Namespace) + r.Log.Info("ignoring endpoint labeled with `consul.hashicorp.com/service-ignore: \"true\"`", "name", req.Name, "namespace", req.Namespace) err = r.deregisterService(apiClient, req.Name, req.Namespace, nil) return ctrl.Result{}, err } @@ -915,14 +914,14 @@ func getHealthCheckStatusReason(healthCheckStatus, podName, podNamespace string) // them only if they are not in endpointsAddressesMap. If the map is nil, it will deregister all instances. If the map // has addresses, it will only deregister instances not in the map. func (r *Controller) deregisterService(apiClient *api.Client, k8sSvcName, k8sSvcNamespace string, endpointsAddressesMap map[string]bool) error { - // Get services matching metadata. - nodesWithSvcs, err := r.serviceInstancesForK8sNodes(apiClient, k8sSvcName, k8sSvcNamespace) + // Get services matching metadata from Consul + nodesWithSvcs, err := r.serviceInstancesForNodes(apiClient, k8sSvcName, k8sSvcNamespace) if err != nil { r.Log.Error(err, "failed to get service instances", "name", k8sSvcName) return err } - // Deregister each service instance that matches the metadata. + var errs error for _, nodeSvcs := range nodesWithSvcs { for _, svc := range nodeSvcs.Services { // We need to get services matching "k8s-service-name" and "k8s-namespace" metadata. @@ -933,42 +932,48 @@ func (r *Controller) deregisterService(apiClient *api.Client, k8sSvcName, k8sSvc if _, ok := endpointsAddressesMap[svc.Address]; !ok { // If the service address is not in the Endpoints addresses, deregister it. r.Log.Info("deregistering service from consul", "svc", svc.ID) - _, err = apiClient.Catalog().Deregister(&api.CatalogDeregistration{ + _, err := apiClient.Catalog().Deregister(&api.CatalogDeregistration{ Node: nodeSvcs.Node.Node, ServiceID: svc.ID, Namespace: svc.Namespace, }, nil) if err != nil { + // Do not exit right away as there might be other services that need to be deregistered. r.Log.Error(err, "failed to deregister service instance", "id", svc.ID) - return err + errs = multierror.Append(errs, err) + } else { + serviceDeregistered = true } - serviceDeregistered = true } } else { r.Log.Info("deregistering service from consul", "svc", svc.ID) - if _, err = apiClient.Catalog().Deregister(&api.CatalogDeregistration{ + _, err := apiClient.Catalog().Deregister(&api.CatalogDeregistration{ Node: nodeSvcs.Node.Node, ServiceID: svc.ID, Namespace: svc.Namespace, - }, nil); err != nil { + }, nil) + if err != nil { + // Do not exit right away as there might be other services that need to be deregistered. r.Log.Error(err, "failed to deregister service instance", "id", svc.ID) - return err + errs = multierror.Append(errs, err) + } else { + serviceDeregistered = true } - serviceDeregistered = true } if r.AuthMethod != "" && serviceDeregistered { r.Log.Info("reconciling ACL tokens for service", "svc", svc.Service) - err = r.deleteACLTokensForServiceInstance(apiClient, svc, k8sSvcNamespace, svc.Meta[constants.MetaKeyPodName]) + err := r.deleteACLTokensForServiceInstance(apiClient, svc, k8sSvcNamespace, svc.Meta[constants.MetaKeyPodName]) if err != nil { r.Log.Error(err, "failed to reconcile ACL tokens for service", "svc", svc.Service) - return err + errs = multierror.Append(errs, err) } } } } - return nil + return errs + } // deleteACLTokensForServiceInstance finds the ACL tokens that belongs to the service instance and deletes it from Consul. @@ -1088,21 +1093,32 @@ func getTokenMetaFromDescription(description string) (map[string]string, error) return tokenMeta, nil } -func (r *Controller) serviceInstancesForK8sNodes(apiClient *api.Client, k8sServiceName, k8sServiceNamespace string) ([]*api.CatalogNodeServiceList, error) { +func (r *Controller) serviceInstancesForNodes(apiClient *api.Client, k8sServiceName, k8sServiceNamespace string) ([]*api.CatalogNodeServiceList, error) { var serviceList []*api.CatalogNodeServiceList - // Get a list of k8s nodes. - var nodeList corev1.NodeList - err := r.Client.List(r.Context, &nodeList) + + // The nodelist may have changed between this point and when the event was raised + // For example, if a pod is evicted because a node has been deleted, there is no guarantee that that node will show up here + // query consul catalog for a list of nodes supporting this service + // quite a lot of results as synthetic nodes are never deregistered. + var nodes []*api.Node + filter := fmt.Sprintf(`Meta[%q] == %q `, "synthetic-node", "true") + nodes, _, err := apiClient.Catalog().Nodes(&api.QueryOptions{Filter: filter, Namespace: namespaces.WildcardNamespace}) if err != nil { return nil, err } - for _, node := range nodeList.Items { + + var errs error + for _, node := range nodes { var nodeServices *api.CatalogNodeServiceList - nodeServices, err = r.serviceInstancesForK8SServiceNameAndNamespace(apiClient, k8sServiceName, k8sServiceNamespace, common.ConsulNodeNameFromK8sNode(node.Name)) - serviceList = append(serviceList, nodeServices) + nodeServices, err := r.serviceInstancesForK8SServiceNameAndNamespace(apiClient, k8sServiceName, k8sServiceNamespace, node.Node) + if err != nil { + errs = multierror.Append(errs, err) + } else { + serviceList = append(serviceList, nodeServices) + } } - return serviceList, err + return serviceList, errs } // serviceInstancesForK8SServiceNameAndNamespace calls Consul's ServicesWithFilter to get the list diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go index ea1ce686d6..477be49e9f 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go @@ -893,6 +893,9 @@ func TestReconcileCreateEndpoint_MultiportService(t *testing.T) { catalogRegistration := &api.CatalogRegistration{ Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: svc, } _, err := consulClient.Catalog().Register(catalogRegistration, nil) @@ -2339,6 +2342,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-service-updated", Service: "service-updated", @@ -2358,6 +2364,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod1-service-updated-sidecar-proxy", @@ -2444,6 +2453,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-service-updated", Service: "service-updated", @@ -2463,6 +2475,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: "127.0.0.1", + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod1-service-updated-sidecar-proxy", @@ -2549,6 +2564,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-service-updated", Service: "service-updated", @@ -2566,6 +2584,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod1-service-updated-sidecar-proxy", @@ -2631,6 +2652,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-different-consul-svc-name", Service: "different-consul-svc-name", @@ -2648,6 +2672,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod1-different-consul-svc-name-sidecar-proxy", @@ -2721,6 +2748,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-service-updated", Service: "service-updated", @@ -2732,6 +2762,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod1-service-updated-sidecar-proxy", @@ -2835,6 +2868,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-service-updated", Service: "service-updated", @@ -2846,6 +2882,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod1-service-updated-sidecar-proxy", @@ -2862,6 +2901,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod2-service-updated", Service: "service-updated", @@ -2873,6 +2915,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod2-service-updated-sidecar-proxy", @@ -2932,6 +2977,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-different-consul-svc-name", Service: "different-consul-svc-name", @@ -2943,6 +2991,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod1-different-consul-svc-name-sidecar-proxy", @@ -2959,6 +3010,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod2-different-consul-svc-name", Service: "different-consul-svc-name", @@ -2970,6 +3024,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod2-different-consul-svc-name-sidecar-proxy", @@ -3015,6 +3072,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-service-updated", Service: "service-updated", @@ -3026,6 +3086,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod1-service-updated-sidecar-proxy", @@ -3042,6 +3105,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod2-service-updated", Service: "service-updated", @@ -3053,6 +3119,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod2-service-updated-sidecar-proxy", @@ -3088,6 +3157,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-different-consul-svc-name", Service: "different-consul-svc-name", @@ -3099,6 +3171,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod1-different-consul-svc-name-sidecar-proxy", @@ -3115,6 +3190,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod2-different-consul-svc-name", Service: "different-consul-svc-name", @@ -3126,6 +3204,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod2-different-consul-svc-name-sidecar-proxy", @@ -3174,6 +3255,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-service-updated", Service: "service-updated", @@ -3191,6 +3275,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod1-service-updated-sidecar-proxy", @@ -3270,6 +3357,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-service-updated", Service: "service-updated", @@ -3287,6 +3377,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod1-service-updated-sidecar-proxy", @@ -3309,6 +3402,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod2-service-updated", Service: "service-updated", @@ -3326,6 +3422,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod2-service-updated-sidecar-proxy", @@ -3409,6 +3508,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-service-updated", Service: "service-updated", @@ -3426,6 +3528,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod1-service-updated-sidecar-proxy", @@ -4116,6 +4221,9 @@ func TestReconcileDeleteEndpoint(t *testing.T) { serviceRegistration := &api.CatalogRegistration{ Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: svc, } _, err := consulClient.Catalog().Register(serviceRegistration, nil) @@ -4262,6 +4370,9 @@ func TestReconcileIgnoresServiceIgnoreLabel(t *testing.T) { serviceRegistration := &api.CatalogRegistration{ Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-" + svcName, Service: svcName, @@ -4385,6 +4496,9 @@ func TestReconcile_podSpecifiesExplicitService(t *testing.T) { _, err := consulClient.Catalog().Register(&api.CatalogRegistration{ Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-" + svcName, Service: svcName, @@ -4542,6 +4656,9 @@ func TestServiceInstancesForK8SServiceNameAndNamespace(t *testing.T) { catalogRegistration := &api.CatalogRegistration{ Node: consulNodeName, Address: "127.0.0.1", + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: svc, } _, err = consulClient.Catalog().Register(catalogRegistration, nil) From ff24495eb60406e863a69bc6910d16a671f6873e Mon Sep 17 00:00:00 2001 From: Sujata Roy <61177855+20sr20@users.noreply.github.com> Date: Thu, 20 Jul 2023 16:05:51 -0700 Subject: [PATCH 292/340] Adding support for Enterprise and other improvement on the Customizing Vault Version for WanFed Test (#2481) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adding support for Enterprise and other improvement on the Customizing Vault Version for WanFed Test This is the extension of the PR - https://github.com/hashicorp/consul-k8s/pull/2043 In this PR, the followings were addressed - 1. Now the vault enterprise version can be provided in the cli command. The previous PR only addressed Vault OSS. 2. Two flags “-no-cleanup-wan-fed” and “test-duration” were introduced to not to cleanup the test environment after successful setup to give it time to do manual testing for features/to reproduce customer issues. Default is 1 hour. 3. This was tested in Kind environment and it works fine. The following was taken out to use the “use-kind” option for WanFed test. //if cfg.UseKind { // t.Skipf("Skipping this test because it's currently flaky on kind") //} * Fix indentation * Fix unit test for deleting gateway w/ consul services * Remove redundant service deregistration code * Exit loop early once registration is found for service * Fix import blocking * Set status on pods added to test * Apply suggestions from code review * Reduce count of test gateways to 10 from 100 --------- Co-authored-by: Nathan Coleman Co-authored-by: Sarah Alsmiller Changes proposed in this PR: - - How I've tested this PR: How I expect reviewers to test this PR: Checklist: - [ ] Tests added - [ ] CHANGELOG entry added > HashiCorp engineers only, community PRs should not add a changelog entry. > Entries should use present tense (e.g. Add support for...) * Removing the changes in vault_namespaces_test.go * Introducing new flag no-cleanup * Removed "go 1.20" from go.work file * cfg.USEKind check is added back * Removed previousy added "Test Duration" flag * Some changes * Some changes --- acceptance/framework/config/config.go | 1 + .../framework/connhelper/connect_helper.go | 8 +- acceptance/framework/consul/cli_cluster.go | 4 +- acceptance/framework/consul/helm_cluster.go | 10 +- acceptance/framework/flags/flags.go | 6 + acceptance/framework/helpers/helpers.go | 4 +- acceptance/framework/k8s/deploy.go | 8 +- acceptance/framework/vault/vault_cluster.go | 31 +- .../api_gateway_external_servers_test.go | 6 +- .../api_gateway_gatewayclassconfig_test.go | 31 +- .../api-gateway/api_gateway_lifecycle_test.go | 12 +- .../api-gateway/api_gateway_tenancy_test.go | 6 +- .../tests/api-gateway/api_gateway_test.go | 15 +- acceptance/tests/cloud/basic_test.go | 5 +- acceptance/tests/cloud/remote_dev_test.go | 4 +- .../config_entries_namespaces_test.go | 2 +- .../config-entries/config_entries_test.go | 2 +- .../connect/connect_external_servers_test.go | 6 +- .../connect/connect_inject_namespaces_test.go | 14 +- .../tests/connect/connect_inject_test.go | 8 +- .../tests/connect/permissive_mtls_test.go | 4 +- .../tests/consul-dns/consul_dns_test.go | 2 +- acceptance/tests/example/example_test.go | 2 +- .../ingress_gateway_namespaces_test.go | 12 +- .../ingress-gateway/ingress_gateway_test.go | 4 +- acceptance/tests/metrics/metrics_test.go | 6 +- .../partitions/partitions_connect_test.go | 48 +-- .../partitions/partitions_gateway_test.go | 24 +- .../tests/partitions/partitions_sync_test.go | 8 +- .../peering_connect_namespaces_test.go | 28 +- .../tests/peering/peering_connect_test.go | 24 +- .../tests/peering/peering_gateway_test.go | 28 +- .../sync/sync_catalog_namespaces_test.go | 4 +- acceptance/tests/sync/sync_catalog_test.go | 6 +- .../terminating_gateway_destinations_test.go | 4 +- .../terminating_gateway_namespaces_test.go | 14 +- .../terminating_gateway_test.go | 4 +- .../tests/vault/vault_namespaces_test.go | 8 +- acceptance/tests/vault/vault_test.go | 8 +- .../tests/vault/vault_tls_auto_reload_test.go | 8 +- acceptance/tests/vault/vault_wan_fed_test.go | 10 +- .../wan_federation_gateway_test.go | 18 +- .../wan-federation/wan_federation_test.go | 6 +- go.work | 11 + go.work.sum | 352 ++++++++++++++++++ 45 files changed, 626 insertions(+), 200 deletions(-) create mode 100644 go.work create mode 100644 go.work.sum diff --git a/acceptance/framework/config/config.go b/acceptance/framework/config/config.go index eada42af20..74bb7daf6e 100644 --- a/acceptance/framework/config/config.go +++ b/acceptance/framework/config/config.go @@ -94,6 +94,7 @@ type TestConfig struct { VaultServerVersion string NoCleanupOnFailure bool + NoCleanup bool DebugDirectory string UseAKS bool diff --git a/acceptance/framework/connhelper/connect_helper.go b/acceptance/framework/connhelper/connect_helper.go index 670307da88..c34f663563 100644 --- a/acceptance/framework/connhelper/connect_helper.go +++ b/acceptance/framework/connhelper/connect_helper.go @@ -108,11 +108,11 @@ func (c *ConnectHelper) DeployClientAndServer(t *testing.T) { logger.Log(t, "creating static-server and static-client deployments") - k8s.DeployKustomize(t, c.Ctx.KubectlOptions(t), c.Cfg.NoCleanupOnFailure, c.Cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, c.Ctx.KubectlOptions(t), c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, c.Cfg.DebugDirectory, "../fixtures/cases/static-server-inject") if c.Cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, c.Ctx.KubectlOptions(t), c.Cfg.NoCleanupOnFailure, c.Cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, c.Ctx.KubectlOptions(t), c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, c.Cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { - k8s.DeployKustomize(t, c.Ctx.KubectlOptions(t), c.Cfg.NoCleanupOnFailure, c.Cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + k8s.DeployKustomize(t, c.Ctx.KubectlOptions(t), c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, c.Cfg.DebugDirectory, "../fixtures/cases/static-client-inject") } // Check that both static-server and static-client have been injected and @@ -140,7 +140,7 @@ func (c *ConnectHelper) CreateResolverRedirect(t *testing.T) { kustomizeDir := "../fixtures/cases/resolver-redirect-virtualip" k8s.KubectlApplyK(t, options, kustomizeDir) - helpers.Cleanup(t, c.Cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, options, kustomizeDir) }) } diff --git a/acceptance/framework/consul/cli_cluster.go b/acceptance/framework/consul/cli_cluster.go index 9e119af76d..a45bcc665c 100644 --- a/acceptance/framework/consul/cli_cluster.go +++ b/acceptance/framework/consul/cli_cluster.go @@ -45,6 +45,7 @@ type CLICluster struct { kubeConfig string kubeContext string noCleanupOnFailure bool + noCleanup bool debugDirectory string logger terratestLogger.TestLogger cli cli.CLI @@ -109,6 +110,7 @@ func NewCLICluster( kubeConfig: cfg.GetPrimaryKubeEnv().KubeConfig, kubeContext: cfg.GetPrimaryKubeEnv().KubeContext, noCleanupOnFailure: cfg.NoCleanupOnFailure, + noCleanup: cfg.NoCleanup, debugDirectory: cfg.DebugDirectory, logger: logger, cli: *cli, @@ -122,7 +124,7 @@ func (c *CLICluster) Create(t *testing.T) { // Make sure we delete the cluster if we receive an interrupt signal and // register cleanup so that we delete the cluster when test finishes. - helpers.Cleanup(t, c.noCleanupOnFailure, func() { + helpers.Cleanup(t, c.noCleanupOnFailure, c.noCleanup, func() { c.Destroy(t) }) diff --git a/acceptance/framework/consul/helm_cluster.go b/acceptance/framework/consul/helm_cluster.go index aa6fdef7d8..81787ebfc3 100644 --- a/acceptance/framework/consul/helm_cluster.go +++ b/acceptance/framework/consul/helm_cluster.go @@ -52,6 +52,7 @@ type HelmCluster struct { runtimeClient client.Client kubernetesClient kubernetes.Interface noCleanupOnFailure bool + noCleanup bool debugDirectory string logger terratestLogger.TestLogger } @@ -107,6 +108,7 @@ func NewHelmCluster( runtimeClient: ctx.ControllerRuntimeClient(t), kubernetesClient: ctx.KubernetesClient(t), noCleanupOnFailure: cfg.NoCleanupOnFailure, + noCleanup: cfg.NoCleanup, debugDirectory: cfg.DebugDirectory, logger: logger, } @@ -117,7 +119,7 @@ func (h *HelmCluster) Create(t *testing.T) { // Make sure we delete the cluster if we receive an interrupt signal and // register cleanup so that we delete the cluster when test finishes. - helpers.Cleanup(t, h.noCleanupOnFailure, func() { + helpers.Cleanup(t, h.noCleanupOnFailure, h.noCleanup, func() { h.Destroy(t) }) @@ -508,7 +510,7 @@ func configurePodSecurityPolicies(t *testing.T, client kubernetes.Interface, cfg } } - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { _ = client.PolicyV1beta1().PodSecurityPolicies().Delete(context.Background(), pspName, metav1.DeleteOptions{}) _ = client.RbacV1().ClusterRoles().Delete(context.Background(), pspName, metav1.DeleteOptions{}) _ = client.RbacV1().RoleBindings(namespace).Delete(context.Background(), pspName, metav1.DeleteOptions{}) @@ -559,7 +561,7 @@ func configureSCCs(t *testing.T, client kubernetes.Interface, cfg *config.TestCo } } - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { _ = client.RbacV1().RoleBindings(namespace).Delete(context.Background(), anyuidRoleBinding, metav1.DeleteOptions{}) _ = client.RbacV1().RoleBindings(namespace).Delete(context.Background(), privilegedRoleBinding, metav1.DeleteOptions{}) }) @@ -601,7 +603,7 @@ func CreateK8sSecret(t *testing.T, client kubernetes.Interface, cfg *config.Test } }) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { _ = client.CoreV1().Secrets(namespace).Delete(context.Background(), secretName, metav1.DeleteOptions{}) }) } diff --git a/acceptance/framework/flags/flags.go b/acceptance/framework/flags/flags.go index ad36099b37..b1ad7e4332 100644 --- a/acceptance/framework/flags/flags.go +++ b/acceptance/framework/flags/flags.go @@ -45,6 +45,7 @@ type TestFlags struct { flagHCPResourceID string flagNoCleanupOnFailure bool + flagNoCleanup bool flagDebugDirectory string @@ -124,6 +125,9 @@ func (t *TestFlags) init() { "If true, the tests will not cleanup Kubernetes resources they create when they finish running."+ "Note this flag must be run with -failfast flag, otherwise subsequent tests will fail.") + flag.BoolVar(&t.flagNoCleanup, "no-cleanup", false, + "If true, the tests will not cleanup Kubernetes resources for Vault test") + flag.StringVar(&t.flagDebugDirectory, "debug-directory", "", "The directory where to write debug information about failed test runs, "+ "such as logs and pod definitions. If not provided, a temporary directory will be created by the tests.") @@ -185,6 +189,7 @@ func (t *TestFlags) TestConfigFromFlags() *config.TestConfig { kubeEnvs := config.NewKubeTestConfigList(t.flagKubeconfigs, t.flagKubecontexts, t.flagKubeNamespaces) c := &config.TestConfig{ + EnableEnterprise: t.flagEnableEnterprise, EnterpriseLicense: t.flagEnterpriseLicense, @@ -215,6 +220,7 @@ func (t *TestFlags) TestConfigFromFlags() *config.TestConfig { HCPResourceID: t.flagHCPResourceID, NoCleanupOnFailure: t.flagNoCleanupOnFailure, + NoCleanup: t.flagNoCleanup, DebugDirectory: tempDir, UseAKS: t.flagUseAKS, UseEKS: t.flagUseEKS, diff --git a/acceptance/framework/helpers/helpers.go b/acceptance/framework/helpers/helpers.go index 3e6ef039b2..f8b1d2f15d 100644 --- a/acceptance/framework/helpers/helpers.go +++ b/acceptance/framework/helpers/helpers.go @@ -87,7 +87,7 @@ func SetupInterruptHandler(cleanup func()) { // Cleanup will both register a cleanup function with t // and SetupInterruptHandler to make sure resources get cleaned up // if an interrupt signal is caught. -func Cleanup(t *testing.T, noCleanupOnFailure bool, cleanup func()) { +func Cleanup(t *testing.T, noCleanupOnFailure bool, noCleanup bool, cleanup func()) { t.Helper() // Always clean up when an interrupt signal is caught. @@ -97,7 +97,7 @@ func Cleanup(t *testing.T, noCleanupOnFailure bool, cleanup func()) { // We need to wrap the cleanup function because t that is passed in to this function // might not have the information on whether the test has failed yet. wrappedCleanupFunc := func() { - if !(noCleanupOnFailure && t.Failed()) { + if !((noCleanupOnFailure && t.Failed()) || noCleanup) { logger.Logf(t, "cleaning up resources for %s", t.Name()) cleanup() } else { diff --git a/acceptance/framework/k8s/deploy.go b/acceptance/framework/k8s/deploy.go index 6834284c33..828586c8cf 100644 --- a/acceptance/framework/k8s/deploy.go +++ b/acceptance/framework/k8s/deploy.go @@ -21,7 +21,7 @@ import ( // Deploy creates a Kubernetes deployment by applying configuration stored at filepath, // sets up a cleanup function and waits for the deployment to become available. -func Deploy(t *testing.T, options *k8s.KubectlOptions, noCleanupOnFailure bool, debugDirectory string, filepath string) { +func Deploy(t *testing.T, options *k8s.KubectlOptions, noCleanupOnFailure bool, noCleanup bool, debugDirectory string, filepath string) { t.Helper() KubectlApply(t, options, filepath) @@ -33,7 +33,7 @@ func Deploy(t *testing.T, options *k8s.KubectlOptions, noCleanupOnFailure bool, err = yaml.NewYAMLOrJSONDecoder(file, 1024).Decode(&deployment) require.NoError(t, err) - helpers.Cleanup(t, noCleanupOnFailure, func() { + helpers.Cleanup(t, noCleanupOnFailure, noCleanup, func() { // Note: this delete command won't wait for pods to be fully terminated. // This shouldn't cause any test pollution because the underlying // objects are deployments, and so when other tests create these @@ -47,7 +47,7 @@ func Deploy(t *testing.T, options *k8s.KubectlOptions, noCleanupOnFailure bool, // DeployKustomize creates a Kubernetes deployment by applying the kustomize directory stored at kustomizeDir, // sets up a cleanup function and waits for the deployment to become available. -func DeployKustomize(t *testing.T, options *k8s.KubectlOptions, noCleanupOnFailure bool, debugDirectory string, kustomizeDir string) { +func DeployKustomize(t *testing.T, options *k8s.KubectlOptions, noCleanupOnFailure bool, noCleanup bool, debugDirectory string, kustomizeDir string) { t.Helper() KubectlApplyK(t, options, kustomizeDir) @@ -59,7 +59,7 @@ func DeployKustomize(t *testing.T, options *k8s.KubectlOptions, noCleanupOnFailu err = yaml.NewYAMLOrJSONDecoder(strings.NewReader(output), 1024).Decode(&deployment) require.NoError(t, err) - helpers.Cleanup(t, noCleanupOnFailure, func() { + helpers.Cleanup(t, noCleanupOnFailure, noCleanup, func() { // Note: this delete command won't wait for pods to be fully terminated. // This shouldn't cause any test pollution because the underlying // objects are deployments, and so when other tests create these diff --git a/acceptance/framework/vault/vault_cluster.go b/acceptance/framework/vault/vault_cluster.go index e0030490c9..4dc832bcb6 100644 --- a/acceptance/framework/vault/vault_cluster.go +++ b/acceptance/framework/vault/vault_cluster.go @@ -6,6 +6,8 @@ package vault import ( "context" "fmt" + "os" + "strings" "testing" "time" @@ -13,6 +15,7 @@ import ( terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" terratestLogger "github.com/gruntwork-io/terratest/modules/logger" "github.com/hashicorp/consul-k8s/acceptance/framework/config" + "github.com/hashicorp/consul-k8s/acceptance/framework/consul" "github.com/hashicorp/consul-k8s/acceptance/framework/environment" "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" @@ -44,6 +47,7 @@ type VaultCluster struct { kubernetesClient kubernetes.Interface noCleanupOnFailure bool + noCleanup bool debugDirectory string logger terratestLogger.TestLogger } @@ -54,12 +58,32 @@ func NewVaultCluster(t *testing.T, ctx environment.TestContext, cfg *config.Test logger := terratestLogger.New(logger.TestLogger{}) kopts := ctx.KubectlOptions(t) + ns := ctx.KubectlOptions(t).Namespace + + entstr := "-ent" values := defaultHelmValues(releaseName) if cfg.EnablePodSecurityPolicies { values["global.psp.enable"] = "true" } + vaultReleaseName := helpers.RandomName() + k8sClient := environment.KubernetesClientFromOptions(t, ctx.KubectlOptions(t)) + vaultLicenseSecretName := fmt.Sprintf("%s-enterprise-license", vaultReleaseName) + vaultLicenseSecretKey := "license" + + vaultEnterpriseLicense := os.Getenv("VAULT_LICENSE") + if cfg.VaultServerVersion != "" { + + if strings.Contains(cfg.VaultServerVersion, entstr) { + + logger.Logf(t, "Creating secret for Vault license") + consul.CreateK8sSecret(t, k8sClient, cfg, ns, vaultLicenseSecretName, vaultLicenseSecretKey, vaultEnterpriseLicense) + + values["server.image.repository"] = "docker.mirror.hashicorp.services/hashicorp/vault-enterprise" + values["server.enterpriseLicense.secretName"] = vaultLicenseSecretName + values["server.enterpriseLicense.secretKey"] = vaultLicenseSecretKey + } values["server.image.tag"] = cfg.VaultServerVersion } vaultHelmChartVersion := defaultVaultHelmChartVersion @@ -89,6 +113,7 @@ func NewVaultCluster(t *testing.T, ctx environment.TestContext, cfg *config.Test kubectlOptions: kopts, kubernetesClient: ctx.KubernetesClient(t), noCleanupOnFailure: cfg.NoCleanupOnFailure, + noCleanup: cfg.NoCleanup, debugDirectory: cfg.DebugDirectory, logger: logger, releaseName: releaseName, @@ -224,7 +249,7 @@ func (v *VaultCluster) Create(t *testing.T, ctx environment.TestContext, vaultNa // Make sure we delete the cluster if we receive an interrupt signal and // register cleanup so that we delete the cluster when test finishes. - helpers.Cleanup(t, v.noCleanupOnFailure, func() { + helpers.Cleanup(t, v.noCleanupOnFailure, v.noCleanup, func() { v.Destroy(t) }) @@ -346,7 +371,7 @@ func (v *VaultCluster) createTLSCerts(t *testing.T) { require.NoError(t, err) t.Cleanup(func() { - if !v.noCleanupOnFailure { + if !(v.noCleanupOnFailure || v.noCleanup) { // We're ignoring error here because secret deletion is best-effort. _ = v.kubernetesClient.CoreV1().Secrets(namespace).Delete(context.Background(), certSecretName(v.releaseName), metav1.DeleteOptions{}) _ = v.kubernetesClient.CoreV1().Secrets(namespace).Delete(context.Background(), CASecretName(v.releaseName), metav1.DeleteOptions{}) @@ -419,7 +444,7 @@ func (v *VaultCluster) initAndUnseal(t *testing.T) { rootTokenSecret := fmt.Sprintf("%s-vault-root-token", v.releaseName) v.logger.Logf(t, "saving Vault root token to %q Kubernetes secret", rootTokenSecret) - helpers.Cleanup(t, v.noCleanupOnFailure, func() { + helpers.Cleanup(t, v.noCleanupOnFailure, v.noCleanup, func() { _ = v.kubernetesClient.CoreV1().Secrets(namespace).Delete(context.Background(), rootTokenSecret, metav1.DeleteOptions{}) }) _, err := v.kubernetesClient.CoreV1().Secrets(namespace).Create(context.Background(), &corev1.Secret{ diff --git a/acceptance/tests/api-gateway/api_gateway_external_servers_test.go b/acceptance/tests/api-gateway/api_gateway_external_servers_test.go index c0fa8bbcca..d14ef59990 100644 --- a/acceptance/tests/api-gateway/api_gateway_external_servers_test.go +++ b/acceptance/tests/api-gateway/api_gateway_external_servers_test.go @@ -60,8 +60,8 @@ func TestAPIGateway_ExternalServers(t *testing.T) { consulCluster.Create(t) logger.Log(t, "creating static-server and static-client deployments") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") // Override the default proxy config settings for this test consulClient, _ := consulCluster.SetupConsulClient(t, true, serverReleaseName) @@ -79,7 +79,7 @@ func TestAPIGateway_ExternalServers(t *testing.T) { logger.Log(t, "creating api-gateway resources") out, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "apply", "-k", "../fixtures/bases/api-gateway") require.NoError(t, err, out) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { // Ignore errors here because if the test ran as expected // the custom resources will have been deleted. k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-k", "../fixtures/bases/api-gateway") diff --git a/acceptance/tests/api-gateway/api_gateway_gatewayclassconfig_test.go b/acceptance/tests/api-gateway/api_gateway_gatewayclassconfig_test.go index 444af6af4d..ffe0424ef1 100644 --- a/acceptance/tests/api-gateway/api_gateway_gatewayclassconfig_test.go +++ b/acceptance/tests/api-gateway/api_gateway_gatewayclassconfig_test.go @@ -63,7 +63,23 @@ func TestAPIGateway_GatewayClassConfig(t *testing.T) { k8sClient := ctx.ControllerRuntimeClient(t) - // Create a GatewayClassConfig. + //create clean namespace + err = k8sClient.Create(context.Background(), &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: namespace, + }, + }) + require.NoError(t, err) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + logger.Log(t, "deleting gateway namesapce") + k8sClient.Delete(context.Background(), &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: namespace, + }, + }) + }) + + // create a GatewayClassConfig with configuration set gatewayClassConfigName := "gateway-class-config" gatewayClassConfig := &v1alpha1.GatewayClassConfig{ ObjectMeta: metav1.ObjectMeta{ @@ -80,7 +96,7 @@ func TestAPIGateway_GatewayClassConfig(t *testing.T) { logger.Log(t, "creating gateway class config") err = k8sClient.Create(context.Background(), gatewayClassConfig) require.NoError(t, err) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { logger.Log(t, "deleting all gateway class configs") k8sClient.DeleteAllOf(context.Background(), &v1alpha1.GatewayClassConfig{}) }) @@ -94,7 +110,7 @@ func TestAPIGateway_GatewayClassConfig(t *testing.T) { // Create gateway class referencing gateway-class-config. logger.Log(t, "creating controlled gateway class") createGatewayClass(t, k8sClient, gatewayClassName, gatewayClassControllerName, gatewayParametersRef) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { logger.Log(t, "deleting all gateway classes") k8sClient.DeleteAllOf(context.Background(), &gwv1beta1.GatewayClass{}) }) @@ -119,7 +135,7 @@ func TestAPIGateway_GatewayClassConfig(t *testing.T) { logger.Log(t, "creating certificate") err = k8sClient.Create(context.Background(), certificate) require.NoError(t, err) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8sClient.Delete(context.Background(), certificate) }) @@ -127,7 +143,12 @@ func TestAPIGateway_GatewayClassConfig(t *testing.T) { gatewayName := "gcctestgateway" + namespace logger.Log(t, "creating controlled gateway") gateway := createGateway(t, k8sClient, gatewayName, namespace, gatewayClassName, certificateName) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + + // make sure it exists + logger.Log(t, "checking that gateway one is synchronized to Consul") + checkConsulExists(t, consulClient, api.APIGateway, gatewayName) + + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { logger.Log(t, "deleting all gateways") k8sClient.DeleteAllOf(context.Background(), &gwv1beta1.Gateway{}, client.InNamespace(namespace)) }) diff --git a/acceptance/tests/api-gateway/api_gateway_lifecycle_test.go b/acceptance/tests/api-gateway/api_gateway_lifecycle_test.go index 08712815cc..e3ffa992ce 100644 --- a/acceptance/tests/api-gateway/api_gateway_lifecycle_test.go +++ b/acceptance/tests/api-gateway/api_gateway_lifecycle_test.go @@ -44,7 +44,7 @@ func TestAPIGateway_Lifecycle(t *testing.T) { // create a service to target targetName := "static-server" logger.Log(t, "creating target server") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") // create a basic GatewayClassConfig gatewayClassConfigName := "controlled-gateway-class-config" @@ -56,7 +56,7 @@ func TestAPIGateway_Lifecycle(t *testing.T) { logger.Log(t, "creating gateway class config") err := k8sClient.Create(context.Background(), gatewayClassConfig) require.NoError(t, err) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { logger.Log(t, "deleting all gateway class configs") k8sClient.DeleteAllOf(context.Background(), &v1alpha1.GatewayClassConfig{}) }) @@ -72,7 +72,7 @@ func TestAPIGateway_Lifecycle(t *testing.T) { logger.Log(t, "creating controlled gateway class one") createGatewayClass(t, k8sClient, controlledGatewayClassOneName, gatewayClassControllerName, gatewayParametersRef) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { logger.Log(t, "deleting all gateway classes") k8sClient.DeleteAllOf(context.Background(), &gwv1beta1.GatewayClass{}) }) @@ -105,7 +105,7 @@ func TestAPIGateway_Lifecycle(t *testing.T) { logger.Log(t, "creating certificate") err = k8sClient.Create(context.Background(), certificate) require.NoError(t, err) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8sClient.Delete(context.Background(), certificate) }) @@ -114,7 +114,7 @@ func TestAPIGateway_Lifecycle(t *testing.T) { logger.Log(t, "creating controlled gateway one") controlledGatewayOne := createGateway(t, k8sClient, controlledGatewayOneName, defaultNamespace, controlledGatewayClassOneName, certificateName) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { logger.Log(t, "deleting all gateways") k8sClient.DeleteAllOf(context.Background(), &gwv1beta1.Gateway{}, client.InNamespace(defaultNamespace)) }) @@ -132,7 +132,7 @@ func TestAPIGateway_Lifecycle(t *testing.T) { logger.Log(t, "creating route one") routeOne := createRoute(t, k8sClient, routeOneName, defaultNamespace, controlledGatewayOneName, targetName) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { logger.Log(t, "deleting all http routes") k8sClient.DeleteAllOf(context.Background(), &gwv1beta1.HTTPRoute{}, client.InNamespace(defaultNamespace)) }) diff --git a/acceptance/tests/api-gateway/api_gateway_tenancy_test.go b/acceptance/tests/api-gateway/api_gateway_tenancy_test.go index 716f09bdba..19e85d60b0 100644 --- a/acceptance/tests/api-gateway/api_gateway_tenancy_test.go +++ b/acceptance/tests/api-gateway/api_gateway_tenancy_test.go @@ -100,7 +100,7 @@ func TestAPIGateway_Tenancy(t *testing.T) { routeNamespace, routeK8SOptions := createNamespace(t, ctx, cfg) logger.Logf(t, "creating target server in %s namespace", serviceNamespace) - k8s.DeployKustomize(t, serviceK8SOptions, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, serviceK8SOptions, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") logger.Logf(t, "creating certificate resources in %s namespace", certificateNamespace) applyFixture(t, cfg, certificateK8SOptions, "cases/api-gateways/certificate") @@ -255,7 +255,7 @@ func applyFixture(t *testing.T, cfg *config.TestConfig, k8sOptions *terratestk8s out, err := k8s.RunKubectlAndGetOutputE(t, k8sOptions, "apply", "-k", path.Join("../fixtures", fixture)) require.NoError(t, err, out) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectlAndGetOutputE(t, k8sOptions, "delete", "-k", path.Join("../fixtures", fixture)) }) } @@ -267,7 +267,7 @@ func createNamespace(t *testing.T, ctx environment.TestContext, cfg *config.Test logger.Logf(t, "creating Kubernetes namespace %s", namespace) k8s.RunKubectl(t, ctx.KubectlOptions(t), "create", "ns", namespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "ns", namespace) }) diff --git a/acceptance/tests/api-gateway/api_gateway_test.go b/acceptance/tests/api-gateway/api_gateway_test.go index 143b793bf8..4b4db38afe 100644 --- a/acceptance/tests/api-gateway/api_gateway_test.go +++ b/acceptance/tests/api-gateway/api_gateway_test.go @@ -7,11 +7,12 @@ import ( "context" "encoding/base64" "fmt" - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" "strconv" "testing" "time" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + "github.com/hashicorp/consul-k8s/acceptance/framework/consul" "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" @@ -75,7 +76,7 @@ func TestAPIGateway_Basic(t *testing.T) { logger.Log(t, "creating api-gateway resources") out, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "apply", "-k", "../fixtures/bases/api-gateway") require.NoError(t, err, out) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { // Ignore errors here because if the test ran as expected // the custom resources will have been deleted. k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-k", "../fixtures/bases/api-gateway") @@ -86,7 +87,7 @@ func TestAPIGateway_Basic(t *testing.T) { logger.Log(t, "creating certificate secret") out, err = k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "apply", "-f", "../fixtures/bases/api-gateway/certificate.yaml") require.NoError(t, err, out) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { // Ignore errors here because if the test ran as expected // the custom resources will have been deleted. k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-f", "../fixtures/bases/api-gateway/certificate.yaml") @@ -98,17 +99,17 @@ func TestAPIGateway_Basic(t *testing.T) { k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "secret", "certificate", "-p", fmt.Sprintf(`{"data":{"tls.crt":"%s","tls.key":"%s"}}`, base64.StdEncoding.EncodeToString(certificate.CertPEM), base64.StdEncoding.EncodeToString(certificate.PrivateKeyPEM)), "--type=merge") logger.Log(t, "creating target http server") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") logger.Log(t, "patching route to target http server") k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "httproute", "http-route", "-p", `{"spec":{"rules":[{"backendRefs":[{"name":"static-server","port":80}]}]}}`, "--type=merge") logger.Log(t, "creating target tcp server") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-server-tcp") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-server-tcp") logger.Log(t, "creating tcp-route") k8s.RunKubectl(t, ctx.KubectlOptions(t), "apply", "-f", "../fixtures/cases/api-gateways/tcproute/route.yaml") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { // Ignore errors here because if the test ran as expected // the custom resources will have been deleted. k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-f", "../fixtures/cases/api-gateways/tcproute/route.yaml") @@ -117,7 +118,7 @@ func TestAPIGateway_Basic(t *testing.T) { // We use the static-client pod so that we can make calls to the api gateway // via kubectl exec without needing a route into the cluster from the test machine. logger.Log(t, "creating static-client pod") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-client") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-client") // Grab a kubernetes client so that we can verify binding // behavior prior to issuing requests through the gateway. diff --git a/acceptance/tests/cloud/basic_test.go b/acceptance/tests/cloud/basic_test.go index 0b5e3da719..e17169700a 100644 --- a/acceptance/tests/cloud/basic_test.go +++ b/acceptance/tests/cloud/basic_test.go @@ -113,7 +113,7 @@ func TestBasicCloud(t *testing.T) { consul.CreateK8sSecret(t, k8sClient, cfg, ns, authUrlSecretName, authUrlSecretKey, authUrlSecretKeyValue) consul.CreateK8sSecret(t, k8sClient, cfg, ns, scadaAddressSecretName, scadaAddressSecretKey, scadaAddressSecretKeyValue) - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/cloud/hcp-mock") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/cloud/hcp-mock") podName, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "get", "pod", "-l", "app=fake-server", "-o", `jsonpath="{.items[0].metadata.name}"`) podName = strings.ReplaceAll(podName, "\"", "") if err != nil { @@ -228,7 +228,8 @@ func TestBasicCloud(t *testing.T) { consulCluster.Create(t) logger.Log(t, "creating static-server deployment") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-server") + + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-server") t.Log("Finished deployment. Validating expected conditions now") // Give some time for collector send metrics time.Sleep(5 * time.Second) diff --git a/acceptance/tests/cloud/remote_dev_test.go b/acceptance/tests/cloud/remote_dev_test.go index aa7dbe70c7..457dc4f269 100644 --- a/acceptance/tests/cloud/remote_dev_test.go +++ b/acceptance/tests/cloud/remote_dev_test.go @@ -173,12 +173,12 @@ func TestRemoteDevCloud(t *testing.T) { logger.Log(t, "setting acl permissions for collector and services") aclDir := "../fixtures/bases/cloud/service-intentions" k8s.KubectlApplyK(t, ctx.KubectlOptions(t), aclDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, ctx.KubectlOptions(t), aclDir) }) logger.Log(t, "creating static-server deployment") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-server") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-server") time.Sleep(1 * time.Hour) // TODO: add in test assertions here diff --git a/acceptance/tests/config-entries/config_entries_namespaces_test.go b/acceptance/tests/config-entries/config_entries_namespaces_test.go index 91d0c69df4..a013c99e09 100644 --- a/acceptance/tests/config-entries/config_entries_namespaces_test.go +++ b/acceptance/tests/config-entries/config_entries_namespaces_test.go @@ -102,7 +102,7 @@ func TestControllerNamespaces(t *testing.T) { if err != nil && !strings.Contains(out, "(AlreadyExists)") { require.NoError(t, err) } - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "ns", KubeNS) }) diff --git a/acceptance/tests/config-entries/config_entries_test.go b/acceptance/tests/config-entries/config_entries_test.go index e37e3d6c7f..a36e9baaf5 100644 --- a/acceptance/tests/config-entries/config_entries_test.go +++ b/acceptance/tests/config-entries/config_entries_test.go @@ -86,7 +86,7 @@ func TestController(t *testing.T) { // endpoint fails initially. out, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "apply", "-k", "../fixtures/bases/crds-oss") require.NoError(r, err, out) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { // Ignore errors here because if the test ran as expected // the custom resources will have been deleted. k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-k", "../fixtures/bases/crds-oss") diff --git a/acceptance/tests/connect/connect_external_servers_test.go b/acceptance/tests/connect/connect_external_servers_test.go index a7b0f656bf..c421c1258e 100644 --- a/acceptance/tests/connect/connect_external_servers_test.go +++ b/acceptance/tests/connect/connect_external_servers_test.go @@ -73,11 +73,11 @@ func TestConnectInject_ExternalServers(t *testing.T) { consulCluster.Create(t) logger.Log(t, "creating static-server and static-client deployments") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") if cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") } // Check that both static-server and static-client have been injected and now have 2 containers. diff --git a/acceptance/tests/connect/connect_inject_namespaces_test.go b/acceptance/tests/connect/connect_inject_namespaces_test.go index f848594cd2..6fb493ce17 100644 --- a/acceptance/tests/connect/connect_inject_namespaces_test.go +++ b/acceptance/tests/connect/connect_inject_namespaces_test.go @@ -100,12 +100,12 @@ func TestConnectInjectNamespaces(t *testing.T) { logger.Logf(t, "creating namespaces %s and %s", staticServerNamespace, StaticClientNamespace) k8s.RunKubectl(t, ctx.KubectlOptions(t), "create", "ns", staticServerNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "ns", staticServerNamespace) }) k8s.RunKubectl(t, ctx.KubectlOptions(t), "create", "ns", StaticClientNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { // Note: this deletion will take longer in cases when the static-client deployment // hasn't yet fully terminated. k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "ns", StaticClientNamespace) @@ -146,11 +146,11 @@ func TestConnectInjectNamespaces(t *testing.T) { } logger.Log(t, "creating static-server and static-client deployments") - k8s.DeployKustomize(t, staticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, staticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") if cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { - k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") + k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") } // Check that both static-server and static-client have been injected and now have 2 containers. @@ -303,7 +303,7 @@ func TestConnectInjectNamespaces_CleanupController(t *testing.T) { logger.Logf(t, "creating namespace %s", StaticClientNamespace) k8s.RunKubectl(t, ctx.KubectlOptions(t), "create", "ns", StaticClientNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "ns", StaticClientNamespace) }) @@ -313,7 +313,7 @@ func TestConnectInjectNamespaces_CleanupController(t *testing.T) { ConfigPath: ctx.KubectlOptions(t).ConfigPath, Namespace: StaticClientNamespace, } - k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") + k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") logger.Log(t, "waiting for static-client to be registered with Consul") consulClient, _ := consulCluster.SetupConsulClient(t, c.secure) diff --git a/acceptance/tests/connect/connect_inject_test.go b/acceptance/tests/connect/connect_inject_test.go index 199bbf0f1b..c239698d2a 100644 --- a/acceptance/tests/connect/connect_inject_test.go +++ b/acceptance/tests/connect/connect_inject_test.go @@ -107,7 +107,7 @@ func TestConnectInject_CleanupKilledPods(t *testing.T) { consulCluster.Create(t) logger.Log(t, "creating static-client deployment") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") logger.Log(t, "waiting for static-client to be registered with Consul") consulClient, _ := consulCluster.SetupConsulClient(t, secure) @@ -200,8 +200,8 @@ func TestConnectInject_MultiportServices(t *testing.T) { } logger.Log(t, "creating multiport static-server and static-client deployments") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/multiport-app") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject-multiport") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/multiport-app") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-inject-multiport") // Check that static-client has been injected and now has 2 containers. podList, err := ctx.KubernetesClient(t).CoreV1().Pods(ctx.KubectlOptions(t).Namespace).List(context.Background(), metav1.ListOptions{ @@ -260,7 +260,7 @@ func TestConnectInject_MultiportServices(t *testing.T) { // pod to static-server. // Deploy static-server. - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") // For outbound connections from the multi port pod, only intentions from the first service in the multiport // pod need to be created, since all upstream connections are made through the first service's envoy proxy. diff --git a/acceptance/tests/connect/permissive_mtls_test.go b/acceptance/tests/connect/permissive_mtls_test.go index 1dcc6fe911..d07c44ae2f 100644 --- a/acceptance/tests/connect/permissive_mtls_test.go +++ b/acceptance/tests/connect/permissive_mtls_test.go @@ -58,7 +58,7 @@ func deployNonMeshClient(t *testing.T, ch connhelper.ConnectHelper) { t.Helper() logger.Log(t, "Creating static-client deployment with connect-inject=false") - k8s.DeployKustomize(t, ch.Ctx.KubectlOptions(t), ch.Cfg.NoCleanupOnFailure, ch.Cfg.DebugDirectory, "../fixtures/bases/static-client") + k8s.DeployKustomize(t, ch.Ctx.KubectlOptions(t), ch.Cfg.NoCleanupOnFailure, ch.Cfg.NoCleanup, ch.Cfg.DebugDirectory, "../fixtures/bases/static-client") requirePodContainers(t, ch, "app=static-client", 1) } @@ -66,7 +66,7 @@ func deployStaticServer(t *testing.T, cfg *config.TestConfig, ch connhelper.Conn t.Helper() logger.Log(t, "Creating static-server deployment with connect-inject=true") - k8s.DeployKustomize(t, ch.Ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, ch.Ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") requirePodContainers(t, ch, "app=static-server", 2) } diff --git a/acceptance/tests/consul-dns/consul_dns_test.go b/acceptance/tests/consul-dns/consul_dns_test.go index c33de7747d..84741175af 100644 --- a/acceptance/tests/consul-dns/consul_dns_test.go +++ b/acceptance/tests/consul-dns/consul_dns_test.go @@ -65,7 +65,7 @@ func TestConsulDNS(t *testing.T) { "run", "-it", dnsPodName, "--restart", "Never", "--image", "anubhavmishra/tiny-tools", "--", "dig", fmt.Sprintf("@%s-consul-dns", releaseName), "consul.service.consul", } - helpers.Cleanup(t, suite.Config().NoCleanupOnFailure, func() { + helpers.Cleanup(t, suite.Config().NoCleanupOnFailure, suite.Config().NoCleanup, func() { // Note: this delete command won't wait for pods to be fully terminated. // This shouldn't cause any test pollution because the underlying // objects are deployments, and so when other tests create these diff --git a/acceptance/tests/example/example_test.go b/acceptance/tests/example/example_test.go index b324ac31fe..07b04d6097 100644 --- a/acceptance/tests/example/example_test.go +++ b/acceptance/tests/example/example_test.go @@ -44,7 +44,7 @@ func TestExample(t *testing.T) { k8s.KubectlApply(t, ctx.KubectlOptions(t), "path/to/config") // Clean up any Kubernetes resources you have created - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDelete(t, ctx.KubectlOptions(t), "path/to/config") }) diff --git a/acceptance/tests/ingress-gateway/ingress_gateway_namespaces_test.go b/acceptance/tests/ingress-gateway/ingress_gateway_namespaces_test.go index ec4878df04..9edb1db010 100644 --- a/acceptance/tests/ingress-gateway/ingress_gateway_namespaces_test.go +++ b/acceptance/tests/ingress-gateway/ingress_gateway_namespaces_test.go @@ -69,7 +69,7 @@ func TestIngressGatewaySingleNamespace(t *testing.T) { logger.Logf(t, "creating Kubernetes namespace %s", testNamespace) k8s.RunKubectl(t, ctx.KubectlOptions(t), "create", "ns", testNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "ns", testNamespace) }) @@ -80,12 +80,12 @@ func TestIngressGatewaySingleNamespace(t *testing.T) { } logger.Logf(t, "creating server in %s namespace", testNamespace) - k8s.DeployKustomize(t, nsK8SOptions, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, nsK8SOptions, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") // We use the static-client pod so that we can make calls to the ingress gateway // via kubectl exec without needing a route into the cluster from the test machine. logger.Logf(t, "creating static-client in %s namespace", testNamespace) - k8s.DeployKustomize(t, nsK8SOptions, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-client") + k8s.DeployKustomize(t, nsK8SOptions, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-client") // With the cluster up, we can create our ingress-gateway config entry. logger.Log(t, "creating config entry") @@ -188,7 +188,7 @@ func TestIngressGatewayNamespaceMirroring(t *testing.T) { logger.Logf(t, "creating Kubernetes namespace %s", testNamespace) k8s.RunKubectl(t, ctx.KubectlOptions(t), "create", "ns", testNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "ns", testNamespace) }) @@ -199,12 +199,12 @@ func TestIngressGatewayNamespaceMirroring(t *testing.T) { } logger.Logf(t, "creating server in %s namespace", testNamespace) - k8s.DeployKustomize(t, nsK8SOptions, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, nsK8SOptions, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") // We use the static-client pod so that we can make calls to the ingress gateway // via kubectl exec without needing a route into the cluster from the test machine. logger.Logf(t, "creating static-client in %s namespace", testNamespace) - k8s.DeployKustomize(t, nsK8SOptions, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-client") + k8s.DeployKustomize(t, nsK8SOptions, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-client") consulClient, _ := consulCluster.SetupConsulClient(t, c.secure) diff --git a/acceptance/tests/ingress-gateway/ingress_gateway_test.go b/acceptance/tests/ingress-gateway/ingress_gateway_test.go index b5df6287b6..d2b3d7193e 100644 --- a/acceptance/tests/ingress-gateway/ingress_gateway_test.go +++ b/acceptance/tests/ingress-gateway/ingress_gateway_test.go @@ -52,12 +52,12 @@ func TestIngressGateway(t *testing.T) { consulCluster.Create(t) logger.Log(t, "creating server") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") // We use the static-client pod so that we can make calls to the ingress gateway // via kubectl exec without needing a route into the cluster from the test machine. logger.Log(t, "creating static-client pod") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-client") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-client") // With the cluster up, we can create our ingress-gateway config entry. logger.Log(t, "creating config entry") diff --git a/acceptance/tests/metrics/metrics_test.go b/acceptance/tests/metrics/metrics_test.go index 2a130f38db..3d40841e36 100644 --- a/acceptance/tests/metrics/metrics_test.go +++ b/acceptance/tests/metrics/metrics_test.go @@ -71,7 +71,7 @@ func TestComponentMetrics(t *testing.T) { // This simulates queries that would be made by a prometheus server that runs externally to the consul // components in the cluster. logger.Log(t, "creating static-client") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-client") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-client") // Server Metrics metricsOutput, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "exec", "deploy/"+StaticClientName, "--", "curl", "--silent", "--show-error", fmt.Sprintf("http://%s:8500/v1/agent/metrics?format=prometheus", fmt.Sprintf("%s-consul-server.%s.svc", releaseName, ns))) @@ -116,13 +116,13 @@ func TestAppMetrics(t *testing.T) { // Deploy service that will emit app and envoy metrics at merged metrics endpoint logger.Log(t, "creating static-metrics-app") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-metrics-app") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-metrics-app") // Create the static-client deployment so we can use it for in-cluster calls to metrics endpoints. // This simulates queries that would be made by a prometheus server that runs externally to the consul // components in the cluster. logger.Log(t, "creating static-client") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-client") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-client") // Merged App Metrics podList, err := ctx.KubernetesClient(t).CoreV1().Pods(ns).List(context.Background(), metav1.ListOptions{LabelSelector: "app=static-metrics-app"}) diff --git a/acceptance/tests/partitions/partitions_connect_test.go b/acceptance/tests/partitions/partitions_connect_test.go index aa73c17047..6b103614c7 100644 --- a/acceptance/tests/partitions/partitions_connect_test.go +++ b/acceptance/tests/partitions/partitions_connect_test.go @@ -204,14 +204,14 @@ func TestPartitions_Connect(t *testing.T) { logger.Logf(t, "creating namespaces %s and %s in servers cluster", staticServerNamespace, StaticClientNamespace) k8s.RunKubectl(t, defaultPartitionClusterContext.KubectlOptions(t), "create", "ns", staticServerNamespace) k8s.RunKubectl(t, defaultPartitionClusterContext.KubectlOptions(t), "create", "ns", StaticClientNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, defaultPartitionClusterContext.KubectlOptions(t), "delete", "ns", staticServerNamespace, StaticClientNamespace) }) logger.Logf(t, "creating namespaces %s and %s in clients cluster", staticServerNamespace, StaticClientNamespace) k8s.RunKubectl(t, secondaryPartitionClusterContext.KubectlOptions(t), "create", "ns", staticServerNamespace) k8s.RunKubectl(t, secondaryPartitionClusterContext.KubectlOptions(t), "create", "ns", StaticClientNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, secondaryPartitionClusterContext.KubectlOptions(t), "delete", "ns", staticServerNamespace, StaticClientNamespace) }) @@ -271,37 +271,37 @@ func TestPartitions_Connect(t *testing.T) { kustomizeDir := "../fixtures/bases/mesh-gateway" k8s.KubectlApplyK(t, defaultPartitionClusterContext.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, defaultPartitionClusterContext.KubectlOptions(t), kustomizeDir) }) k8s.KubectlApplyK(t, secondaryPartitionClusterContext.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, secondaryPartitionClusterContext.KubectlOptions(t), kustomizeDir) }) // This section of the tests runs the in-partition networking tests. t.Run("in-partition", func(t *testing.T) { logger.Log(t, "test in-partition networking") logger.Log(t, "creating static-server and static-client deployments in server cluster") - k8s.DeployKustomize(t, defaultPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, defaultPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") if cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, defaultPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, defaultPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { if c.destinationNamespace == defaultNamespace { - k8s.DeployKustomize(t, defaultPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + k8s.DeployKustomize(t, defaultPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") } else { - k8s.DeployKustomize(t, defaultPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") + k8s.DeployKustomize(t, defaultPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") } } logger.Log(t, "creating static-server and static-client deployments in client cluster") - k8s.DeployKustomize(t, secondaryPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, secondaryPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") if cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { if c.destinationNamespace == defaultNamespace { - k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") } else { - k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") + k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") } } // Check that both static-server and static-client have been injected and now have 2 containers in server cluster. @@ -383,7 +383,7 @@ func TestPartitions_Connect(t *testing.T) { require.NoError(t, err) _, _, err = consulClient.ConfigEntries().Set(intention, &api.WriteOptions{Partition: secondaryPartition}) require.NoError(t, err) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { _, err := consulClient.ConfigEntries().Delete(api.ServiceIntentions, staticServerName, &api.WriteOptions{Partition: defaultPartition}) require.NoError(t, err) _, err = consulClient.ConfigEntries().Delete(api.ServiceIntentions, staticServerName, &api.WriteOptions{Partition: secondaryPartition}) @@ -424,25 +424,25 @@ func TestPartitions_Connect(t *testing.T) { t.Run("cross-partition", func(t *testing.T) { logger.Log(t, "test cross-partition networking") logger.Log(t, "creating static-server and static-client deployments in server cluster") - k8s.DeployKustomize(t, defaultPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, defaultPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") if cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, defaultPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, defaultPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { if c.destinationNamespace == defaultNamespace { - k8s.DeployKustomize(t, defaultPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-partitions/default-ns-partition") + k8s.DeployKustomize(t, defaultPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-partitions/default-ns-partition") } else { - k8s.DeployKustomize(t, defaultPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-partitions/ns-partition") + k8s.DeployKustomize(t, defaultPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-partitions/ns-partition") } } logger.Log(t, "creating static-server and static-client deployments in client cluster") - k8s.DeployKustomize(t, secondaryPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, secondaryPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") if cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { if c.destinationNamespace == defaultNamespace { - k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-partitions/default-ns-default-partition") + k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-partitions/default-ns-default-partition") } else { - k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-partitions/ns-default-partition") + k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-partitions/ns-default-partition") } } // Check that both static-server and static-client have been injected and now have 2 containers in server cluster. @@ -496,14 +496,14 @@ func TestPartitions_Connect(t *testing.T) { if c.destinationNamespace == defaultNamespace { k8s.KubectlApplyK(t, defaultPartitionClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/default-partition-default") k8s.KubectlApplyK(t, secondaryPartitionClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/secondary-partition-default") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, defaultPartitionClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/default-partition-default") k8s.KubectlDeleteK(t, secondaryPartitionClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/secondary-partition-default") }) } else { k8s.KubectlApplyK(t, defaultPartitionClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/default-partition-ns1") k8s.KubectlApplyK(t, secondaryPartitionClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/secondary-partition-ns1") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, defaultPartitionClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/default-partition-ns1") k8s.KubectlDeleteK(t, secondaryPartitionClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/secondary-partition-ns1") }) @@ -551,7 +551,7 @@ func TestPartitions_Connect(t *testing.T) { intention.Sources[0].Partition = defaultPartition _, _, err = consulClient.ConfigEntries().Set(intention, &api.WriteOptions{Partition: secondaryPartition}) require.NoError(t, err) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { _, err := consulClient.ConfigEntries().Delete(api.ServiceIntentions, staticServerName, &api.WriteOptions{Partition: defaultPartition}) require.NoError(t, err) _, err = consulClient.ConfigEntries().Delete(api.ServiceIntentions, staticServerName, &api.WriteOptions{Partition: secondaryPartition}) diff --git a/acceptance/tests/partitions/partitions_gateway_test.go b/acceptance/tests/partitions/partitions_gateway_test.go index 5c85e6725b..acdb81fa65 100644 --- a/acceptance/tests/partitions/partitions_gateway_test.go +++ b/acceptance/tests/partitions/partitions_gateway_test.go @@ -147,14 +147,14 @@ func TestPartitions_Gateway(t *testing.T) { logger.Logf(t, "creating namespaces %s and %s in servers cluster", staticServerNamespace, StaticClientNamespace) k8s.RunKubectl(t, defaultPartitionClusterContext.KubectlOptions(t), "create", "ns", staticServerNamespace) k8s.RunKubectl(t, defaultPartitionClusterContext.KubectlOptions(t), "create", "ns", StaticClientNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, defaultPartitionClusterContext.KubectlOptions(t), "delete", "ns", staticServerNamespace, StaticClientNamespace) }) logger.Logf(t, "creating namespaces %s and %s in clients cluster", staticServerNamespace, StaticClientNamespace) k8s.RunKubectl(t, secondaryPartitionClusterContext.KubectlOptions(t), "create", "ns", staticServerNamespace) k8s.RunKubectl(t, secondaryPartitionClusterContext.KubectlOptions(t), "create", "ns", StaticClientNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, secondaryPartitionClusterContext.KubectlOptions(t), "delete", "ns", staticServerNamespace, StaticClientNamespace) }) @@ -202,12 +202,12 @@ func TestPartitions_Gateway(t *testing.T) { kustomizeDir := "../fixtures/cases/api-gateways/mesh" k8s.KubectlApplyK(t, defaultPartitionClusterContext.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, defaultPartitionClusterContext.KubectlOptions(t), kustomizeDir) }) k8s.KubectlApplyK(t, secondaryPartitionClusterContext.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, secondaryPartitionClusterContext.KubectlOptions(t), kustomizeDir) }) @@ -216,12 +216,12 @@ func TestPartitions_Gateway(t *testing.T) { // Since we're deploying the gateway in the secondary cluster, we create the static client // in the secondary as well. logger.Log(t, "creating static-client pod in secondary partition cluster") - k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-client") + k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-client") logger.Log(t, "creating api-gateway resources") out, err := k8s.RunKubectlAndGetOutputE(t, secondaryPartitionClusterStaticServerOpts, "apply", "-k", "../fixtures/bases/api-gateway") require.NoError(t, err, out) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { // Ignore errors here because if the test ran as expected // the custom resources will have been deleted. k8s.RunKubectlAndGetOutputE(t, secondaryPartitionClusterStaticServerOpts, "delete", "-k", "../fixtures/bases/api-gateway") @@ -253,7 +253,7 @@ func TestPartitions_Gateway(t *testing.T) { t.Run("in-partition", func(t *testing.T) { logger.Log(t, "test in-partition networking") logger.Log(t, "creating target server in secondary partition cluster") - k8s.DeployKustomize(t, secondaryPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, secondaryPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") logger.Log(t, "patching route to target server") k8s.RunKubectl(t, secondaryPartitionClusterStaticServerOpts, "patch", "httproute", "http-route", "-p", `{"spec":{"rules":[{"backendRefs":[{"group":"consul.hashicorp.com","kind":"MeshService","name":"mesh-service","port":80}]}]}}`, "--type=merge") @@ -277,7 +277,7 @@ func TestPartitions_Gateway(t *testing.T) { logger.Log(t, "creating intention") _, _, err = consulClient.ConfigEntries().Set(intention, &api.WriteOptions{Partition: secondaryPartition}) require.NoError(t, err) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { _, err = consulClient.ConfigEntries().Delete(api.ServiceIntentions, staticServerName, &api.WriteOptions{Partition: secondaryPartition}) require.NoError(t, err) }) @@ -291,17 +291,17 @@ func TestPartitions_Gateway(t *testing.T) { logger.Log(t, "test cross-partition networking") logger.Log(t, "creating target server in default partition cluster") - k8s.DeployKustomize(t, defaultPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, defaultPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") logger.Log(t, "creating exported services") k8s.KubectlApplyK(t, defaultPartitionClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/default-partition-ns1") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, defaultPartitionClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/default-partition-ns1") }) logger.Log(t, "creating local service resolver") k8s.KubectlApplyK(t, secondaryPartitionClusterStaticServerOpts, "../fixtures/cases/api-gateways/resolver") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, secondaryPartitionClusterStaticServerOpts, "../fixtures/cases/api-gateways/resolver") }) @@ -328,7 +328,7 @@ func TestPartitions_Gateway(t *testing.T) { logger.Log(t, "creating intention") _, _, err = consulClient.ConfigEntries().Set(intention, &api.WriteOptions{Partition: defaultPartition}) require.NoError(t, err) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { _, err = consulClient.ConfigEntries().Delete(api.ServiceIntentions, staticServerName, &api.WriteOptions{Partition: defaultPartition}) require.NoError(t, err) }) diff --git a/acceptance/tests/partitions/partitions_sync_test.go b/acceptance/tests/partitions/partitions_sync_test.go index 8eaaff099e..da95d7f272 100644 --- a/acceptance/tests/partitions/partitions_sync_test.go +++ b/acceptance/tests/partitions/partitions_sync_test.go @@ -195,13 +195,13 @@ func TestPartitions_Sync(t *testing.T) { logger.Logf(t, "creating namespaces %s in servers cluster", staticServerNamespace) k8s.RunKubectl(t, primaryClusterContext.KubectlOptions(t), "create", "ns", staticServerNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, primaryClusterContext.KubectlOptions(t), "delete", "ns", staticServerNamespace) }) logger.Logf(t, "creating namespaces %s in clients cluster", staticServerNamespace) k8s.RunKubectl(t, secondaryClusterContext.KubectlOptions(t), "create", "ns", staticServerNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, secondaryClusterContext.KubectlOptions(t), "delete", "ns", staticServerNamespace) }) @@ -241,9 +241,9 @@ func TestPartitions_Sync(t *testing.T) { logger.Log(t, "creating a static-server with a service") // create service in default partition. - k8s.DeployKustomize(t, primaryStaticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-server") + k8s.DeployKustomize(t, primaryStaticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-server") // create service in secondary partition. - k8s.DeployKustomize(t, secondaryStaticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-server") + k8s.DeployKustomize(t, secondaryStaticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-server") logger.Log(t, "checking that the service has been synced to Consul") var services map[string][]string diff --git a/acceptance/tests/peering/peering_connect_namespaces_test.go b/acceptance/tests/peering/peering_connect_namespaces_test.go index 622e547091..618248c6a9 100644 --- a/acceptance/tests/peering/peering_connect_namespaces_test.go +++ b/acceptance/tests/peering/peering_connect_namespaces_test.go @@ -166,12 +166,12 @@ func TestPeering_ConnectNamespaces(t *testing.T) { kustomizeMeshDir := "../fixtures/bases/mesh-peering" k8s.KubectlApplyK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) }) k8s.KubectlApplyK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) }) @@ -198,7 +198,7 @@ func TestPeering_ConnectNamespaces(t *testing.T) { // Create the peering acceptor on the client peer. k8s.KubectlApply(t, staticClientPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-acceptor.yaml") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDelete(t, staticClientPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-acceptor.yaml") }) @@ -214,7 +214,7 @@ func TestPeering_ConnectNamespaces(t *testing.T) { // Create the peering dialer on the server peer. k8s.KubectlApply(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-dialer.yaml") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, staticServerPeerClusterContext.KubectlOptions(t), "delete", "secret", "api-token") k8s.KubectlDelete(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-dialer.yaml") }) @@ -232,13 +232,13 @@ func TestPeering_ConnectNamespaces(t *testing.T) { logger.Logf(t, "creating namespaces %s in server peer", staticServerNamespace) k8s.RunKubectl(t, staticServerPeerClusterContext.KubectlOptions(t), "create", "ns", staticServerNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, staticServerPeerClusterContext.KubectlOptions(t), "delete", "ns", staticServerNamespace) }) logger.Logf(t, "creating namespaces %s in client peer", staticClientNamespace) k8s.RunKubectl(t, staticClientPeerClusterContext.KubectlOptions(t), "create", "ns", staticClientNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, staticClientPeerClusterContext.KubectlOptions(t), "delete", "ns", staticClientNamespace) }) @@ -256,26 +256,26 @@ func TestPeering_ConnectNamespaces(t *testing.T) { kustomizeDir := "../fixtures/bases/mesh-gateway" k8s.KubectlApplyK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeDir) }) k8s.KubectlApplyK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeDir) }) logger.Log(t, "creating static-server in server peer") - k8s.DeployKustomize(t, staticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, staticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") logger.Log(t, "creating static-client deployments in client peer") if cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { if c.destinationNamespace == defaultNamespace { - k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-peers/default-namespace") + k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-peers/default-namespace") } else { - k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-peers/non-default-namespace") + k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-peers/non-default-namespace") } } // Check that both static-server and static-client have been injected and now have 2 containers. @@ -312,12 +312,12 @@ func TestPeering_ConnectNamespaces(t *testing.T) { logger.Log(t, "creating exported services") if c.destinationNamespace == defaultNamespace { k8s.KubectlApplyK(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/cases/crd-peers/default-namespace") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/cases/crd-peers/default-namespace") }) } else { k8s.KubectlApplyK(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/cases/crd-peers/non-default-namespace") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/cases/crd-peers/non-default-namespace") }) } diff --git a/acceptance/tests/peering/peering_connect_test.go b/acceptance/tests/peering/peering_connect_test.go index a14cf3a805..d92ab59990 100644 --- a/acceptance/tests/peering/peering_connect_test.go +++ b/acceptance/tests/peering/peering_connect_test.go @@ -121,12 +121,12 @@ func TestPeering_Connect(t *testing.T) { kustomizeMeshDir := "../fixtures/bases/mesh-peering" k8s.KubectlApplyK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) }) k8s.KubectlApplyK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) }) @@ -153,7 +153,7 @@ func TestPeering_Connect(t *testing.T) { // Create the peering acceptor on the client peer. k8s.KubectlApply(t, staticClientPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-acceptor.yaml") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDelete(t, staticClientPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-acceptor.yaml") }) @@ -169,7 +169,7 @@ func TestPeering_Connect(t *testing.T) { // Create the peering dialer on the server peer. k8s.KubectlApply(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-dialer.yaml") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, staticServerPeerClusterContext.KubectlOptions(t), "delete", "secret", "api-token") k8s.KubectlDelete(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-dialer.yaml") }) @@ -187,13 +187,13 @@ func TestPeering_Connect(t *testing.T) { logger.Logf(t, "creating namespaces %s in server peer", staticServerNamespace) k8s.RunKubectl(t, staticServerPeerClusterContext.KubectlOptions(t), "create", "ns", staticServerNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, staticServerPeerClusterContext.KubectlOptions(t), "delete", "ns", staticServerNamespace) }) logger.Logf(t, "creating namespaces %s in client peer", staticClientNamespace) k8s.RunKubectl(t, staticClientPeerClusterContext.KubectlOptions(t), "create", "ns", staticClientNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, staticClientPeerClusterContext.KubectlOptions(t), "delete", "ns", staticClientNamespace) }) @@ -203,23 +203,23 @@ func TestPeering_Connect(t *testing.T) { kustomizeDir := "../fixtures/bases/mesh-gateway" k8s.KubectlApplyK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeDir) }) k8s.KubectlApplyK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeDir) }) logger.Log(t, "creating static-server in server peer") - k8s.DeployKustomize(t, staticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, staticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") logger.Log(t, "creating static-client deployments in client peer") if cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { - k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-peers/default") + k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-peers/default") } // Check that both static-server and static-client have been injected and now have 2 containers. podList, err := staticServerPeerClusterContext.KubernetesClient(t).CoreV1().Pods(metav1.NamespaceAll).List(context.Background(), metav1.ListOptions{ @@ -249,7 +249,7 @@ func TestPeering_Connect(t *testing.T) { logger.Log(t, "creating exported services") k8s.KubectlApplyK(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/cases/crd-peers/default") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/cases/crd-peers/default") }) diff --git a/acceptance/tests/peering/peering_gateway_test.go b/acceptance/tests/peering/peering_gateway_test.go index 17824b8e69..3ba04980a9 100644 --- a/acceptance/tests/peering/peering_gateway_test.go +++ b/acceptance/tests/peering/peering_gateway_test.go @@ -113,12 +113,12 @@ func TestPeering_Gateway(t *testing.T) { kustomizeMeshDir := "../fixtures/bases/mesh-peering" k8s.KubectlApplyK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) }) k8s.KubectlApplyK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) }) @@ -145,7 +145,7 @@ func TestPeering_Gateway(t *testing.T) { // Create the peering acceptor on the client peer. k8s.KubectlApply(t, staticClientPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-acceptor.yaml") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDelete(t, staticClientPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-acceptor.yaml") }) @@ -161,7 +161,7 @@ func TestPeering_Gateway(t *testing.T) { // Create the peering dialer on the server peer. k8s.KubectlApply(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-dialer.yaml") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, staticServerPeerClusterContext.KubectlOptions(t), "delete", "secret", "api-token") k8s.KubectlDelete(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-dialer.yaml") }) @@ -179,13 +179,13 @@ func TestPeering_Gateway(t *testing.T) { logger.Logf(t, "creating namespaces %s in server peer", staticServerNamespace) k8s.RunKubectl(t, staticServerPeerClusterContext.KubectlOptions(t), "create", "ns", staticServerNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, staticServerPeerClusterContext.KubectlOptions(t), "delete", "ns", staticServerNamespace) }) logger.Logf(t, "creating namespaces %s in client peer", staticClientNamespace) k8s.RunKubectl(t, staticClientPeerClusterContext.KubectlOptions(t), "create", "ns", staticClientNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, staticClientPeerClusterContext.KubectlOptions(t), "delete", "ns", staticClientNamespace) }) @@ -195,12 +195,12 @@ func TestPeering_Gateway(t *testing.T) { kustomizeDir := "../fixtures/cases/api-gateways/mesh" k8s.KubectlApplyK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeDir) }) k8s.KubectlApplyK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeDir) }) @@ -209,21 +209,21 @@ func TestPeering_Gateway(t *testing.T) { // Since we're deploying the gateway in the secondary cluster, we create the static client // in the secondary as well. logger.Log(t, "creating static-client pod in client peer") - k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-peers/non-default-namespace") + k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-peers/non-default-namespace") logger.Log(t, "creating static-server in server peer") - k8s.DeployKustomize(t, staticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, staticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") logger.Log(t, "creating exported services") k8s.KubectlApplyK(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/cases/crd-peers/non-default-namespace") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/cases/crd-peers/non-default-namespace") }) logger.Log(t, "creating api-gateway resources in client peer") out, err := k8s.RunKubectlAndGetOutputE(t, staticClientOpts, "apply", "-k", "../fixtures/bases/api-gateway") require.NoError(t, err, out) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { // Ignore errors here because if the test ran as expected // the custom resources will have been deleted. k8s.RunKubectlAndGetOutputE(t, staticClientOpts, "delete", "-k", "../fixtures/bases/api-gateway") @@ -253,7 +253,7 @@ func TestPeering_Gateway(t *testing.T) { logger.Log(t, "creating local service resolver") k8s.KubectlApplyK(t, staticClientOpts, "../fixtures/cases/api-gateways/peer-resolver") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, staticClientOpts, "../fixtures/cases/api-gateways/peer-resolver") }) @@ -280,7 +280,7 @@ func TestPeering_Gateway(t *testing.T) { logger.Log(t, "creating intention") _, _, err = staticServerPeerClient.ConfigEntries().Set(intention, &api.WriteOptions{}) require.NoError(t, err) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { _, err = staticServerPeerClient.ConfigEntries().Delete(api.ServiceIntentions, staticServerName, &api.WriteOptions{}) require.NoError(t, err) }) diff --git a/acceptance/tests/sync/sync_catalog_namespaces_test.go b/acceptance/tests/sync/sync_catalog_namespaces_test.go index 7634220b6b..67123d6e4f 100644 --- a/acceptance/tests/sync/sync_catalog_namespaces_test.go +++ b/acceptance/tests/sync/sync_catalog_namespaces_test.go @@ -97,12 +97,12 @@ func TestSyncCatalogNamespaces(t *testing.T) { logger.Logf(t, "creating namespace %s", staticServerNamespace) k8s.RunKubectl(t, ctx.KubectlOptions(t), "create", "ns", staticServerNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "ns", staticServerNamespace) }) logger.Log(t, "creating a static-server with a service") - k8s.DeployKustomize(t, staticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-server") + k8s.DeployKustomize(t, staticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-server") consulClient, _ := consulCluster.SetupConsulClient(t, c.secure) diff --git a/acceptance/tests/sync/sync_catalog_test.go b/acceptance/tests/sync/sync_catalog_test.go index 2ca8b1ee1f..dc30570422 100644 --- a/acceptance/tests/sync/sync_catalog_test.go +++ b/acceptance/tests/sync/sync_catalog_test.go @@ -50,7 +50,7 @@ func TestSyncCatalog(t *testing.T) { consulCluster.Create(t) logger.Log(t, "creating a static-server with a service") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), suite.Config().NoCleanupOnFailure, suite.Config().DebugDirectory, "../fixtures/bases/static-server") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), suite.Config().NoCleanupOnFailure, suite.Config().NoCleanup, suite.Config().DebugDirectory, "../fixtures/bases/static-server") consulClient, _ := consulCluster.SetupConsulClient(t, c.secure) @@ -120,7 +120,7 @@ func TestSyncCatalogWithIngress(t *testing.T) { // endpoint fails initially. out, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "apply", "-k", "../fixtures/bases/ingress") require.NoError(r, err, out) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { // Ignore errors here because if the test ran as expected // the custom resources will have been deleted. k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-k", "../fixtures/bases/ingress") @@ -130,7 +130,7 @@ func TestSyncCatalogWithIngress(t *testing.T) { consulCluster.Create(t) logger.Log(t, "creating a static-server with a service") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), suite.Config().NoCleanupOnFailure, suite.Config().DebugDirectory, "../fixtures/bases/static-server") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), suite.Config().NoCleanupOnFailure, suite.Config().NoCleanup, suite.Config().DebugDirectory, "../fixtures/bases/static-server") consulClient, _ := consulCluster.SetupConsulClient(t, c.secure) diff --git a/acceptance/tests/terminating-gateway/terminating_gateway_destinations_test.go b/acceptance/tests/terminating-gateway/terminating_gateway_destinations_test.go index 109975d731..62485c6e44 100644 --- a/acceptance/tests/terminating-gateway/terminating_gateway_destinations_test.go +++ b/acceptance/tests/terminating-gateway/terminating_gateway_destinations_test.go @@ -73,7 +73,7 @@ func TestTerminatingGatewayDestinations(t *testing.T) { // Deploy a static-server that will play the role of an external service. logger.Log(t, "creating static-server deployment") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-server-https") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-server-https") // If ACLs are enabled we need to update the role of the terminating gateway // with service:write permissions to the static-server service @@ -91,7 +91,7 @@ func TestTerminatingGatewayDestinations(t *testing.T) { // Deploy the static client logger.Log(t, "deploying static client") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") staticServerIP, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "get", "po", "-l", "app=static-server", `-o=jsonpath={.items[0].status.podIP}`) require.NoError(t, err) diff --git a/acceptance/tests/terminating-gateway/terminating_gateway_namespaces_test.go b/acceptance/tests/terminating-gateway/terminating_gateway_namespaces_test.go index 7ad9f7da10..73250ea810 100644 --- a/acceptance/tests/terminating-gateway/terminating_gateway_namespaces_test.go +++ b/acceptance/tests/terminating-gateway/terminating_gateway_namespaces_test.go @@ -62,7 +62,7 @@ func TestTerminatingGatewaySingleNamespace(t *testing.T) { logger.Logf(t, "creating Kubernetes namespace %s", testNamespace) k8s.RunKubectl(t, ctx.KubectlOptions(t), "create", "ns", testNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "ns", testNamespace) }) @@ -74,7 +74,7 @@ func TestTerminatingGatewaySingleNamespace(t *testing.T) { // Deploy a static-server that will play the role of an external service. logger.Log(t, "creating static-server deployment") - k8s.DeployKustomize(t, nsK8SOptions, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-server") + k8s.DeployKustomize(t, nsK8SOptions, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-server") // Register the external service. registerExternalService(t, consulClient, testNamespace) @@ -91,7 +91,7 @@ func TestTerminatingGatewaySingleNamespace(t *testing.T) { // Deploy the static client. logger.Log(t, "deploying static client") - k8s.DeployKustomize(t, nsK8SOptions, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") + k8s.DeployKustomize(t, nsK8SOptions, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") // If ACLs are enabled, test that intentions prevent connections. if c.secure { @@ -159,14 +159,14 @@ func TestTerminatingGatewayNamespaceMirroring(t *testing.T) { logger.Logf(t, "creating Kubernetes namespace %s", testNamespace) k8s.RunKubectl(t, ctx.KubectlOptions(t), "create", "ns", testNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "ns", testNamespace) }) StaticClientNamespace := "ns2" logger.Logf(t, "creating Kubernetes namespace %s", StaticClientNamespace) k8s.RunKubectl(t, ctx.KubectlOptions(t), "create", "ns", StaticClientNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "ns", StaticClientNamespace) }) @@ -183,7 +183,7 @@ func TestTerminatingGatewayNamespaceMirroring(t *testing.T) { // Deploy a static-server that will play the role of an external service. logger.Log(t, "creating static-server deployment") - k8s.DeployKustomize(t, ns1K8SOptions, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-server") + k8s.DeployKustomize(t, ns1K8SOptions, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-server") // Register the external service registerExternalService(t, consulClient, testNamespace) @@ -200,7 +200,7 @@ func TestTerminatingGatewayNamespaceMirroring(t *testing.T) { // Deploy the static client logger.Log(t, "deploying static client") - k8s.DeployKustomize(t, ns2K8SOptions, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") + k8s.DeployKustomize(t, ns2K8SOptions, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") // If ACLs are enabled, test that intentions prevent connections. if c.secure { diff --git a/acceptance/tests/terminating-gateway/terminating_gateway_test.go b/acceptance/tests/terminating-gateway/terminating_gateway_test.go index 25792e9abb..6e7e95032f 100644 --- a/acceptance/tests/terminating-gateway/terminating_gateway_test.go +++ b/acceptance/tests/terminating-gateway/terminating_gateway_test.go @@ -51,7 +51,7 @@ func TestTerminatingGateway(t *testing.T) { // Deploy a static-server that will play the role of an external service. logger.Log(t, "creating static-server deployment") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-server") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-server") // Once the cluster is up, register the external service, then create the config entry. consulClient, _ := consulCluster.SetupConsulClient(t, c.secure) @@ -71,7 +71,7 @@ func TestTerminatingGateway(t *testing.T) { // Deploy the static client logger.Log(t, "deploying static client") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") // If ACLs are enabled, test that intentions prevent connections. if c.secure { diff --git a/acceptance/tests/vault/vault_namespaces_test.go b/acceptance/tests/vault/vault_namespaces_test.go index 68fa819a84..4a9ca092d0 100644 --- a/acceptance/tests/vault/vault_namespaces_test.go +++ b/acceptance/tests/vault/vault_namespaces_test.go @@ -265,13 +265,13 @@ func TestVault_VaultNamespace(t *testing.T) { // Deploy two services and check that they can talk to each other. logger.Log(t, "creating static-server and static-client deployments") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") if cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") } - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, ctx.KubectlOptions(t), "../fixtures/bases/intention") }) k8s.KubectlApplyK(t, ctx.KubectlOptions(t), "../fixtures/bases/intention") diff --git a/acceptance/tests/vault/vault_test.go b/acceptance/tests/vault/vault_test.go index 47d58b68c5..9dab0a3e71 100644 --- a/acceptance/tests/vault/vault_test.go +++ b/acceptance/tests/vault/vault_test.go @@ -350,13 +350,13 @@ func testVault(t *testing.T, testAutoBootstrap bool) { // Deploy two services and check that they can talk to each other. logger.Log(t, "creating static-server and static-client deployments") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") if cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") } - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, ctx.KubectlOptions(t), "../fixtures/bases/intention") }) k8s.KubectlApplyK(t, ctx.KubectlOptions(t), "../fixtures/bases/intention") diff --git a/acceptance/tests/vault/vault_tls_auto_reload_test.go b/acceptance/tests/vault/vault_tls_auto_reload_test.go index c3a3ae4034..d5d4d33c4c 100644 --- a/acceptance/tests/vault/vault_tls_auto_reload_test.go +++ b/acceptance/tests/vault/vault_tls_auto_reload_test.go @@ -246,13 +246,13 @@ func TestVault_TLSAutoReload(t *testing.T) { // Deploy two services and check that they can talk to each other. logger.Log(t, "creating static-server and static-client deployments") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") if cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") } - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, ctx.KubectlOptions(t), "../fixtures/bases/intention") }) k8s.KubectlApplyK(t, ctx.KubectlOptions(t), "../fixtures/bases/intention") diff --git a/acceptance/tests/vault/vault_wan_fed_test.go b/acceptance/tests/vault/vault_wan_fed_test.go index d8c00b732a..fa63c4d5fb 100644 --- a/acceptance/tests/vault/vault_wan_fed_test.go +++ b/acceptance/tests/vault/vault_wan_fed_test.go @@ -31,6 +31,7 @@ import ( // in the secondary that will treat the Vault server in the primary as an external server. func TestVault_WANFederationViaGateways(t *testing.T) { cfg := suite.Config() + if cfg.UseKind { t.Skipf("Skipping this test because it's currently flaky on kind") } @@ -491,16 +492,18 @@ func TestVault_WANFederationViaGateways(t *testing.T) { logger.Log(t, "creating proxy-defaults config") kustomizeDir := "../fixtures/bases/mesh-gateway" k8s.KubectlApplyK(t, primaryCtx.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, primaryCtx.KubectlOptions(t), kustomizeDir) }) // Check that we can connect services over the mesh gateways. + logger.Log(t, "creating static-server in dc2") - k8s.DeployKustomize(t, secondaryCtx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, secondaryCtx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") logger.Log(t, "creating static-client in dc1") - k8s.DeployKustomize(t, primaryCtx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-multi-dc") + k8s.DeployKustomize(t, primaryCtx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-multi-dc") logger.Log(t, "creating intention") _, _, err = primaryClient.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{ @@ -517,6 +520,7 @@ func TestVault_WANFederationViaGateways(t *testing.T) { logger.Log(t, "checking that connection is successful") k8s.CheckStaticServerConnectionSuccessful(t, primaryCtx.KubectlOptions(t), StaticClientName, "http://localhost:1234") + } // vaultAddress returns Vault's server URL depending on test configuration. diff --git a/acceptance/tests/wan-federation/wan_federation_gateway_test.go b/acceptance/tests/wan-federation/wan_federation_gateway_test.go index c87ee7197b..6abacda438 100644 --- a/acceptance/tests/wan-federation/wan_federation_gateway_test.go +++ b/acceptance/tests/wan-federation/wan_federation_gateway_test.go @@ -130,25 +130,25 @@ func TestWANFederation_Gateway(t *testing.T) { logger.Log(t, "creating proxy-defaults config in dc1") kustomizeDir := "../fixtures/cases/api-gateways/mesh" k8s.KubectlApplyK(t, primaryContext.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, primaryContext.KubectlOptions(t), kustomizeDir) }) // these clients are just there so we can exec in and curl on them. logger.Log(t, "creating static-client in dc1") - k8s.DeployKustomize(t, primaryContext.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-multi-dc") + k8s.DeployKustomize(t, primaryContext.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-multi-dc") logger.Log(t, "creating static-client in dc2") - k8s.DeployKustomize(t, secondaryContext.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-multi-dc") + k8s.DeployKustomize(t, secondaryContext.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-multi-dc") t.Run("from primary to secondary", func(t *testing.T) { logger.Log(t, "creating static-server in dc2") - k8s.DeployKustomize(t, secondaryContext.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, secondaryContext.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") logger.Log(t, "creating api-gateway resources in dc1") out, err := k8s.RunKubectlAndGetOutputE(t, primaryContext.KubectlOptions(t), "apply", "-k", "../fixtures/bases/api-gateway") require.NoError(t, err, out) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { // Ignore errors here because if the test ran as expected // the custom resources will have been deleted. k8s.RunKubectlAndGetOutputE(t, primaryContext.KubectlOptions(t), "delete", "-k", "../fixtures/bases/api-gateway") @@ -156,7 +156,7 @@ func TestWANFederation_Gateway(t *testing.T) { // create a service resolver for doing cross-dc redirects. k8s.KubectlApplyK(t, secondaryContext.KubectlOptions(t), "../fixtures/cases/api-gateways/dc1-to-dc2-resolver") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, secondaryContext.KubectlOptions(t), "../fixtures/cases/api-gateways/dc1-to-dc2-resolver") }) @@ -170,12 +170,12 @@ func TestWANFederation_Gateway(t *testing.T) { t.Run("from secondary to primary", func(t *testing.T) { // Check that we can connect services over the mesh gateways logger.Log(t, "creating static-server in dc1") - k8s.DeployKustomize(t, primaryContext.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, primaryContext.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") logger.Log(t, "creating api-gateway resources in dc2") out, err := k8s.RunKubectlAndGetOutputE(t, secondaryContext.KubectlOptions(t), "apply", "-k", "../fixtures/bases/api-gateway") require.NoError(t, err, out) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { // Ignore errors here because if the test ran as expected // the custom resources will have been deleted. k8s.RunKubectlAndGetOutputE(t, secondaryContext.KubectlOptions(t), "delete", "-k", "../fixtures/bases/api-gateway") @@ -183,7 +183,7 @@ func TestWANFederation_Gateway(t *testing.T) { // create a service resolver for doing cross-dc redirects. k8s.KubectlApplyK(t, secondaryContext.KubectlOptions(t), "../fixtures/cases/api-gateways/dc2-to-dc1-resolver") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, secondaryContext.KubectlOptions(t), "../fixtures/cases/api-gateways/dc2-to-dc1-resolver") }) diff --git a/acceptance/tests/wan-federation/wan_federation_test.go b/acceptance/tests/wan-federation/wan_federation_test.go index e7a128887e..bae8e8e9da 100644 --- a/acceptance/tests/wan-federation/wan_federation_test.go +++ b/acceptance/tests/wan-federation/wan_federation_test.go @@ -157,16 +157,16 @@ func TestWANFederation(t *testing.T) { logger.Log(t, "creating proxy-defaults config") kustomizeDir := "../fixtures/bases/mesh-gateway" k8s.KubectlApplyK(t, secondaryContext.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { k8s.KubectlDeleteK(t, secondaryContext.KubectlOptions(t), kustomizeDir) }) // Check that we can connect services over the mesh gateways logger.Log(t, "creating static-server in dc2") - k8s.DeployKustomize(t, secondaryContext.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, secondaryContext.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") logger.Log(t, "creating static-client in dc1") - k8s.DeployKustomize(t, primaryContext.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-multi-dc") + k8s.DeployKustomize(t, primaryContext.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-multi-dc") if c.secure { logger.Log(t, "creating intention") diff --git a/go.work b/go.work new file mode 100644 index 0000000000..318dd011c0 --- /dev/null +++ b/go.work @@ -0,0 +1,11 @@ + +use ( + ./acceptance + ./charts + ./cli + ./control-plane + ./control-plane/cni + ./hack/aws-acceptance-test-cleanup + ./hack/copy-crds-to-chart + ./hack/helm-reference-gen +) diff --git a/go.work.sum b/go.work.sum new file mode 100644 index 0000000000..2961d25a18 --- /dev/null +++ b/go.work.sum @@ -0,0 +1,352 @@ +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= +cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= +cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= +cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= +cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= +cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= +cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= +cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= +cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= +cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= +cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= +cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= +cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= +cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= +cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= +cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= +cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= +cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= +cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= +cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= +cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= +cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= +cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= +cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= +cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= +cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= +cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= +cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= +cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= +cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= +cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= +cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= +cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= +cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= +cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= +cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= +cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= +cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= +cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= +cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= +cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= +cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= +cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= +cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= +cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= +cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= +cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= +cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= +cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= +cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= +cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= +cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= +cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= +cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= +cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= +cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= +cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= +cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= +cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= +cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= +cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= +cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= +cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= +cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= +cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= +cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= +cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= +cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= +cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= +cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= +cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= +cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= +cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= +cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= +cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= +cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= +cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= +cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= +cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= +cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= +cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= +cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= +cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= +cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= +cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= +cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= +cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= +cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= +cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= +cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= +cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= +cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= +cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= +cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= +cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= +cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= +cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= +cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= +cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= +cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= +cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= +cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= +cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= +cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= +cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= +cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= +cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= +cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= +cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= +cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= +cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= +cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= +cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= +cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= +cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= +cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= +cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= +cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= +cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= +cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= +cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= +cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= +cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= +cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= +cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= +cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= +cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= +github.com/Azure/azure-sdk-for-go v46.0.0+incompatible h1:4qlEOCDcDQZTGczYGzbGYCdJfVpZLIs8AEo5+MoXBPw= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.1 h1:bvUhZciHydpBxBmCheUgxxbSwJy7xcfjkUsjUcqSojc= +github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= +github.com/ahmetb/gen-crd-api-reference-docs v0.3.0/go.mod h1:TdjdkYhlOifCQWPs1UdTma97kQQMozf5h26hTuG70u8= +github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= +github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= +github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= +github.com/gobuffalo/flect v0.3.0/go.mod h1:5pf3aGnsvqvCj50AVni7mJJF8ICxGZ8HomberC3pXLE= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/cel-go v0.12.6/go.mod h1:Jk7ljRzLBhkmiAwBoUxB1sZSCVBAzkqPF25olK/iRDw= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/googleapis/gax-go/v2 v2.7.1 h1:gF4c0zjUP2H/s/hEGyLA3I0fA2ZWjzYiONAD6cvPr8A= +github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI= +github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= +github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= +github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= +github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw= +github.com/onsi/ginkgo/v2 v2.6.0/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc= +github.com/onsi/ginkgo/v2 v2.6.1/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= +github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= +github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM= +github.com/onsi/gomega v1.24.2/go.mod h1:gs3J10IS7Z7r7eXRoNJIrNqU4ToQukCJhFtKrWgHWnk= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= +github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= +github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= +github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/vmware/govmomi v0.20.3 h1:gpw/0Ku+6RgF3jsi7fnCLmlcikBHfKBCUcu1qgc16OU= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +go.etcd.io/etcd/api/v3 v3.5.5/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8= +go.etcd.io/etcd/client/pkg/v3 v3.5.5/go.mod h1:ggrwbk069qxpKPq8/FKkQ3Xq9y39kbFR4LnKszpRXeQ= +go.etcd.io/etcd/client/v2 v2.305.5/go.mod h1:zQjKllfqfBVyVStbt4FaosoX2iYd8fV/GRy/PbowgP4= +go.etcd.io/etcd/client/v3 v3.5.5/go.mod h1:aApjR4WGlSumpnJ2kloS75h6aHUmAyaPLjHMxpc7E7c= +go.etcd.io/etcd/pkg/v3 v3.5.5/go.mod h1:6ksYFxttiUGzC2uxyqiyOEvhAiD0tuIqSZkX3TyPdaE= +go.etcd.io/etcd/raft/v3 v3.5.5/go.mod h1:76TA48q03g1y1VpTue92jZLr9lIHKUNcYdZOOGyx8rI= +go.etcd.io/etcd/server/v3 v3.5.5/go.mod h1:rZ95vDw/jrvsbj9XpTqPrTAB9/kzchVdhRirySPkUBc= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.25.0/go.mod h1:E5NNboN0UqSAki0Atn9kVwaN7I+l25gGxDqBueo/74E= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.35.0/go.mod h1:h8TWwRAhQpOd0aM5nYsRD8+flnkj+526GEIVlarH7eY= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.35.0/go.mod h1:9NiG9I2aHTKkcxqCILhjtyNA1QEiCjdBACv4IvrFQ+c= +go.opentelemetry.io/otel v1.0.1/go.mod h1:OPEOD4jIT2SlZPMmwT6FqZz2C0ZNdQqiWcoK6M0SNFU= +go.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.10.0/go.mod h1:78XhIg8Ht9vR4tbLNUhXsiOnE2HOuSeKAiAcoVQEpOY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.0.1/go.mod h1:Kv8liBeVNFkkkbilbgWRpV+wWuu+H5xdOT6HAgd30iw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.10.0/go.mod h1:Krqnjl22jUJ0HgMzw5eveuCvFDXY4nSYb4F8t5gdrag= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.1/go.mod h1:xOvWoTOrQjxjW61xtOmD/WKGRYb/P4NzRo3bs65U6Rk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.10.0/go.mod h1:OfUCyyIiDvNXHWpcWgbF+MWvqPZiNa3YDEnivcnYsV0= +go.opentelemetry.io/otel/metric v0.31.0/go.mod h1:ohmwj9KTSIeBnDBm/ZwH2PSZxZzoOaG2xZeekTRzL5A= +go.opentelemetry.io/otel/sdk v1.0.1/go.mod h1:HrdXne+BiwsOHYYkBE5ysIcv2bvdZstxzmCQhxTcZkI= +go.opentelemetry.io/otel/sdk v1.10.0/go.mod h1:vO06iKzD5baltJz1zarxMCNHFpUlUiOy4s65ECtn6kE= +go.opentelemetry.io/otel/sdk v1.11.1/go.mod h1:/l3FE4SupHJ12TduVjUkZtlfFqDCQJlOlithYrdktys= +go.opentelemetry.io/otel/trace v1.0.1/go.mod h1:5g4i4fKLaX2BQpSBsxw8YYcgKpMMSW3x7ZTuYBr3sUk= +go.opentelemetry.io/otel/trace v1.10.0/go.mod h1:Sij3YYczqAdz+EhmGhE6TpTxUO5/F/AzrK+kxfGqySM= +go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= +golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= +google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= +google.golang.org/api v0.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE= +google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +k8s.io/apiserver v0.26.3 h1:blBpv+yOiozkPH2aqClhJmJY+rp53Tgfac4SKPDJnU4= +k8s.io/apiserver v0.26.3/go.mod h1:CJe/VoQNcXdhm67EvaVjYXxR3QyfwpceKPuPaeLibTA= +k8s.io/code-generator v0.26.3/go.mod h1:ryaiIKwfxEJEaywEzx3dhWOydpVctKYbqLajJf0O8dI= +k8s.io/gengo v0.0.0-20201203183100-97869a43a9d9/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/gengo v0.0.0-20220902162205-c0856e24416d/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/klog v0.2.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= +k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kms v0.26.3/go.mod h1:69qGnf1NsFOQP07fBYqNLZklqEHSJF024JqYCaeVxHg= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= +k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.36/go.mod h1:WxjusMwXlKzfAs4p9km6XJRndVt2FROgMVCE4cdohFo= +sigs.k8s.io/controller-tools v0.11.4/go.mod h1:qcfX7jfcfYD/b7lAhvqAyTbt/px4GpvN88WKLFFv7p8= +sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU= From 8b45de8b153c158d68c972b9e872acc1e4b06825 Mon Sep 17 00:00:00 2001 From: skpratt Date: Thu, 20 Jul 2023 23:43:57 -0500 Subject: [PATCH 293/340] Differentiate FIPS linux package names (#2599) --- .github/workflows/build.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1d7942617d..12904b3070 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -79,8 +79,8 @@ jobs: - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s.exe" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "darwin", goarch: "amd64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "darwin", goarch: "arm64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s" } - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto", fips: ".fips1402" } - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "arm64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto CC=aarch64-linux-gnu-gcc", fips: ".fips1402" } + - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto", fips: ".fips1402", pkg_suffix: "-fips" } + - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "arm64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto CC=aarch64-linux-gnu-gcc", fips: ".fips1402", pkg_suffix: "-fips" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s.exe", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=cngcrypto", fips: ".fips1402" } # control-plane @@ -96,8 +96,8 @@ jobs: - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane.exe" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "darwin", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "darwin", goarch: "arm64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto", fips: ".fips1402" } - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "arm64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto CC=aarch64-linux-gnu-gcc", fips: ".fips1402" } + - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto", fips: ".fips1402", pkg_suffix: "-fips" } + - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "arm64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto CC=aarch64-linux-gnu-gcc", fips: ".fips1402", pkg_suffix: "-fips" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane.exe", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=cngcrypto", fips: ".fips1402" } # consul-cni @@ -112,8 +112,8 @@ jobs: - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni.exe" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "darwin", goarch: "amd64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "darwin", goarch: "arm64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni" } - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto", fips: ".fips1402" } - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "arm64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto CC=aarch64-linux-gnu-gcc", fips: ".fips1402" } + - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto", fips: ".fips1402", pkg_suffix: "-fips" } + - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "arm64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto CC=aarch64-linux-gnu-gcc", fips: ".fips1402", pkg_suffix: "-fips" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni.exe", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=cngcrypto", fips: ".fips1402" } @@ -174,7 +174,7 @@ jobs: if: ${{ matrix.goos == 'linux' && matrix.component == 'cli' && matrix.goarch == 'amd64'}} uses: hashicorp/actions-packaging-linux@v1 with: - name: consul-k8s + name: consul-k8s${{ matrix.pkg_suffix }} description: "consul-k8s provides a cli interface to first-class integrations between Consul and Kubernetes." arch: ${{ matrix.goarch }} version: ${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }} From efa2be8fe702bbbdbbc86fcecc8ba33a28c9f650 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Fri, 21 Jul 2023 09:47:53 -0700 Subject: [PATCH 294/340] added make target for checking for hashicorppreview (#2603) * added make target for checking for hashicorppreview * added check to prepare-release make target --- Makefile | 10 ++++++++-- .../build-support/scripts/check-hashicorppreview.sh | 9 +++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100755 control-plane/build-support/scripts/check-hashicorppreview.sh diff --git a/Makefile b/Makefile index 1d62035dbb..4289b81b9b 100644 --- a/Makefile +++ b/Makefile @@ -97,6 +97,10 @@ terraform-fmt: @terraform fmt -recursive .PHONY: terraform-fmt +# Check for hashicorppreview containers +check-preview-containers: + @source $(CURDIR)/control-plane/build-support/scripts/check-hashicorppreview.sh + # ===========> CLI Targets cli-dev: @@ -221,7 +225,7 @@ aks-test-packages: check-env: @printenv | grep "CONSUL_K8S" -prepare-release: ## Sets the versions, updates changelog to prepare this repository to release +prepare-release-script: ## Sets the versions, updates changelog to prepare this repository to release ifndef CONSUL_K8S_RELEASE_VERSION $(error CONSUL_K8S_RELEASE_VERSION is required) endif @@ -234,7 +238,9 @@ endif ifndef CONSUL_K8S_CONSUL_VERSION $(error CONSUL_K8S_CONSUL_VERSION is required) endif - source $(CURDIR)/control-plane/build-support/scripts/functions.sh; prepare_release $(CURDIR) $(CONSUL_K8S_RELEASE_VERSION) "$(CONSUL_K8S_RELEASE_DATE)" $(CONSUL_K8S_LAST_RELEASE_GIT_TAG) $(CONSUL_K8S_CONSUL_VERSION) $(CONSUL_K8S_CONSUL_DATAPLANE_VERSION) $(CONSUL_K8S_PRERELEASE_VERSION) + @source $(CURDIR)/control-plane/build-support/scripts/functions.sh; prepare_release $(CURDIR) $(CONSUL_K8S_RELEASE_VERSION) "$(CONSUL_K8S_RELEASE_DATE)" $(CONSUL_K8S_LAST_RELEASE_GIT_TAG) $(CONSUL_K8S_CONSUL_VERSION) $(CONSUL_K8S_CONSUL_DATAPLANE_VERSION) $(CONSUL_K8S_PRERELEASE_VERSION); \ + +prepare-release: prepare-release-script check-preview-containers prepare-dev: ifndef CONSUL_K8S_RELEASE_VERSION diff --git a/control-plane/build-support/scripts/check-hashicorppreview.sh b/control-plane/build-support/scripts/check-hashicorppreview.sh new file mode 100755 index 0000000000..cd694dad93 --- /dev/null +++ b/control-plane/build-support/scripts/check-hashicorppreview.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 +echo "Checking charts for hashicorpreview images. . ." +if grep -rnw -e 'hashicorppreview' './charts'; then + echo Charts contain hashicorppreview images. If this is intended for release, please remove the preview images. +else + echo Charts do not contain hashicorpreview images, ready for release! +fi \ No newline at end of file From e2adf6fe0ada638295ae2fdeadba054abf65cfe6 Mon Sep 17 00:00:00 2001 From: Michael Zalimeni Date: Fri, 21 Jul 2023 13:13:34 -0400 Subject: [PATCH 295/340] Increase golangci-lint timeout to 10m (#2621) This is meant to solve for recurrent timeouts in several steps, particularly `golangci-lint-control-plane` and `golang-ci-lint-cli`. An accompanying change in `consul-k8s-workflows` should disable caching until the (unclear) root of the issue can be resolved, or we can disable or clear cache in a more targeted way that solves for these cases. --- .golangci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.golangci.yml b/.golangci.yml index 142f5c2722..dcad005d10 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -34,4 +34,4 @@ linters-settings: simplify: true run: - timeout: 5m + timeout: 10m From 1690fe297fdd02afafeb3713b978a30cf846b86e Mon Sep 17 00:00:00 2001 From: Paul Glass Date: Mon, 24 Jul 2023 10:38:31 -0500 Subject: [PATCH 296/340] Fix TestAPIGateway_GatewayClassConfig (#2631) * Fix TestAPIGateway_GatewayClassConfig * Remove stray files from bad merge --- .../api_gateway_gatewayclassconfig_test.go | 20 - go.work | 11 - go.work.sum | 352 ------------------ 3 files changed, 383 deletions(-) delete mode 100644 go.work delete mode 100644 go.work.sum diff --git a/acceptance/tests/api-gateway/api_gateway_gatewayclassconfig_test.go b/acceptance/tests/api-gateway/api_gateway_gatewayclassconfig_test.go index ffe0424ef1..89ba07a1e7 100644 --- a/acceptance/tests/api-gateway/api_gateway_gatewayclassconfig_test.go +++ b/acceptance/tests/api-gateway/api_gateway_gatewayclassconfig_test.go @@ -63,22 +63,6 @@ func TestAPIGateway_GatewayClassConfig(t *testing.T) { k8sClient := ctx.ControllerRuntimeClient(t) - //create clean namespace - err = k8sClient.Create(context.Background(), &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: namespace, - }, - }) - require.NoError(t, err) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - logger.Log(t, "deleting gateway namesapce") - k8sClient.Delete(context.Background(), &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: namespace, - }, - }) - }) - // create a GatewayClassConfig with configuration set gatewayClassConfigName := "gateway-class-config" gatewayClassConfig := &v1alpha1.GatewayClassConfig{ @@ -144,10 +128,6 @@ func TestAPIGateway_GatewayClassConfig(t *testing.T) { logger.Log(t, "creating controlled gateway") gateway := createGateway(t, k8sClient, gatewayName, namespace, gatewayClassName, certificateName) - // make sure it exists - logger.Log(t, "checking that gateway one is synchronized to Consul") - checkConsulExists(t, consulClient, api.APIGateway, gatewayName) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { logger.Log(t, "deleting all gateways") k8sClient.DeleteAllOf(context.Background(), &gwv1beta1.Gateway{}, client.InNamespace(namespace)) diff --git a/go.work b/go.work deleted file mode 100644 index 318dd011c0..0000000000 --- a/go.work +++ /dev/null @@ -1,11 +0,0 @@ - -use ( - ./acceptance - ./charts - ./cli - ./control-plane - ./control-plane/cni - ./hack/aws-acceptance-test-cleanup - ./hack/copy-crds-to-chart - ./hack/helm-reference-gen -) diff --git a/go.work.sum b/go.work.sum deleted file mode 100644 index 2961d25a18..0000000000 --- a/go.work.sum +++ /dev/null @@ -1,352 +0,0 @@ -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= -cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= -cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= -cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= -cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= -cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= -cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= -cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= -cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= -cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= -cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= -cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= -cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= -cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= -cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= -cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= -cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= -cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= -cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= -cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= -cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= -cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= -cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= -cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= -cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= -cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= -cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= -cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= -cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= -cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= -cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= -cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= -cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= -cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= -cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= -cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= -cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= -cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= -cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= -cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= -cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= -cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= -cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= -cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= -cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= -cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= -cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= -cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= -cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= -cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= -cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= -cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= -cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= -cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= -cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= -cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= -cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= -cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= -cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= -cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= -cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= -cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= -cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= -cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= -cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= -cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= -cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= -cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= -cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= -cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= -cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= -cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= -cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= -cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= -cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= -cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= -cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= -cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= -cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= -cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= -cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= -cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= -cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= -cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= -cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= -cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= -cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= -cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= -cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= -cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= -cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= -cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= -cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= -cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= -cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= -cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= -cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= -cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= -cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= -cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= -cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= -cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= -cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= -cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= -cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= -cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= -cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= -cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= -cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= -cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= -cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= -cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= -cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= -cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= -cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= -cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= -cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= -cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= -cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= -cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= -cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= -cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= -cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= -cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= -cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= -cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= -cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= -github.com/Azure/azure-sdk-for-go v46.0.0+incompatible h1:4qlEOCDcDQZTGczYGzbGYCdJfVpZLIs8AEo5+MoXBPw= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.1 h1:bvUhZciHydpBxBmCheUgxxbSwJy7xcfjkUsjUcqSojc= -github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= -github.com/ahmetb/gen-crd-api-reference-docs v0.3.0/go.mod h1:TdjdkYhlOifCQWPs1UdTma97kQQMozf5h26hTuG70u8= -github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= -github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= -github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= -github.com/gobuffalo/flect v0.3.0/go.mod h1:5pf3aGnsvqvCj50AVni7mJJF8ICxGZ8HomberC3pXLE= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/cel-go v0.12.6/go.mod h1:Jk7ljRzLBhkmiAwBoUxB1sZSCVBAzkqPF25olK/iRDw= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= -github.com/googleapis/gax-go/v2 v2.7.1 h1:gF4c0zjUP2H/s/hEGyLA3I0fA2ZWjzYiONAD6cvPr8A= -github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= -github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI= -github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= -github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= -github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= -github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw= -github.com/onsi/ginkgo/v2 v2.6.0/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc= -github.com/onsi/ginkgo/v2 v2.6.1/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo= -github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= -github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= -github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM= -github.com/onsi/gomega v1.24.2/go.mod h1:gs3J10IS7Z7r7eXRoNJIrNqU4ToQukCJhFtKrWgHWnk= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= -github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= -github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/vmware/govmomi v0.20.3 h1:gpw/0Ku+6RgF3jsi7fnCLmlcikBHfKBCUcu1qgc16OU= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.etcd.io/etcd/api/v3 v3.5.5/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8= -go.etcd.io/etcd/client/pkg/v3 v3.5.5/go.mod h1:ggrwbk069qxpKPq8/FKkQ3Xq9y39kbFR4LnKszpRXeQ= -go.etcd.io/etcd/client/v2 v2.305.5/go.mod h1:zQjKllfqfBVyVStbt4FaosoX2iYd8fV/GRy/PbowgP4= -go.etcd.io/etcd/client/v3 v3.5.5/go.mod h1:aApjR4WGlSumpnJ2kloS75h6aHUmAyaPLjHMxpc7E7c= -go.etcd.io/etcd/pkg/v3 v3.5.5/go.mod h1:6ksYFxttiUGzC2uxyqiyOEvhAiD0tuIqSZkX3TyPdaE= -go.etcd.io/etcd/raft/v3 v3.5.5/go.mod h1:76TA48q03g1y1VpTue92jZLr9lIHKUNcYdZOOGyx8rI= -go.etcd.io/etcd/server/v3 v3.5.5/go.mod h1:rZ95vDw/jrvsbj9XpTqPrTAB9/kzchVdhRirySPkUBc= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.25.0/go.mod h1:E5NNboN0UqSAki0Atn9kVwaN7I+l25gGxDqBueo/74E= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.35.0/go.mod h1:h8TWwRAhQpOd0aM5nYsRD8+flnkj+526GEIVlarH7eY= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.35.0/go.mod h1:9NiG9I2aHTKkcxqCILhjtyNA1QEiCjdBACv4IvrFQ+c= -go.opentelemetry.io/otel v1.0.1/go.mod h1:OPEOD4jIT2SlZPMmwT6FqZz2C0ZNdQqiWcoK6M0SNFU= -go.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.10.0/go.mod h1:78XhIg8Ht9vR4tbLNUhXsiOnE2HOuSeKAiAcoVQEpOY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.0.1/go.mod h1:Kv8liBeVNFkkkbilbgWRpV+wWuu+H5xdOT6HAgd30iw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.10.0/go.mod h1:Krqnjl22jUJ0HgMzw5eveuCvFDXY4nSYb4F8t5gdrag= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.1/go.mod h1:xOvWoTOrQjxjW61xtOmD/WKGRYb/P4NzRo3bs65U6Rk= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.10.0/go.mod h1:OfUCyyIiDvNXHWpcWgbF+MWvqPZiNa3YDEnivcnYsV0= -go.opentelemetry.io/otel/metric v0.31.0/go.mod h1:ohmwj9KTSIeBnDBm/ZwH2PSZxZzoOaG2xZeekTRzL5A= -go.opentelemetry.io/otel/sdk v1.0.1/go.mod h1:HrdXne+BiwsOHYYkBE5ysIcv2bvdZstxzmCQhxTcZkI= -go.opentelemetry.io/otel/sdk v1.10.0/go.mod h1:vO06iKzD5baltJz1zarxMCNHFpUlUiOy4s65ECtn6kE= -go.opentelemetry.io/otel/sdk v1.11.1/go.mod h1:/l3FE4SupHJ12TduVjUkZtlfFqDCQJlOlithYrdktys= -go.opentelemetry.io/otel/trace v1.0.1/go.mod h1:5g4i4fKLaX2BQpSBsxw8YYcgKpMMSW3x7ZTuYBr3sUk= -go.opentelemetry.io/otel/trace v1.10.0/go.mod h1:Sij3YYczqAdz+EhmGhE6TpTxUO5/F/AzrK+kxfGqySM= -go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= -golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= -google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= -google.golang.org/api v0.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE= -google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= -google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= -google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= -google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -k8s.io/apiserver v0.26.3 h1:blBpv+yOiozkPH2aqClhJmJY+rp53Tgfac4SKPDJnU4= -k8s.io/apiserver v0.26.3/go.mod h1:CJe/VoQNcXdhm67EvaVjYXxR3QyfwpceKPuPaeLibTA= -k8s.io/code-generator v0.26.3/go.mod h1:ryaiIKwfxEJEaywEzx3dhWOydpVctKYbqLajJf0O8dI= -k8s.io/gengo v0.0.0-20201203183100-97869a43a9d9/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/gengo v0.0.0-20220902162205-c0856e24416d/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog v0.2.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= -k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kms v0.26.3/go.mod h1:69qGnf1NsFOQP07fBYqNLZklqEHSJF024JqYCaeVxHg= -k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= -k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.36/go.mod h1:WxjusMwXlKzfAs4p9km6XJRndVt2FROgMVCE4cdohFo= -sigs.k8s.io/controller-tools v0.11.4/go.mod h1:qcfX7jfcfYD/b7lAhvqAyTbt/px4GpvN88WKLFFv7p8= -sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU= From 3932e2882837c4db37e6c1acadcc959901b57b62 Mon Sep 17 00:00:00 2001 From: Paul Glass Date: Mon, 24 Jul 2023 11:06:15 -0500 Subject: [PATCH 297/340] Support running with restricted PSA enforcement enabled (part 1) (#2572) Support restricted PSA enforcement in a basic setup. This is enough to get a basic setup with ACLs and TLS working and an acceptance test passing (but does not update every component). On OpenShift, we have the option to set the security context or not. If the security context is unset, then it is set automatically by OpenShift SCCs. However, we prefer to set the security context to avoid useless warnings on OpenShift and to reduce the config difference between OpenShift and plain Kube. By default, OpenShift namespaces have the audit and warn PSA labels set to restricted, so we receive pod security warnings when deploying Consul to OpenShift even though the pods will be able to run. Helm chart changes: * Add a helper to the helm chart to define a "restricted" container security context (when pod security policies are not enabled) * Update the following container securityContexts to use the "restricted" settings (not exhaustive) - gateway-cleanup-job.yaml - gateway-resources-job.yaml - gossip-encryption-autogenerate-job.yaml - server-acl-init-cleanup-job.yaml - only if `.Values.server.containerSecurityContext.server.acl-init` is unset - server-acl-init-job.yaml - only if `.Values.server.containerSecurityContext.server.acl-init` is unset - server-statefulset.yaml: - the locality-init container receives the restricted context - the consul container receives the restricted context only if `.Values.server.containerSecurityContext.server` is unset - tls-init-cleanup-job.yaml - only if `.Values.server.containerSecurityContext.server.tls-init` is unset - tls-init-job.yaml - only if `.Values.server.containerSecurityContext.server.tls-init` is unset - webhook-cert-manager-deployment.yaml Acceptance test changes: * When `-enable-openshift` and `-enable-cni` are set, configure the CNI settings correctly for OpenShift. * Add the `-enable-restricted-psa-enforcement` test flag. When this is set, the tests assume the Consul namespace has restricted PSA enforcement enabled. The tests will deploy the CNI (if enabled) into the `kube-system` namespace. Compatible test cases will deploy applications outside of the Consul namespace. * Update the ConnectHelper to configure the NetworkAttachmentDefinition required to be compatible with the CNI on OpenShift. * Add fixtures for static-client and static-server for OpenShift. This is necessary because the deployment configs must reference the network attachment definition when using the CNI on OpenShift. * Update tests in the `acceptance/tests/connect` directory to either run or skip based on -enable-cni and -enable-openshift --- .changelog/2572.txt | 3 + acceptance/framework/config/config.go | 22 +++- acceptance/framework/config/config_test.go | 1 + .../framework/connhelper/connect_helper.go | 112 ++++++++++++++---- .../framework/consul/helm_cluster_test.go | 6 + .../framework/environment/environment.go | 10 ++ acceptance/framework/flags/flags.go | 14 ++- .../connect/connect_external_servers_test.go | 2 + .../connect/connect_inject_namespaces_test.go | 2 + .../tests/connect/connect_inject_test.go | 30 +++-- .../connect/connect_proxy_lifecycle_test.go | 1 + .../tests/connect/permissive_mtls_test.go | 1 + .../bases/openshift/network-attachment.yaml | 17 +++ .../kustomization.yaml | 8 ++ .../static-client-openshift-inject/patch.yaml | 14 +++ .../kustomization.yaml | 8 ++ .../static-client-openshift-tproxy/patch.yaml | 18 +++ .../kustomization.yaml | 8 ++ .../cases/static-server-openshift/patch.yaml | 42 +++++++ charts/consul/templates/_helpers.tpl | 25 +++- .../templates/connect-inject-deployment.yaml | 1 + .../consul/templates/gateway-cleanup-job.yaml | 1 + .../templates/gateway-resources-job.yaml | 1 + .../gossip-encryption-autogenerate-job.yaml | 1 + .../server-acl-init-cleanup-job.yaml | 3 + .../consul/templates/server-acl-init-job.yaml | 3 + .../consul/templates/server-statefulset.yaml | 5 +- .../templates/tls-init-cleanup-job.yaml | 3 + charts/consul/templates/tls-init-job.yaml | 3 + .../webhook-cert-manager-deployment.yaml | 1 + .../consul/test/unit/server-statefulset.bats | 70 ++++++++++- 31 files changed, 397 insertions(+), 39 deletions(-) create mode 100644 .changelog/2572.txt create mode 100644 acceptance/tests/fixtures/bases/openshift/network-attachment.yaml create mode 100644 acceptance/tests/fixtures/cases/static-client-openshift-inject/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/static-client-openshift-inject/patch.yaml create mode 100644 acceptance/tests/fixtures/cases/static-client-openshift-tproxy/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/static-client-openshift-tproxy/patch.yaml create mode 100644 acceptance/tests/fixtures/cases/static-server-openshift/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/static-server-openshift/patch.yaml diff --git a/.changelog/2572.txt b/.changelog/2572.txt new file mode 100644 index 0000000000..4bc6c4ba50 --- /dev/null +++ b/.changelog/2572.txt @@ -0,0 +1,3 @@ +```release-note:improvement +helm: set container securityContexts to match the `restricted` Pod Security Standards policy to support running Consul in a namespace with restricted PSA enforcement enabled +``` diff --git a/acceptance/framework/config/config.go b/acceptance/framework/config/config.go index 74bb7daf6e..ee07df63fd 100644 --- a/acceptance/framework/config/config.go +++ b/acceptance/framework/config/config.go @@ -10,6 +10,7 @@ import ( "path/filepath" "strconv" "strings" + "testing" "github.com/hashicorp/go-version" "gopkg.in/yaml.v2" @@ -73,7 +74,8 @@ type TestConfig struct { EnablePodSecurityPolicies bool - EnableCNI bool + EnableCNI bool + EnableRestrictedPSAEnforcement bool EnableTransparentProxy bool @@ -135,10 +137,22 @@ func (t *TestConfig) HelmValuesFromConfig() (map[string]string, error) { if t.EnableCNI { setIfNotEmpty(helmValues, "connectInject.cni.enabled", "true") + setIfNotEmpty(helmValues, "connectInject.cni.logLevel", "debug") // GKE is currently the only cloud provider that uses a different CNI bin dir. if t.UseGKE { setIfNotEmpty(helmValues, "connectInject.cni.cniBinDir", "/home/kubernetes/bin") } + if t.EnableOpenshift { + setIfNotEmpty(helmValues, "connectInject.cni.multus", "true") + setIfNotEmpty(helmValues, "connectInject.cni.cniBinDir", "/var/lib/cni/bin") + setIfNotEmpty(helmValues, "connectInject.cni.cniNetDir", "/etc/kubernetes/cni/net.d") + } + + if t.EnableRestrictedPSAEnforcement { + // The CNI requires privilege, so when restricted PSA enforcement is enabled on the Consul + // namespace it must be run in a different privileged namespace. + setIfNotEmpty(helmValues, "connectInject.cni.namespace", "kube-system") + } } setIfNotEmpty(helmValues, "connectInject.transparentProxy.defaultEnabled", strconv.FormatBool(t.EnableTransparentProxy)) @@ -220,6 +234,12 @@ func (t *TestConfig) entImage() (string, error) { return fmt.Sprintf("hashicorp/consul-enterprise:%s%s-ent", consulImageVersion, preRelease), nil } +func (c *TestConfig) SkipWhenOpenshiftAndCNI(t *testing.T) { + if c.EnableOpenshift && c.EnableCNI { + t.Skip("skipping because -enable-cni and -enable-openshift are set and this test doesn't deploy apps correctly") + } +} + // setIfNotEmpty sets key to val in map m if value is not empty. func setIfNotEmpty(m map[string]string, key, val string) { if val != "" { diff --git a/acceptance/framework/config/config_test.go b/acceptance/framework/config/config_test.go index 96f0f0e7eb..df981e26fa 100644 --- a/acceptance/framework/config/config_test.go +++ b/acceptance/framework/config/config_test.go @@ -116,6 +116,7 @@ func TestConfig_HelmValuesFromConfig(t *testing.T) { }, map[string]string{ "connectInject.cni.enabled": "true", + "connectInject.cni.logLevel": "debug", "connectInject.transparentProxy.defaultEnabled": "false", }, }, diff --git a/acceptance/framework/connhelper/connect_helper.go b/acceptance/framework/connhelper/connect_helper.go index c34f663563..2eb18c9dbb 100644 --- a/acceptance/framework/connhelper/connect_helper.go +++ b/acceptance/framework/connhelper/connect_helper.go @@ -6,9 +6,11 @@ package connhelper import ( "context" "strconv" + "strings" "testing" "time" + terratestK8s "github.com/gruntwork-io/terratest/modules/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/config" "github.com/hashicorp/consul-k8s/acceptance/framework/consul" "github.com/hashicorp/consul-k8s/acceptance/framework/environment" @@ -44,7 +46,12 @@ type ConnectHelper struct { // ReleaseName is the name of the Consul cluster. ReleaseName string + // Ctx is used to deploy Consul Ctx environment.TestContext + // UseAppNamespace is used top optionally deploy applications into a separate namespace. + // If unset, the namespace associated with Ctx is used. + UseAppNamespace bool + Cfg *config.TestConfig // consulCluster is the cluster to use for the test. @@ -82,6 +89,14 @@ func (c *ConnectHelper) Upgrade(t *testing.T) { c.consulCluster.Upgrade(t, c.helmValues()) } +func (c *ConnectHelper) KubectlOptsForApp(t *testing.T) *terratestK8s.KubectlOptions { + opts := c.Ctx.KubectlOptions(t) + if !c.UseAppNamespace { + return opts + } + return c.Ctx.KubectlOptionsForNamespace(opts.Namespace + "-apps") +} + // DeployClientAndServer deploys a client and server pod to the Kubernetes // cluster which will be used to test service mesh connectivity. If the Secure // flag is true, a pre-check is done to ensure that the ACL tokens for the test @@ -108,23 +123,46 @@ func (c *ConnectHelper) DeployClientAndServer(t *testing.T) { logger.Log(t, "creating static-server and static-client deployments") - k8s.DeployKustomize(t, c.Ctx.KubectlOptions(t), c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, c.Cfg.DebugDirectory, "../fixtures/cases/static-server-inject") - if c.Cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, c.Ctx.KubectlOptions(t), c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, c.Cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + c.setupAppNamespace(t) + + opts := c.KubectlOptsForApp(t) + if c.Cfg.EnableCNI && c.Cfg.EnableOpenshift { + // On OpenShift with the CNI, we need to create a network attachment definition in the namespace + // where the applications are running, and the app deployment configs need to reference that network + // attachment definition. + + // TODO: A base fixture is the wrong place for these files + k8s.KubectlApply(t, opts, "../fixtures/bases/openshift/") + helpers.Cleanup(t, c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, func() { + k8s.KubectlDelete(t, opts, "../fixtures/bases/openshift/") + }) + + k8s.DeployKustomize(t, opts, c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, c.Cfg.DebugDirectory, "../fixtures/cases/static-server-openshift") + if c.Cfg.EnableTransparentProxy { + k8s.DeployKustomize(t, opts, c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, c.Cfg.DebugDirectory, "../fixtures/cases/static-client-openshift-tproxy") + } else { + k8s.DeployKustomize(t, opts, c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, c.Cfg.DebugDirectory, "../fixtures/cases/static-client-openshift-inject") + } } else { - k8s.DeployKustomize(t, c.Ctx.KubectlOptions(t), c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, c.Cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + k8s.DeployKustomize(t, c.Ctx.KubectlOptions(t), c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, c.Cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + if c.Cfg.EnableTransparentProxy { + k8s.DeployKustomize(t, c.Ctx.KubectlOptions(t), c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, c.Cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + } else { + k8s.DeployKustomize(t, c.Ctx.KubectlOptions(t), c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, c.Cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + } } - // Check that both static-server and static-client have been injected and // now have 2 containers. retry.RunWith( &retry.Timer{Timeout: 30 * time.Second, Wait: 100 * time.Millisecond}, t, func(r *retry.R) { for _, labelSelector := range []string{"app=static-server", "app=static-client"} { - podList, err := c.Ctx.KubernetesClient(t).CoreV1().Pods(c.Ctx.KubectlOptions(t).Namespace).List(context.Background(), metav1.ListOptions{ - LabelSelector: labelSelector, - FieldSelector: `status.phase=Running`, - }) + podList, err := c.Ctx.KubernetesClient(t).CoreV1(). + Pods(opts.Namespace). + List(context.Background(), metav1.ListOptions{ + LabelSelector: labelSelector, + FieldSelector: `status.phase=Running`, + }) require.NoError(r, err) require.Len(r, podList.Items, 1) require.Len(r, podList.Items[0].Spec.Containers, 2) @@ -132,16 +170,46 @@ func (c *ConnectHelper) DeployClientAndServer(t *testing.T) { }) } +// setupAppNamespace creates a namespace where applications are deployed. This +// does nothing if UseAppNamespace is not set. The app namespace is relevant +// when testing with restricted PSA enforcement enabled. +func (c *ConnectHelper) setupAppNamespace(t *testing.T) { + if !c.UseAppNamespace { + return + } + opts := c.KubectlOptsForApp(t) + // If we are deploying apps in another namespace, create the namespace. + + _, err := k8s.RunKubectlAndGetOutputE(t, opts, "create", "ns", opts.Namespace) + if err != nil && strings.Contains(err.Error(), "AlreadyExists") { + return + } + require.NoError(t, err) + helpers.Cleanup(t, c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, func() { + k8s.RunKubectl(t, opts, "delete", "ns", opts.Namespace) + }) + + if c.Cfg.EnableRestrictedPSAEnforcement { + // Allow anything to run in the app namespace. + k8s.RunKubectl(t, opts, "label", "--overwrite", "ns", opts.Namespace, + "pod-security.kubernetes.io/enforce=privileged", + "pod-security.kubernetes.io/enforce-version=v1.24", + ) + } + +} + // CreateResolverRedirect creates a resolver that redirects to a static-server, a corresponding k8s service, // and intentions. This helper is primarly used to ensure that the virtual-ips are persisted to consul properly. func (c *ConnectHelper) CreateResolverRedirect(t *testing.T) { logger.Log(t, "creating resolver redirect") - options := c.Ctx.KubectlOptions(t) + opts := c.KubectlOptsForApp(t) + c.setupAppNamespace(t) kustomizeDir := "../fixtures/cases/resolver-redirect-virtualip" - k8s.KubectlApplyK(t, options, kustomizeDir) + k8s.KubectlApplyK(t, opts, kustomizeDir) helpers.Cleanup(t, c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, func() { - k8s.KubectlDeleteK(t, options, kustomizeDir) + k8s.KubectlDeleteK(t, opts, kustomizeDir) }) } @@ -149,10 +217,11 @@ func (c *ConnectHelper) CreateResolverRedirect(t *testing.T) { // server fails when no intentions are configured. func (c *ConnectHelper) TestConnectionFailureWithoutIntention(t *testing.T) { logger.Log(t, "checking that the connection is not successful because there's no intention") + opts := c.KubectlOptsForApp(t) if c.Cfg.EnableTransparentProxy { - k8s.CheckStaticServerConnectionFailing(t, c.Ctx.KubectlOptions(t), StaticClientName, "http://static-server") + k8s.CheckStaticServerConnectionFailing(t, opts, StaticClientName, "http://static-server") } else { - k8s.CheckStaticServerConnectionFailing(t, c.Ctx.KubectlOptions(t), StaticClientName, "http://localhost:1234") + k8s.CheckStaticServerConnectionFailing(t, opts, StaticClientName, "http://localhost:1234") } } @@ -177,11 +246,12 @@ func (c *ConnectHelper) CreateIntention(t *testing.T) { // static-client pod once the intention is set. func (c *ConnectHelper) TestConnectionSuccess(t *testing.T) { logger.Log(t, "checking that connection is successful") + opts := c.KubectlOptsForApp(t) if c.Cfg.EnableTransparentProxy { // todo: add an assertion that the traffic is going through the proxy - k8s.CheckStaticServerConnectionSuccessful(t, c.Ctx.KubectlOptions(t), StaticClientName, "http://static-server") + k8s.CheckStaticServerConnectionSuccessful(t, opts, StaticClientName, "http://static-server") } else { - k8s.CheckStaticServerConnectionSuccessful(t, c.Ctx.KubectlOptions(t), StaticClientName, "http://localhost:1234") + k8s.CheckStaticServerConnectionSuccessful(t, opts, StaticClientName, "http://localhost:1234") } } @@ -192,8 +262,10 @@ func (c *ConnectHelper) TestConnectionFailureWhenUnhealthy(t *testing.T) { // Test that kubernetes readiness status is synced to Consul. // Create a file called "unhealthy" at "/tmp/" so that the readiness probe // of the static-server pod fails. + opts := c.KubectlOptsForApp(t) + logger.Log(t, "testing k8s -> consul health checks sync by making the static-server unhealthy") - k8s.RunKubectl(t, c.Ctx.KubectlOptions(t), "exec", "deploy/"+StaticServerName, "--", "touch", "/tmp/unhealthy") + k8s.RunKubectl(t, opts, "exec", "deploy/"+StaticServerName, "--", "touch", "/tmp/unhealthy") // The readiness probe should take a moment to be reflected in Consul, // CheckStaticServerConnection will retry until Consul marks the service @@ -205,20 +277,20 @@ func (c *ConnectHelper) TestConnectionFailureWhenUnhealthy(t *testing.T) { // other tests. logger.Log(t, "checking that connection is unsuccessful") if c.Cfg.EnableTransparentProxy { - k8s.CheckStaticServerConnectionMultipleFailureMessages(t, c.Ctx.KubectlOptions(t), StaticClientName, false, []string{ + k8s.CheckStaticServerConnectionMultipleFailureMessages(t, opts, StaticClientName, false, []string{ "curl: (56) Recv failure: Connection reset by peer", "curl: (52) Empty reply from server", "curl: (7) Failed to connect to static-server port 80: Connection refused", }, "", "http://static-server") } else { - k8s.CheckStaticServerConnectionMultipleFailureMessages(t, c.Ctx.KubectlOptions(t), StaticClientName, false, []string{ + k8s.CheckStaticServerConnectionMultipleFailureMessages(t, opts, StaticClientName, false, []string{ "curl: (56) Recv failure: Connection reset by peer", "curl: (52) Empty reply from server", }, "", "http://localhost:1234") } // Return the static-server to a "healthy state". - k8s.RunKubectl(t, c.Ctx.KubectlOptions(t), "exec", "deploy/"+StaticServerName, "--", "rm", "/tmp/unhealthy") + k8s.RunKubectl(t, opts, "exec", "deploy/"+StaticServerName, "--", "rm", "/tmp/unhealthy") } // helmValues uses the Secure and AutoEncrypt fields to set values for the Helm diff --git a/acceptance/framework/consul/helm_cluster_test.go b/acceptance/framework/consul/helm_cluster_test.go index 9c44006d43..011ca23e0f 100644 --- a/acceptance/framework/consul/helm_cluster_test.go +++ b/acceptance/framework/consul/helm_cluster_test.go @@ -8,6 +8,7 @@ import ( "github.com/gruntwork-io/terratest/modules/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/config" + "github.com/hashicorp/consul-k8s/acceptance/framework/environment" "github.com/stretchr/testify/require" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/fake" @@ -80,9 +81,14 @@ func (c *ctx) Name() string { func (c *ctx) KubectlOptions(_ *testing.T) *k8s.KubectlOptions { return &k8s.KubectlOptions{} } +func (c *ctx) KubectlOptionsForNamespace(ns string) *k8s.KubectlOptions { + return &k8s.KubectlOptions{} +} func (c *ctx) KubernetesClient(_ *testing.T) kubernetes.Interface { return fake.NewSimpleClientset() } func (c *ctx) ControllerRuntimeClient(_ *testing.T) client.Client { return runtimefake.NewClientBuilder().Build() } + +var _ environment.TestContext = (*ctx)(nil) diff --git a/acceptance/framework/environment/environment.go b/acceptance/framework/environment/environment.go index 9150f4ff03..7014a3c05f 100644 --- a/acceptance/framework/environment/environment.go +++ b/acceptance/framework/environment/environment.go @@ -35,6 +35,8 @@ type TestEnvironment interface { // for example, information about a specific Kubernetes cluster. type TestContext interface { KubectlOptions(t *testing.T) *k8s.KubectlOptions + // TODO: I don't love this. + KubectlOptionsForNamespace(ns string) *k8s.KubectlOptions KubernetesClient(t *testing.T) kubernetes.Interface ControllerRuntimeClient(t *testing.T) client.Client } @@ -138,6 +140,14 @@ func (k kubernetesContext) KubectlOptions(t *testing.T) *k8s.KubectlOptions { return k.options } +func (k kubernetesContext) KubectlOptionsForNamespace(ns string) *k8s.KubectlOptions { + return &k8s.KubectlOptions{ + ContextName: k.kubeContextName, + ConfigPath: k.pathToKubeConfig, + Namespace: ns, + } +} + // KubernetesClientFromOptions takes KubectlOptions and returns Kubernetes API client. func KubernetesClientFromOptions(t *testing.T, options *k8s.KubectlOptions) kubernetes.Interface { configPath, err := options.GetConfigPath(t) diff --git a/acceptance/framework/flags/flags.go b/acceptance/framework/flags/flags.go index b1ad7e4332..ce8e1ea726 100644 --- a/acceptance/framework/flags/flags.go +++ b/acceptance/framework/flags/flags.go @@ -27,7 +27,8 @@ type TestFlags struct { flagEnablePodSecurityPolicies bool - flagEnableCNI bool + flagEnableCNI bool + flagEnableRestrictedPSAEnforcement bool flagEnableTransparentProxy bool @@ -116,6 +117,13 @@ func (t *TestFlags) init() { flag.BoolVar(&t.flagEnableCNI, "enable-cni", false, "If true, the test suite will run tests with consul-cni plugin enabled. "+ "In general, this will only run against tests that are mesh related (connect, mesh-gateway, peering, etc") + flag.BoolVar(&t.flagEnableRestrictedPSAEnforcement, "enable-restricted-psa-enforcement", false, + "If true, this indicates that Consul is being run in a namespace with restricted PSA enforcement enabled. "+ + "The tests do not configure Consul's namespace with PSA enforcement enabled. This must configured before tests are run. "+ + "The CNI and test applications need more privilege than is allowed in a restricted namespace. "+ + "When set, the CNI will be deployed into the kube-system namespace, and in supported test cases, applications "+ + "are deployed, by default, into a namespace named '-apps' instead of being deployed into the "+ + "Consul namespace.") flag.BoolVar(&t.flagEnableTransparentProxy, "enable-transparent-proxy", false, "If true, the test suite will run tests with transparent proxy enabled. "+ @@ -176,6 +184,7 @@ func (t *TestFlags) Validate() error { if t.flagEnableEnterprise && t.flagEnterpriseLicense == "" { return errors.New("-enable-enterprise provided without setting env var CONSUL_ENT_LICENSE with consul license") } + return nil } @@ -200,7 +209,8 @@ func (t *TestFlags) TestConfigFromFlags() *config.TestConfig { EnablePodSecurityPolicies: t.flagEnablePodSecurityPolicies, - EnableCNI: t.flagEnableCNI, + EnableCNI: t.flagEnableCNI, + EnableRestrictedPSAEnforcement: t.flagEnableRestrictedPSAEnforcement, EnableTransparentProxy: t.flagEnableTransparentProxy, diff --git a/acceptance/tests/connect/connect_external_servers_test.go b/acceptance/tests/connect/connect_external_servers_test.go index c421c1258e..46f9e14573 100644 --- a/acceptance/tests/connect/connect_external_servers_test.go +++ b/acceptance/tests/connect/connect_external_servers_test.go @@ -30,6 +30,8 @@ func TestConnectInject_ExternalServers(t *testing.T) { caseName := fmt.Sprintf("secure: %t", secure) t.Run(caseName, func(t *testing.T) { cfg := suite.Config() + cfg.SkipWhenOpenshiftAndCNI(t) + ctx := suite.Environment().DefaultContext(t) serverHelmValues := map[string]string{ diff --git a/acceptance/tests/connect/connect_inject_namespaces_test.go b/acceptance/tests/connect/connect_inject_namespaces_test.go index 6fb493ce17..7b7785a44f 100644 --- a/acceptance/tests/connect/connect_inject_namespaces_test.go +++ b/acceptance/tests/connect/connect_inject_namespaces_test.go @@ -34,6 +34,7 @@ func TestConnectInjectNamespaces(t *testing.T) { if !cfg.EnableEnterprise { t.Skipf("skipping this test because -enable-enterprise is not set") } + cfg.SkipWhenOpenshiftAndCNI(t) cases := []struct { name string @@ -246,6 +247,7 @@ func TestConnectInjectNamespaces_CleanupController(t *testing.T) { if !cfg.EnableEnterprise { t.Skipf("skipping this test because -enable-enterprise is not set") } + cfg.SkipWhenOpenshiftAndCNI(t) consulDestNS := "consul-dest" cases := []struct { diff --git a/acceptance/tests/connect/connect_inject_test.go b/acceptance/tests/connect/connect_inject_test.go index c239698d2a..7c7f17fc0a 100644 --- a/acceptance/tests/connect/connect_inject_test.go +++ b/acceptance/tests/connect/connect_inject_test.go @@ -38,11 +38,12 @@ func TestConnectInject(t *testing.T) { releaseName := helpers.RandomName() connHelper := connhelper.ConnectHelper{ - ClusterKind: consul.Helm, - Secure: c.secure, - ReleaseName: releaseName, - Ctx: ctx, - Cfg: cfg, + ClusterKind: consul.Helm, + Secure: c.secure, + ReleaseName: releaseName, + Ctx: ctx, + UseAppNamespace: cfg.EnableRestrictedPSAEnforcement, + Cfg: cfg, } connHelper.Setup(t) @@ -71,11 +72,12 @@ func TestConnectInject_VirtualIPFailover(t *testing.T) { releaseName := helpers.RandomName() connHelper := connhelper.ConnectHelper{ - ClusterKind: consul.Helm, - Secure: true, - ReleaseName: releaseName, - Ctx: ctx, - Cfg: cfg, + ClusterKind: consul.Helm, + Secure: true, + ReleaseName: releaseName, + Ctx: ctx, + UseAppNamespace: cfg.EnableRestrictedPSAEnforcement, + Cfg: cfg, } connHelper.Setup(t) @@ -84,7 +86,8 @@ func TestConnectInject_VirtualIPFailover(t *testing.T) { connHelper.CreateResolverRedirect(t) connHelper.DeployClientAndServer(t) - k8s.CheckStaticServerConnectionSuccessful(t, connHelper.Ctx.KubectlOptions(t), "static-client", "http://resolver-redirect") + opts := connHelper.KubectlOptsForApp(t) + k8s.CheckStaticServerConnectionSuccessful(t, opts, "static-client", "http://resolver-redirect") } // Test the endpoints controller cleans up force-killed pods. @@ -93,6 +96,9 @@ func TestConnectInject_CleanupKilledPods(t *testing.T) { name := fmt.Sprintf("secure: %t", secure) t.Run(name, func(t *testing.T) { cfg := suite.Config() + + cfg.SkipWhenOpenshiftAndCNI(t) + ctx := suite.Environment().DefaultContext(t) helmValues := map[string]string{ @@ -161,6 +167,8 @@ func TestConnectInject_MultiportServices(t *testing.T) { name := fmt.Sprintf("secure: %t", secure) t.Run(name, func(t *testing.T) { cfg := suite.Config() + cfg.SkipWhenOpenshiftAndCNI(t) + ctx := suite.Environment().DefaultContext(t) helmValues := map[string]string{ diff --git a/acceptance/tests/connect/connect_proxy_lifecycle_test.go b/acceptance/tests/connect/connect_proxy_lifecycle_test.go index 321c002a4c..39dd0b4ae7 100644 --- a/acceptance/tests/connect/connect_proxy_lifecycle_test.go +++ b/acceptance/tests/connect/connect_proxy_lifecycle_test.go @@ -34,6 +34,7 @@ const ( // Test the endpoints controller cleans up force-killed pods. func TestConnectInject_ProxyLifecycleShutdown(t *testing.T) { cfg := suite.Config() + cfg.SkipWhenOpenshiftAndCNI(t) for _, testCfg := range []LifecycleShutdownConfig{ {secure: false, helmValues: map[string]string{ diff --git a/acceptance/tests/connect/permissive_mtls_test.go b/acceptance/tests/connect/permissive_mtls_test.go index d07c44ae2f..929d56acfd 100644 --- a/acceptance/tests/connect/permissive_mtls_test.go +++ b/acceptance/tests/connect/permissive_mtls_test.go @@ -23,6 +23,7 @@ func TestConnectInject_PermissiveMTLS(t *testing.T) { if !cfg.EnableTransparentProxy { t.Skipf("skipping this because -enable-transparent-proxy is not set") } + cfg.SkipWhenOpenshiftAndCNI(t) ctx := suite.Environment().DefaultContext(t) diff --git a/acceptance/tests/fixtures/bases/openshift/network-attachment.yaml b/acceptance/tests/fixtures/bases/openshift/network-attachment.yaml new file mode 100644 index 0000000000..4b3f7948ee --- /dev/null +++ b/acceptance/tests/fixtures/bases/openshift/network-attachment.yaml @@ -0,0 +1,17 @@ +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: consul-cni +spec: + config: '{ + "cniVersion": "0.3.1", + "type": "consul-cni", + "cni_bin_dir": "/var/lib/cni/bin", + "cni_net_dir": "/etc/kubernetes/cni/net.d", + "kubeconfig": "ZZZ-consul-cni-kubeconfig", + "log_level": "debug", + "multus": true, + "name": "consul-cni", + "type": "consul-cni" + }' + diff --git a/acceptance/tests/fixtures/cases/static-client-openshift-inject/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-openshift-inject/kustomization.yaml new file mode 100644 index 0000000000..4d4a53b87f --- /dev/null +++ b/acceptance/tests/fixtures/cases/static-client-openshift-inject/kustomization.yaml @@ -0,0 +1,8 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ../../bases/static-client + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/static-client-openshift-inject/patch.yaml b/acceptance/tests/fixtures/cases/static-client-openshift-inject/patch.yaml new file mode 100644 index 0000000000..8cc6d10411 --- /dev/null +++ b/acceptance/tests/fixtures/cases/static-client-openshift-inject/patch.yaml @@ -0,0 +1,14 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: static-client +spec: + template: + metadata: + annotations: + "consul.hashicorp.com/connect-inject": "true" + "consul.hashicorp.com/connect-service-upstreams": "static-server:1234" + "k8s.v1.cni.cncf.io/networks": '[{ "name":"consul-cni" }]' diff --git a/acceptance/tests/fixtures/cases/static-client-openshift-tproxy/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-openshift-tproxy/kustomization.yaml new file mode 100644 index 0000000000..4d4a53b87f --- /dev/null +++ b/acceptance/tests/fixtures/cases/static-client-openshift-tproxy/kustomization.yaml @@ -0,0 +1,8 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ../../bases/static-client + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/static-client-openshift-tproxy/patch.yaml b/acceptance/tests/fixtures/cases/static-client-openshift-tproxy/patch.yaml new file mode 100644 index 0000000000..3b9c91fcc0 --- /dev/null +++ b/acceptance/tests/fixtures/cases/static-client-openshift-tproxy/patch.yaml @@ -0,0 +1,18 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +# When using the CNI on OpenShift, we need to specify the +# network attachment definition for the pods to use. This assumes +# that one named 'consul-cni' was created by the acceptance tests. + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: static-client +spec: + template: + metadata: + annotations: + "consul.hashicorp.com/connect-inject": "true" + "k8s.v1.cni.cncf.io/networks": '[{ "name":"consul-cni" }]' + diff --git a/acceptance/tests/fixtures/cases/static-server-openshift/kustomization.yaml b/acceptance/tests/fixtures/cases/static-server-openshift/kustomization.yaml new file mode 100644 index 0000000000..bc50c78adf --- /dev/null +++ b/acceptance/tests/fixtures/cases/static-server-openshift/kustomization.yaml @@ -0,0 +1,8 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ../../bases/static-server + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/static-server-openshift/patch.yaml b/acceptance/tests/fixtures/cases/static-server-openshift/patch.yaml new file mode 100644 index 0000000000..8e2ed857f3 --- /dev/null +++ b/acceptance/tests/fixtures/cases/static-server-openshift/patch.yaml @@ -0,0 +1,42 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: static-server +spec: + template: + metadata: + annotations: + "consul.hashicorp.com/connect-inject": "true" + "k8s.v1.cni.cncf.io/networks": '[{ "name":"consul-cni" }]' + spec: + containers: + - name: static-server + image: docker.mirror.hashicorp.services/kschoche/http-echo:latest + args: + - -text="hello world" + - -listen=:8080 + ports: + - containerPort: 8080 + name: http + livenessProbe: + httpGet: + port: 8080 + initialDelaySeconds: 1 + failureThreshold: 1 + periodSeconds: 1 + startupProbe: + httpGet: + port: 8080 + initialDelaySeconds: 1 + failureThreshold: 30 + periodSeconds: 1 + readinessProbe: + exec: + command: ['sh', '-c', 'test ! -f /tmp/unhealthy'] + initialDelaySeconds: 1 + failureThreshold: 1 + periodSeconds: 1 + serviceAccountName: static-server diff --git a/charts/consul/templates/_helpers.tpl b/charts/consul/templates/_helpers.tpl index 1b866888c0..18f57b188c 100644 --- a/charts/consul/templates/_helpers.tpl +++ b/charts/consul/templates/_helpers.tpl @@ -15,6 +15,29 @@ as well as the global.name setting. {{- end -}} {{- end -}} +{{- define "consul.restrictedSecurityContext" -}} +{{- if not .Values.global.enablePodSecurityPolicies -}} +securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault +{{- if not .Values.global.openshift.enabled -}} +{{/* +We must set runAsUser or else the root user will be used in some cases and +containers will fail to start due to runAsNonRoot above (e.g. +tls-init-cleanup). On OpenShift, runAsUser is automatically. We pick user 100 +because it is a non-root user id that exists in the consul, consul-dataplane, +and consul-k8s-control-plane images. +*/}} + runAsUser: 100 +{{- end -}} +{{- end -}} +{{- end -}} + {{- define "consul.vaultSecretTemplate" -}} | {{ "{{" }}- with secret "{{ .secretName }}" -{{ "}}" }} @@ -422,4 +445,4 @@ Usage: {{ template "consul.validateTelemetryCollectorCloud" . }} {{- if or (and .Values.telemetryCollector.cloud.clientSecret.secretName .Values.telemetryCollector.cloud.clientSecret.secretKey .Values.telemetryCollector.cloud.clientId.secretName .Values.telemetryCollector.cloud.clientId.secretKey (not .Values.global.cloud.resourceId.secretKey)) }} {{fail "When telemetryCollector has clientId and clientSecret .global.cloud.resourceId.secretKey must be set"}} {{- end }} -{{- end -}} \ No newline at end of file +{{- end -}} diff --git a/charts/consul/templates/connect-inject-deployment.yaml b/charts/consul/templates/connect-inject-deployment.yaml index 14c3961b4e..e726c9ecc9 100644 --- a/charts/consul/templates/connect-inject-deployment.yaml +++ b/charts/consul/templates/connect-inject-deployment.yaml @@ -94,6 +94,7 @@ spec: - containerPort: 8080 name: webhook-server protocol: TCP + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} env: - name: NAMESPACE valueFrom: diff --git a/charts/consul/templates/gateway-cleanup-job.yaml b/charts/consul/templates/gateway-cleanup-job.yaml index 8731aaed81..a987c3b591 100644 --- a/charts/consul/templates/gateway-cleanup-job.yaml +++ b/charts/consul/templates/gateway-cleanup-job.yaml @@ -40,6 +40,7 @@ spec: containers: - name: gateway-cleanup image: {{ .Values.global.imageK8S }} + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} command: - consul-k8s-control-plane args: diff --git a/charts/consul/templates/gateway-resources-job.yaml b/charts/consul/templates/gateway-resources-job.yaml index 5fcd96cad3..3a29f75e66 100644 --- a/charts/consul/templates/gateway-resources-job.yaml +++ b/charts/consul/templates/gateway-resources-job.yaml @@ -40,6 +40,7 @@ spec: containers: - name: gateway-resources image: {{ .Values.global.imageK8S }} + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} command: - consul-k8s-control-plane args: diff --git a/charts/consul/templates/gossip-encryption-autogenerate-job.yaml b/charts/consul/templates/gossip-encryption-autogenerate-job.yaml index 9d296478a1..240bfe3f9c 100644 --- a/charts/consul/templates/gossip-encryption-autogenerate-job.yaml +++ b/charts/consul/templates/gossip-encryption-autogenerate-job.yaml @@ -48,6 +48,7 @@ spec: containers: - name: gossip-encryption-autogen image: "{{ .Values.global.imageK8S }}" + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} command: - "/bin/sh" - "-ec" diff --git a/charts/consul/templates/server-acl-init-cleanup-job.yaml b/charts/consul/templates/server-acl-init-cleanup-job.yaml index 4d0aa8f05d..c9f6763bd8 100644 --- a/charts/consul/templates/server-acl-init-cleanup-job.yaml +++ b/charts/consul/templates/server-acl-init-cleanup-job.yaml @@ -60,6 +60,9 @@ spec: containers: - name: server-acl-init-cleanup image: {{ .Values.global.imageK8S }} + {{- if not .Values.server.containerSecurityContext.aclInit }} + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} + {{- end }} command: - consul-k8s-control-plane args: diff --git a/charts/consul/templates/server-acl-init-job.yaml b/charts/consul/templates/server-acl-init-job.yaml index 6625e23b38..c3d4a710e8 100644 --- a/charts/consul/templates/server-acl-init-job.yaml +++ b/charts/consul/templates/server-acl-init-job.yaml @@ -129,6 +129,9 @@ spec: containers: - name: server-acl-init-job image: {{ .Values.global.imageK8S }} + {{- if not .Values.server.containerSecurityContext.aclInit }} + {{- include "consul.restrictedSecurityContext" . | nindent 8 }} + {{- end }} env: - name: NAMESPACE valueFrom: diff --git a/charts/consul/templates/server-statefulset.yaml b/charts/consul/templates/server-statefulset.yaml index 9efbcb8085..2ad04f0755 100644 --- a/charts/consul/templates/server-statefulset.yaml +++ b/charts/consul/templates/server-statefulset.yaml @@ -238,6 +238,7 @@ spec: volumeMounts: - name: extra-config mountPath: /consul/extra-config + {{- include "consul.restrictedSecurityContext" . | nindent 8 }} containers: - name: consul image: "{{ default .Values.global.image .Values.server.image }}" @@ -527,9 +528,11 @@ spec: {{- toYaml .Values.server.resources | nindent 12 }} {{- end }} {{- end }} - {{- if not .Values.global.openshift.enabled }} + {{- if .Values.server.containerSecurityContext.server }} securityContext: {{- toYaml .Values.server.containerSecurityContext.server | nindent 12 }} + {{- else }} + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} {{- end }} {{- if .Values.server.extraContainers }} {{ toYaml .Values.server.extraContainers | nindent 8 }} diff --git a/charts/consul/templates/tls-init-cleanup-job.yaml b/charts/consul/templates/tls-init-cleanup-job.yaml index 69b5a30849..2254a38ed2 100644 --- a/charts/consul/templates/tls-init-cleanup-job.yaml +++ b/charts/consul/templates/tls-init-cleanup-job.yaml @@ -48,6 +48,9 @@ spec: containers: - name: tls-init-cleanup image: "{{ .Values.global.image }}" + {{- if not .Values.server.containerSecurityContext.tlsInit }} + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} + {{- end }} env: - name: NAMESPACE valueFrom: diff --git a/charts/consul/templates/tls-init-job.yaml b/charts/consul/templates/tls-init-job.yaml index 5839f07dbf..12d3acbad8 100644 --- a/charts/consul/templates/tls-init-job.yaml +++ b/charts/consul/templates/tls-init-job.yaml @@ -63,6 +63,9 @@ spec: containers: - name: tls-init image: "{{ .Values.global.imageK8S }}" + {{- if not .Values.server.containerSecurityContext.tlsInit }} + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} + {{- end }} env: - name: NAMESPACE valueFrom: diff --git a/charts/consul/templates/webhook-cert-manager-deployment.yaml b/charts/consul/templates/webhook-cert-manager-deployment.yaml index dd93c039d2..7ba25b330c 100644 --- a/charts/consul/templates/webhook-cert-manager-deployment.yaml +++ b/charts/consul/templates/webhook-cert-manager-deployment.yaml @@ -51,6 +51,7 @@ spec: -deployment-namespace={{ .Release.Namespace }} image: {{ .Values.global.imageK8S }} name: webhook-cert-manager + {{- include "consul.restrictedSecurityContext" . | nindent 8 }} resources: limits: cpu: 100m diff --git a/charts/consul/test/unit/server-statefulset.bats b/charts/consul/test/unit/server-statefulset.bats index 108fd9bbf8..a60884d20c 100755 --- a/charts/consul/test/unit/server-statefulset.bats +++ b/charts/consul/test/unit/server-statefulset.bats @@ -829,9 +829,9 @@ load _helpers } #-------------------------------------------------------------------- -# global.openshift.enabled & client.containerSecurityContext +# global.openshift.enabled && server.containerSecurityContext -@test "server/StatefulSet: container level securityContexts are not set when global.openshift.enabled=true" { +@test "server/StatefulSet: Can set container level securityContexts when global.openshift.enabled=true" { cd `chart_dir` local manifest=$(helm template \ -s templates/server-statefulset.yaml \ @@ -839,8 +839,72 @@ load _helpers --set 'server.containerSecurityContext.server.privileged=false' \ . | tee /dev/stderr) + local actual=$(echo "$manifest" | yq -r '.spec.template.spec.containers | map(select(.name == "consul")) | .[0].securityContext.privileged') + [ "${actual}" = "false" ] +} + +#-------------------------------------------------------------------- +# global.openshift.enabled + +@test "server/StatefulSet: restricted container securityContexts are set when global.openshift.enabled=true" { + cd `chart_dir` + local manifest=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.openshift.enabled=true' \ + . | tee /dev/stderr) + + local expected=$(echo '{ + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": ["ALL"] + }, + "runAsNonRoot": true, + "seccompProfile": { + "type": "RuntimeDefault" + } + }') + + # Check consul container local actual=$(echo "$manifest" | yq -r '.spec.template.spec.containers | map(select(.name == "consul")) | .[0].securityContext') - [ "${actual}" = "null" ] + local equal=$(jq -n --argjson a "$actual" --argjson b "$expected" '$a == $b') + [ "$equal" == "true" ] + + # Check locality-init container + local actual=$(echo "$manifest" | yq -r '.spec.template.spec.initContainers | map(select(.name == "locality-init")) | .[0].securityContext') + local equal=$(jq -n --argjson a "$actual" --argjson b "$expected" '$a == $b') + [ "$equal" == "true" ] +} + +#-------------------------------------------------------------------- +# global.openshift.enabled = false + +@test "server/StatefulSet: restricted container securityContexts are set by default" { + cd `chart_dir` + local manifest=$(helm template \ + -s templates/server-statefulset.yaml \ + . | tee /dev/stderr) + + local expected=$(echo '{ + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": ["ALL"] + }, + "runAsNonRoot": true, + "seccompProfile": { + "type": "RuntimeDefault" + }, + "runAsUser": 100 + }') + + # Check consul container + local actual=$(echo "$manifest" | yq -r '.spec.template.spec.containers | map(select(.name == "consul")) | .[0].securityContext') + local equal=$(jq -n --argjson a "$actual" --argjson b "$expected" '$a == $b') + [ "$equal" == "true" ] + + # Check locality-init container + local actual=$(echo "$manifest" | yq -r '.spec.template.spec.initContainers | map(select(.name == "locality-init")) | .[0].securityContext') + local equal=$(jq -n --argjson a "$actual" --argjson b "$expected" '$a == $b') + [ "$equal" == "true" ] } #-------------------------------------------------------------------- From a924e88554a2c2a8ce1110734c15660e7688a451 Mon Sep 17 00:00:00 2001 From: skpratt Date: Mon, 24 Jul 2023 12:18:27 -0500 Subject: [PATCH 298/340] change fips delimiter to + (#2480) (#2591) --- .github/workflows/build.yml | 30 +++++++++++++++--------------- cli/version/version.go | 2 +- control-plane/version/version.go | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 12904b3070..f79369b440 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -79,9 +79,9 @@ jobs: - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s.exe" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "darwin", goarch: "amd64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "darwin", goarch: "arm64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s" } - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto", fips: ".fips1402", pkg_suffix: "-fips" } - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "arm64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto CC=aarch64-linux-gnu-gcc", fips: ".fips1402", pkg_suffix: "-fips" } - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s.exe", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=cngcrypto", fips: ".fips1402" } + - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto", fips: "+fips1402", pkg_suffix: "-fips" } + - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "arm64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto CC=aarch64-linux-gnu-gcc", fips: "+fips1402", pkg_suffix: "-fips" } + - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s.exe", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=cngcrypto", fips: "+fips1402" } # control-plane - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "freebsd", goarch: "386", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } @@ -96,9 +96,9 @@ jobs: - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane.exe" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "darwin", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "darwin", goarch: "arm64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto", fips: ".fips1402", pkg_suffix: "-fips" } - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "arm64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto CC=aarch64-linux-gnu-gcc", fips: ".fips1402", pkg_suffix: "-fips" } - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane.exe", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=cngcrypto", fips: ".fips1402" } + - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto", fips: "+fips1402", pkg_suffix: "-fips" } + - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "arm64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto CC=aarch64-linux-gnu-gcc", fips: "+fips1402", pkg_suffix: "-fips" } + - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane.exe", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=cngcrypto", fips: "+fips1402" } # consul-cni - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "freebsd", goarch: "386", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni" } @@ -112,9 +112,9 @@ jobs: - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni.exe" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "darwin", goarch: "amd64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "darwin", goarch: "arm64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni" } - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto", fips: ".fips1402", pkg_suffix: "-fips" } - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "arm64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto CC=aarch64-linux-gnu-gcc", fips: ".fips1402", pkg_suffix: "-fips" } - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni.exe", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=cngcrypto", fips: ".fips1402" } + - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto", fips: "+fips1402", pkg_suffix: "-fips" } + - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "arm64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto CC=aarch64-linux-gnu-gcc", fips: "+fips1402", pkg_suffix: "-fips" } + - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni.exe", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=cngcrypto", fips: "+fips1402" } fail-fast: true @@ -129,7 +129,7 @@ jobs: go-version: ${{ matrix.go }} - name: Replace Go for Windows FIPS with Microsoft Go - if: ${{ matrix.fips == '.fips1402' && matrix.goos == 'windows' }} + if: ${{ matrix.fips == '+fips1402' && matrix.goos == 'windows' }} run: | # Uninstall standard Go and use microsoft/go instead rm -rf /home/runner/actions-runner/_work/_tool/go @@ -143,7 +143,7 @@ jobs: fi - name: Install cross-compiler for FIPS on arm - if: ${{ matrix.fips == '.fips1402' && matrix.goarch == 'arm64' }} + if: ${{ matrix.fips == '+fips1402' && matrix.goarch == 'arm64' }} run: | sudo apt-get update --allow-releaseinfo-change-suite --allow-releaseinfo-change-version && sudo apt-get install -y gcc-aarch64-linux-gnu @@ -252,8 +252,8 @@ jobs: - { goos: "linux", goarch: "arm64" } - { goos: "linux", goarch: "386" } - { goos: "linux", goarch: "amd64" } - - { goos: "linux", goarch: "amd64", fips: ".fips1402" } - - { goos: "linux", goarch: "arm64", fips: ".fips1402" } + - { goos: "linux", goarch: "amd64", fips: "+fips1402" } + - { goos: "linux", goarch: "arm64", fips: "+fips1402" } env: repo: ${{ github.event.repository.name }} version: ${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }} @@ -323,7 +323,7 @@ jobs: matrix: include: - { arch: "amd64" } - - { arch: "amd64", fips: ".fips1402" } + - { arch: "amd64", fips: "+fips1402" } env: repo: ${{ github.event.repository.name }} version: ${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }} @@ -386,7 +386,7 @@ jobs: strategy: matrix: arch: [ "amd64" ] - fips: [ ".fips1402", "" ] + fips: [ "+fips1402", "" ] env: repo: ${{ github.event.repository.name }} version: ${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }} diff --git a/cli/version/version.go b/cli/version/version.go index 9cde4a2d66..952cd9ca81 100644 --- a/cli/version/version.go +++ b/cli/version/version.go @@ -40,7 +40,7 @@ func GetHumanVersion() string { } if IsFIPS() { - version += ".fips1402" + version += "+fips1402" } if release != "" { diff --git a/control-plane/version/version.go b/control-plane/version/version.go index 9cde4a2d66..952cd9ca81 100644 --- a/control-plane/version/version.go +++ b/control-plane/version/version.go @@ -40,7 +40,7 @@ func GetHumanVersion() string { } if IsFIPS() { - version += ".fips1402" + version += "+fips1402" } if release != "" { From 5b57e6340dff44157cb7a984ac7220e47849dfb9 Mon Sep 17 00:00:00 2001 From: Michael Zalimeni Date: Mon, 24 Jul 2023 16:59:34 -0400 Subject: [PATCH 299/340] [NET-4865] security: Upgrade Go and net/http CVE-2023-29406 (#2642) security: Upgrade Go and net/http Upgrade to Go 1.20.6 and `net/http` 1.12.0 to resolve CVE-2023-29406. --- .changelog/2642.txt | 4 ++++ .go-version | 2 +- acceptance/go.mod | 10 +++++----- acceptance/go.sum | 20 ++++++++++---------- cli/go.mod | 10 +++++----- cli/go.sum | 19 ++++++++++--------- control-plane/go.mod | 10 +++++----- control-plane/go.sum | 20 ++++++++++---------- 8 files changed, 50 insertions(+), 45 deletions(-) create mode 100644 .changelog/2642.txt diff --git a/.changelog/2642.txt b/.changelog/2642.txt new file mode 100644 index 0000000000..5278ed705c --- /dev/null +++ b/.changelog/2642.txt @@ -0,0 +1,4 @@ +```release-note:security +Upgrade to use Go 1.20.6 and `x/net/http` 0.12.0. +This resolves [CVE-2023-29406](https://github.com/advisories/GHSA-f8f7-69v5-w4vx)(`net/http`). +``` diff --git a/.go-version b/.go-version index 0bd54efd31..e63679c766 100644 --- a/.go-version +++ b/.go-version @@ -1 +1 @@ -1.20.4 +1.20.6 diff --git a/acceptance/go.mod b/acceptance/go.mod index 382d9f8225..b19015eda4 100644 --- a/acceptance/go.mod +++ b/acceptance/go.mod @@ -119,14 +119,14 @@ require ( go.opentelemetry.io/otel v1.11.1 // indirect go.opentelemetry.io/otel/trace v1.11.1 // indirect go.uber.org/atomic v1.9.0 // indirect - golang.org/x/crypto v0.1.0 // indirect + golang.org/x/crypto v0.11.0 // indirect golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect golang.org/x/mod v0.9.0 // indirect - golang.org/x/net v0.10.0 // indirect + golang.org/x/net v0.12.0 // indirect golang.org/x/oauth2 v0.6.0 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/term v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/term v0.10.0 // indirect + golang.org/x/text v0.11.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.7.0 // indirect gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect diff --git a/acceptance/go.sum b/acceptance/go.sum index 53b9400c42..43a557f32b 100644 --- a/acceptance/go.sum +++ b/acceptance/go.sum @@ -800,8 +800,8 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -887,8 +887,8 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= 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= @@ -984,13 +984,13 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1001,8 +1001,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/cli/go.mod b/cli/go.mod index cdee871f38..5744e9dad0 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -17,7 +17,7 @@ require ( github.com/olekukonko/tablewriter v0.0.5 github.com/posener/complete v1.2.3 github.com/stretchr/testify v1.8.3 - golang.org/x/text v0.9.0 + golang.org/x/text v0.11.0 helm.sh/helm/v3 v3.9.4 k8s.io/api v0.25.0 k8s.io/apiextensions-apiserver v0.25.0 @@ -166,13 +166,13 @@ require ( github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect go.mongodb.org/mongo-driver v1.11.1 // indirect go.starlark.net v0.0.0-20230128213706-3f75dec8e403 // indirect - golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect + golang.org/x/crypto v0.11.0 // indirect golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect - golang.org/x/net v0.10.0 // indirect + golang.org/x/net v0.12.0 // indirect golang.org/x/oauth2 v0.6.0 // indirect golang.org/x/sync v0.2.0 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/term v0.8.0 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/term v0.10.0 // indirect golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect diff --git a/cli/go.sum b/cli/go.sum index 41b515d2e4..7861cb73d3 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -873,8 +873,9 @@ golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -962,8 +963,8 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= 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= @@ -1075,14 +1076,14 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1092,8 +1093,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/control-plane/go.mod b/control-plane/go.mod index 71396e2fd1..9d184840cb 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -29,7 +29,7 @@ require ( github.com/stretchr/testify v1.8.3 go.uber.org/zap v1.24.0 golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 - golang.org/x/text v0.9.0 + golang.org/x/text v0.11.0 golang.org/x/time v0.3.0 gomodules.xyz/jsonpatch/v2 v2.3.0 gopkg.in/yaml.v2 v2.4.0 @@ -143,13 +143,13 @@ require ( go.opencensus.io v0.22.4 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.6.0 // indirect - golang.org/x/crypto v0.1.0 // indirect + golang.org/x/crypto v0.11.0 // indirect golang.org/x/mod v0.9.0 // indirect - golang.org/x/net v0.10.0 // indirect + golang.org/x/net v0.12.0 // indirect golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect golang.org/x/sync v0.2.0 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/term v0.8.0 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/term v0.10.0 // indirect golang.org/x/tools v0.7.0 // indirect google.golang.org/api v0.30.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/control-plane/go.sum b/control-plane/go.sum index 2f7cbde2fc..fa88dab62a 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -574,8 +574,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -655,8 +655,8 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= 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= @@ -743,13 +743,13 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -759,8 +759,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 6b26d91a8a2c1b5f9e4716779cf3b13aa35bfdd6 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Tue, 25 Jul 2023 15:18:51 -0400 Subject: [PATCH 300/340] Consul client always logs into the local datacenter (#2652) The consul client always logs into the local datacenter --- .changelog/2652.txt | 3 +++ charts/consul/templates/client-daemonset.yaml | 4 ---- charts/consul/test/unit/client-daemonset.bats | 23 ------------------- 3 files changed, 3 insertions(+), 27 deletions(-) create mode 100644 .changelog/2652.txt diff --git a/.changelog/2652.txt b/.changelog/2652.txt new file mode 100644 index 0000000000..efa290c0e7 --- /dev/null +++ b/.changelog/2652.txt @@ -0,0 +1,3 @@ +```release-note:bug +helm: fix CONSUL_LOGIN_DATACENTER for consul client-daemonset. +``` \ No newline at end of file diff --git a/charts/consul/templates/client-daemonset.yaml b/charts/consul/templates/client-daemonset.yaml index 09a70b394e..345c5c731e 100644 --- a/charts/consul/templates/client-daemonset.yaml +++ b/charts/consul/templates/client-daemonset.yaml @@ -510,11 +510,7 @@ spec: value: "component=client,pod=$(NAMESPACE)/$(POD_NAME)" {{- end }} - name: CONSUL_LOGIN_DATACENTER - {{- if and .Values.global.federation.enabled .Values.global.federation.primaryDatacenter }} - value: {{ .Values.global.federation.primaryDatacenter }} - {{- else }} value: {{ .Values.global.datacenter }} - {{- end}} command: - "/bin/sh" - "-ec" diff --git a/charts/consul/test/unit/client-daemonset.bats b/charts/consul/test/unit/client-daemonset.bats index 4c38207635..6e7a030cb1 100755 --- a/charts/consul/test/unit/client-daemonset.bats +++ b/charts/consul/test/unit/client-daemonset.bats @@ -2127,29 +2127,6 @@ rollingUpdate: [[ "$output" =~ "If global.federation.enabled is true, global.adminPartitions.enabled must be false because they are mutually exclusive" ]] } -@test "client/DaemonSet: consul login datacenter is set to primary when when federation enabled in non-primary datacenter" { - cd `chart_dir` - local object=$(helm template \ - -s templates/client-daemonset.yaml \ - --set 'client.enabled=true' \ - --set 'meshGateway.enabled=true' \ - --set 'global.acls.manageSystemACLs=true' \ - --set 'global.datacenter=dc1' \ - --set 'global.federation.enabled=true' \ - --set 'global.federation.primaryDatacenter=dc2' \ - --set 'global.tls.enabled=true' \ - . | tee /dev/stderr | - yq '.spec.template.spec.initContainers[] | select(.name == "client-acl-init")' | tee /dev/stderr) - - local actual=$(echo $object | - yq '[.env[11].name] | any(contains("CONSUL_LOGIN_DATACENTER"))' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(echo $object | - yq '[.env[11].value] | any(contains("dc2"))' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - #-------------------------------------------------------------------- # extraContainers From 89a1c6ddfebe00050d7a99a4525aa4da3e6a72ab Mon Sep 17 00:00:00 2001 From: Mark Campbell-Vincent Date: Tue, 25 Jul 2023 16:54:20 -0400 Subject: [PATCH 301/340] Add support for requestTimeout in Service Resolver spec (#2641) * Add support for requestTimeout in Service Resolver spec * preserve serviceresolvers.yaml Preserving yaml from main, only adding requesttimeout property. * update generated.deepcopy.go * Use latest controller-gen to generate CRDs --------- Co-authored-by: Ashwin Venkatesh --- .../crd-controlplanerequestlimits.yaml | 5 +- .../templates/crd-exportedservices.yaml | 5 +- .../templates/crd-gatewayclassconfigs.yaml | 5 +- .../consul/templates/crd-gatewayclasses.yaml | 3 +- charts/consul/templates/crd-gateways.yaml | 3 +- charts/consul/templates/crd-grpcroutes.yaml | 3 +- charts/consul/templates/crd-httproutes.yaml | 3 +- .../consul/templates/crd-ingressgateways.yaml | 5 +- charts/consul/templates/crd-jwtproviders.yaml | 5 +- charts/consul/templates/crd-meshes.yaml | 5 +- charts/consul/templates/crd-meshservices.yaml | 5 +- .../templates/crd-peeringacceptors.yaml | 5 +- .../consul/templates/crd-peeringdialers.yaml | 5 +- .../consul/templates/crd-proxydefaults.yaml | 5 +- .../consul/templates/crd-referencegrants.yaml | 3 + .../consul/templates/crd-samenessgroups.yaml | 5 +- .../consul/templates/crd-servicedefaults.yaml | 5 +- .../templates/crd-serviceintentions.yaml | 5 +- .../templates/crd-serviceresolvers.yaml | 18 +- .../consul/templates/crd-servicerouters.yaml | 5 +- .../templates/crd-servicesplitters.yaml | 5 +- charts/consul/templates/crd-tcproutes.yaml | 3 + .../templates/crd-terminatinggateways.yaml | 5 +- charts/consul/templates/crd-tlsroutes.yaml | 3 + charts/consul/templates/crd-udproutes.yaml | 3 + .../api/v1alpha1/serviceresolver_types.go | 4 + .../v1alpha1/serviceresolver_types_test.go | 4 + .../api/v1alpha1/zz_generated.deepcopy.go | 158 ++++++++++++++++-- ...shicorp.com_controlplanerequestlimits.yaml | 3 +- ...consul.hashicorp.com_exportedservices.yaml | 3 +- ...sul.hashicorp.com_gatewayclassconfigs.yaml | 3 +- .../consul.hashicorp.com_ingressgateways.yaml | 3 +- .../consul.hashicorp.com_jwtproviders.yaml | 3 +- .../bases/consul.hashicorp.com_meshes.yaml | 3 +- .../consul.hashicorp.com_meshservices.yaml | 3 +- ...consul.hashicorp.com_peeringacceptors.yaml | 3 +- .../consul.hashicorp.com_peeringdialers.yaml | 3 +- .../consul.hashicorp.com_proxydefaults.yaml | 3 +- .../consul.hashicorp.com_samenessgroups.yaml | 3 +- .../consul.hashicorp.com_servicedefaults.yaml | 3 +- ...onsul.hashicorp.com_serviceintentions.yaml | 3 +- ...consul.hashicorp.com_serviceresolvers.yaml | 16 +- .../consul.hashicorp.com_servicerouters.yaml | 3 +- ...consul.hashicorp.com_servicesplitters.yaml | 3 +- ...sul.hashicorp.com_terminatinggateways.yaml | 3 +- control-plane/config/rbac/role.yaml | 4 - control-plane/config/webhook/manifests.yaml | 4 - 47 files changed, 241 insertions(+), 119 deletions(-) diff --git a/charts/consul/templates/crd-controlplanerequestlimits.yaml b/charts/consul/templates/crd-controlplanerequestlimits.yaml index bd1d6118b9..67ff258eb8 100644 --- a/charts/consul/templates/crd-controlplanerequestlimits.yaml +++ b/charts/consul/templates/crd-controlplanerequestlimits.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: controlplanerequestlimits.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: ControlPlaneRequestLimit diff --git a/charts/consul/templates/crd-exportedservices.yaml b/charts/consul/templates/crd-exportedservices.yaml index 7ffddf7537..8581ac4e88 100644 --- a/charts/consul/templates/crd-exportedservices.yaml +++ b/charts/consul/templates/crd-exportedservices.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: exportedservices.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: ExportedServices diff --git a/charts/consul/templates/crd-gatewayclassconfigs.yaml b/charts/consul/templates/crd-gatewayclassconfigs.yaml index 65d425edc4..7060757b23 100644 --- a/charts/consul/templates/crd-gatewayclassconfigs.yaml +++ b/charts/consul/templates/crd-gatewayclassconfigs.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: gatewayclassconfigs.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: GatewayClassConfig diff --git a/charts/consul/templates/crd-gatewayclasses.yaml b/charts/consul/templates/crd-gatewayclasses.yaml index 93435b7fce..8f381a7065 100644 --- a/charts/consul/templates/crd-gatewayclasses.yaml +++ b/charts/consul/templates/crd-gatewayclasses.yaml @@ -1,3 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 {{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -6,7 +8,6 @@ metadata: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 gateway.networking.k8s.io/bundle-version: v0.6.2 gateway.networking.k8s.io/channel: experimental - creationTimestamp: null labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} diff --git a/charts/consul/templates/crd-gateways.yaml b/charts/consul/templates/crd-gateways.yaml index 41df34942a..d9c381b1de 100644 --- a/charts/consul/templates/crd-gateways.yaml +++ b/charts/consul/templates/crd-gateways.yaml @@ -1,3 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 {{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -6,7 +8,6 @@ metadata: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 gateway.networking.k8s.io/bundle-version: v0.6.2 gateway.networking.k8s.io/channel: experimental - creationTimestamp: null labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} diff --git a/charts/consul/templates/crd-grpcroutes.yaml b/charts/consul/templates/crd-grpcroutes.yaml index 739ed2c659..cc126d73c3 100644 --- a/charts/consul/templates/crd-grpcroutes.yaml +++ b/charts/consul/templates/crd-grpcroutes.yaml @@ -1,3 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 {{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -6,7 +8,6 @@ metadata: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 gateway.networking.k8s.io/bundle-version: v0.6.2 gateway.networking.k8s.io/channel: experimental - creationTimestamp: null labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} diff --git a/charts/consul/templates/crd-httproutes.yaml b/charts/consul/templates/crd-httproutes.yaml index bba3672d16..5f45528567 100644 --- a/charts/consul/templates/crd-httproutes.yaml +++ b/charts/consul/templates/crd-httproutes.yaml @@ -1,3 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 {{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -6,7 +8,6 @@ metadata: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 gateway.networking.k8s.io/bundle-version: v0.6.2 gateway.networking.k8s.io/channel: experimental - creationTimestamp: null labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} diff --git a/charts/consul/templates/crd-ingressgateways.yaml b/charts/consul/templates/crd-ingressgateways.yaml index ef33890461..eff7ef61a9 100644 --- a/charts/consul/templates/crd-ingressgateways.yaml +++ b/charts/consul/templates/crd-ingressgateways.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: ingressgateways.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: IngressGateway diff --git a/charts/consul/templates/crd-jwtproviders.yaml b/charts/consul/templates/crd-jwtproviders.yaml index c7d20883e8..fa87f37489 100644 --- a/charts/consul/templates/crd-jwtproviders.yaml +++ b/charts/consul/templates/crd-jwtproviders.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: jwtproviders.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: JWTProvider diff --git a/charts/consul/templates/crd-meshes.yaml b/charts/consul/templates/crd-meshes.yaml index cdc11b6ed9..f2549b5111 100644 --- a/charts/consul/templates/crd-meshes.yaml +++ b/charts/consul/templates/crd-meshes.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: meshes.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: Mesh diff --git a/charts/consul/templates/crd-meshservices.yaml b/charts/consul/templates/crd-meshservices.yaml index 859c8683ee..aa808113a2 100644 --- a/charts/consul/templates/crd-meshservices.yaml +++ b/charts/consul/templates/crd-meshservices.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: meshservices.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: MeshService diff --git a/charts/consul/templates/crd-peeringacceptors.yaml b/charts/consul/templates/crd-peeringacceptors.yaml index 3822f3bdfe..40f7f1d4d6 100644 --- a/charts/consul/templates/crd-peeringacceptors.yaml +++ b/charts/consul/templates/crd-peeringacceptors.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: peeringacceptors.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: PeeringAcceptor diff --git a/charts/consul/templates/crd-peeringdialers.yaml b/charts/consul/templates/crd-peeringdialers.yaml index 405361c486..bfe4778d0c 100644 --- a/charts/consul/templates/crd-peeringdialers.yaml +++ b/charts/consul/templates/crd-peeringdialers.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: peeringdialers.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: PeeringDialer diff --git a/charts/consul/templates/crd-proxydefaults.yaml b/charts/consul/templates/crd-proxydefaults.yaml index 30dd25f674..a224effc12 100644 --- a/charts/consul/templates/crd-proxydefaults.yaml +++ b/charts/consul/templates/crd-proxydefaults.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: proxydefaults.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: ProxyDefaults diff --git a/charts/consul/templates/crd-referencegrants.yaml b/charts/consul/templates/crd-referencegrants.yaml index db9cf12027..d50211291d 100644 --- a/charts/consul/templates/crd-referencegrants.yaml +++ b/charts/consul/templates/crd-referencegrants.yaml @@ -1,4 +1,7 @@ {{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: diff --git a/charts/consul/templates/crd-samenessgroups.yaml b/charts/consul/templates/crd-samenessgroups.yaml index c1d1c85a8e..7cc3b71ae1 100644 --- a/charts/consul/templates/crd-samenessgroups.yaml +++ b/charts/consul/templates/crd-samenessgroups.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: samenessgroups.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: SamenessGroup diff --git a/charts/consul/templates/crd-servicedefaults.yaml b/charts/consul/templates/crd-servicedefaults.yaml index c926ece62a..e295732bfa 100644 --- a/charts/consul/templates/crd-servicedefaults.yaml +++ b/charts/consul/templates/crd-servicedefaults.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: servicedefaults.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: ServiceDefaults diff --git a/charts/consul/templates/crd-serviceintentions.yaml b/charts/consul/templates/crd-serviceintentions.yaml index 335d2eff7a..5f849f65ba 100644 --- a/charts/consul/templates/crd-serviceintentions.yaml +++ b/charts/consul/templates/crd-serviceintentions.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: serviceintentions.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: ServiceIntentions diff --git a/charts/consul/templates/crd-serviceresolvers.yaml b/charts/consul/templates/crd-serviceresolvers.yaml index 99cc1bb090..a18cc94de4 100644 --- a/charts/consul/templates/crd-serviceresolvers.yaml +++ b/charts/consul/templates/crd-serviceresolvers.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: serviceresolvers.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: ServiceResolver @@ -228,12 +227,13 @@ spec: type: object type: object prioritizeByLocality: - description: PrioritizeByLocality contains the configuration for - locality aware routing. + description: PrioritizeByLocality controls whether the locality of + services within the local partition will be used to prioritize connectivity. properties: mode: - description: Mode specifies the behavior of PrioritizeByLocality - routing. Valid values are "", "none", and "failover". + description: 'Mode specifies the type of prioritization that will + be performed when selecting nodes in the local partition. Valid + values are: "" (default "none"), "none", and "failover".' type: string type: object redirect: @@ -275,6 +275,10 @@ spec: If empty the default subset is used. type: string type: object + requestTimeout: + description: RequestTimeout is the timeout for receiving an HTTP response + from this service before the connection is terminated. + type: string subsets: additionalProperties: properties: diff --git a/charts/consul/templates/crd-servicerouters.yaml b/charts/consul/templates/crd-servicerouters.yaml index 0157f646b4..c5ba99466c 100644 --- a/charts/consul/templates/crd-servicerouters.yaml +++ b/charts/consul/templates/crd-servicerouters.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: servicerouters.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: ServiceRouter diff --git a/charts/consul/templates/crd-servicesplitters.yaml b/charts/consul/templates/crd-servicesplitters.yaml index 18fb10341e..abe3ac85cc 100644 --- a/charts/consul/templates/crd-servicesplitters.yaml +++ b/charts/consul/templates/crd-servicesplitters.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: servicesplitters.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: ServiceSplitter diff --git a/charts/consul/templates/crd-tcproutes.yaml b/charts/consul/templates/crd-tcproutes.yaml index b5bc7be13c..a17f457a78 100644 --- a/charts/consul/templates/crd-tcproutes.yaml +++ b/charts/consul/templates/crd-tcproutes.yaml @@ -1,4 +1,7 @@ {{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: diff --git a/charts/consul/templates/crd-terminatinggateways.yaml b/charts/consul/templates/crd-terminatinggateways.yaml index 955496aeee..cd58d1679c 100644 --- a/charts/consul/templates/crd-terminatinggateways.yaml +++ b/charts/consul/templates/crd-terminatinggateways.yaml @@ -4,16 +4,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: terminatinggateways.consul.hashicorp.com +spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd -spec: group: consul.hashicorp.com names: kind: TerminatingGateway diff --git a/charts/consul/templates/crd-tlsroutes.yaml b/charts/consul/templates/crd-tlsroutes.yaml index 1acd1b973a..be72f47d65 100644 --- a/charts/consul/templates/crd-tlsroutes.yaml +++ b/charts/consul/templates/crd-tlsroutes.yaml @@ -1,4 +1,7 @@ {{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: diff --git a/charts/consul/templates/crd-udproutes.yaml b/charts/consul/templates/crd-udproutes.yaml index 0661b24c1a..fe331cca30 100644 --- a/charts/consul/templates/crd-udproutes.yaml +++ b/charts/consul/templates/crd-udproutes.yaml @@ -1,4 +1,7 @@ {{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: diff --git a/control-plane/api/v1alpha1/serviceresolver_types.go b/control-plane/api/v1alpha1/serviceresolver_types.go index e5ce3c6fa6..714cd94c2a 100644 --- a/control-plane/api/v1alpha1/serviceresolver_types.go +++ b/control-plane/api/v1alpha1/serviceresolver_types.go @@ -76,6 +76,9 @@ type ServiceResolverSpec struct { // ConnectTimeout is the timeout for establishing new network connections // to this service. ConnectTimeout metav1.Duration `json:"connectTimeout,omitempty"` + // RequestTimeout is the timeout for receiving an HTTP response from this + // service before the connection is terminated. + RequestTimeout metav1.Duration `json:"requestTimeout,omitempty"` // LoadBalancer determines the load balancing policy and configuration for services // issuing requests to this upstream service. LoadBalancer *LoadBalancer `json:"loadBalancer,omitempty"` @@ -317,6 +320,7 @@ func (in *ServiceResolver) ToConsul(datacenter string) capi.ConfigEntry { Redirect: in.Spec.Redirect.toConsul(), Failover: in.Spec.Failover.toConsul(), ConnectTimeout: in.Spec.ConnectTimeout.Duration, + RequestTimeout: in.Spec.RequestTimeout.Duration, LoadBalancer: in.Spec.LoadBalancer.toConsul(), PrioritizeByLocality: in.Spec.PrioritizeByLocality.toConsul(), Meta: meta(datacenter), diff --git a/control-plane/api/v1alpha1/serviceresolver_types_test.go b/control-plane/api/v1alpha1/serviceresolver_types_test.go index 3a3d5a6016..11751aaa2a 100644 --- a/control-plane/api/v1alpha1/serviceresolver_types_test.go +++ b/control-plane/api/v1alpha1/serviceresolver_types_test.go @@ -104,6 +104,7 @@ func TestServiceResolver_MatchesConsul(t *testing.T) { }, }, ConnectTimeout: metav1.Duration{Duration: 1 * time.Second}, + RequestTimeout: metav1.Duration{Duration: 1 * time.Second}, LoadBalancer: &LoadBalancer{ Policy: "policy", RingHashConfig: &RingHashConfig{ @@ -188,6 +189,7 @@ func TestServiceResolver_MatchesConsul(t *testing.T) { }, }, ConnectTimeout: 1 * time.Second, + RequestTimeout: 1 * time.Second, LoadBalancer: &capi.LoadBalancer{ Policy: "policy", RingHashConfig: &capi.RingHashConfig{ @@ -321,6 +323,7 @@ func TestServiceResolver_ToConsul(t *testing.T) { }, }, ConnectTimeout: metav1.Duration{Duration: 1 * time.Second}, + RequestTimeout: metav1.Duration{Duration: 1 * time.Second}, LoadBalancer: &LoadBalancer{ Policy: "policy", RingHashConfig: &RingHashConfig{ @@ -405,6 +408,7 @@ func TestServiceResolver_ToConsul(t *testing.T) { }, }, ConnectTimeout: 1 * time.Second, + RequestTimeout: 1 * time.Second, LoadBalancer: &capi.LoadBalancer{ Policy: "policy", RingHashConfig: &capi.RingHashConfig{ diff --git a/control-plane/api/v1alpha1/zz_generated.deepcopy.go b/control-plane/api/v1alpha1/zz_generated.deepcopy.go index 0787f24097..05000031ad 100644 --- a/control-plane/api/v1alpha1/zz_generated.deepcopy.go +++ b/control-plane/api/v1alpha1/zz_generated.deepcopy.go @@ -126,6 +126,7 @@ func (in *ControlPlaneRequestLimitList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ControlPlaneRequestLimitSpec) DeepCopyInto(out *ControlPlaneRequestLimitSpec) { *out = *in + out.ReadWriteRatesConfig = in.ReadWriteRatesConfig if in.ACL != nil { in, out := &in.ACL, &out.ACL *out = new(ReadWriteRatesConfig) @@ -928,6 +929,78 @@ func (in *IntentionHTTPPermission) DeepCopy() *IntentionHTTPPermission { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IntentionJWTClaimVerification) DeepCopyInto(out *IntentionJWTClaimVerification) { + *out = *in + if in.Path != nil { + in, out := &in.Path, &out.Path + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntentionJWTClaimVerification. +func (in *IntentionJWTClaimVerification) DeepCopy() *IntentionJWTClaimVerification { + if in == nil { + return nil + } + out := new(IntentionJWTClaimVerification) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IntentionJWTProvider) DeepCopyInto(out *IntentionJWTProvider) { + *out = *in + if in.VerifyClaims != nil { + in, out := &in.VerifyClaims, &out.VerifyClaims + *out = make([]*IntentionJWTClaimVerification, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(IntentionJWTClaimVerification) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntentionJWTProvider. +func (in *IntentionJWTProvider) DeepCopy() *IntentionJWTProvider { + if in == nil { + return nil + } + out := new(IntentionJWTProvider) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IntentionJWTRequirement) DeepCopyInto(out *IntentionJWTRequirement) { + *out = *in + if in.Providers != nil { + in, out := &in.Providers, &out.Providers + *out = make([]*IntentionJWTProvider, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(IntentionJWTProvider) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntentionJWTRequirement. +func (in *IntentionJWTRequirement) DeepCopy() *IntentionJWTRequirement { + if in == nil { + return nil + } + out := new(IntentionJWTRequirement) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IntentionPermission) DeepCopyInto(out *IntentionPermission) { *out = *in @@ -936,6 +1009,11 @@ func (in *IntentionPermission) DeepCopyInto(out *IntentionPermission) { *out = new(IntentionHTTPPermission) (*in).DeepCopyInto(*out) } + if in.JWT != nil { + in, out := &in.JWT, &out.JWT + *out = new(IntentionJWTRequirement) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntentionPermission. @@ -1123,6 +1201,31 @@ func (in *JWTLocationQueryParam) DeepCopy() *JWTLocationQueryParam { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in JWTLocations) DeepCopyInto(out *JWTLocations) { + { + in := &in + *out = make(JWTLocations, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(JWTLocation) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWTLocations. +func (in JWTLocations) DeepCopy() JWTLocations { + if in == nil { + return nil + } + out := new(JWTLocations) + in.DeepCopyInto(out) + return *out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *JWTProvider) DeepCopyInto(out *JWTProvider) { *out = *in @@ -1952,6 +2055,21 @@ func (in *ProxyDefaultsSpec) DeepCopy() *ProxyDefaultsSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ReadWriteRatesConfig) DeepCopyInto(out *ReadWriteRatesConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReadWriteRatesConfig. +func (in *ReadWriteRatesConfig) DeepCopy() *ReadWriteRatesConfig { + if in == nil { + return nil + } + out := new(ReadWriteRatesConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RemoteJWKS) DeepCopyInto(out *RemoteJWKS) { *out = *in @@ -1987,20 +2105,6 @@ func (in *RetryPolicyBackOff) DeepCopy() *RetryPolicyBackOff { return out } -func (in *ReadWriteRatesConfig) DeepCopyInto(out *ReadWriteRatesConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReadWriteRatesConfig. -func (in *ReadWriteRatesConfig) DeepCopy() *ReadWriteRatesConfig { - if in == nil { - return nil - } - out := new(ReadWriteRatesConfig) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RingHashConfig) DeepCopyInto(out *RingHashConfig) { *out = *in @@ -2372,6 +2476,11 @@ func (in *ServiceIntentionsSpec) DeepCopyInto(out *ServiceIntentionsSpec) { } } } + if in.JWT != nil { + in, out := &in.JWT, &out.JWT + *out = new(IntentionJWTRequirement) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceIntentionsSpec. @@ -2509,6 +2618,21 @@ func (in *ServiceResolverList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceResolverPrioritizeByLocality) DeepCopyInto(out *ServiceResolverPrioritizeByLocality) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceResolverPrioritizeByLocality. +func (in *ServiceResolverPrioritizeByLocality) DeepCopy() *ServiceResolverPrioritizeByLocality { + if in == nil { + return nil + } + out := new(ServiceResolverPrioritizeByLocality) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServiceResolverRedirect) DeepCopyInto(out *ServiceResolverRedirect) { *out = *in @@ -2547,11 +2671,17 @@ func (in *ServiceResolverSpec) DeepCopyInto(out *ServiceResolverSpec) { } } out.ConnectTimeout = in.ConnectTimeout + out.RequestTimeout = in.RequestTimeout if in.LoadBalancer != nil { in, out := &in.LoadBalancer, &out.LoadBalancer *out = new(LoadBalancer) (*in).DeepCopyInto(*out) } + if in.PrioritizeByLocality != nil { + in, out := &in.PrioritizeByLocality, &out.PrioritizeByLocality + *out = new(ServiceResolverPrioritizeByLocality) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceResolverSpec. diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_controlplanerequestlimits.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_controlplanerequestlimits.yaml index 2eef465ada..4d1d808428 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_controlplanerequestlimits.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_controlplanerequestlimits.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: controlplanerequestlimits.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml index f066c90612..dac72f3646 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: exportedservices.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml index a8393cd8fd..44eff52492 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: gatewayclassconfigs.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml index f7ccf205d9..e9cf081721 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: ingressgateways.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml index 8ca1ec0748..7506cc57dc 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: jwtproviders.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml index bc46b6ab37..16dd398f99 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: meshes.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml index 0871fc32e5..125883bdc5 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: meshservices.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml index f6f9eda72b..894228a218 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: peeringacceptors.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml index 7e0927c169..51c3e38319 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: peeringdialers.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml index 7396816f7e..1be3b37703 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: proxydefaults.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml index 23de092485..259ca7b910 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: samenessgroups.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml index a5501a98d2..83503f11f3 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: servicedefaults.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml index cd28173ba8..9553c73450 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: serviceintentions.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml index ec52c04e05..b83a859dc4 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: serviceresolvers.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -224,12 +223,13 @@ spec: type: object type: object prioritizeByLocality: - description: PrioritizeByLocality contains the configuration for - locality aware routing. + description: PrioritizeByLocality controls whether the locality of + services within the local partition will be used to prioritize connectivity. properties: mode: - description: Mode specifies the behavior of PrioritizeByLocality - routing. Valid values are "", "none", and "failover". + description: 'Mode specifies the type of prioritization that will + be performed when selecting nodes in the local partition. Valid + values are: "" (default "none"), "none", and "failover".' type: string type: object redirect: @@ -271,6 +271,10 @@ spec: If empty the default subset is used. type: string type: object + requestTimeout: + description: RequestTimeout is the timeout for receiving an HTTP response + from this service before the connection is terminated. + type: string subsets: additionalProperties: properties: diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml index 5919e23005..04590cc007 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: servicerouters.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml index d5848ed6ec..3a47472ba7 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: servicesplitters.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml index 4910e42829..acf61cde4c 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml @@ -6,8 +6,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: terminatinggateways.consul.hashicorp.com spec: group: consul.hashicorp.com diff --git a/control-plane/config/rbac/role.yaml b/control-plane/config/rbac/role.yaml index 7f90780e02..74328a8ae3 100644 --- a/control-plane/config/rbac/role.yaml +++ b/control-plane/config/rbac/role.yaml @@ -1,11 +1,7 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - creationTimestamp: null name: manager-role rules: - apiGroups: diff --git a/control-plane/config/webhook/manifests.yaml b/control-plane/config/webhook/manifests.yaml index 0861f9253a..a515888527 100644 --- a/control-plane/config/webhook/manifests.yaml +++ b/control-plane/config/webhook/manifests.yaml @@ -1,11 +1,7 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - --- apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: - creationTimestamp: null name: mutating-webhook-configuration webhooks: - admissionReviewVersions: From 94414a72dc3ed576621c31ce2f75d4d6bc214d54 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Wed, 26 Jul 2023 10:32:57 -0400 Subject: [PATCH 302/340] Increase timeout for acl replication to 60 seconds and poll every 500 ms (#2656) increase timeout for acl replication to 60 seconds and poll every 500 ms --- .changelog/2656.txt | 3 +++ control-plane/subcommand/common/common.go | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 .changelog/2656.txt diff --git a/.changelog/2656.txt b/.changelog/2656.txt new file mode 100644 index 0000000000..07436087d3 --- /dev/null +++ b/.changelog/2656.txt @@ -0,0 +1,3 @@ +```release-note:improvement +control-plane: increase timeout after login for ACL replication to 60 seconds +``` \ No newline at end of file diff --git a/control-plane/subcommand/common/common.go b/control-plane/subcommand/common/common.go index 598ba66ea5..1636c0b10e 100644 --- a/control-plane/subcommand/common/common.go +++ b/control-plane/subcommand/common/common.go @@ -39,8 +39,8 @@ const ( // The number of times to attempt ACL Login. numLoginRetries = 100 - raftReplicationTimeout = 2 * time.Second - tokenReadPollingInterval = 100 * time.Millisecond + raftReplicationTimeout = 60 * time.Second + tokenReadPollingInterval = 500 * time.Millisecond ) // Logger returns an hclog instance with log level set and JSON logging enabled/disabled, or an error if level is invalid. From 596a2a78d8e512003f2f4855d4cd312d79910f81 Mon Sep 17 00:00:00 2001 From: Paul Glass Date: Wed, 26 Jul 2023 15:24:38 -0500 Subject: [PATCH 303/340] Update changelog to address cloud auto-join change in 1.0.0 (#2667) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54bdba549b..0d60f91b67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -404,6 +404,7 @@ BREAKING CHANGES: * `client.enabled` now defaults to `false`. Setting it to `true` will deploy client agents, however, none of the consul-k8s components will use clients for their operation. * `global.imageEnvoy` is no longer used for sidecar proxies, as well as mesh, terminating, and ingress gateways. * `externalServers.grpcPort` default is now `8502` instead of `8503`. + * `externalServers.hosts` no longer supports [cloud auto-join](https://developer.hashicorp.com/consul/docs/install/cloud-auto-join) strings directly. Instead, include an [`exec=`](https://github.com/hashicorp/go-netaddrs#command-line-tool-usage) string in the `externalServers.hosts` list to invoke the `discover` CLI. For example, the following string invokes the `discover` CLI with a cloud auto-join string: `exec=discover -q addrs provider=aws region=us-west-2 tag_key=consul-server tag_value=true`. The `discover` CLI is included in the official `hashicorp/consul-dataplane` images by default. * `meshGateway.service.enabled` value is removed. Mesh gateways now will always have a Kubernetes service as this is required to register them as a service with Consul. * `meshGateway.initCopyConsulContainer`, `ingressGateways.initCopyConsulContainer`, `terminatingGateways.initCopyConsulContainer` values are removed. * `connectInject.enabled` now defaults to `true`. [[GH-1551](https://github.com/hashicorp/consul-k8s/pull/1551)] From f026d439b553071444899edcde8925ac4f317cc7 Mon Sep 17 00:00:00 2001 From: Nathan Coleman Date: Wed, 26 Jul 2023 17:22:46 -0500 Subject: [PATCH 304/340] NET-4967: Fix helm install when setting copyAnnotations or nodeSelector for apiGateway (#2597) * Support multiline nodeSelector arg * Support multiline service annotations arg * Update test assertions * Add changelog entry --- .changelog/2597.txt | 3 +++ .../consul/templates/gateway-resources-job.yaml | 6 ++++-- .../consul/test/unit/gateway-resources-job.bats | 16 +++++++++++----- 3 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 .changelog/2597.txt diff --git a/.changelog/2597.txt b/.changelog/2597.txt new file mode 100644 index 0000000000..83cc369b6d --- /dev/null +++ b/.changelog/2597.txt @@ -0,0 +1,3 @@ +```release-note:bug +api-gateway: fix helm install when setting copyAnnotations or nodeSelector +``` diff --git a/charts/consul/templates/gateway-resources-job.yaml b/charts/consul/templates/gateway-resources-job.yaml index 3a29f75e66..1fa712759d 100644 --- a/charts/consul/templates/gateway-resources-job.yaml +++ b/charts/consul/templates/gateway-resources-job.yaml @@ -88,13 +88,15 @@ spec: {{- end}} {{- end}} {{- if .Values.connectInject.apiGateway.managedGatewayClass.nodeSelector }} - - -node-selector={{ .Values.connectInject.apiGateway.managedGatewayClass.nodeSelector }} + - -node-selector + - {{- toYaml .Values.connectInject.apiGateway.managedGatewayClass.nodeSelector | nindent 14 -}} {{- end }} {{- if .Values.connectInject.apiGateway.managedGatewayClass.tolerations }} - -tolerations={{ .Values.connectInject.apiGateway.managedGatewayClass.tolerations }} {{- end }} {{- if .Values.connectInject.apiGateway.managedGatewayClass.copyAnnotations.service }} - - -service-annotations={{ .Values.connectInject.apiGateway.managedGatewayClass.copyAnnotations.service.annotations }} + - -service-annotations + - {{- toYaml .Values.connectInject.apiGateway.managedGatewayClass.copyAnnotations.service.annotations | nindent 14 -}} {{- end }} - -service-type={{ .Values.connectInject.apiGateway.managedGatewayClass.serviceType }} {{- end}} diff --git a/charts/consul/test/unit/gateway-resources-job.bats b/charts/consul/test/unit/gateway-resources-job.bats index 27bba00ed5..d79838770d 100644 --- a/charts/consul/test/unit/gateway-resources-job.bats +++ b/charts/consul/test/unit/gateway-resources-job.bats @@ -107,14 +107,20 @@ target=templates/gateway-resources-job.yaml local actual=$(echo "$spec" | jq 'any(index("-service-type=Foo"))') [ "${actual}" = "true" ] - local actual=$(echo "$spec" | jq '.[12] | ."-node-selector=foo"') - [ "${actual}" = "\"bar\"" ] + local actual=$(echo "$spec" | jq '.[12]') + [ "${actual}" = "\"-node-selector\"" ] + + local actual=$(echo "$spec" | jq '.[13]') + [ "${actual}" = "\"foo: bar\"" ] - local actual=$(echo "$spec" | jq '.[13] | ."-tolerations=- key"') + local actual=$(echo "$spec" | jq '.[14] | ."-tolerations=- key"') [ "${actual}" = "\"bar\"" ] - local actual=$(echo "$spec" | jq '.[14]') - [ "${actual}" = "\"-service-annotations=- bingo\"" ] + local actual=$(echo "$spec" | jq '.[15]') + [ "${actual}" = "\"-service-annotations\"" ] + + local actual=$(echo "$spec" | jq '.[16]') + [ "${actual}" = "\"- bingo\"" ] } From 7bb0a57aec89dbdab4a6313536ae4de540e43d5f Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Thu, 27 Jul 2023 09:47:02 -0400 Subject: [PATCH 305/340] Fix ordering of licence in templates (#2675) --- charts/consul/templates/crd-gatewayclasses.yaml | 3 ++- charts/consul/templates/crd-gateways.yaml | 3 ++- charts/consul/templates/crd-grpcroutes.yaml | 3 ++- charts/consul/templates/crd-httproutes.yaml | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/charts/consul/templates/crd-gatewayclasses.yaml b/charts/consul/templates/crd-gatewayclasses.yaml index 8f381a7065..b0725c2baf 100644 --- a/charts/consul/templates/crd-gatewayclasses.yaml +++ b/charts/consul/templates/crd-gatewayclasses.yaml @@ -1,6 +1,7 @@ +{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 -{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} + apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: diff --git a/charts/consul/templates/crd-gateways.yaml b/charts/consul/templates/crd-gateways.yaml index d9c381b1de..923a75477d 100644 --- a/charts/consul/templates/crd-gateways.yaml +++ b/charts/consul/templates/crd-gateways.yaml @@ -1,6 +1,7 @@ +{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 -{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} + apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: diff --git a/charts/consul/templates/crd-grpcroutes.yaml b/charts/consul/templates/crd-grpcroutes.yaml index cc126d73c3..07412ba60b 100644 --- a/charts/consul/templates/crd-grpcroutes.yaml +++ b/charts/consul/templates/crd-grpcroutes.yaml @@ -1,6 +1,7 @@ +{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 -{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} + apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: diff --git a/charts/consul/templates/crd-httproutes.yaml b/charts/consul/templates/crd-httproutes.yaml index 5f45528567..d9055e7406 100644 --- a/charts/consul/templates/crd-httproutes.yaml +++ b/charts/consul/templates/crd-httproutes.yaml @@ -1,6 +1,7 @@ +{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 -{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} + apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: From b6d3e61058e60e8dc644c3de66a0d186123d907e Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Thu, 27 Jul 2023 13:48:26 -0700 Subject: [PATCH 306/340] Mw/net 4260 phase 2 automate the k8s sameness tests (#2579) * add kustomize files - These reflect the different test cases - sameness.yaml defines the ordered list of failovers - static-server responds with a unique name so we can track failover order - static-client includes both DNS and CURL in the image used so we can exec in for testing * add sameness tests - We do a bunch of infra setup for peering and partitions, but after the initial setup only partitions are tested - We test service failover, dns failover and PQ failover scenarios * add 4 kind clusters to make target - The sameness tests require 4 kind clusters, so the make target will now spin up 4 kind clusters - not all tests need 4 kind clusters, but the entire suite of tests can be run with 4 * increase kubectl timeout to 90s - add variable for configuring timeout - timeout was triggering locally on intel mac machine, so this timeout should cover our devs lowest performing machines * add sameness test to test packages * Fix comments on partition connect test --- Makefile | 16 +- .../kind_acceptance_test_packages.yaml | 9 +- acceptance/framework/k8s/kubectl.go | 15 +- .../sameness/default-ns/kustomization.yaml | 5 + .../bases/sameness/default-ns/sameness.yaml | 12 + .../exportedservices-ap1.yaml | 9 + .../exportedservices-ap1/kustomization.yaml | 5 + .../sameness/override-ns/intentions.yaml | 12 + .../sameness/override-ns/kustomization.yaml | 7 + .../override-ns/payment-service-resolver.yaml | 13 + .../override-ns/service-defaults.yaml | 6 + .../bases/sameness/peering/kustomization.yaml | 5 + .../fixtures/bases/sameness/peering/mesh.yaml | 7 + .../ap1-partition/kustomization.yaml | 8 + .../ap1-partition/patch.yaml | 16 + .../default-partition/kustomization.yaml | 8 + .../default-partition/patch.yaml | 16 + .../static-client/default/kustomization.yaml | 8 + .../sameness/static-client/default/patch.yaml | 22 + .../partition/kustomization.yaml | 8 + .../static-client/partition/patch.yaml | 22 + .../static-server/default/kustomization.yaml | 8 + .../sameness/static-server/default/patch.yaml | 23 + .../partition/kustomization.yaml | 8 + .../static-server/partition/patch.yaml | 23 + .../partitions/partitions_connect_test.go | 4 +- acceptance/tests/sameness/main_test.go | 28 ++ acceptance/tests/sameness/sameness_test.go | 454 ++++++++++++++++++ 28 files changed, 764 insertions(+), 13 deletions(-) create mode 100644 acceptance/tests/fixtures/bases/sameness/default-ns/kustomization.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/default-ns/sameness.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/exportedservices-ap1/exportedservices-ap1.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/exportedservices-ap1/kustomization.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/override-ns/intentions.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/override-ns/kustomization.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/override-ns/payment-service-resolver.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/override-ns/service-defaults.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/peering/kustomization.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/peering/mesh.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/exported-services/ap1-partition/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/exported-services/ap1-partition/patch.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/exported-services/default-partition/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/exported-services/default-partition/patch.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/static-client/default/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/static-client/default/patch.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/static-client/partition/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/static-client/partition/patch.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/static-server/default/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/static-server/default/patch.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/static-server/partition/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/static-server/partition/patch.yaml create mode 100644 acceptance/tests/sameness/main_test.go create mode 100644 acceptance/tests/sameness/sameness_test.go diff --git a/Makefile b/Makefile index 4289b81b9b..e2c39de2ea 100644 --- a/Makefile +++ b/Makefile @@ -130,22 +130,26 @@ kind-cni-calico: kubectl create -f $(CURDIR)/acceptance/framework/environment/cni-kind/custom-resources.yaml @sleep 20 -# Helper target for doing local cni acceptance testing -kind-cni: +kind-delete: kind delete cluster --name dc1 kind delete cluster --name dc2 + kind delete cluster --name dc3 + kind delete cluster --name dc4 + + +# Helper target for doing local cni acceptance testing +kind-cni: kind-delete kind create cluster --config=$(CURDIR)/acceptance/framework/environment/cni-kind/kind.config --name dc1 --image $(KIND_NODE_IMAGE) make kind-cni-calico kind create cluster --config=$(CURDIR)/acceptance/framework/environment/cni-kind/kind.config --name dc2 --image $(KIND_NODE_IMAGE) make kind-cni-calico # Helper target for doing local acceptance testing -kind: - kind delete cluster --name dc1 - kind delete cluster --name dc2 +kind: kind-delete kind create cluster --name dc1 --image $(KIND_NODE_IMAGE) kind create cluster --name dc2 --image $(KIND_NODE_IMAGE) - + kind create cluster --name dc3 --image $(KIND_NODE_IMAGE) + kind create cluster --name dc4 --image $(KIND_NODE_IMAGE) # ===========> Shared Targets diff --git a/acceptance/ci-inputs/kind_acceptance_test_packages.yaml b/acceptance/ci-inputs/kind_acceptance_test_packages.yaml index 8677b83c4e..e0e126bbda 100644 --- a/acceptance/ci-inputs/kind_acceptance_test_packages.yaml +++ b/acceptance/ci-inputs/kind_acceptance_test_packages.yaml @@ -3,7 +3,8 @@ - {runner: 0, test-packages: "partitions"} - {runner: 1, test-packages: "peering"} -- {runner: 2, test-packages: "connect snapshot-agent wan-federation"} -- {runner: 3, test-packages: "cli vault metrics"} -- {runner: 4, test-packages: "api-gateway ingress-gateway sync example consul-dns"} -- {runner: 5, test-packages: "config-entries terminating-gateway basic"} \ No newline at end of file +- {runner: 2, test-packages: "sameness"} +- {runner: 3, test-packages: "connect snapshot-agent wan-federation"} +- {runner: 4, test-packages: "cli vault metrics"} +- {runner: 5, test-packages: "api-gateway ingress-gateway sync example consul-dns"} +- {runner: 6, test-packages: "config-entries terminating-gateway basic"} diff --git a/acceptance/framework/k8s/kubectl.go b/acceptance/framework/k8s/kubectl.go index ea90212e04..acfedbdb3d 100644 --- a/acceptance/framework/k8s/kubectl.go +++ b/acceptance/framework/k8s/kubectl.go @@ -4,6 +4,7 @@ package k8s import ( + "fmt" "strings" "testing" "time" @@ -16,6 +17,10 @@ import ( "github.com/stretchr/testify/require" ) +const ( + kubectlTimeout = "--timeout=120s" +) + // kubeAPIConnectErrs are errors that sometimes occur when talking to the // Kubernetes API related to connection issues. var kubeAPIConnectErrs = []string{ @@ -97,7 +102,7 @@ func KubectlApplyK(t *testing.T, options *k8s.KubectlOptions, kustomizeDir strin // deletes it from the cluster by running 'kubectl delete -f'. // If there's an error deleting the file, fail the test. func KubectlDelete(t *testing.T, options *k8s.KubectlOptions, configPath string) { - _, err := RunKubectlAndGetOutputE(t, options, "delete", "--timeout=60s", "-f", configPath) + _, err := RunKubectlAndGetOutputE(t, options, "delete", kubectlTimeout, "-f", configPath) require.NoError(t, err) } @@ -107,7 +112,13 @@ func KubectlDelete(t *testing.T, options *k8s.KubectlOptions, configPath string) func KubectlDeleteK(t *testing.T, options *k8s.KubectlOptions, kustomizeDir string) { // Ignore not found errors because Kubernetes automatically cleans up the kube secrets that we deployed // referencing the ServiceAccount when it is deleted. - _, err := RunKubectlAndGetOutputE(t, options, "delete", "--timeout=60s", "--ignore-not-found", "-k", kustomizeDir) + _, err := RunKubectlAndGetOutputE(t, options, "delete", kubectlTimeout, "--ignore-not-found", "-k", kustomizeDir) + require.NoError(t, err) +} + +// KubectlScale takes a deployment and scales it to the provided number of replicas. +func KubectlScale(t *testing.T, options *k8s.KubectlOptions, deployment string, replicas int) { + _, err := RunKubectlAndGetOutputE(t, options, "scale", kubectlTimeout, fmt.Sprintf("--replicas=%d", replicas), deployment) require.NoError(t, err) } diff --git a/acceptance/tests/fixtures/bases/sameness/default-ns/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/default-ns/kustomization.yaml new file mode 100644 index 0000000000..3f9d23c28a --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/default-ns/kustomization.yaml @@ -0,0 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - sameness.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/default-ns/sameness.yaml b/acceptance/tests/fixtures/bases/sameness/default-ns/sameness.yaml new file mode 100644 index 0000000000..0eb7d9e008 --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/default-ns/sameness.yaml @@ -0,0 +1,12 @@ +apiVersion: consul.hashicorp.com/v1alpha1 +kind: SamenessGroup +metadata: + name: mine +spec: + members: + - partition: default + - partition: ap1 + - peer: cluster-01-a + - peer: cluster-01-b + - peer: cluster-02-a + - peer: cluster-03-a diff --git a/acceptance/tests/fixtures/bases/sameness/exportedservices-ap1/exportedservices-ap1.yaml b/acceptance/tests/fixtures/bases/sameness/exportedservices-ap1/exportedservices-ap1.yaml new file mode 100644 index 0000000000..3dc494dd43 --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/exportedservices-ap1/exportedservices-ap1.yaml @@ -0,0 +1,9 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ExportedServices +metadata: + name: ap1 +spec: + services: [] diff --git a/acceptance/tests/fixtures/bases/sameness/exportedservices-ap1/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/exportedservices-ap1/kustomization.yaml new file mode 100644 index 0000000000..1793fa6db7 --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/exportedservices-ap1/kustomization.yaml @@ -0,0 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - exportedservices-ap1.yaml diff --git a/acceptance/tests/fixtures/bases/sameness/override-ns/intentions.yaml b/acceptance/tests/fixtures/bases/sameness/override-ns/intentions.yaml new file mode 100644 index 0000000000..425b9fe21d --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/override-ns/intentions.yaml @@ -0,0 +1,12 @@ +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ServiceIntentions +metadata: + name: static-server +spec: + destination: + name: static-server + sources: + - name: static-client + namespace: ns1 + samenessGroup: mine + action: allow diff --git a/acceptance/tests/fixtures/bases/sameness/override-ns/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/override-ns/kustomization.yaml new file mode 100644 index 0000000000..adfd1c827b --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/override-ns/kustomization.yaml @@ -0,0 +1,7 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - intentions.yaml + - payment-service-resolver.yaml + - service-defaults.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/override-ns/payment-service-resolver.yaml b/acceptance/tests/fixtures/bases/sameness/override-ns/payment-service-resolver.yaml new file mode 100644 index 0000000000..b2b6b68c3d --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/override-ns/payment-service-resolver.yaml @@ -0,0 +1,13 @@ +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ServiceResolver +metadata: + name: static-server +spec: + connectTimeout: 15s + failover: + '*': + samenessGroup: mine + policy: + mode: order-by-locality + regions: + - us-west-2 diff --git a/acceptance/tests/fixtures/bases/sameness/override-ns/service-defaults.yaml b/acceptance/tests/fixtures/bases/sameness/override-ns/service-defaults.yaml new file mode 100644 index 0000000000..f88d143728 --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/override-ns/service-defaults.yaml @@ -0,0 +1,6 @@ +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ServiceDefaults +metadata: + name: static-server +spec: + protocol: http \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/peering/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/peering/kustomization.yaml new file mode 100644 index 0000000000..926e91236d --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/peering/kustomization.yaml @@ -0,0 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - mesh.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/peering/mesh.yaml b/acceptance/tests/fixtures/bases/sameness/peering/mesh.yaml new file mode 100644 index 0000000000..de84382d3e --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/peering/mesh.yaml @@ -0,0 +1,7 @@ +apiVersion: consul.hashicorp.com/v1alpha1 +kind: Mesh +metadata: + name: mesh +spec: + peering: + peerThroughMeshGateways: true diff --git a/acceptance/tests/fixtures/cases/sameness/exported-services/ap1-partition/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/exported-services/ap1-partition/kustomization.yaml new file mode 100644 index 0000000000..2a2f47a332 --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/exported-services/ap1-partition/kustomization.yaml @@ -0,0 +1,8 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ../../../../bases/sameness/exportedservices-ap1 + +patchesStrategicMerge: +- patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/exported-services/ap1-partition/patch.yaml b/acceptance/tests/fixtures/cases/sameness/exported-services/ap1-partition/patch.yaml new file mode 100644 index 0000000000..d71e8211ba --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/exported-services/ap1-partition/patch.yaml @@ -0,0 +1,16 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ExportedServices +metadata: + name: ap1 +spec: + services: + - name: static-server + namespace: ns2 + consumers: + - samenessGroup: mine + - name: mesh-gateway + consumers: + - samenessGroup: mine diff --git a/acceptance/tests/fixtures/cases/sameness/exported-services/default-partition/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/exported-services/default-partition/kustomization.yaml new file mode 100644 index 0000000000..05de6151fc --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/exported-services/default-partition/kustomization.yaml @@ -0,0 +1,8 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ../../../../bases/exportedservices-default + +patchesStrategicMerge: +- patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/exported-services/default-partition/patch.yaml b/acceptance/tests/fixtures/cases/sameness/exported-services/default-partition/patch.yaml new file mode 100644 index 0000000000..9bb440637e --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/exported-services/default-partition/patch.yaml @@ -0,0 +1,16 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ExportedServices +metadata: + name: default +spec: + services: + - name: static-server + namespace: ns2 + consumers: + - samenessGroup: mine + - name: mesh-gateway + consumers: + - samenessGroup: mine diff --git a/acceptance/tests/fixtures/cases/sameness/static-client/default/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-client/default/kustomization.yaml new file mode 100644 index 0000000000..227f223c9f --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/static-client/default/kustomization.yaml @@ -0,0 +1,8 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ../../../../bases/static-client + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/sameness/static-client/default/patch.yaml b/acceptance/tests/fixtures/cases/sameness/static-client/default/patch.yaml new file mode 100644 index 0000000000..1775e9abb1 --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/static-client/default/patch.yaml @@ -0,0 +1,22 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: static-client +spec: + template: + metadata: + annotations: + 'consul.hashicorp.com/connect-inject': 'true' + 'consul.hashicorp.com/connect-service-upstreams': 'static-server.ns2.default:8080' + spec: + containers: + - name: static-client + image: anubhavmishra/tiny-tools:latest + # Just spin & wait forever, we'll use `kubectl exec` to demo + command: ['/bin/sh', '-c', '--'] + args: ['while true; do sleep 30; done;'] + # If ACLs are enabled, the serviceAccountName must match the Consul service name. + serviceAccountName: static-client \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/sameness/static-client/partition/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-client/partition/kustomization.yaml new file mode 100644 index 0000000000..227f223c9f --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/static-client/partition/kustomization.yaml @@ -0,0 +1,8 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ../../../../bases/static-client + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/sameness/static-client/partition/patch.yaml b/acceptance/tests/fixtures/cases/sameness/static-client/partition/patch.yaml new file mode 100644 index 0000000000..c1a14c6070 --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/static-client/partition/patch.yaml @@ -0,0 +1,22 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: static-client +spec: + template: + metadata: + annotations: + 'consul.hashicorp.com/connect-inject': 'true' + 'consul.hashicorp.com/connect-service-upstreams': 'static-server.ns2.ap1:8080' + spec: + containers: + - name: static-client + image: anubhavmishra/tiny-tools:latest + # Just spin & wait forever, we'll use `kubectl exec` to demo + command: ['/bin/sh', '-c', '--'] + args: ['while true; do sleep 30; done;'] + # If ACLs are enabled, the serviceAccountName must match the Consul service name. + serviceAccountName: static-client diff --git a/acceptance/tests/fixtures/cases/sameness/static-server/default/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-server/default/kustomization.yaml new file mode 100644 index 0000000000..c15bfe7ba7 --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/static-server/default/kustomization.yaml @@ -0,0 +1,8 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ../../../../bases/static-server + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/sameness/static-server/default/patch.yaml b/acceptance/tests/fixtures/cases/sameness/static-server/default/patch.yaml new file mode 100644 index 0000000000..ca27b7ba42 --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/static-server/default/patch.yaml @@ -0,0 +1,23 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: static-server +spec: + template: + metadata: + annotations: + "consul.hashicorp.com/connect-inject": "true" + spec: + containers: + - name: static-server + image: docker.mirror.hashicorp.services/hashicorp/http-echo:alpine + args: + - -text="cluster-01-a" + - -listen=:8080 + ports: + - containerPort: 8080 + name: http + serviceAccountName: static-server diff --git a/acceptance/tests/fixtures/cases/sameness/static-server/partition/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-server/partition/kustomization.yaml new file mode 100644 index 0000000000..c15bfe7ba7 --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/static-server/partition/kustomization.yaml @@ -0,0 +1,8 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ../../../../bases/static-server + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/sameness/static-server/partition/patch.yaml b/acceptance/tests/fixtures/cases/sameness/static-server/partition/patch.yaml new file mode 100644 index 0000000000..044115d1d1 --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/static-server/partition/patch.yaml @@ -0,0 +1,23 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: static-server +spec: + template: + metadata: + annotations: + "consul.hashicorp.com/connect-inject": "true" + spec: + containers: + - name: static-server + image: docker.mirror.hashicorp.services/hashicorp/http-echo:alpine + args: + - -text="cluster-01-b" + - -listen=:8080 + ports: + - containerPort: 8080 + name: http + serviceAccountName: static-server diff --git a/acceptance/tests/partitions/partitions_connect_test.go b/acceptance/tests/partitions/partitions_connect_test.go index 6b103614c7..c53e5b6171 100644 --- a/acceptance/tests/partitions/partitions_connect_test.go +++ b/acceptance/tests/partitions/partitions_connect_test.go @@ -108,6 +108,7 @@ func TestPartitions_Connect(t *testing.T) { "dns.enableRedirection": strconv.FormatBool(cfg.EnableTransparentProxy), } + // Setup the default partition defaultPartitionHelmValues := make(map[string]string) // On Kind, there are no load balancers but since all clusters @@ -129,6 +130,7 @@ func TestPartitions_Connect(t *testing.T) { serverConsulCluster := consul.NewHelmCluster(t, defaultPartitionHelmValues, defaultPartitionClusterContext, cfg, releaseName) serverConsulCluster.Create(t) + // Copy secrets from the default partition to the secondary partition // Get the TLS CA certificate and key secret from the server cluster and apply it to the client cluster. caCertSecretName := fmt.Sprintf("%s-consul-ca-cert", releaseName) @@ -146,7 +148,7 @@ func TestPartitions_Connect(t *testing.T) { k8sAuthMethodHost := k8s.KubernetesAPIServerHost(t, cfg, secondaryPartitionClusterContext) - // Create client cluster. + // Create secondary partition cluster. secondaryPartitionHelmValues := map[string]string{ "global.enabled": "false", diff --git a/acceptance/tests/sameness/main_test.go b/acceptance/tests/sameness/main_test.go new file mode 100644 index 0000000000..67e6ee42b7 --- /dev/null +++ b/acceptance/tests/sameness/main_test.go @@ -0,0 +1,28 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package sameness + +import ( + "fmt" + "os" + "testing" + + testsuite "github.com/hashicorp/consul-k8s/acceptance/framework/suite" +) + +var suite testsuite.Suite + +func TestMain(m *testing.M) { + suite = testsuite.NewSuite(m) + + expectedNumberOfClusters := 4 + + if suite.Config().EnableMultiCluster && suite.Config().IsExpectedClusterCount(expectedNumberOfClusters) && suite.Config().UseKind { + os.Exit(suite.Run()) + } else { + fmt.Println(fmt.Sprintf("Skipping sameness tests because either -enable-multi-cluster is "+ + "not set, the number of clusters did not match the expected count of %d, or --useKind is false. "+ + "Sameness acceptance tests are currently only suopported on Kind clusters", expectedNumberOfClusters)) + } +} diff --git a/acceptance/tests/sameness/sameness_test.go b/acceptance/tests/sameness/sameness_test.go new file mode 100644 index 0000000000..a7a926cd42 --- /dev/null +++ b/acceptance/tests/sameness/sameness_test.go @@ -0,0 +1,454 @@ +package sameness + +import ( + "context" + "fmt" + "strconv" + "testing" + + terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" + "github.com/hashicorp/consul-k8s/acceptance/framework/config" + "github.com/hashicorp/consul-k8s/acceptance/framework/consul" + "github.com/hashicorp/consul-k8s/acceptance/framework/environment" + "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" + "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" + "github.com/hashicorp/consul-k8s/acceptance/framework/logger" + "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/sdk/testutil/retry" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + primaryDatacenterPartition = "ap1" + primaryServerDatacenter = "dc1" + peer1Datacenter = "dc2" + peer2Datacenter = "dc3" + staticClientNamespace = "ns1" + staticServerNamespace = "ns2" + + keyPrimaryServer = "server" + keyPartition = "partition" + keyPeer1 = "peer1" + keyPeer2 = "peer2" + + staticServerDeployment = "deploy/static-server" + staticClientDeployment = "deploy/static-client" + + primaryServerClusterName = "cluster-01-a" + partitionClusterName = "cluster-01-b" +) + +func TestFailover_Connect(t *testing.T) { + env := suite.Environment() + cfg := suite.Config() + + if !cfg.EnableEnterprise { + t.Skipf("skipping this test because -enable-enterprise is not set") + } + + cases := []struct { + name string + ACLsEnabled bool + }{ + { + "default failover", + false, + }, + { + "secure failover", + true, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + /* + Architecture Overview: + Primary Datacenter (DC1) + Default Partition + Peer -> DC2 (cluster-02-a) + Peer -> DC3 (cluster-03-a) + AP1 Partition + Peer -> DC2 (cluster-02-a) + Peer -> DC3 (cluster-03-a) + Datacenter 2 (DC2) + Default Partition + Peer -> DC1 (cluster-01-a) + Peer -> DC1 (cluster-01-b) + Peer -> DC3 (cluster-03-a) + Datacenter 3 (DC3) + Default Partition + Peer -> DC1 (cluster-01-a) + Peer -> DC1 (cluster-01-b) + Peer -> DC2 (cluster-02-a) + + + Architecture Diagram + failover scenarios from perspective of DC1 Default Partition Static-Server + +-------------------------------------------+ + | | + | DC1 | + | | + | +-----------------------------+ | +-----------------------------------+ + | | | | | DC2 | + | | +------------------+ | | Failover 2 | +------------------+ | + | | | +-------+--------+-----------------+------>| | | + | | | Static-Server | | | | | Static-Server | | + | | | +-------+---+ | | | | | + | | | | | | | | | | | + | | | | | | | | | | | + | | | +-------+---+----+-------------+ | | | | + | | +------------------+ | | | | | +------------------+ | + | | Admin Partitions: Default | | | | | | + | | Name: cluster-01-a | | | | | Admin Partitions: Default | + | | | | | | | Name: cluster-02-a | + | +-----------------------------+ | | | | | + | | | | +-----------------------------------+ + | Failover 1| | Failover 3 | + | +-------------------------------+ | | | +-----------------------------------+ + | | | | | | | DC3 | + | | +------------------+ | | | | | +------------------+ | + | | | | | | | | | | Static-Server | | + | | | Static-Server | | | | | | | | | + | | | | | | | | | | | | + | | | | | | | +---+------>| | | + | | | |<------+--+ | | | | | + | | | | | | | +------------------+ | + | | +------------------+ | | | | + | | Admin Partitions: ap1 | | | Admin Partitions: Default | + | | Name: cluster-01-b | | | Name: cluster-03-a | + | | | | | | + | +-------------------------------+ | | | + | | +-----------------------------------+ + +-------------------------------------------+ + */ + + members := map[string]*member{ + keyPrimaryServer: {context: env.DefaultContext(t), hasServer: true}, + keyPartition: {context: env.Context(t, 1), hasServer: false}, + keyPeer1: {context: env.Context(t, 2), hasServer: true}, + keyPeer2: {context: env.Context(t, 3), hasServer: true}, + } + + // Setup Namespaces. + for _, v := range members { + createNamespaces(t, cfg, v.context) + } + + // Create the Default Cluster. + commonHelmValues := map[string]string{ + "global.peering.enabled": "true", + + "global.tls.enabled": "true", + "global.tls.httpsOnly": strconv.FormatBool(c.ACLsEnabled), + + "global.enableConsulNamespaces": "true", + + "global.adminPartitions.enabled": "true", + + "global.logLevel": "debug", + + "global.acls.manageSystemACLs": strconv.FormatBool(c.ACLsEnabled), + + "connectInject.enabled": "true", + "connectInject.consulNamespaces.mirroringK8S": "true", + + "meshGateway.enabled": "true", + "meshGateway.replicas": "1", + + "dns.enabled": "true", + } + + defaultPartitionHelmValues := map[string]string{ + "global.datacenter": primaryServerDatacenter, + } + + // On Kind, there are no load balancers but since all clusters + // share the same node network (docker bridge), we can use + // a NodePort service so that we can access node(s) in a different Kind cluster. + if cfg.UseKind { + defaultPartitionHelmValues["meshGateway.service.type"] = "NodePort" + defaultPartitionHelmValues["meshGateway.service.nodePort"] = "30200" + defaultPartitionHelmValues["server.exposeService.type"] = "NodePort" + defaultPartitionHelmValues["server.exposeService.nodePort.https"] = "30000" + defaultPartitionHelmValues["server.exposeService.nodePort.grpc"] = "30100" + } + helpers.MergeMaps(defaultPartitionHelmValues, commonHelmValues) + + releaseName := helpers.RandomName() + members[keyPrimaryServer].helmCluster = consul.NewHelmCluster(t, defaultPartitionHelmValues, members[keyPrimaryServer].context, cfg, releaseName) + members[keyPrimaryServer].helmCluster.Create(t) + + // Get the TLS CA certificate and key secret from the server cluster and apply it to the client cluster. + caCertSecretName := fmt.Sprintf("%s-consul-ca-cert", releaseName) + + logger.Logf(t, "retrieving ca cert secret %s from the server cluster and applying to the client cluster", caCertSecretName) + k8s.CopySecret(t, members[keyPrimaryServer].context, members[keyPartition].context, caCertSecretName) + + // Create Secondary Partition Cluster which will apply the primary datacenter. + partitionToken := fmt.Sprintf("%s-consul-partitions-acl-token", releaseName) + if c.ACLsEnabled { + logger.Logf(t, "retrieving partition token secret %s from the server cluster and applying to the client cluster", partitionToken) + k8s.CopySecret(t, members[keyPrimaryServer].context, members[keyPartition].context, partitionToken) + } + + partitionServiceName := fmt.Sprintf("%s-consul-expose-servers", releaseName) + partitionSvcAddress := k8s.ServiceHost(t, cfg, members[keyPrimaryServer].context, partitionServiceName) + + k8sAuthMethodHost := k8s.KubernetesAPIServerHost(t, cfg, members[keyPartition].context) + + secondaryPartitionHelmValues := map[string]string{ + "global.enabled": "false", + "global.datacenter": primaryServerDatacenter, + + "global.adminPartitions.name": primaryDatacenterPartition, + + "global.tls.caCert.secretName": caCertSecretName, + "global.tls.caCert.secretKey": "tls.crt", + + "externalServers.enabled": "true", + "externalServers.hosts[0]": partitionSvcAddress, + "externalServers.tlsServerName": fmt.Sprintf("server.%s.consul", primaryServerDatacenter), + "global.server.enabled": "false", + } + + if c.ACLsEnabled { + // Setup partition token and auth method host if ACLs enabled. + secondaryPartitionHelmValues["global.acls.bootstrapToken.secretName"] = partitionToken + secondaryPartitionHelmValues["global.acls.bootstrapToken.secretKey"] = "token" + secondaryPartitionHelmValues["externalServers.k8sAuthMethodHost"] = k8sAuthMethodHost + } + + if cfg.UseKind { + secondaryPartitionHelmValues["externalServers.httpsPort"] = "30000" + secondaryPartitionHelmValues["externalServers.grpcPort"] = "30100" + secondaryPartitionHelmValues["meshGateway.service.type"] = "NodePort" + secondaryPartitionHelmValues["meshGateway.service.nodePort"] = "30200" + } + helpers.MergeMaps(secondaryPartitionHelmValues, commonHelmValues) + + members[keyPartition].helmCluster = consul.NewHelmCluster(t, secondaryPartitionHelmValues, members[keyPartition].context, cfg, releaseName) + members[keyPartition].helmCluster.Create(t) + + // Create Peer 1 Cluster. + PeerOneHelmValues := map[string]string{ + "global.datacenter": peer1Datacenter, + } + + if cfg.UseKind { + PeerOneHelmValues["server.exposeGossipAndRPCPorts"] = "true" + PeerOneHelmValues["meshGateway.service.type"] = "NodePort" + PeerOneHelmValues["meshGateway.service.nodePort"] = "30100" + } + helpers.MergeMaps(PeerOneHelmValues, commonHelmValues) + + members[keyPeer1].helmCluster = consul.NewHelmCluster(t, PeerOneHelmValues, members[keyPeer1].context, cfg, releaseName) + members[keyPeer1].helmCluster.Create(t) + + // Create Peer 2 Cluster. + PeerTwoHelmValues := map[string]string{ + "global.datacenter": peer2Datacenter, + } + + if cfg.UseKind { + PeerTwoHelmValues["server.exposeGossipAndRPCPorts"] = "true" + PeerTwoHelmValues["meshGateway.service.type"] = "NodePort" + PeerTwoHelmValues["meshGateway.service.nodePort"] = "30100" + } + helpers.MergeMaps(PeerTwoHelmValues, commonHelmValues) + + members[keyPeer2].helmCluster = consul.NewHelmCluster(t, PeerTwoHelmValues, members[keyPeer2].context, cfg, releaseName) + members[keyPeer2].helmCluster.Create(t) + + // Create a ProxyDefaults resource to configure services to use the mesh + // gateways and set server and client opts. + for k, v := range members { + logger.Logf(t, "applying resources on %s", v.context.KubectlOptions(t).ContextName) + + // Client will use the client namespace. + members[k].clientOpts = &terratestk8s.KubectlOptions{ + ContextName: v.context.KubectlOptions(t).ContextName, + ConfigPath: v.context.KubectlOptions(t).ConfigPath, + Namespace: staticClientNamespace, + } + + // Server will use the server namespace. + members[k].serverOpts = &terratestk8s.KubectlOptions{ + ContextName: v.context.KubectlOptions(t).ContextName, + ConfigPath: v.context.KubectlOptions(t).ConfigPath, + Namespace: staticServerNamespace, + } + + // Sameness Defaults need to be applied first so that the sameness group exists. + applyResources(t, cfg, "../fixtures/bases/mesh-gateway", members[k].context.KubectlOptions(t)) + applyResources(t, cfg, "../fixtures/bases/sameness/default-ns", members[k].context.KubectlOptions(t)) + applyResources(t, cfg, "../fixtures/bases/sameness/override-ns", members[k].serverOpts) + + // Only assign a client if the cluster is running a Consul server. + if v.hasServer { + members[k].client, _ = members[k].helmCluster.SetupConsulClient(t, c.ACLsEnabled) + } + } + + // TODO: Add further setup for peering, right now the rest of this test will only cover Partitions + // Create static server deployments. + logger.Log(t, "creating static-server and static-client deployments") + k8s.DeployKustomize(t, members[keyPrimaryServer].serverOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, + "../fixtures/cases/sameness/static-server/default") + k8s.DeployKustomize(t, members[keyPartition].serverOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, + "../fixtures/cases/sameness/static-server/partition") + + // Create static client deployments. + k8s.DeployKustomize(t, members[keyPrimaryServer].clientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, + "../fixtures/cases/sameness/static-client/default") + k8s.DeployKustomize(t, members[keyPartition].clientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, + "../fixtures/cases/sameness/static-client/partition") + + // Verify that both static-server and static-client have been injected and now have 2 containers in server cluster. + // Also get the server IP + for _, labelSelector := range []string{"app=static-server", "app=static-client"} { + podList, err := members[keyPrimaryServer].context.KubernetesClient(t).CoreV1().Pods(metav1.NamespaceAll).List(context.Background(), + metav1.ListOptions{LabelSelector: labelSelector}) + require.NoError(t, err) + require.Len(t, podList.Items, 1) + require.Len(t, podList.Items[0].Spec.Containers, 2) + if labelSelector == "app=static-server" { + ip := &podList.Items[0].Status.PodIP + require.NotNil(t, ip) + logger.Logf(t, "default-static-server-ip: %s", *ip) + members[keyPrimaryServer].staticServerIP = ip + } + + podList, err = members[keyPartition].context.KubernetesClient(t).CoreV1().Pods(metav1.NamespaceAll).List(context.Background(), + metav1.ListOptions{LabelSelector: labelSelector}) + require.NoError(t, err) + require.Len(t, podList.Items, 1) + require.Len(t, podList.Items[0].Spec.Containers, 2) + if labelSelector == "app=static-server" { + ip := &podList.Items[0].Status.PodIP + require.NotNil(t, ip) + logger.Logf(t, "partition-static-server-ip: %s", *ip) + members[keyPartition].staticServerIP = ip + } + } + + logger.Log(t, "creating exported services") + applyResources(t, cfg, "../fixtures/cases/sameness/exported-services/default-partition", members[keyPrimaryServer].context.KubectlOptions(t)) + applyResources(t, cfg, "../fixtures/cases/sameness/exported-services/ap1-partition", members[keyPartition].context.KubectlOptions(t)) + + // Setup DNS. + dnsService, err := members[keyPrimaryServer].context.KubernetesClient(t).CoreV1().Services("default").Get(context.Background(), fmt.Sprintf("%s-%s", releaseName, "consul-dns"), metav1.GetOptions{}) + require.NoError(t, err) + dnsIP := dnsService.Spec.ClusterIP + logger.Logf(t, "dnsIP: %s", dnsIP) + + // Setup Prepared Query. + definition := &api.PreparedQueryDefinition{ + Name: "my-query", + Service: api.ServiceQuery{ + Service: "static-server", + SamenessGroup: "mine", + Namespace: staticServerNamespace, + OnlyPassing: false, + }, + } + resp, _, err := members[keyPrimaryServer].client.PreparedQuery().Create(definition, &api.WriteOptions{}) + require.NoError(t, err) + logger.Logf(t, "PQ ID: %s", resp) + + logger.Log(t, "all infrastructure up and running") + logger.Log(t, "verifying failover scenarios") + + const dnsLookup = "static-server.service.ns2.ns.mine.sg.consul" + const dnsPQLookup = "my-query.query.consul" + + // Verify initial server. + serviceFailoverCheck(t, primaryServerClusterName, members[keyPrimaryServer]) + + // Verify initial dns. + dnsFailoverCheck(t, releaseName, dnsIP, dnsLookup, members[keyPrimaryServer], members[keyPrimaryServer]) + + // Verify initial dns with PQ. + dnsFailoverCheck(t, releaseName, dnsIP, dnsPQLookup, members[keyPrimaryServer], members[keyPrimaryServer]) + + // Scale down static-server on the server, will fail over to partition. + k8s.KubectlScale(t, members[keyPrimaryServer].serverOpts, staticServerDeployment, 0) + + // Verify failover to partition. + serviceFailoverCheck(t, partitionClusterName, members[keyPrimaryServer]) + + // Verify dns failover to partition. + dnsFailoverCheck(t, releaseName, dnsIP, dnsLookup, members[keyPrimaryServer], members[keyPartition]) + + // Verify prepared query failover. + dnsFailoverCheck(t, releaseName, dnsIP, dnsPQLookup, members[keyPrimaryServer], members[keyPartition]) + + logger.Log(t, "tests complete") + }) + } +} + +type member struct { + context environment.TestContext + helmCluster *consul.HelmCluster + client *api.Client + hasServer bool + serverOpts *terratestk8s.KubectlOptions + clientOpts *terratestk8s.KubectlOptions + staticServerIP *string +} + +func createNamespaces(t *testing.T, cfg *config.TestConfig, context environment.TestContext) { + logger.Logf(t, "creating namespaces in %s", context.KubectlOptions(t).ContextName) + k8s.RunKubectl(t, context.KubectlOptions(t), "create", "ns", staticServerNamespace) + k8s.RunKubectl(t, context.KubectlOptions(t), "create", "ns", staticClientNamespace) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + k8s.RunKubectl(t, context.KubectlOptions(t), "delete", "ns", staticClientNamespace, staticServerNamespace) + }) +} + +func applyResources(t *testing.T, cfg *config.TestConfig, kustomizeDir string, opts *terratestk8s.KubectlOptions) { + k8s.KubectlApplyK(t, opts, kustomizeDir) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + k8s.KubectlDeleteK(t, opts, kustomizeDir) + }) +} + +// serviceFailoverCheck verifies that the server failed over as expected by checking that curling the `static-server` +// using the `static-client` responds with the expected cluster name. Each static-server responds with a uniquue +// name so that we can verify failover occured as expected. +func serviceFailoverCheck(t *testing.T, expectedClusterName string, server *member) { + retry.Run(t, func(r *retry.R) { + resp, err := k8s.RunKubectlAndGetOutputE(t, server.clientOpts, "exec", "-i", + staticClientDeployment, "-c", "static-client", "--", "curl", "localhost:8080") + require.NoError(r, err) + assert.Contains(r, resp, expectedClusterName) + logger.Log(t, resp) + }) +} + +func dnsFailoverCheck(t *testing.T, releaseName string, dnsIP string, dnsQuery string, server, failover *member) { + retry.Run(t, func(r *retry.R) { + logs, err := k8s.RunKubectlAndGetOutputE(t, server.clientOpts, "exec", "-i", + staticClientDeployment, "-c", "static-client", "--", "dig", fmt.Sprintf("@%s-consul-dns.default", releaseName), dnsQuery) + require.NoError(r, err) + + // When the `dig` request is successful, a section of its response looks like the following: + // + // ;; ANSWER SECTION: + // static-server.service.mine.sg.ns2.ns.consul. 0 IN A + // + // ;; Query time: 2 msec + // ;; SERVER: #() + // ;; WHEN: Mon Aug 10 15:02:40 UTC 2020 + // ;; MSG SIZE rcvd: 98 + // + // We assert on the existence of the ANSWER SECTION, The consul-server IPs being present in + // the ANSWER SECTION and the DNS IP mentioned in the SERVER: field + + assert.Contains(r, logs, fmt.Sprintf("SERVER: %s", dnsIP)) + assert.Contains(r, logs, "ANSWER SECTION:") + assert.Contains(r, logs, *failover.staticServerIP) + }) +} From 89ee905738aca9e1f68aff47676c3bd60d7dc237 Mon Sep 17 00:00:00 2001 From: Ganesh S Date: Fri, 28 Jul 2023 10:02:15 -0700 Subject: [PATCH 307/340] Added logLevel field for components (#2302) * Added logLevel field for components * Add changelog * Fix tests * Rename 2298.txt to 2302.txt * Address comments * Fix tests * Fix helm tests * Address comments * Add client and server loglevels * Fix bats * Update changelog * Fix bats tests --- .changelog/2302.txt | 13 ++++ .../templates/client-config-configmap.yaml | 6 ++ .../create-federation-secret-job.yaml | 2 +- .../gossip-encryption-autogenerate-job.yaml | 2 +- .../ingress-gateways-deployment.yaml | 4 +- .../templates/mesh-gateway-deployment.yaml | 4 +- .../server-acl-init-cleanup-job.yaml | 2 +- .../consul/templates/server-acl-init-job.yaml | 2 +- .../templates/server-config-configmap.yaml | 3 + .../telemetry-collector-deployment.yaml | 4 +- .../terminating-gateways-deployment.yaml | 4 +- charts/consul/templates/tls-init-job.yaml | 2 +- .../test/unit/client-config-configmap.bats | 26 ++++++++ charts/consul/test/unit/client-daemonset.bats | 6 +- .../unit/create-federation-secret-job.bats | 38 ++++++++++++ .../gossip-encryption-autogenerate-job.bats | 30 +++++++++ .../unit/ingress-gateways-deployment.bats | 61 +++++++++++++++++++ .../test/unit/mesh-gateway-deployment.bats | 61 +++++++++++++++++++ .../unit/server-acl-init-cleanup-job.bats | 28 +++++++++ .../consul/test/unit/server-acl-init-job.bats | 30 +++++++++ .../test/unit/server-config-configmap.bats | 21 +++++++ .../unit/telemetry-collector-deployment.bats | 57 +++++++++++++++++ .../unit/terminating-gateways-deployment.bats | 61 +++++++++++++++++++ charts/consul/test/unit/tls-init-job.bats | 30 +++++++++ charts/consul/values.yaml | 39 ++++++++++++ 25 files changed, 520 insertions(+), 16 deletions(-) create mode 100644 .changelog/2302.txt diff --git a/.changelog/2302.txt b/.changelog/2302.txt new file mode 100644 index 0000000000..7bf7e6b0f6 --- /dev/null +++ b/.changelog/2302.txt @@ -0,0 +1,13 @@ +```release-note:improvement +Add support to provide the logLevel flag via helm for multiple low level components. Introduces the following fields +1. `global.acls.logLevel` +2. `global.tls.logLevel` +3. `global.federation.logLevel` +4. `global.gossipEncryption.logLevel` +5. `server.logLevel` +6. `client.logLevel` +7. `meshGateway.logLevel` +8. `ingressGateways.logLevel` +9. `terminatingGateways.logLevel` +10. `telemetryCollector.logLevel` +``` diff --git a/charts/consul/templates/client-config-configmap.yaml b/charts/consul/templates/client-config-configmap.yaml index f9650a100b..d91a4d21bf 100644 --- a/charts/consul/templates/client-config-configmap.yaml +++ b/charts/consul/templates/client-config-configmap.yaml @@ -19,6 +19,12 @@ data: "auto_reload_config": true {{- end }} } + log-level.json: |- + { + {{- if .Values.client.logLevel }} + "log_level": "{{ .Values.client.logLevel | upper }}" + {{- end }} + } extra-from-values.json: |- {{ tpl .Values.client.extraConfig . | trimAll "\"" | indent 4 }} central-config.json: |- diff --git a/charts/consul/templates/create-federation-secret-job.yaml b/charts/consul/templates/create-federation-secret-job.yaml index 4f83a1f82a..bc3e0a988b 100644 --- a/charts/consul/templates/create-federation-secret-job.yaml +++ b/charts/consul/templates/create-federation-secret-job.yaml @@ -119,7 +119,7 @@ spec: - "-ec" - | consul-k8s-control-plane create-federation-secret \ - -log-level={{ .Values.global.logLevel }} \ + -log-level={{ default .Values.global.logLevel .Values.global.federation.logLevel }} \ -log-json={{ .Values.global.logJSON }} \ {{- if (or .Values.global.gossipEncryption.autoGenerate (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey)) }} -gossip-key-file=/consul/gossip/gossip.key \ diff --git a/charts/consul/templates/gossip-encryption-autogenerate-job.yaml b/charts/consul/templates/gossip-encryption-autogenerate-job.yaml index 240bfe3f9c..02fb3ea168 100644 --- a/charts/consul/templates/gossip-encryption-autogenerate-job.yaml +++ b/charts/consul/templates/gossip-encryption-autogenerate-job.yaml @@ -57,7 +57,7 @@ spec: -namespace={{ .Release.Namespace }} \ -secret-name={{ template "consul.fullname" . }}-gossip-encryption-key \ -secret-key="key" \ - -log-level={{ .Values.global.logLevel }} \ + -log-level={{ default .Values.global.logLevel .Values.global.gossipEncryption.logLevel }} \ -log-json={{ .Values.global.logJSON }} resources: requests: diff --git a/charts/consul/templates/ingress-gateways-deployment.yaml b/charts/consul/templates/ingress-gateways-deployment.yaml index 4f72031855..328c06ee3e 100644 --- a/charts/consul/templates/ingress-gateways-deployment.yaml +++ b/charts/consul/templates/ingress-gateways-deployment.yaml @@ -211,7 +211,7 @@ spec: -gateway-kind="ingress-gateway" \ -proxy-id-file=/consul/service/proxy-id \ -service-name={{ template "consul.fullname" $root }}-{{ .name }} \ - -log-level={{ default $root.Values.global.logLevel }} \ + -log-level={{ default $root.Values.global.logLevel $root.Values.ingressGateways.logLevel }} \ -log-json={{ $root.Values.global.logJSON }} volumeMounts: - name: consul-service @@ -319,7 +319,7 @@ spec: {{- if $root.Values.global.adminPartitions.enabled }} - -service-partition={{ $root.Values.global.adminPartitions.name }} {{- end }} - - -log-level={{ default $root.Values.global.logLevel }} + - -log-level={{ default $root.Values.global.logLevel $root.Values.ingressGateways.logLevel }} - -log-json={{ $root.Values.global.logJSON }} {{- if (and $root.Values.global.metrics.enabled $root.Values.global.metrics.enableGatewayMetrics) }} - -telemetry-prom-scrape-path=/metrics diff --git a/charts/consul/templates/mesh-gateway-deployment.yaml b/charts/consul/templates/mesh-gateway-deployment.yaml index 449d6ae492..1936138db3 100644 --- a/charts/consul/templates/mesh-gateway-deployment.yaml +++ b/charts/consul/templates/mesh-gateway-deployment.yaml @@ -161,7 +161,7 @@ spec: -gateway-kind="mesh-gateway" \ -proxy-id-file=/consul/service/proxy-id \ -service-name={{ .Values.meshGateway.consulServiceName }} \ - -log-level={{ default .Values.global.logLevel }} \ + -log-level={{ default .Values.global.logLevel .Values.meshGateway.logLevel }} \ -log-json={{ .Values.global.logJSON }} volumeMounts: - name: consul-service @@ -267,7 +267,7 @@ spec: {{- if .Values.global.adminPartitions.enabled }} - -service-partition={{ .Values.global.adminPartitions.name }} {{- end }} - - -log-level={{ default .Values.global.logLevel }} + - -log-level={{ default .Values.global.logLevel .Values.meshGateway.logLevel }} - -log-json={{ .Values.global.logJSON }} {{- if (and .Values.global.metrics.enabled .Values.global.metrics.enableGatewayMetrics) }} - -telemetry-prom-scrape-path=/metrics diff --git a/charts/consul/templates/server-acl-init-cleanup-job.yaml b/charts/consul/templates/server-acl-init-cleanup-job.yaml index c9f6763bd8..39754d6c6f 100644 --- a/charts/consul/templates/server-acl-init-cleanup-job.yaml +++ b/charts/consul/templates/server-acl-init-cleanup-job.yaml @@ -67,7 +67,7 @@ spec: - consul-k8s-control-plane args: - delete-completed-job - - -log-level={{ .Values.global.logLevel }} + - -log-level={{ default .Values.global.logLevel .Values.global.acls.logLevel }} - -log-json={{ .Values.global.logJSON }} - -k8s-namespace={{ .Release.Namespace }} - {{ template "consul.fullname" . }}-server-acl-init diff --git a/charts/consul/templates/server-acl-init-job.yaml b/charts/consul/templates/server-acl-init-job.yaml index c3d4a710e8..e8a06cf7aa 100644 --- a/charts/consul/templates/server-acl-init-job.yaml +++ b/charts/consul/templates/server-acl-init-job.yaml @@ -171,7 +171,7 @@ spec: CONSUL_FULLNAME="{{template "consul.fullname" . }}" consul-k8s-control-plane server-acl-init \ - -log-level={{ .Values.global.logLevel }} \ + -log-level={{ default .Values.global.logLevel .Values.global.acls.logLevel}} \ -log-json={{ .Values.global.logJSON }} \ -resource-prefix=${CONSUL_FULLNAME} \ -k8s-namespace={{ .Release.Namespace }} \ diff --git a/charts/consul/templates/server-config-configmap.yaml b/charts/consul/templates/server-config-configmap.yaml index 7e3d251001..6c102f0ae3 100644 --- a/charts/consul/templates/server-config-configmap.yaml +++ b/charts/consul/templates/server-config-configmap.yaml @@ -27,6 +27,9 @@ data: }, "datacenter": "{{ .Values.global.datacenter }}", "data_dir": "/consul/data", + {{- if .Values.server.logLevel }} + "log_level": "{{ .Values.server.logLevel | upper }}", + {{- end }} "domain": "{{ .Values.global.domain }}", "limits": { "request_limits": { diff --git a/charts/consul/templates/telemetry-collector-deployment.yaml b/charts/consul/templates/telemetry-collector-deployment.yaml index b729273dd8..4b5bb6df7b 100644 --- a/charts/consul/templates/telemetry-collector-deployment.yaml +++ b/charts/consul/templates/telemetry-collector-deployment.yaml @@ -115,7 +115,7 @@ spec: - -ec - |- consul-k8s-control-plane connect-init -pod-name=${POD_NAME} -pod-namespace=${POD_NAMESPACE} \ - -log-level={{ default .Values.global.logLevel }} \ + -log-level={{ default .Values.global.logLevel .Values.telemetryCollector.logLevel }} \ -log-json={{ .Values.global.logJSON }} \ -service-account-name="consul-telemetry-collector" \ -service-name="" \ @@ -303,7 +303,7 @@ spec: {{- if .Values.global.metrics.enabled }} - -telemetry-prom-scrape-path=/metrics {{- end }} - - -log-level={{ default .Values.global.logLevel }} + - -log-level={{ default .Values.global.logLevel .Values.telemetryCollector.logLevel }} - -log-json={{ .Values.global.logJSON }} - -envoy-concurrency=2 {{- if and .Values.externalServers.enabled .Values.externalServers.skipServerWatch }} diff --git a/charts/consul/templates/terminating-gateways-deployment.yaml b/charts/consul/templates/terminating-gateways-deployment.yaml index 2f2cb9a921..fdf2c17d05 100644 --- a/charts/consul/templates/terminating-gateways-deployment.yaml +++ b/charts/consul/templates/terminating-gateways-deployment.yaml @@ -196,7 +196,7 @@ spec: -gateway-kind="terminating-gateway" \ -proxy-id-file=/consul/service/proxy-id \ -service-name={{ .name }} \ - -log-level={{ default $root.Values.global.logLevel }} \ + -log-level={{ default $root.Values.global.logLevel $root.Values.terminatingGateways.logLevel }} \ -log-json={{ $root.Values.global.logJSON }} volumeMounts: - name: consul-service @@ -300,7 +300,7 @@ spec: {{- if $root.Values.global.adminPartitions.enabled }} - -service-partition={{ $root.Values.global.adminPartitions.name }} {{- end }} - - -log-level={{ default $root.Values.global.logLevel }} + - -log-level={{ default $root.Values.global.logLevel $root.Values.terminatingGateways.logLevel }} - -log-json={{ $root.Values.global.logJSON }} {{- if (and $root.Values.global.metrics.enabled $root.Values.global.metrics.enableGatewayMetrics) }} - -telemetry-prom-scrape-path=/metrics diff --git a/charts/consul/templates/tls-init-job.yaml b/charts/consul/templates/tls-init-job.yaml index 12d3acbad8..47651fe14b 100644 --- a/charts/consul/templates/tls-init-job.yaml +++ b/charts/consul/templates/tls-init-job.yaml @@ -80,7 +80,7 @@ spec: # and use * at the start of the dns name when setting -additional-dnsname. set -o noglob consul-k8s-control-plane tls-init \ - -log-level={{ .Values.global.logLevel }} \ + -log-level={{ default .Values.global.logLevel .Values.global.tls.logLevel }} \ -log-json={{ .Values.global.logJSON }} \ -domain={{ .Values.global.domain }} \ -days=730 \ diff --git a/charts/consul/test/unit/client-config-configmap.bats b/charts/consul/test/unit/client-config-configmap.bats index 5fc4a186d9..1f1443a156 100755 --- a/charts/consul/test/unit/client-config-configmap.bats +++ b/charts/consul/test/unit/client-config-configmap.bats @@ -95,3 +95,29 @@ load _helpers [ "${actual}" = null ] } + +#-------------------------------------------------------------------- +# logLevel + +@test "client/ConfigMap: client.logLevel is empty" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/client-config-configmap.yaml \ + --set 'client.enabled=true' \ + . | tee /dev/stderr | + yq -r '.data["log-level.json"]' | jq -r .log_level | tee /dev/stderr) + + [ "${actual}" = "null" ] +} + +@test "client/ConfigMap: client.logLevel is non empty" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/client-config-configmap.yaml \ + --set 'client.enabled=true' \ + --set 'client.logLevel=DEBUG' \ + . | tee /dev/stderr | + yq -r '.data["log-level.json"]' | jq -r .log_level | tee /dev/stderr) + + [ "${actual}" = "DEBUG" ] +} diff --git a/charts/consul/test/unit/client-daemonset.bats b/charts/consul/test/unit/client-daemonset.bats index 6e7a030cb1..d512ad8ab2 100755 --- a/charts/consul/test/unit/client-daemonset.bats +++ b/charts/consul/test/unit/client-daemonset.bats @@ -621,7 +621,7 @@ load _helpers --set 'client.enabled=true' \ . | tee /dev/stderr | yq -r '.spec.template.metadata.annotations."consul.hashicorp.com/config-checksum"' | tee /dev/stderr) - [ "${actual}" = f9be2829fed80a127e3752e10be32f29c2f9ca0ea548abcf3d4fc2c985cb7201 ] + [ "${actual}" = 4fa9ddc3abc4c79eafccb19e5beef80006b7c9736b867d8873554ca03f42a6b3 ] } @test "client/DaemonSet: config-checksum annotation changes when extraConfig is provided" { @@ -632,7 +632,7 @@ load _helpers --set 'client.extraConfig="{\"hello\": \"world\"}"' \ . | tee /dev/stderr | yq -r '.spec.template.metadata.annotations."consul.hashicorp.com/config-checksum"' | tee /dev/stderr) - [ "${actual}" = e9fb5f0b4ff4e36a89e8ca2dc1aed2072306e0dd6d4cc60b3edf155cf8dbe2e9 ] + [ "${actual}" = 42b99932385e7a0580b134fe36a9bda405aab2e375593326677b9838708f0796 ] } @test "client/DaemonSet: config-checksum annotation changes when connectInject.enabled=true" { @@ -643,7 +643,7 @@ load _helpers --set 'connectInject.enabled=true' \ . | tee /dev/stderr | yq -r '.spec.template.metadata.annotations."consul.hashicorp.com/config-checksum"' | tee /dev/stderr) - [ "${actual}" = f9be2829fed80a127e3752e10be32f29c2f9ca0ea548abcf3d4fc2c985cb7201 ] + [ "${actual}" = 4fa9ddc3abc4c79eafccb19e5beef80006b7c9736b867d8873554ca03f42a6b3 ] } #-------------------------------------------------------------------- diff --git a/charts/consul/test/unit/create-federation-secret-job.bats b/charts/consul/test/unit/create-federation-secret-job.bats index e528f28f0e..872cf2e36c 100644 --- a/charts/consul/test/unit/create-federation-secret-job.bats +++ b/charts/consul/test/unit/create-federation-secret-job.bats @@ -418,3 +418,41 @@ load _helpers [ "${actualTemplateFoo}" = "bar" ] [ "${actualTemplateBaz}" = "qux" ] } + +#-------------------------------------------------------------------- +# logLevel + +@test "createFederationSecret/Job: logLevel is not set by default" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/create-federation-secret-job.yaml \ + --set 'global.federation.enabled=true' \ + --set 'meshGateway.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'global.tls.enabled=true' \ + --set 'global.federation.createFederationSecret=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "createFederationSecret/Job: override the global.logLevel flag with global.federation.logLevel" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/create-federation-secret-job.yaml \ + --set 'global.federation.enabled=true' \ + --set 'meshGateway.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'global.tls.enabled=true' \ + --set 'global.federation.createFederationSecret=true' \ + --set 'global.federation.logLevel=debug' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=debug"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} diff --git a/charts/consul/test/unit/gossip-encryption-autogenerate-job.bats b/charts/consul/test/unit/gossip-encryption-autogenerate-job.bats index 662b523bc0..7520696182 100644 --- a/charts/consul/test/unit/gossip-encryption-autogenerate-job.bats +++ b/charts/consul/test/unit/gossip-encryption-autogenerate-job.bats @@ -105,3 +105,33 @@ load _helpers [ "${actualTemplateFoo}" = "bar" ] [ "${actualTemplateBaz}" = "qux" ] } + +#-------------------------------------------------------------------- +# logLevel + +@test "gossipEncryptionAutogenerate/Job: uses the global.logLevel flag by default" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/gossip-encryption-autogenerate-job.yaml \ + --set 'global.gossipEncryption.autoGenerate=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "gossipEncryptionAutogenerate/Job: overrides the global.logLevel flag when global.gossipEncryption.logLevel is set" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/gossip-encryption-autogenerate-job.yaml \ + --set 'global.gossipEncryption.autoGenerate=true' \ + --set 'global.gossipEncryption.logLevel=debug' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=debug"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} diff --git a/charts/consul/test/unit/ingress-gateways-deployment.bats b/charts/consul/test/unit/ingress-gateways-deployment.bats index 8ed76be13a..e8390278a8 100644 --- a/charts/consul/test/unit/ingress-gateways-deployment.bats +++ b/charts/consul/test/unit/ingress-gateways-deployment.bats @@ -1504,3 +1504,64 @@ key2: value2' \ [ "${actualTemplateFoo}" = "bar" ] [ "${actualTemplateBaz}" = "qux" ] } + +#-------------------------------------------------------------------- +# logLevel + +@test "ingressGateways/Deployment: use global.logLevel by default" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/ingress-gateways-deployment.yaml \ + --set 'ingressGateways.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.initContainers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "ingressGateways/Deployment: override global.logLevel when ingressGateways.logLevel is set" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/ingress-gateways-deployment.yaml \ + --set 'ingressGateways.enabled=true' \ + --set 'ingressGateways.logLevel=warn' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.initContainers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=warn"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "ingressGateways/Deployment: use global.logLevel by default for dataplane container" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/ingress-gateways-deployment.yaml \ + --set 'ingressGateways.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].args' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "ingressGateways/Deployment: override global.logLevel when ingressGateways.logLevel is set for dataplane container" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/ingress-gateways-deployment.yaml \ + --set 'ingressGateways.enabled=true' \ + --set 'ingressGateways.logLevel=trace' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].args' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=trace"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} \ No newline at end of file diff --git a/charts/consul/test/unit/mesh-gateway-deployment.bats b/charts/consul/test/unit/mesh-gateway-deployment.bats index 588b026d40..d58def05da 100755 --- a/charts/consul/test/unit/mesh-gateway-deployment.bats +++ b/charts/consul/test/unit/mesh-gateway-deployment.bats @@ -1644,3 +1644,64 @@ key2: value2' \ [ "${actualTemplateFoo}" = "bar" ] [ "${actualTemplateBaz}" = "qux" ] } + +#-------------------------------------------------------------------- +# logLevel + +@test "meshGateway/Deployment: use global.logLevel by default" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/mesh-gateway-deployment.yaml \ + --set 'meshGateway.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.initContainers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "meshGateway/Deployment: override global.logLevel when meshGateway.logLevel is set" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/mesh-gateway-deployment.yaml \ + --set 'meshGateway.enabled=true' \ + --set 'meshGateway.logLevel=warn' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.initContainers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=warn"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "meshGateway/Deployment: use global.logLevel by default for dataplane container" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/mesh-gateway-deployment.yaml \ + --set 'meshGateway.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].args' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "meshGateway/Deployment: override global.logLevel when meshGateway.logLevel is set for dataplane container" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/mesh-gateway-deployment.yaml \ + --set 'meshGateway.enabled=true' \ + --set 'meshGateway.logLevel=warn' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].args' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=warn"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} \ No newline at end of file diff --git a/charts/consul/test/unit/server-acl-init-cleanup-job.bats b/charts/consul/test/unit/server-acl-init-cleanup-job.bats index c886b2ec51..8743ea4a8d 100644 --- a/charts/consul/test/unit/server-acl-init-cleanup-job.bats +++ b/charts/consul/test/unit/server-acl-init-cleanup-job.bats @@ -161,6 +161,34 @@ load _helpers } #-------------------------------------------------------------------- +# logLevel + +@test "serverACLInitCleanup/Job: use global.logLevel by default" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/server-acl-init-cleanup-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].args' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "serverACLInitCleanup/Job: override global.logLevel when global.acls.logLevel is set" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/server-acl-init-cleanup-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.acls.logLevel=debug' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].args' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=debug"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} # resources @test "serverACLInitCleanup/Job: resources defined by default" { diff --git a/charts/consul/test/unit/server-acl-init-job.bats b/charts/consul/test/unit/server-acl-init-job.bats index 17c3e63935..1dc55a9551 100644 --- a/charts/consul/test/unit/server-acl-init-job.bats +++ b/charts/consul/test/unit/server-acl-init-job.bats @@ -2219,6 +2219,36 @@ load _helpers [ "${actualTemplateBaz}" = "qux" ] } +#-------------------------------------------------------------------- +# logLevel + +@test "serverACLInit/Job: use global.logLevel by default" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "serverACLInit/Job: override global.logLevel when global.acls.logLevel is set" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.acls.logLevel=debug' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=debug"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + #-------------------------------------------------------------------- # resources diff --git a/charts/consul/test/unit/server-config-configmap.bats b/charts/consul/test/unit/server-config-configmap.bats index d55c10dd3a..643caeb0a1 100755 --- a/charts/consul/test/unit/server-config-configmap.bats +++ b/charts/consul/test/unit/server-config-configmap.bats @@ -1196,4 +1196,25 @@ load _helpers local actual=$(echo $object | jq -r .audit.sink.MySink3.type | tee /dev/stderr) [ "${actual}" = "file" ] +} + +@test "server/ConfigMap: server.logLevel is empty" { + cd `chart_dir` + local configmap=$(helm template \ + -s templates/server-config-configmap.yaml \ + . | tee /dev/stderr | + yq -r '.data["server.json"]' | jq -r .log_level | tee /dev/stderr) + + [ "${configmap}" = "null" ] +} + +@test "server/ConfigMap: server.logLevel is non empty" { + cd `chart_dir` + local configmap=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'server.logLevel=debug' \ + . | tee /dev/stderr | + yq -r '.data["server.json"]' | jq -r .log_level | tee /dev/stderr) + + [ "${configmap}" = "DEBUG" ] } \ No newline at end of file diff --git a/charts/consul/test/unit/telemetry-collector-deployment.bats b/charts/consul/test/unit/telemetry-collector-deployment.bats index 705447621e..7809039e11 100755 --- a/charts/consul/test/unit/telemetry-collector-deployment.bats +++ b/charts/consul/test/unit/telemetry-collector-deployment.bats @@ -1074,3 +1074,60 @@ MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ yq -r 'map(select(.name == "foo")) | .[0].value' | tee /dev/stderr) [ "${actual}" = "bar" ] } + +#-------------------------------------------------------------------- +# logLevel + +@test "telemetryCollector/Deployment: use global.logLevel by default" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.initContainers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "telemetryCollector/Deployment: override global.logLevel when telemetryCollector.logLevel is set" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.logLevel=warn' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.initContainers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=warn"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "telemetryCollector/Deployment: use global.logLevel by default for dataplane container" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[1].args' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "telemetryCollector/Deployment: override global.logLevel when telemetryCollector.logLevel is set for dataplane container" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.logLevel=debug' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[1].args' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=debug"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} \ No newline at end of file diff --git a/charts/consul/test/unit/terminating-gateways-deployment.bats b/charts/consul/test/unit/terminating-gateways-deployment.bats index 523138a351..1dc3befbdf 100644 --- a/charts/consul/test/unit/terminating-gateways-deployment.bats +++ b/charts/consul/test/unit/terminating-gateways-deployment.bats @@ -1504,3 +1504,64 @@ key2: value2' \ [ "${actualTemplateFoo}" = "bar" ] [ "${actualTemplateBaz}" = "qux" ] } + +#-------------------------------------------------------------------- +# logLevel + +@test "terminatingGateways/Deployment: use global.logLevel by default" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/terminating-gateways-deployment.yaml \ + --set 'terminatingGateways.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.initContainers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "terminatingGateways/Deployment: override global.logLevel when terminatingGateways.logLevel is set" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/terminating-gateways-deployment.yaml \ + --set 'terminatingGateways.enabled=true' \ + --set 'terminatingGateways.logLevel=debug' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.initContainers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=debug"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "terminatingGateways/Deployment: use global.logLevel by default for dataplane container" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/terminating-gateways-deployment.yaml \ + --set 'terminatingGateways.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].args' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "terminatingGateways/Deployment: override global.logLevel when terminatingGateways.logLevel is set for dataplane container" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/terminating-gateways-deployment.yaml \ + --set 'terminatingGateways.enabled=true' \ + --set 'terminatingGateways.logLevel=debug' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].args' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=debug"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} diff --git a/charts/consul/test/unit/tls-init-job.bats b/charts/consul/test/unit/tls-init-job.bats index bf1f84a0a6..f71edc43d5 100644 --- a/charts/consul/test/unit/tls-init-job.bats +++ b/charts/consul/test/unit/tls-init-job.bats @@ -208,6 +208,36 @@ load _helpers [ "${actualTemplateBaz}" = "qux" ] } +#-------------------------------------------------------------------- +# logLevel + +@test "tlsInit/Job: use global.logLevel by default" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/tls-init-job.yaml \ + --set 'global.tls.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "tlsInit/Job: override global.logLevel when global.tls.logLevel is set" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/tls-init-job.yaml \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.logLevel=error' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=error"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + #-------------------------------------------------------------------- # server.containerSecurityContext.tlsInit diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 81065d4bb2..d373a240f0 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -289,6 +289,9 @@ global: # The key within the Kubernetes secret or Vault secret key that holds the gossip # encryption key. secretKey: "" + # Override global log verbosity level for gossip-encryption-autogenerate-job pods. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" # A list of addresses of upstream DNS servers that are used to recursively resolve DNS queries. # These values are given as `-recursor` flags to Consul servers and clients. @@ -307,6 +310,10 @@ global: # This setting is required for [Cluster Peering](https://developer.hashicorp.com/consul/docs/connect/cluster-peering/k8s). enabled: false + # Override global log verbosity level. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + # If true, turns on the auto-encrypt feature on clients and servers. # It also switches consul-k8s-control-plane components to retrieve the CA from the servers # via the API. Requires Consul 1.7.1+. @@ -406,6 +413,10 @@ global: # This requires Consul >= 1.4. manageSystemACLs: false + # Override global log verbosity level. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + # A Kubernetes or Vault secret containing the bootstrap token to use for creating policies and # tokens for all Consul and consul-k8s-control-plane components. If `secretName` and `secretKey` # are unset, a default secret name and secret key are used. If the secret is populated, then @@ -578,6 +589,10 @@ global: # @type: string k8sAuthMethodHost: null + # Override global log verbosity level for the create-federation-secret-job pods. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + # Configures metrics for Consul service mesh metrics: # Configures the Helm chart’s components @@ -731,6 +746,10 @@ server: # @type: boolean enabled: "-" + # Override global log verbosity level. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + # The name of the Docker image (including any tag) for the containers running # Consul server agents. # @type: string @@ -1368,6 +1387,10 @@ client: # @type: boolean enabled: false + # Override global log verbosity level. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + # The name of the Docker image (including any tag) for the containers # running Consul client agents. # @type: string @@ -2643,6 +2666,10 @@ meshGateway: # Requirements: consul 1.6.0+ if using `global.acls.manageSystemACLs``. enabled: false + # Override global log verbosity level for mesh-gateway-deployment pods. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + # Number of replicas for the Deployment. replicas: 1 @@ -2855,6 +2882,10 @@ ingressGateways: # Enable ingress gateway deployment. Requires `connectInject.enabled=true`. enabled: false + # Override global log verbosity level for ingress-gateways-deployment pods. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + # Defaults sets default values for all gateway fields. With the exception # of annotations, defining any of these values in the `gateways` list # will override the default values provided here. Annotations will @@ -3021,6 +3052,10 @@ terminatingGateways: # Enable terminating gateway deployment. Requires `connectInject.enabled=true`. enabled: false + # Override global log verbosity level. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + # Defaults sets default values for all gateway fields. With the exception # of annotations, defining any of these values in the `gateways` list # will override the default values provided here. Annotations will @@ -3359,6 +3394,10 @@ telemetryCollector: # @type: boolean enabled: false + # Override global log verbosity level. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + # The name of the Docker image (including any tag) for the containers running # the consul-telemetry-collector # @type: string From 3e1f79912fa72634b20ba4f31cdc1bf9c9e92803 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Fri, 28 Jul 2023 13:46:07 -0400 Subject: [PATCH 308/340] Add missing tsccr entries (#2682) --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f79369b440..414c875b26 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -193,7 +193,7 @@ jobs: - name: Test rpm package if: ${{ matrix.goos == 'linux' && matrix.component == 'cli' && matrix.goarch == 'amd64'}} - uses: addnab/docker-run-action@v3 # TSCCR: no entry for repository "addnab/docker-run-action" + uses: addnab/docker-run-action@4f65fabd2431ebc8d299f8e5a018d79a769ae185 # v3 with: image: registry.access.redhat.com/ubi9/ubi:latest options: -v ${{ github.workspace }}:/work @@ -218,7 +218,7 @@ jobs: - name: Test debian package if: ${{ matrix.goos == 'linux' && matrix.component == 'cli' && matrix.goarch == 'amd64'}} - uses: addnab/docker-run-action@v3 # TSCCR: no entry for repository "addnab/docker-run-action" + uses: addnab/docker-run-action@4f65fabd2431ebc8d299f8e5a018d79a769ae185 # v3 with: image: ubuntu:latest options: -v ${{ github.workspace }}:/work From 63567cb3eee78eb66a1589c53d919430fb93a027 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Fri, 28 Jul 2023 14:57:33 -0400 Subject: [PATCH 309/340] Use controller-gen 0.8.0 for CRDs (#2684) - Add missing license headers. --- Makefile | 2 +- .../fixtures/bases/openshift/network-attachment.yaml | 3 +++ .../fixtures/bases/sameness/default-ns/sameness.yaml | 3 +++ .../bases/sameness/override-ns/intentions.yaml | 3 +++ .../override-ns/payment-service-resolver.yaml | 3 +++ .../bases/sameness/override-ns/service-defaults.yaml | 3 +++ .../tests/fixtures/bases/sameness/peering/mesh.yaml | 3 +++ acceptance/tests/sameness/sameness_test.go | 3 +++ .../templates/crd-controlplanerequestlimits.yaml | 11 +++++++++-- charts/consul/templates/crd-exportedservices.yaml | 11 +++++++++-- charts/consul/templates/crd-gatewayclassconfigs.yaml | 11 +++++++++-- charts/consul/templates/crd-ingressgateways.yaml | 11 +++++++++-- charts/consul/templates/crd-jwtproviders.yaml | 11 +++++++++-- charts/consul/templates/crd-meshes.yaml | 11 +++++++++-- charts/consul/templates/crd-meshservices.yaml | 11 +++++++++-- charts/consul/templates/crd-peeringacceptors.yaml | 11 +++++++++-- charts/consul/templates/crd-peeringdialers.yaml | 11 +++++++++-- charts/consul/templates/crd-proxydefaults.yaml | 11 +++++++++-- charts/consul/templates/crd-referencegrants.yaml | 6 +++--- charts/consul/templates/crd-samenessgroups.yaml | 11 +++++++++-- charts/consul/templates/crd-servicedefaults.yaml | 11 +++++++++-- charts/consul/templates/crd-serviceintentions.yaml | 11 +++++++++-- charts/consul/templates/crd-serviceresolvers.yaml | 11 +++++++++-- charts/consul/templates/crd-servicerouters.yaml | 11 +++++++++-- charts/consul/templates/crd-servicesplitters.yaml | 11 +++++++++-- charts/consul/templates/crd-terminatinggateways.yaml | 11 +++++++++-- ...onsul.hashicorp.com_controlplanerequestlimits.yaml | 9 ++++++++- .../bases/consul.hashicorp.com_exportedservices.yaml | 9 ++++++++- .../consul.hashicorp.com_gatewayclassconfigs.yaml | 9 ++++++++- .../bases/consul.hashicorp.com_ingressgateways.yaml | 9 ++++++++- .../crd/bases/consul.hashicorp.com_jwtproviders.yaml | 9 ++++++++- .../config/crd/bases/consul.hashicorp.com_meshes.yaml | 9 ++++++++- .../crd/bases/consul.hashicorp.com_meshservices.yaml | 9 ++++++++- .../bases/consul.hashicorp.com_peeringacceptors.yaml | 9 ++++++++- .../bases/consul.hashicorp.com_peeringdialers.yaml | 9 ++++++++- .../crd/bases/consul.hashicorp.com_proxydefaults.yaml | 9 ++++++++- .../bases/consul.hashicorp.com_samenessgroups.yaml | 9 ++++++++- .../bases/consul.hashicorp.com_servicedefaults.yaml | 9 ++++++++- .../bases/consul.hashicorp.com_serviceintentions.yaml | 9 ++++++++- .../bases/consul.hashicorp.com_serviceresolvers.yaml | 9 ++++++++- .../bases/consul.hashicorp.com_servicerouters.yaml | 9 ++++++++- .../bases/consul.hashicorp.com_servicesplitters.yaml | 9 ++++++++- .../consul.hashicorp.com_terminatinggateways.yaml | 9 ++++++++- control-plane/config/rbac/role.yaml | 4 ++++ control-plane/config/webhook/manifests.yaml | 4 ++++ 45 files changed, 322 insertions(+), 55 deletions(-) diff --git a/Makefile b/Makefile index e2c39de2ea..a141fa22cd 100644 --- a/Makefile +++ b/Makefile @@ -172,7 +172,7 @@ ifeq (, $(shell which controller-gen)) CONTROLLER_GEN_TMP_DIR=$$(mktemp -d) ;\ cd $$CONTROLLER_GEN_TMP_DIR ;\ go mod init tmp ;\ - go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.12.0 ;\ + go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.8.0 ;\ rm -rf $$CONTROLLER_GEN_TMP_DIR ;\ } CONTROLLER_GEN=$(shell go env GOPATH)/bin/controller-gen diff --git a/acceptance/tests/fixtures/bases/openshift/network-attachment.yaml b/acceptance/tests/fixtures/bases/openshift/network-attachment.yaml index 4b3f7948ee..c2f36c5e1a 100644 --- a/acceptance/tests/fixtures/bases/openshift/network-attachment.yaml +++ b/acceptance/tests/fixtures/bases/openshift/network-attachment.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: "k8s.cni.cncf.io/v1" kind: NetworkAttachmentDefinition metadata: diff --git a/acceptance/tests/fixtures/bases/sameness/default-ns/sameness.yaml b/acceptance/tests/fixtures/bases/sameness/default-ns/sameness.yaml index 0eb7d9e008..4d27ed72ae 100644 --- a/acceptance/tests/fixtures/bases/sameness/default-ns/sameness.yaml +++ b/acceptance/tests/fixtures/bases/sameness/default-ns/sameness.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: SamenessGroup metadata: diff --git a/acceptance/tests/fixtures/bases/sameness/override-ns/intentions.yaml b/acceptance/tests/fixtures/bases/sameness/override-ns/intentions.yaml index 425b9fe21d..ae075c85e4 100644 --- a/acceptance/tests/fixtures/bases/sameness/override-ns/intentions.yaml +++ b/acceptance/tests/fixtures/bases/sameness/override-ns/intentions.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceIntentions metadata: diff --git a/acceptance/tests/fixtures/bases/sameness/override-ns/payment-service-resolver.yaml b/acceptance/tests/fixtures/bases/sameness/override-ns/payment-service-resolver.yaml index b2b6b68c3d..4257294c6b 100644 --- a/acceptance/tests/fixtures/bases/sameness/override-ns/payment-service-resolver.yaml +++ b/acceptance/tests/fixtures/bases/sameness/override-ns/payment-service-resolver.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceResolver metadata: diff --git a/acceptance/tests/fixtures/bases/sameness/override-ns/service-defaults.yaml b/acceptance/tests/fixtures/bases/sameness/override-ns/service-defaults.yaml index f88d143728..87f6a71f32 100644 --- a/acceptance/tests/fixtures/bases/sameness/override-ns/service-defaults.yaml +++ b/acceptance/tests/fixtures/bases/sameness/override-ns/service-defaults.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceDefaults metadata: diff --git a/acceptance/tests/fixtures/bases/sameness/peering/mesh.yaml b/acceptance/tests/fixtures/bases/sameness/peering/mesh.yaml index de84382d3e..2fb6a04bb6 100644 --- a/acceptance/tests/fixtures/bases/sameness/peering/mesh.yaml +++ b/acceptance/tests/fixtures/bases/sameness/peering/mesh.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: consul.hashicorp.com/v1alpha1 kind: Mesh metadata: diff --git a/acceptance/tests/sameness/sameness_test.go b/acceptance/tests/sameness/sameness_test.go index a7a926cd42..3971ccf27a 100644 --- a/acceptance/tests/sameness/sameness_test.go +++ b/acceptance/tests/sameness/sameness_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package sameness import ( diff --git a/charts/consul/templates/crd-controlplanerequestlimits.yaml b/charts/consul/templates/crd-controlplanerequestlimits.yaml index 67ff258eb8..2b0c45a621 100644 --- a/charts/consul/templates/crd-controlplanerequestlimits.yaml +++ b/charts/consul/templates/crd-controlplanerequestlimits.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: controlplanerequestlimits.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: ControlPlaneRequestLimit @@ -193,4 +194,10 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-exportedservices.yaml b/charts/consul/templates/crd-exportedservices.yaml index 8581ac4e88..591500cb12 100644 --- a/charts/consul/templates/crd-exportedservices.yaml +++ b/charts/consul/templates/crd-exportedservices.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: exportedservices.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: ExportedServices @@ -137,4 +138,10 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-gatewayclassconfigs.yaml b/charts/consul/templates/crd-gatewayclassconfigs.yaml index 7060757b23..4ab6570e31 100644 --- a/charts/consul/templates/crd-gatewayclassconfigs.yaml +++ b/charts/consul/templates/crd-gatewayclassconfigs.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: gatewayclassconfigs.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: GatewayClassConfig @@ -141,4 +142,10 @@ spec: type: object served: true storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-ingressgateways.yaml b/charts/consul/templates/crd-ingressgateways.yaml index eff7ef61a9..a01fafd8dd 100644 --- a/charts/consul/templates/crd-ingressgateways.yaml +++ b/charts/consul/templates/crd-ingressgateways.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: ingressgateways.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: IngressGateway @@ -367,4 +368,10 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-jwtproviders.yaml b/charts/consul/templates/crd-jwtproviders.yaml index fa87f37489..8a51d16b68 100644 --- a/charts/consul/templates/crd-jwtproviders.yaml +++ b/charts/consul/templates/crd-jwtproviders.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: jwtproviders.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: JWTProvider @@ -255,4 +256,10 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-meshes.yaml b/charts/consul/templates/crd-meshes.yaml index f2549b5111..0710d41280 100644 --- a/charts/consul/templates/crd-meshes.yaml +++ b/charts/consul/templates/crd-meshes.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: meshes.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: Mesh @@ -205,4 +206,10 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-meshservices.yaml b/charts/consul/templates/crd-meshservices.yaml index aa808113a2..df8f673bdc 100644 --- a/charts/consul/templates/crd-meshservices.yaml +++ b/charts/consul/templates/crd-meshservices.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: meshservices.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: MeshService @@ -54,4 +55,10 @@ spec: type: object served: true storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-peeringacceptors.yaml b/charts/consul/templates/crd-peeringacceptors.yaml index 40f7f1d4d6..e06e830f04 100644 --- a/charts/consul/templates/crd-peeringacceptors.yaml +++ b/charts/consul/templates/crd-peeringacceptors.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: peeringacceptors.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: PeeringAcceptor @@ -144,4 +145,10 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-peeringdialers.yaml b/charts/consul/templates/crd-peeringdialers.yaml index bfe4778d0c..e24401e761 100644 --- a/charts/consul/templates/crd-peeringdialers.yaml +++ b/charts/consul/templates/crd-peeringdialers.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: peeringdialers.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: PeeringDialer @@ -144,4 +145,10 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-proxydefaults.yaml b/charts/consul/templates/crd-proxydefaults.yaml index a224effc12..362672c1c1 100644 --- a/charts/consul/templates/crd-proxydefaults.yaml +++ b/charts/consul/templates/crd-proxydefaults.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: proxydefaults.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: ProxyDefaults @@ -253,4 +254,10 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-referencegrants.yaml b/charts/consul/templates/crd-referencegrants.yaml index d50211291d..6ae177d987 100644 --- a/charts/consul/templates/crd-referencegrants.yaml +++ b/charts/consul/templates/crd-referencegrants.yaml @@ -7,15 +7,15 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 - gateway.networking.k8s.io/bundle-version: v0.6.2 - gateway.networking.k8s.io/channel: experimental - creationTimestamp: null labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null name: referencegrants.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io diff --git a/charts/consul/templates/crd-samenessgroups.yaml b/charts/consul/templates/crd-samenessgroups.yaml index 7cc3b71ae1..60beb5662c 100644 --- a/charts/consul/templates/crd-samenessgroups.yaml +++ b/charts/consul/templates/crd-samenessgroups.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: samenessgroups.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: SamenessGroup @@ -127,4 +128,10 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-servicedefaults.yaml b/charts/consul/templates/crd-servicedefaults.yaml index e295732bfa..870f5ad86c 100644 --- a/charts/consul/templates/crd-servicedefaults.yaml +++ b/charts/consul/templates/crd-servicedefaults.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: servicedefaults.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: ServiceDefaults @@ -493,4 +494,10 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-serviceintentions.yaml b/charts/consul/templates/crd-serviceintentions.yaml index 5f849f65ba..c4d2b5f20d 100644 --- a/charts/consul/templates/crd-serviceintentions.yaml +++ b/charts/consul/templates/crd-serviceintentions.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: serviceintentions.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: ServiceIntentions @@ -309,4 +310,10 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-serviceresolvers.yaml b/charts/consul/templates/crd-serviceresolvers.yaml index a18cc94de4..eb5643fe49 100644 --- a/charts/consul/templates/crd-serviceresolvers.yaml +++ b/charts/consul/templates/crd-serviceresolvers.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: serviceresolvers.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: ServiceResolver @@ -346,4 +347,10 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-servicerouters.yaml b/charts/consul/templates/crd-servicerouters.yaml index c5ba99466c..f28da9e7c1 100644 --- a/charts/consul/templates/crd-servicerouters.yaml +++ b/charts/consul/templates/crd-servicerouters.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: servicerouters.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: ServiceRouter @@ -310,4 +311,10 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-servicesplitters.yaml b/charts/consul/templates/crd-servicesplitters.yaml index abe3ac85cc..a2af050c3d 100644 --- a/charts/consul/templates/crd-servicesplitters.yaml +++ b/charts/consul/templates/crd-servicesplitters.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: servicesplitters.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: ServiceSplitter @@ -184,4 +185,10 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-terminatinggateways.yaml b/charts/consul/templates/crd-terminatinggateways.yaml index cd58d1679c..583c218be8 100644 --- a/charts/consul/templates/crd-terminatinggateways.yaml +++ b/charts/consul/templates/crd-terminatinggateways.yaml @@ -4,15 +4,16 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: terminatinggateways.consul.hashicorp.com -spec: labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd +spec: group: consul.hashicorp.com names: kind: TerminatingGateway @@ -135,4 +136,10 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] {{- end }} diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_controlplanerequestlimits.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_controlplanerequestlimits.yaml index 4d1d808428..11da54e9ac 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_controlplanerequestlimits.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_controlplanerequestlimits.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: controlplanerequestlimits.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -189,3 +190,9 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml index dac72f3646..0b6b969856 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: exportedservices.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -133,3 +134,9 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml index 44eff52492..e60e4a1cfa 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: gatewayclassconfigs.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -137,3 +138,9 @@ spec: type: object served: true storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml index e9cf081721..fd8ebc86ff 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: ingressgateways.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -363,3 +364,9 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml index 7506cc57dc..2e8ac24330 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: jwtproviders.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -251,3 +252,9 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml index 16dd398f99..adbb12bba6 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: meshes.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -201,3 +202,9 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml index 125883bdc5..04f8f493e7 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: meshservices.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -50,3 +51,9 @@ spec: type: object served: true storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml index 894228a218..50df179f04 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: peeringacceptors.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -140,3 +141,9 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml index 51c3e38319..01e4363f14 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: peeringdialers.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -140,3 +141,9 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml index 1be3b37703..7084980bf0 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: proxydefaults.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -249,3 +250,9 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml index 259ca7b910..c71a211f63 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: samenessgroups.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -123,3 +124,9 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml index 83503f11f3..5a2c7a58fd 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: servicedefaults.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -489,3 +490,9 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml index 9553c73450..a4efd6e958 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: serviceintentions.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -305,3 +306,9 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml index b83a859dc4..0146eca982 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: serviceresolvers.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -342,3 +343,9 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml index 04590cc007..31f5ee2924 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: servicerouters.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -306,3 +307,9 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml index 3a47472ba7..aa2b592c94 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: servicesplitters.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -180,3 +181,9 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml index acf61cde4c..b465cd9494 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml @@ -6,7 +6,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: terminatinggateways.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -131,3 +132,9 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/control-plane/config/rbac/role.yaml b/control-plane/config/rbac/role.yaml index 74328a8ae3..7f90780e02 100644 --- a/control-plane/config/rbac/role.yaml +++ b/control-plane/config/rbac/role.yaml @@ -1,7 +1,11 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + creationTimestamp: null name: manager-role rules: - apiGroups: diff --git a/control-plane/config/webhook/manifests.yaml b/control-plane/config/webhook/manifests.yaml index a515888527..0861f9253a 100644 --- a/control-plane/config/webhook/manifests.yaml +++ b/control-plane/config/webhook/manifests.yaml @@ -1,7 +1,11 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + --- apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: + creationTimestamp: null name: mutating-webhook-configuration webhooks: - admissionReviewVersions: From 3cb0cce4f21dcc6111355b092ba1c22984576243 Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Fri, 28 Jul 2023 16:08:17 -0400 Subject: [PATCH 310/340] Fix ingress (#2687) --- .changelog/2687.txt | 3 +++ charts/consul/templates/ui-ingress.yaml | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changelog/2687.txt diff --git a/.changelog/2687.txt b/.changelog/2687.txt new file mode 100644 index 0000000000..5fa4a92b4d --- /dev/null +++ b/.changelog/2687.txt @@ -0,0 +1,3 @@ +```release-note:bug +helm: fix ui ingress manifest formatting, and exclude `ingressClass` when not defined. +``` \ No newline at end of file diff --git a/charts/consul/templates/ui-ingress.yaml b/charts/consul/templates/ui-ingress.yaml index 0414a7cc2d..f8c7f92a77 100644 --- a/charts/consul/templates/ui-ingress.yaml +++ b/charts/consul/templates/ui-ingress.yaml @@ -25,9 +25,11 @@ metadata: {{ tpl .Values.ui.ingress.annotations . | nindent 4 | trim }} {{- end }} spec: + {{- if ne .Values.ui.ingress.ingressClassName "" }} ingressClassName: {{ .Values.ui.ingress.ingressClassName }} + {{- end }} rules: - {{ $global := .Values.global }} + {{- $global := .Values.global }} {{- if or ( gt .Capabilities.KubeVersion.Major "1" ) ( ge .Capabilities.KubeVersion.Minor "19" ) }} {{- range .Values.ui.ingress.hosts }} - host: {{ .host | quote }} From 6835b1ef5367752d245981b2af15873f50a3ecae Mon Sep 17 00:00:00 2001 From: Michael Zalimeni Date: Fri, 28 Jul 2023 16:36:50 -0400 Subject: [PATCH 311/340] [NET-4865] Bump golang.org/x/net to 0.12.0 in cni (#2668) * Bump golang.org/x/net to 0.12.0 in cni This was missed in 5b57e6340dff44157cb7a984ac7220e47849dfb9 as part of a general upgrade of that dependency. * Bump server-connection-manager to v0.1.3 Tidying up following CVE dependency bumps, leading to a new release of this library. --- control-plane/cni/go.mod | 8 ++++---- control-plane/cni/go.sum | 16 ++++++++-------- control-plane/go.mod | 2 +- control-plane/go.sum | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/control-plane/cni/go.mod b/control-plane/cni/go.mod index b594015392..fe67475524 100644 --- a/control-plane/cni/go.mod +++ b/control-plane/cni/go.mod @@ -30,11 +30,11 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/net v0.7.0 // indirect + golang.org/x/net v0.12.0 // indirect golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/term v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/term v0.10.0 // indirect + golang.org/x/text v0.11.0 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.27.1 // indirect diff --git a/control-plane/cni/go.sum b/control-plane/cni/go.sum index 845baf6231..f95d4d991a 100644 --- a/control-plane/cni/go.sum +++ b/control-plane/cni/go.sum @@ -287,8 +287,8 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= 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= @@ -339,21 +339,21 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/control-plane/go.mod b/control-plane/go.mod index 9d184840cb..8469502795 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -9,7 +9,7 @@ require ( github.com/google/go-cmp v0.5.9 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83383d - github.com/hashicorp/consul-server-connection-manager v0.1.2 + github.com/hashicorp/consul-server-connection-manager v0.1.3 github.com/hashicorp/consul/api v1.22.0-rc1 github.com/hashicorp/consul/sdk v0.14.0-rc1 github.com/hashicorp/go-bexpr v0.1.11 diff --git a/control-plane/go.sum b/control-plane/go.sum index fa88dab62a..39e89efec8 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -262,8 +262,8 @@ github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEo github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83383d h1:RJ1MZ8JKnfgKQ1kR3IBQAMpOpzXrdseZAYN/QR//MFM= github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83383d/go.mod h1:IHIHMzkoMwlv6rLsgwcoFBVYupR7/1pKEOHBMjD4L0k= -github.com/hashicorp/consul-server-connection-manager v0.1.2 h1:tNVQHUPuMbd+cMdD8kd+qkZUYpmLmrHMAV/49f4L53I= -github.com/hashicorp/consul-server-connection-manager v0.1.2/go.mod h1:NzQoVi1KcxGI2SangsDue8+ZPuXZWs+6BKAKrDNyg+w= +github.com/hashicorp/consul-server-connection-manager v0.1.3 h1:fxsZ15XBNNWhV26yBVdCcnxHwSRgf9wqHGS2ZVCQIhc= +github.com/hashicorp/consul-server-connection-manager v0.1.3/go.mod h1:Md2IGKaFJ4ek9GUA0pW1S2R60wpquMOUs27GiD9kZd0= github.com/hashicorp/consul/api v1.22.0-rc1 h1:ePmGqndeMgaI38KUbSA/CqTzeEAIogXyWnfNJzglo70= github.com/hashicorp/consul/api v1.22.0-rc1/go.mod h1:wtduXtbAqSGtBdi3tyA5SSAYGAG51rBejV9SEUBciMY= github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3usQMXZF6ziK3SzXU= From da99ce404f3f35f87dd51ace4fb11f68097fdfc8 Mon Sep 17 00:00:00 2001 From: Michael Zalimeni Date: Mon, 31 Jul 2023 09:55:04 -0400 Subject: [PATCH 312/340] Fix default Ent image tag in acceptance tests (#2683) * Fix default Ent image tag in acceptance tests Rather than hard-coding the Docker repository and parsing the non-Ent image tag for a version, simply replace the image name and retain other coordinates. This is consistent with our tagging scheme introduced in https://github.com/hashicorp/consul/pull/13541 and will allow for using `hashicorppreview` images seamlessly regardless of whether OSS or Ent is being tested. * Add make target for loading images in kind Complement other multi-cluster make targets by supporting image loading across kind clusters. --- Makefile | 7 ++++++ acceptance/framework/config/config.go | 21 ++++++---------- acceptance/framework/config/config_test.go | 29 ++++++++++++++-------- 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/Makefile b/Makefile index a141fa22cd..e9a02a01b3 100644 --- a/Makefile +++ b/Makefile @@ -151,6 +151,13 @@ kind: kind-delete kind create cluster --name dc3 --image $(KIND_NODE_IMAGE) kind create cluster --name dc4 --image $(KIND_NODE_IMAGE) +# Helper target for loading local dev images (run with `DEV_IMAGE=...` to load non-k8s images) +kind-load: + kind load docker-image --name dc1 $(DEV_IMAGE) + kind load docker-image --name dc2 $(DEV_IMAGE) + kind load docker-image --name dc3 $(DEV_IMAGE) + kind load docker-image --name dc4 $(DEV_IMAGE) + # ===========> Shared Targets help: ## Show targets and their descriptions. diff --git a/acceptance/framework/config/config.go b/acceptance/framework/config/config.go index ee07df63fd..a638732c37 100644 --- a/acceptance/framework/config/config.go +++ b/acceptance/framework/config/config.go @@ -217,21 +217,16 @@ func (t *TestConfig) entImage() (string, error) { } // Otherwise, assume that we have an image tag with a version in it. - consulImageSplits := strings.Split(v.Global.Image, ":") - if len(consulImageSplits) != 2 { - return "", fmt.Errorf("could not determine consul version from global.image: %s", v.Global.Image) - } - consulImageVersion := consulImageSplits[1] - - var preRelease string - // Handle versions like 1.9.0-rc1. - if strings.Contains(consulImageVersion, "-") { - split := strings.Split(consulImageVersion, "-") - consulImageVersion = split[0] - preRelease = fmt.Sprintf("-%s", split[1]) + // Use the same Docker repository and tagging scheme, but replace 'consul' with 'consul-enterprise'. + imageTag := strings.Replace(v.Global.Image, "/consul:", "/consul-enterprise:", 1) + + // We currently add an '-ent' suffix to release versions of enterprise images (nightly previews + // do not include this suffix). + if strings.HasPrefix(imageTag, "hashicorp/consul-enterprise:") { + imageTag = fmt.Sprintf("%s-ent", imageTag) } - return fmt.Sprintf("hashicorp/consul-enterprise:%s%s-ent", consulImageVersion, preRelease), nil + return imageTag, nil } func (c *TestConfig) SkipWhenOpenshiftAndCNI(t *testing.T) { diff --git a/acceptance/framework/config/config_test.go b/acceptance/framework/config/config_test.go index df981e26fa..4d432da3b0 100644 --- a/acceptance/framework/config/config_test.go +++ b/acceptance/framework/config/config_test.go @@ -137,25 +137,34 @@ func TestConfig_HelmValuesFromConfig_EntImage(t *testing.T) { expErr string }{ { - consulImage: "hashicorp/consul:1.9.0", - expImage: "hashicorp/consul-enterprise:1.9.0-ent", + consulImage: "hashicorp/consul:1.15.3", + expImage: "hashicorp/consul-enterprise:1.15.3-ent", }, { - consulImage: "hashicorp/consul:1.8.5-rc1", - expImage: "hashicorp/consul-enterprise:1.8.5-rc1-ent", + consulImage: "hashicorp/consul:1.16.0-rc1", + expImage: "hashicorp/consul-enterprise:1.16.0-rc1-ent", }, { - consulImage: "hashicorp/consul:1.7.0-beta3", - expImage: "hashicorp/consul-enterprise:1.7.0-beta3-ent", - }, - { - consulImage: "invalid", - expErr: "could not determine consul version from global.image: invalid", + consulImage: "hashicorp/consul:1.14.0-beta1", + expImage: "hashicorp/consul-enterprise:1.14.0-beta1-ent", }, { consulImage: "hashicorp/consul@sha256:oioi2452345kjhlkh", expImage: "hashicorp/consul@sha256:oioi2452345kjhlkh", }, + // Nightly tags differ from release tags ('-ent' suffix is omitted) + { + consulImage: "docker.mirror.hashicorp.services/hashicorppreview/consul:1.17-dev", + expImage: "docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.17-dev", + }, + { + consulImage: "docker.mirror.hashicorp.services/hashicorppreview/consul:1.17-dev-ubi", + expImage: "docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.17-dev-ubi", + }, + { + consulImage: "docker.mirror.hashicorp.services/hashicorppreview/consul@sha256:oioi2452345kjhlkh", + expImage: "docker.mirror.hashicorp.services/hashicorppreview/consul@sha256:oioi2452345kjhlkh", + }, } for _, tt := range tests { t.Run(tt.consulImage, func(t *testing.T) { From 8379be90d833aee74ec2ffc21f6068bab006fb41 Mon Sep 17 00:00:00 2001 From: Michael Zalimeni Date: Wed, 2 Aug 2023 13:04:28 -0400 Subject: [PATCH 313/340] [NET-5146] security: Upgrade Go and `x/net` (#2710) security: Upgrade Go and x/net Upgrade to Go 1.20.7 and `x/net` 1.13.0 to resolve [CVE-2023-29409](https://nvd.nist.gov/vuln/detail/CVE-2023-29409) and [CVE-2023-3978](https://nvd.nist.gov/vuln/detail/CVE-2023-3978). --- .changelog/2710.txt | 5 +++++ .go-version | 2 +- acceptance/go.mod | 2 +- acceptance/go.sum | 4 ++-- cli/go.mod | 2 +- cli/go.sum | 4 ++-- control-plane/cni/go.mod | 2 +- control-plane/cni/go.sum | 4 ++-- control-plane/go.mod | 2 +- control-plane/go.sum | 4 ++-- 10 files changed, 18 insertions(+), 13 deletions(-) create mode 100644 .changelog/2710.txt diff --git a/.changelog/2710.txt b/.changelog/2710.txt new file mode 100644 index 0000000000..1d37b32dfb --- /dev/null +++ b/.changelog/2710.txt @@ -0,0 +1,5 @@ +```release-note:security +Upgrade to use Go 1.20.7 and `x/net` 0.13.0. +This resolves [CVE-2023-29409](https://nvd.nist.gov/vuln/detail/CVE-2023-29409)(`crypto/tls`) +and [CVE-2023-3978](https://nvd.nist.gov/vuln/detail/CVE-2023-3978)(`net/html`). +``` diff --git a/.go-version b/.go-version index e63679c766..8909929f6e 100644 --- a/.go-version +++ b/.go-version @@ -1 +1 @@ -1.20.6 +1.20.7 diff --git a/acceptance/go.mod b/acceptance/go.mod index b19015eda4..1dd344a5c8 100644 --- a/acceptance/go.mod +++ b/acceptance/go.mod @@ -122,7 +122,7 @@ require ( golang.org/x/crypto v0.11.0 // indirect golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect golang.org/x/mod v0.9.0 // indirect - golang.org/x/net v0.12.0 // indirect + golang.org/x/net v0.13.0 // indirect golang.org/x/oauth2 v0.6.0 // indirect golang.org/x/sys v0.10.0 // indirect golang.org/x/term v0.10.0 // indirect diff --git a/acceptance/go.sum b/acceptance/go.sum index 43a557f32b..7361efbbb6 100644 --- a/acceptance/go.sum +++ b/acceptance/go.sum @@ -887,8 +887,8 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY= +golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= 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= diff --git a/cli/go.mod b/cli/go.mod index 5744e9dad0..3e92113d18 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -168,7 +168,7 @@ require ( go.starlark.net v0.0.0-20230128213706-3f75dec8e403 // indirect golang.org/x/crypto v0.11.0 // indirect golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect - golang.org/x/net v0.12.0 // indirect + golang.org/x/net v0.13.0 // indirect golang.org/x/oauth2 v0.6.0 // indirect golang.org/x/sync v0.2.0 // indirect golang.org/x/sys v0.10.0 // indirect diff --git a/cli/go.sum b/cli/go.sum index 7861cb73d3..6b54e646b2 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -963,8 +963,8 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY= +golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= 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= diff --git a/control-plane/cni/go.mod b/control-plane/cni/go.mod index fe67475524..e8fcc980ef 100644 --- a/control-plane/cni/go.mod +++ b/control-plane/cni/go.mod @@ -30,7 +30,7 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/net v0.12.0 // indirect + golang.org/x/net v0.13.0 // indirect golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect golang.org/x/sys v0.10.0 // indirect golang.org/x/term v0.10.0 // indirect diff --git a/control-plane/cni/go.sum b/control-plane/cni/go.sum index f95d4d991a..8f4c0668ea 100644 --- a/control-plane/cni/go.sum +++ b/control-plane/cni/go.sum @@ -287,8 +287,8 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY= +golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= 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= diff --git a/control-plane/go.mod b/control-plane/go.mod index 8469502795..2dcd78848c 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -145,7 +145,7 @@ require ( go.uber.org/multierr v1.6.0 // indirect golang.org/x/crypto v0.11.0 // indirect golang.org/x/mod v0.9.0 // indirect - golang.org/x/net v0.12.0 // indirect + golang.org/x/net v0.13.0 // indirect golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect golang.org/x/sync v0.2.0 // indirect golang.org/x/sys v0.10.0 // indirect diff --git a/control-plane/go.sum b/control-plane/go.sum index 39e89efec8..dec3ba9eb4 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -655,8 +655,8 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY= +golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= 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= From 61c77616d390b9077b9bbfa7b9d0f80bae847571 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Wed, 2 Aug 2023 16:07:27 -0400 Subject: [PATCH 314/340] Increase timeout while waiting for vault server to be ready (#2709) increase timeout while waiting for server to be ready and fix require.Equal check --- acceptance/framework/vault/vault_cluster.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acceptance/framework/vault/vault_cluster.go b/acceptance/framework/vault/vault_cluster.go index 4dc832bcb6..8b82e41841 100644 --- a/acceptance/framework/vault/vault_cluster.go +++ b/acceptance/framework/vault/vault_cluster.go @@ -414,12 +414,12 @@ func (v *VaultCluster) initAndUnseal(t *testing.T) { v.logger.Logf(t, "initializing and unsealing Vault") namespace := v.helmOptions.KubectlOptions.Namespace - retrier := &retry.Timer{Timeout: 2 * time.Minute, Wait: 1 * time.Second} + retrier := &retry.Timer{Timeout: 4 * time.Minute, Wait: 1 * time.Second} retry.RunWith(retrier, t, func(r *retry.R) { // Wait for vault server pod to be running so that we can create Vault client without errors. serverPod, err := v.kubernetesClient.CoreV1().Pods(namespace).Get(context.Background(), fmt.Sprintf("%s-vault-0", v.releaseName), metav1.GetOptions{}) require.NoError(r, err) - require.Equal(r, serverPod.Status.Phase, corev1.PodRunning) + require.Equal(r, corev1.PodRunning, serverPod.Status.Phase) // Set up the client so that we can make API calls to initialize and unseal. v.vaultClient = v.SetupVaultClient(t) From 939e7c31993b0f3c83feceea79ae6c2e3064f700 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Thu, 3 Aug 2023 16:43:08 -0400 Subject: [PATCH 315/340] Acceptance tests: increase api-gateway retries (#2716) * Increase the retries and add config entry retries --- .../api_gateway_external_servers_test.go | 2 +- .../api-gateway/api_gateway_lifecycle_test.go | 14 ++++---- .../api-gateway/api_gateway_tenancy_test.go | 6 ++-- .../tests/api-gateway/api_gateway_test.go | 35 +++++++++++-------- .../partitions/partitions_gateway_test.go | 21 +++++++++++ 5 files changed, 52 insertions(+), 26 deletions(-) diff --git a/acceptance/tests/api-gateway/api_gateway_external_servers_test.go b/acceptance/tests/api-gateway/api_gateway_external_servers_test.go index d14ef59990..aa0934dc65 100644 --- a/acceptance/tests/api-gateway/api_gateway_external_servers_test.go +++ b/acceptance/tests/api-gateway/api_gateway_external_servers_test.go @@ -96,7 +96,7 @@ func TestAPIGateway_ExternalServers(t *testing.T) { // leader election so we may need to wait a long time for // the reconcile loop to run (hence a ~1m timeout here). var gatewayAddress string - retryCheck(t, 30, func(r *retry.R) { + retryCheck(t, 60, func(r *retry.R) { var gateway gwv1beta1.Gateway err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway", Namespace: "default"}, &gateway) require.NoError(r, err) diff --git a/acceptance/tests/api-gateway/api_gateway_lifecycle_test.go b/acceptance/tests/api-gateway/api_gateway_lifecycle_test.go index e3ffa992ce..f6f66ed995 100644 --- a/acceptance/tests/api-gateway/api_gateway_lifecycle_test.go +++ b/acceptance/tests/api-gateway/api_gateway_lifecycle_test.go @@ -196,7 +196,7 @@ func TestAPIGateway_Lifecycle(t *testing.T) { // check that the route is unbound and all Consul objects and Kubernetes statuses are cleaned up logger.Log(t, "checking that http route one is not bound to gateway two") - retryCheck(t, 30, func(r *retry.R) { + retryCheck(t, 60, func(r *retry.R) { var route gwv1beta1.HTTPRoute err := k8sClient.Get(context.Background(), types.NamespacedName{Name: routeOneName, Namespace: defaultNamespace}, &route) require.NoError(r, err) @@ -246,7 +246,7 @@ func TestAPIGateway_Lifecycle(t *testing.T) { // check that the Kubernetes gateway is cleaned up logger.Log(t, "checking that gateway one is cleaned up in Kubernetes") - retryCheck(t, 30, func(r *retry.R) { + retryCheck(t, 60, func(r *retry.R) { var route gwv1beta1.Gateway err := k8sClient.Get(context.Background(), types.NamespacedName{Name: controlledGatewayOneName, Namespace: defaultNamespace}, &route) require.NoError(r, err) @@ -299,7 +299,7 @@ func checkConsulNotExists(t *testing.T, client *api.Client, kind, name string, n opts.Namespace = namespace[0] } - retryCheck(t, 30, func(r *retry.R) { + retryCheck(t, 60, func(r *retry.R) { _, _, err := client.ConfigEntries().Get(kind, name, opts) require.Error(r, err) require.EqualError(r, err, fmt.Sprintf("Unexpected response code: 404 (Config entry not found for %q / %q)", kind, name)) @@ -309,7 +309,7 @@ func checkConsulNotExists(t *testing.T, client *api.Client, kind, name string, n func checkConsulExists(t *testing.T, client *api.Client, kind, name string) { t.Helper() - retryCheck(t, 30, func(r *retry.R) { + retryCheck(t, 60, func(r *retry.R) { _, _, err := client.ConfigEntries().Get(kind, name, nil) require.NoError(r, err) }) @@ -318,7 +318,7 @@ func checkConsulExists(t *testing.T, client *api.Client, kind, name string) { func checkConsulRouteParent(t *testing.T, client *api.Client, name, parent string) { t.Helper() - retryCheck(t, 30, func(r *retry.R) { + retryCheck(t, 60, func(r *retry.R) { entry, _, err := client.ConfigEntries().Get(api.HTTPRoute, name, nil) require.NoError(r, err) route := entry.(*api.HTTPRouteConfigEntry) @@ -331,7 +331,7 @@ func checkConsulRouteParent(t *testing.T, client *api.Client, name, parent strin func checkEmptyRoute(t *testing.T, client client.Client, name, namespace string) { t.Helper() - retryCheck(t, 30, func(r *retry.R) { + retryCheck(t, 60, func(r *retry.R) { var route gwv1beta1.HTTPRoute err := client.Get(context.Background(), types.NamespacedName{Name: name, Namespace: namespace}, &route) require.NoError(r, err) @@ -344,7 +344,7 @@ func checkEmptyRoute(t *testing.T, client client.Client, name, namespace string) func checkRouteBound(t *testing.T, client client.Client, name, namespace, parent string) { t.Helper() - retryCheck(t, 30, func(r *retry.R) { + retryCheck(t, 60, func(r *retry.R) { var route gwv1beta1.HTTPRoute err := client.Get(context.Background(), types.NamespacedName{Name: name, Namespace: namespace}, &route) require.NoError(r, err) diff --git a/acceptance/tests/api-gateway/api_gateway_tenancy_test.go b/acceptance/tests/api-gateway/api_gateway_tenancy_test.go index 19e85d60b0..f7b0ac6d79 100644 --- a/acceptance/tests/api-gateway/api_gateway_tenancy_test.go +++ b/acceptance/tests/api-gateway/api_gateway_tenancy_test.go @@ -131,7 +131,7 @@ func TestAPIGateway_Tenancy(t *testing.T) { k8sClient := ctx.ControllerRuntimeClient(t) consulClient, _ := consulCluster.SetupConsulClient(t, c.secure) - retryCheck(t, 60, func(r *retry.R) { + retryCheck(t, 120, func(r *retry.R) { var gateway gwv1beta1.Gateway err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway", Namespace: gatewayNamespace}, &gateway) require.NoError(r, err) @@ -153,7 +153,7 @@ func TestAPIGateway_Tenancy(t *testing.T) { checkConsulNotExists(t, consulClient, api.APIGateway, "gateway", namespaceForConsul(c.namespaceMirroring, gatewayNamespace)) // route failure - retryCheck(t, 30, func(r *retry.R) { + retryCheck(t, 60, func(r *retry.R) { var httproute gwv1beta1.HTTPRoute err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "route", Namespace: routeNamespace}, &httproute) require.NoError(r, err) @@ -175,7 +175,7 @@ func TestAPIGateway_Tenancy(t *testing.T) { createReferenceGrant(t, k8sClient, "route-service", routeNamespace, serviceNamespace) // gateway updated with references allowed - retryCheck(t, 30, func(r *retry.R) { + retryCheck(t, 60, func(r *retry.R) { var gateway gwv1beta1.Gateway err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway", Namespace: gatewayNamespace}, &gateway) require.NoError(r, err) diff --git a/acceptance/tests/api-gateway/api_gateway_test.go b/acceptance/tests/api-gateway/api_gateway_test.go index 4b4db38afe..721bbf2527 100644 --- a/acceptance/tests/api-gateway/api_gateway_test.go +++ b/acceptance/tests/api-gateway/api_gateway_test.go @@ -101,6 +101,11 @@ func TestAPIGateway_Basic(t *testing.T) { logger.Log(t, "creating target http server") k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + // We use the static-client pod so that we can make calls to the api gateway + // via kubectl exec without needing a route into the cluster from the test machine. + logger.Log(t, "creating static-client pod") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-client") + logger.Log(t, "patching route to target http server") k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "httproute", "http-route", "-p", `{"spec":{"rules":[{"backendRefs":[{"name":"static-server","port":80}]}]}}`, "--type=merge") @@ -115,11 +120,6 @@ func TestAPIGateway_Basic(t *testing.T) { k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-f", "../fixtures/cases/api-gateways/tcproute/route.yaml") }) - // We use the static-client pod so that we can make calls to the api gateway - // via kubectl exec without needing a route into the cluster from the test machine. - logger.Log(t, "creating static-client pod") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-client") - // Grab a kubernetes client so that we can verify binding // behavior prior to issuing requests through the gateway. k8sClient := ctx.ControllerRuntimeClient(t) @@ -128,7 +128,7 @@ func TestAPIGateway_Basic(t *testing.T) { // leader election so we may need to wait a long time for // the reconcile loop to run (hence the 1m timeout here). var gatewayAddress string - counter := &retry.Counter{Count: 60, Wait: 2 * time.Second} + counter := &retry.Counter{Count: 120, Wait: 2 * time.Second} retry.RunWith(counter, t, func(r *retry.R) { var gateway gwv1beta1.Gateway err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway", Namespace: "default"}, &gateway) @@ -209,17 +209,22 @@ func TestAPIGateway_Basic(t *testing.T) { checkStatusCondition(t, tcpRoute.Status.Parents[0].Conditions, trueCondition("ConsulAccepted", "Accepted")) // check that the Consul entries were created - entry, _, err := consulClient.ConfigEntries().Get(api.APIGateway, "gateway", nil) - require.NoError(t, err) - gateway := entry.(*api.APIGatewayConfigEntry) + var gateway *api.APIGatewayConfigEntry + var httpRoute *api.HTTPRouteConfigEntry + var route *api.TCPRouteConfigEntry + retry.RunWith(counter, t, func(r *retry.R) { + entry, _, err := consulClient.ConfigEntries().Get(api.APIGateway, "gateway", nil) + require.NoError(r, err) + gateway = entry.(*api.APIGatewayConfigEntry) - entry, _, err = consulClient.ConfigEntries().Get(api.HTTPRoute, "http-route", nil) - require.NoError(t, err) - httpRoute := entry.(*api.HTTPRouteConfigEntry) + entry, _, err = consulClient.ConfigEntries().Get(api.HTTPRoute, "http-route", nil) + require.NoError(r, err) + httpRoute = entry.(*api.HTTPRouteConfigEntry) - entry, _, err = consulClient.ConfigEntries().Get(api.TCPRoute, "tcp-route", nil) - require.NoError(t, err) - route := entry.(*api.TCPRouteConfigEntry) + entry, _, err = consulClient.ConfigEntries().Get(api.TCPRoute, "tcp-route", nil) + require.NoError(r, err) + route = entry.(*api.TCPRouteConfigEntry) + }) // now check the gateway status conditions checkConsulStatusCondition(t, gateway.Status.Conditions, trueConsulCondition("Accepted", "Accepted")) diff --git a/acceptance/tests/partitions/partitions_gateway_test.go b/acceptance/tests/partitions/partitions_gateway_test.go index acdb81fa65..a90a790cb6 100644 --- a/acceptance/tests/partitions/partitions_gateway_test.go +++ b/acceptance/tests/partitions/partitions_gateway_test.go @@ -18,6 +18,7 @@ import ( "github.com/hashicorp/consul/api" "github.com/hashicorp/consul/sdk/testutil/retry" "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" @@ -255,6 +256,16 @@ func TestPartitions_Gateway(t *testing.T) { logger.Log(t, "creating target server in secondary partition cluster") k8s.DeployKustomize(t, secondaryPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + // Check that static-server injected 2 containers. + for _, labelSelector := range []string{"app=static-server"} { + podList, err := secondaryPartitionClusterContext.KubernetesClient(t).CoreV1().Pods(metav1.NamespaceAll).List(context.Background(), metav1.ListOptions{ + LabelSelector: labelSelector, + }) + require.NoError(t, err) + require.Len(t, podList.Items, 1) + require.Len(t, podList.Items[0].Spec.Containers, 2) + } + logger.Log(t, "patching route to target server") k8s.RunKubectl(t, secondaryPartitionClusterStaticServerOpts, "patch", "httproute", "http-route", "-p", `{"spec":{"rules":[{"backendRefs":[{"group":"consul.hashicorp.com","kind":"MeshService","name":"mesh-service","port":80}]}]}}`, "--type=merge") @@ -293,6 +304,16 @@ func TestPartitions_Gateway(t *testing.T) { logger.Log(t, "creating target server in default partition cluster") k8s.DeployKustomize(t, defaultPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + // Check that static-server injected 2 containers. + for _, labelSelector := range []string{"app=static-server"} { + podList, err := defaultPartitionClusterContext.KubernetesClient(t).CoreV1().Pods(metav1.NamespaceAll).List(context.Background(), metav1.ListOptions{ + LabelSelector: labelSelector, + }) + require.NoError(t, err) + require.Len(t, podList.Items, 1) + require.Len(t, podList.Items[0].Spec.Containers, 2) + } + logger.Log(t, "creating exported services") k8s.KubectlApplyK(t, defaultPartitionClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/default-partition-ns1") helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { From 671675d49fbca59272fbbd54becd6366620704a5 Mon Sep 17 00:00:00 2001 From: Nathan Coleman Date: Mon, 7 Aug 2023 22:47:05 -0400 Subject: [PATCH 316/340] NET-3908: allow configuration of SecurityContextConstraints when running on OpenShift (#2184) Co-authored-by: Melisa Griffin --- .changelog/2184.txt | 3 + .../templates/connect-inject-clusterrole.yaml | 10 +++ .../templates/crd-gatewayclassconfigs.yaml | 4 ++ .../templates/gateway-resources-job.yaml | 3 + .../test/unit/connect-inject-clusterrole.bats | 24 +++++++ .../test/unit/gateway-resources-job.bats | 17 +++++ charts/consul/values.yaml | 5 ++ cli/helm/values.go | 11 +-- .../api-gateway/common/helm_config.go | 6 +- .../api-gateway/gatekeeper/gatekeeper.go | 2 +- .../api-gateway/gatekeeper/gatekeeper_test.go | 72 ++++++++++++++++--- control-plane/api-gateway/gatekeeper/init.go | 19 ++--- control-plane/api-gateway/gatekeeper/role.go | 15 +++- .../api-gateway/gatekeeper/rolebinding.go | 4 +- .../api-gateway/gatekeeper/serviceaccount.go | 2 +- .../api/v1alpha1/api_gateway_types.go | 3 + .../api/v1alpha1/api_gateway_types_test.go | 1 + .../subcommand/gateway-resources/command.go | 6 ++ .../gateway-resources/command_test.go | 2 + 19 files changed, 180 insertions(+), 29 deletions(-) create mode 100644 .changelog/2184.txt diff --git a/.changelog/2184.txt b/.changelog/2184.txt new file mode 100644 index 0000000000..bdcb6039fd --- /dev/null +++ b/.changelog/2184.txt @@ -0,0 +1,3 @@ +```release-note:feature +api-gateway: support deploying to OpenShift 4.11 +``` diff --git a/charts/consul/templates/connect-inject-clusterrole.yaml b/charts/consul/templates/connect-inject-clusterrole.yaml index 8c0bbe9bf7..f1f6b3878f 100644 --- a/charts/consul/templates/connect-inject-clusterrole.yaml +++ b/charts/consul/templates/connect-inject-clusterrole.yaml @@ -186,4 +186,14 @@ rules: - "get" - "list" - "watch" +{{- if .Values.global.openshift.enabled }} +- apiGroups: + - security.openshift.io + resources: + - securitycontextconstraints + resourceNames: + - {{ .Values.connectInject.apiGateway.managedGatewayClass.openshiftSCCName }} + verbs: + - use + {{- end }} {{- end }} diff --git a/charts/consul/templates/crd-gatewayclassconfigs.yaml b/charts/consul/templates/crd-gatewayclassconfigs.yaml index 4ab6570e31..38625c9368 100644 --- a/charts/consul/templates/crd-gatewayclassconfigs.yaml +++ b/charts/consul/templates/crd-gatewayclassconfigs.yaml @@ -138,6 +138,10 @@ spec: type: string type: object type: array + openshiftSCCName: + description: The name of an existing SecurityContextConstraints + resource to bind to the managed role when running on OpenShift. + type: string type: object type: object served: true diff --git a/charts/consul/templates/gateway-resources-job.yaml b/charts/consul/templates/gateway-resources-job.yaml index 1fa712759d..048af9fc26 100644 --- a/charts/consul/templates/gateway-resources-job.yaml +++ b/charts/consul/templates/gateway-resources-job.yaml @@ -99,6 +99,9 @@ spec: - {{- toYaml .Values.connectInject.apiGateway.managedGatewayClass.copyAnnotations.service.annotations | nindent 14 -}} {{- end }} - -service-type={{ .Values.connectInject.apiGateway.managedGatewayClass.serviceType }} + {{- if .Values.global.openshift.enabled }} + - -openshift-scc-name={{ .Values.connectInject.apiGateway.managedGatewayClass.openshiftSCCName }} + {{- end }} {{- end}} resources: requests: diff --git a/charts/consul/test/unit/connect-inject-clusterrole.bats b/charts/consul/test/unit/connect-inject-clusterrole.bats index ace8c18d4a..d02b9eacde 100644 --- a/charts/consul/test/unit/connect-inject-clusterrole.bats +++ b/charts/consul/test/unit/connect-inject-clusterrole.bats @@ -217,3 +217,27 @@ load _helpers local actual=$(echo $object | yq -r '.verbs | index("watch")' | tee /dev/stderr) [ "${actual}" != null ] } + +#-------------------------------------------------------------------- +# openshift + +@test "connectInject/ClusterRole: adds permission to securitycontextconstraints for Openshift with global.openshift.enabled=true with default apiGateway Openshift SCC Name" { + cd `chart_dir` + local object=$(helm template \ + -s templates/connect-inject-clusterrole.yaml \ + --set 'global.openshift.enabled=true' \ + . | tee /dev/stderr | + yq '.rules[13].resourceNames | index("restricted-v2")' | tee /dev/stderr) + [ "${object}" == 0 ] +} + +@test "connectInject/ClusterRole: adds permission to securitycontextconstraints for Openshift with global.openshift.enabled=true and sets apiGateway Openshift SCC Name" { + cd `chart_dir` + local object=$(helm template \ + -s templates/connect-inject-clusterrole.yaml \ + --set 'global.openshift.enabled=true' \ + --set 'connectInject.apiGateway.managedGatewayClass.openshiftSCCName=fakescc' \ + . | tee /dev/stderr | + yq '.rules[13].resourceNames | index("fakescc")' | tee /dev/stderr) + [ "${object}" == 0 ] +} diff --git a/charts/consul/test/unit/gateway-resources-job.bats b/charts/consul/test/unit/gateway-resources-job.bats index d79838770d..09322bd2a7 100644 --- a/charts/consul/test/unit/gateway-resources-job.bats +++ b/charts/consul/test/unit/gateway-resources-job.bats @@ -92,6 +92,7 @@ target=templates/gateway-resources-job.yaml --set 'connectInject.apiGateway.managedGatewayClass.tolerations=- key: bar' \ --set 'connectInject.apiGateway.managedGatewayClass.copyAnnotations.service.annotations=- bingo' \ --set 'connectInject.apiGateway.managedGatewayClass.serviceType=Foo' \ + --set 'connectInject.apiGateway.managedGatewayClass.openshiftSCCName=hello' \ . | tee /dev/stderr | yq '.spec.template.spec.containers[0].args' | tee /dev/stderr) @@ -121,6 +122,22 @@ target=templates/gateway-resources-job.yaml local actual=$(echo "$spec" | jq '.[16]') [ "${actual}" = "\"- bingo\"" ] + + local actual=$(echo "$spec" | jq '.[17]') + [ "${actual}" = "\"-service-type=Foo\"" ] +} + +@test "apiGateway/GatewayClassConfig: custom configuration openshift enabled" { + cd `chart_dir` + local spec=$(helm template \ + -s $target \ + --set 'global.openshift.enabled=true' \ + --set 'connectInject.apiGateway.managedGatewayClass.openshiftSCCName=hello' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].args' | tee /dev/stderr) + + local actual=$(echo "$spec" | jq '.[13]') + [ "${actual}" = "\"-openshift-scc-name=hello\"" ] } diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index d373a240f0..487b6f9ac1 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -2201,6 +2201,11 @@ connectInject: maxInstances: 1 minInstances: 1 + # The name of the OpenShift SecurityContextConstraints resource to use for Gateways. + # Only applicable if `global.openshift.enabled` is true. + # @type: string + openshiftSCCName: "restricted-v2" + # Configuration for the ServiceAccount created for the api-gateway component serviceAccount: # This value defines additional annotations for the client service account. This should be formatted as a multi-line diff --git a/cli/helm/values.go b/cli/helm/values.go index 06671382d1..31b3508e80 100644 --- a/cli/helm/values.go +++ b/cli/helm/values.go @@ -576,11 +576,12 @@ type CopyAnnotations struct { } type ManagedGatewayClass struct { - Enabled bool `yaml:"enabled"` - NodeSelector interface{} `yaml:"nodeSelector"` - ServiceType string `yaml:"serviceType"` - UseHostPorts bool `yaml:"useHostPorts"` - CopyAnnotations CopyAnnotations `yaml:"copyAnnotations"` + Enabled bool `yaml:"enabled"` + NodeSelector interface{} `yaml:"nodeSelector"` + ServiceType string `yaml:"serviceType"` + UseHostPorts bool `yaml:"useHostPorts"` + CopyAnnotations CopyAnnotations `yaml:"copyAnnotations"` + OpenshiftSCCName string `yaml:"openshiftSCCName"` } type Service struct { diff --git a/control-plane/api-gateway/common/helm_config.go b/control-plane/api-gateway/common/helm_config.go index f0d4dc7988..d83b298d92 100644 --- a/control-plane/api-gateway/common/helm_config.go +++ b/control-plane/api-gateway/common/helm_config.go @@ -11,6 +11,7 @@ import ( const componentAuthMethod = "k8s-component-auth-method" // HelmConfig is the configuration of gateways that comes in from the user's Helm values. +// This is a combination of the apiGateway stanza and other settings that impact api-gateways. type HelmConfig struct { // ImageDataplane is the Consul Dataplane image to use in gateway deployments. ImageDataplane string @@ -18,7 +19,6 @@ type HelmConfig struct { ConsulDestinationNamespace string NamespaceMirroringPrefix string EnableNamespaces bool - EnableOpenShift bool EnableNamespaceMirroring bool AuthMethod string // LogLevel is the logging level of the deployed Consul Dataplanes. @@ -30,6 +30,10 @@ type HelmConfig struct { ConsulTLSServerName string ConsulCACert string ConsulConfig ConsulConfig + + // EnableOpenShift indicates whether we're deploying into an OpenShift environment + // and should create SecurityContextConstraints. + EnableOpenShift bool } type ConsulConfig struct { diff --git a/control-plane/api-gateway/gatekeeper/gatekeeper.go b/control-plane/api-gateway/gatekeeper/gatekeeper.go index 19444831ee..6cb7170fc8 100644 --- a/control-plane/api-gateway/gatekeeper/gatekeeper.go +++ b/control-plane/api-gateway/gatekeeper/gatekeeper.go @@ -96,7 +96,7 @@ func (g *Gatekeeper) namespacedName(gateway gwv1beta1.Gateway) types.NamespacedN } func (g *Gatekeeper) serviceAccountName(gateway gwv1beta1.Gateway, config common.HelmConfig) string { - if config.AuthMethod == "" { + if config.AuthMethod == "" && !config.EnableOpenShift { return "" } return gateway.Name diff --git a/control-plane/api-gateway/gatekeeper/gatekeeper_test.go b/control-plane/api-gateway/gatekeeper/gatekeeper_test.go index 069643e301..30cc78d464 100644 --- a/control-plane/api-gateway/gatekeeper/gatekeeper_test.go +++ b/control-plane/api-gateway/gatekeeper/gatekeeper_test.go @@ -193,7 +193,7 @@ func TestUpsert(t *testing.T) { configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), }, roles: []*rbac.Role{ - configureRole(name, namespace, labels, "1"), + configureRole(name, namespace, labels, "1", false), }, roleBindings: []*rbac.RoleBinding{ configureRoleBinding(name, namespace, labels, "1"), @@ -319,7 +319,7 @@ func TestUpsert(t *testing.T) { configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), }, roles: []*rbac.Role{ - configureRole(name, namespace, labels, "1"), + configureRole(name, namespace, labels, "1", false), }, roleBindings: []*rbac.RoleBinding{ configureRoleBinding(name, namespace, labels, "1"), @@ -342,7 +342,7 @@ func TestUpsert(t *testing.T) { configureDeployment(name, namespace, labels, 3, nil, nil, "", "2"), }, roles: []*rbac.Role{ - configureRole(name, namespace, labels, "1"), + configureRole(name, namespace, labels, "1", false), }, roleBindings: []*rbac.RoleBinding{ configureRoleBinding(name, namespace, labels, "1"), @@ -400,7 +400,7 @@ func TestUpsert(t *testing.T) { configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), }, roles: []*rbac.Role{ - configureRole(name, namespace, labels, "1"), + configureRole(name, namespace, labels, "1", false), }, roleBindings: []*rbac.RoleBinding{ configureRoleBinding(name, namespace, labels, "1"), @@ -428,7 +428,7 @@ func TestUpsert(t *testing.T) { configureDeployment(name, namespace, labels, 3, nil, nil, "", "2"), }, roles: []*rbac.Role{ - configureRole(name, namespace, labels, "1"), + configureRole(name, namespace, labels, "1", false), }, roleBindings: []*rbac.RoleBinding{ configureRoleBinding(name, namespace, labels, "1"), @@ -603,6 +603,50 @@ func TestUpsert(t *testing.T) { serviceAccounts: []*corev1.ServiceAccount{}, }, }, + "create a new gateway with openshift enabled": { + gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: gwv1beta1.GatewaySpec{ + Listeners: listeners, + }, + }, + gatewayClassConfig: v1alpha1.GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-gatewayclassconfig", + }, + Spec: v1alpha1.GatewayClassConfigSpec{ + DeploymentSpec: v1alpha1.DeploymentSpec{ + DefaultInstances: common.PointerTo(int32(3)), + MaxInstances: common.PointerTo(int32(3)), + MinInstances: common.PointerTo(int32(1)), + }, + CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, + OpenshiftSCCName: "test-api-gateway", + }, + }, + helmConfig: common.HelmConfig{ + EnableOpenShift: true, + }, + initialResources: resources{}, + finalResources: resources{ + deployments: []*appsv1.Deployment{ + configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), + }, + roles: []*rbac.Role{ + configureRole(name, namespace, labels, "1", true), + }, + roleBindings: []*rbac.RoleBinding{ + configureRoleBinding(name, namespace, labels, "1"), + }, + services: []*corev1.Service{}, + serviceAccounts: []*corev1.ServiceAccount{ + configureServiceAccount(name, namespace, labels, "1"), + }, + }, + }, } for name, tc := range cases { @@ -754,7 +798,7 @@ func TestDelete(t *testing.T) { configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), }, roles: []*rbac.Role{ - configureRole(name, namespace, labels, "1"), + configureRole(name, namespace, labels, "1", false), }, roleBindings: []*rbac.RoleBinding{ configureRoleBinding(name, namespace, labels, "1"), @@ -1057,7 +1101,19 @@ func configureDeployment(name, namespace string, labels map[string]string, repli } } -func configureRole(name, namespace string, labels map[string]string, resourceVersion string) *rbac.Role { +func configureRole(name, namespace string, labels map[string]string, resourceVersion string, openshiftEnabled bool) *rbac.Role { + rules := []rbac.PolicyRule{} + + if openshiftEnabled { + rules = []rbac.PolicyRule{ + { + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + ResourceNames: []string{name + "-api-gateway"}, + Verbs: []string{"use"}, + }, + } + } return &rbac.Role{ TypeMeta: metav1.TypeMeta{ APIVersion: "rbac.authorization.k8s.io/v1", @@ -1078,7 +1134,7 @@ func configureRole(name, namespace string, labels map[string]string, resourceVer }, }, }, - Rules: []rbac.PolicyRule{}, + Rules: rules, } } diff --git a/control-plane/api-gateway/gatekeeper/init.go b/control-plane/api-gateway/gatekeeper/init.go index 35360b7f87..f3d4ad1f95 100644 --- a/control-plane/api-gateway/gatekeeper/init.go +++ b/control-plane/api-gateway/gatekeeper/init.go @@ -168,14 +168,17 @@ func initContainer(config common.HelmConfig, name, namespace string) (corev1.Con }) } - container.SecurityContext = &corev1.SecurityContext{ - RunAsUser: pointer.Int64(initContainersUserAndGroupID), - RunAsGroup: pointer.Int64(initContainersUserAndGroupID), - RunAsNonRoot: pointer.Bool(true), - Privileged: pointer.Bool(false), - Capabilities: &corev1.Capabilities{ - Drop: []corev1.Capability{"ALL"}, - }, + // Openshift Assigns the security context for us, do not enable if it is enabled. + if !config.EnableOpenShift { + container.SecurityContext = &corev1.SecurityContext{ + RunAsUser: pointer.Int64(initContainersUserAndGroupID), + RunAsGroup: pointer.Int64(initContainersUserAndGroupID), + RunAsNonRoot: pointer.Bool(true), + Privileged: pointer.Bool(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"ALL"}, + }, + } } return container, nil diff --git a/control-plane/api-gateway/gatekeeper/role.go b/control-plane/api-gateway/gatekeeper/role.go index eb8431075a..705e9bffff 100644 --- a/control-plane/api-gateway/gatekeeper/role.go +++ b/control-plane/api-gateway/gatekeeper/role.go @@ -19,7 +19,7 @@ import ( ) func (g *Gatekeeper) upsertRole(ctx context.Context, gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config common.HelmConfig) error { - if config.AuthMethod == "" { + if config.AuthMethod == "" && !config.EnableOpenShift { return g.deleteRole(ctx, types.NamespacedName{Namespace: gateway.Namespace, Name: gateway.Name}) } @@ -40,7 +40,7 @@ func (g *Gatekeeper) upsertRole(ctx context.Context, gateway gwv1beta1.Gateway, return errors.New("role not owned by controller") } - role = g.role(gateway, gcc) + role = g.role(gateway, gcc, config) if err := ctrl.SetControllerReference(&gateway, role, g.Client.Scheme()); err != nil { return err } @@ -62,7 +62,7 @@ func (g *Gatekeeper) deleteRole(ctx context.Context, gwName types.NamespacedName return nil } -func (g *Gatekeeper) role(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig) *rbac.Role { +func (g *Gatekeeper) role(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config common.HelmConfig) *rbac.Role { role := &rbac.Role{ ObjectMeta: metav1.ObjectMeta{ Name: gateway.Name, @@ -81,5 +81,14 @@ func (g *Gatekeeper) role(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassCo }) } + if config.EnableOpenShift { + role.Rules = append(role.Rules, rbac.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + ResourceNames: []string{gcc.Spec.OpenshiftSCCName}, + Verbs: []string{"use"}, + }) + } + return role } diff --git a/control-plane/api-gateway/gatekeeper/rolebinding.go b/control-plane/api-gateway/gatekeeper/rolebinding.go index 8891a754e6..1a60e752c8 100644 --- a/control-plane/api-gateway/gatekeeper/rolebinding.go +++ b/control-plane/api-gateway/gatekeeper/rolebinding.go @@ -19,7 +19,7 @@ import ( ) func (g *Gatekeeper) upsertRoleBinding(ctx context.Context, gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config common.HelmConfig) error { - if config.AuthMethod == "" { + if config.AuthMethod == "" && !config.EnableOpenShift { return g.deleteRole(ctx, types.NamespacedName{Namespace: gateway.Namespace, Name: gateway.Name}) } @@ -66,7 +66,7 @@ func (g *Gatekeeper) deleteRoleBinding(ctx context.Context, gwName types.Namespa func (g *Gatekeeper) roleBinding(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config common.HelmConfig) *rbac.RoleBinding { // Create resources for reference. This avoids bugs if naming patterns change. serviceAccount := g.serviceAccount(gateway) - role := g.role(gateway, gcc) + role := g.role(gateway, gcc, config) return &rbac.RoleBinding{ ObjectMeta: metav1.ObjectMeta{ diff --git a/control-plane/api-gateway/gatekeeper/serviceaccount.go b/control-plane/api-gateway/gatekeeper/serviceaccount.go index 47336867aa..d1c5c9883a 100644 --- a/control-plane/api-gateway/gatekeeper/serviceaccount.go +++ b/control-plane/api-gateway/gatekeeper/serviceaccount.go @@ -18,7 +18,7 @@ import ( ) func (g *Gatekeeper) upsertServiceAccount(ctx context.Context, gateway gwv1beta1.Gateway, config common.HelmConfig) error { - if config.AuthMethod == "" { + if config.AuthMethod == "" && !config.EnableOpenShift { return g.deleteServiceAccount(ctx, types.NamespacedName{Namespace: gateway.Namespace, Name: gateway.Name}) } diff --git a/control-plane/api/v1alpha1/api_gateway_types.go b/control-plane/api/v1alpha1/api_gateway_types.go index f9be4bdb47..7f0b958701 100644 --- a/control-plane/api/v1alpha1/api_gateway_types.go +++ b/control-plane/api/v1alpha1/api_gateway_types.go @@ -59,6 +59,9 @@ type GatewayClassConfigSpec struct { // The name of an existing Kubernetes PodSecurityPolicy to bind to the managed ServiceAccount if ACLs are managed. PodSecurityPolicy string `json:"podSecurityPolicy,omitempty"` + + // The name of the OpenShift SecurityContextConstraints resource for this gateway class to use. + OpenshiftSCCName string `json:"openshiftSCCName,omitempty"` } // +k8s:deepcopy-gen=true diff --git a/control-plane/api/v1alpha1/api_gateway_types_test.go b/control-plane/api/v1alpha1/api_gateway_types_test.go index 1f9d8ebef0..6e0690b9b2 100644 --- a/control-plane/api/v1alpha1/api_gateway_types_test.go +++ b/control-plane/api/v1alpha1/api_gateway_types_test.go @@ -21,6 +21,7 @@ func TestGatewayClassConfigDeepCopy(t *testing.T) { NodeSelector: map[string]string{ "test": "test", }, + OpenshiftSCCName: "restricted-v2", } config := &GatewayClassConfig{ ObjectMeta: metav1.ObjectMeta{ diff --git a/control-plane/subcommand/gateway-resources/command.go b/control-plane/subcommand/gateway-resources/command.go index 2da2abccb1..3ad3ff7f53 100644 --- a/control-plane/subcommand/gateway-resources/command.go +++ b/control-plane/subcommand/gateway-resources/command.go @@ -71,6 +71,8 @@ type Command struct { flagTolerations string // this is a multiline yaml string matching the tolerations array flagServiceAnnotations string // this is a multiline yaml string array of annotations to allow + flagOpenshiftSCCName string + k8sClient client.Client once sync.Once @@ -123,6 +125,9 @@ func (c *Command) init() { c.flags.StringVar(&c.flagServiceAnnotations, "service-annotations", "", "The annotations to copy over from a gateway to its service.", ) + c.flags.StringVar(&c.flagOpenshiftSCCName, "openshift-scc-name", "", + "Name of security context constraint to use for gateways on Openshift.", + ) c.k8s = &flags.K8SFlags{} flags.Merge(c.flags, c.k8s.Flags()) @@ -197,6 +202,7 @@ func (c *Command) Run(args []string) int { MaxInstances: nonZeroOrNil(c.flagDeploymentMaxInstances), MinInstances: nonZeroOrNil(c.flagDeploymentMinInstances), }, + OpenshiftSCCName: c.flagOpenshiftSCCName, }, } diff --git a/control-plane/subcommand/gateway-resources/command_test.go b/control-plane/subcommand/gateway-resources/command_test.go index 0c40e67244..f60e376042 100644 --- a/control-plane/subcommand/gateway-resources/command_test.go +++ b/control-plane/subcommand/gateway-resources/command_test.go @@ -163,6 +163,7 @@ bar: 2`, flagServiceAnnotations: ` - foo - bar`, + flagOpenshiftSCCName: "restricted-v2", }, }, } { @@ -245,6 +246,7 @@ func TestRun(t *testing.T) { "-release-name", "test", "-component", "test", "-controller-name", "test", + "-openshift-scc-name", "restricted-v2", }) require.Equal(t, 0, code) From 71cdbc24fa8ad3bf3571b4c71744a5171cafdd50 Mon Sep 17 00:00:00 2001 From: Melisa Griffin Date: Tue, 8 Aug 2023 10:47:26 -0400 Subject: [PATCH 317/340] Gateway privileged port mapping (#2707) * Adds port mapping to Gateway Class Config to avoid running container on privileged ports Co-authored-by: Nathan Coleman --- .changelog/2707.txt | 3 + .../templates/crd-gatewayclassconfigs.yaml | 9 ++ .../templates/gateway-resources-job.yaml | 1 + charts/consul/values.yaml | 6 + cli/helm/values.go | 13 +- control-plane/api-gateway/binding/binder.go | 4 +- control-plane/api-gateway/binding/result.go | 8 +- .../api-gateway/binding/validation.go | 19 ++- .../api-gateway/binding/validation_test.go | 31 ++++- .../api-gateway/common/helm_config.go | 4 + .../api-gateway/common/translation.go | 22 +++- .../api-gateway/common/translation_test.go | 2 +- .../api-gateway/gatekeeper/gatekeeper_test.go | 112 ++++++++++++++---- .../api-gateway/gatekeeper/service.go | 6 +- .../api/v1alpha1/api_gateway_types.go | 3 + .../subcommand/gateway-resources/command.go | 9 +- 16 files changed, 209 insertions(+), 43 deletions(-) create mode 100644 .changelog/2707.txt diff --git a/.changelog/2707.txt b/.changelog/2707.txt new file mode 100644 index 0000000000..370aaa7c17 --- /dev/null +++ b/.changelog/2707.txt @@ -0,0 +1,3 @@ +```release-note:feature +api-gateway: adds ability to map privileged ports on Gateway listeners to unprivileged ports so that containers do not require additional privileges +``` diff --git a/charts/consul/templates/crd-gatewayclassconfigs.yaml b/charts/consul/templates/crd-gatewayclassconfigs.yaml index 38625c9368..8140902f78 100644 --- a/charts/consul/templates/crd-gatewayclassconfigs.yaml +++ b/charts/consul/templates/crd-gatewayclassconfigs.yaml @@ -142,6 +142,15 @@ spec: description: The name of an existing SecurityContextConstraints resource to bind to the managed role when running on OpenShift. type: string + mapPrivilegedContainerPorts: + type: integer + format: int32 + minimum: 0 + maximum: 64512 + description: mapPrivilegedContainerPorts is the value which Consul will add to privileged container port + values (ports < 1024) defined on a Gateway when the number is greater than 0. This cannot be more than + 64512 as the highest privileged port is 1023, which would then map to 65535, which is the highest + valid port number. type: object type: object served: true diff --git a/charts/consul/templates/gateway-resources-job.yaml b/charts/consul/templates/gateway-resources-job.yaml index 048af9fc26..de64e2d70d 100644 --- a/charts/consul/templates/gateway-resources-job.yaml +++ b/charts/consul/templates/gateway-resources-job.yaml @@ -102,6 +102,7 @@ spec: {{- if .Values.global.openshift.enabled }} - -openshift-scc-name={{ .Values.connectInject.apiGateway.managedGatewayClass.openshiftSCCName }} {{- end }} + - -map-privileged-container-ports={{ .Values.connectInject.apiGateway.managedGatewayClass.mapPrivilegedContainerPorts }} {{- end}} resources: requests: diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 487b6f9ac1..954680262a 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -2206,6 +2206,12 @@ connectInject: # @type: string openshiftSCCName: "restricted-v2" + # This value defines the amount we will add to privileged container ports on gateways that use this class. + # This is useful if you don't want to give your containers extra permissions to run privileged ports. + # Example: The gateway listener is defined on port 80, but the underlying value of the port on the container + # will be the 80 + the number defined below. + mapPrivilegedContainerPorts: 0 + # Configuration for the ServiceAccount created for the api-gateway component serviceAccount: # This value defines additional annotations for the client service account. This should be formatted as a multi-line diff --git a/cli/helm/values.go b/cli/helm/values.go index 31b3508e80..19e19d8d05 100644 --- a/cli/helm/values.go +++ b/cli/helm/values.go @@ -576,12 +576,13 @@ type CopyAnnotations struct { } type ManagedGatewayClass struct { - Enabled bool `yaml:"enabled"` - NodeSelector interface{} `yaml:"nodeSelector"` - ServiceType string `yaml:"serviceType"` - UseHostPorts bool `yaml:"useHostPorts"` - CopyAnnotations CopyAnnotations `yaml:"copyAnnotations"` - OpenshiftSCCName string `yaml:"openshiftSCCName"` + Enabled bool `yaml:"enabled"` + NodeSelector interface{} `yaml:"nodeSelector"` + ServiceType string `yaml:"serviceType"` + UseHostPorts bool `yaml:"useHostPorts"` + CopyAnnotations CopyAnnotations `yaml:"copyAnnotations"` + OpenshiftSCCName string `yaml:"openshiftSCCName"` + MapPrivilegedContainerPorts int `yaml:"mapPrivilegedContainerPorts"` } type Service struct { diff --git a/control-plane/api-gateway/binding/binder.go b/control-plane/api-gateway/binding/binder.go index 7fbf18d412..7798a6b49c 100644 --- a/control-plane/api-gateway/binding/binder.go +++ b/control-plane/api-gateway/binding/binder.go @@ -132,7 +132,7 @@ func (b *Binder) Snapshot() *Snapshot { // calculate the status for the gateway gatewayValidation = validateGateway(b.config.Gateway, registrationPods, b.config.ConsulGateway) - listenerValidation = validateListeners(b.config.Gateway, b.config.Gateway.Spec.Listeners, b.config.Resources) + listenerValidation = validateListeners(b.config.Gateway, b.config.Gateway.Spec.Listeners, b.config.Resources, b.config.GatewayClassConfig) } // used for tracking how many routes have successfully bound to which listeners @@ -182,7 +182,7 @@ func (b *Binder) Snapshot() *Snapshot { if b.config.ConsulGateway != nil { consulStatus = b.config.ConsulGateway.Status } - entry := b.config.Translator.ToAPIGateway(b.config.Gateway, b.config.Resources) + entry := b.config.Translator.ToAPIGateway(b.config.Gateway, b.config.Resources, gatewayClassConfig) snapshot.Consul.Updates = append(snapshot.Consul.Updates, &common.ConsulUpdateOperation{ Entry: entry, OnUpdate: b.handleGatewaySyncStatus(snapshot, &b.config.Gateway, consulStatus), diff --git a/control-plane/api-gateway/binding/result.go b/control-plane/api-gateway/binding/result.go index b148e441e2..1953d4836c 100644 --- a/control-plane/api-gateway/binding/result.go +++ b/control-plane/api-gateway/binding/result.go @@ -242,6 +242,12 @@ var ( // We map anything under here to a custom ListenerConditionReason of Invalid on // an Accepted status type. errListenerNoTLSPassthrough = errors.New("TLS passthrough is not supported") + + // This custom listener validation error is used to differentiate between an errListenerPortUnavailable because of + // direct port conflicts defined by the user (two listeners on the same port) vs a port conflict because we map + // privileged ports by adding the value passed into the gatewayClassConfig. + // (i.e. one listener on 80 with a privileged port mapping of 2000, and one listener on 2080 would conflict). + errListenerMappedToPrivilegedPortMapping = errors.New("listener conflicts with privileged port mapped by GatewayClassConfig privileged port mapping setting") ) // listenerValidationResult contains the result of internally validating a single listener @@ -291,7 +297,7 @@ func (l listenerValidationResult) programmedCondition(generation int64) metav1.C func (l listenerValidationResult) acceptedCondition(generation int64) metav1.Condition { now := timeFunc() switch l.acceptedErr { - case errListenerPortUnavailable: + case errListenerPortUnavailable, errListenerMappedToPrivilegedPortMapping: return metav1.Condition{ Type: "Accepted", Status: metav1.ConditionFalse, diff --git a/control-plane/api-gateway/binding/validation.go b/control-plane/api-gateway/binding/validation.go index a57cf598a4..d5a97499ca 100644 --- a/control-plane/api-gateway/binding/validation.go +++ b/control-plane/api-gateway/binding/validation.go @@ -226,7 +226,7 @@ func validateCertificateData(secret corev1.Secret) error { // validateListeners validates the given listeners both internally and with respect to each // other for purposes of setting "Conflicted" status conditions. -func validateListeners(gateway gwv1beta1.Gateway, listeners []gwv1beta1.Listener, resources *common.ResourceMap) listenerValidationResults { +func validateListeners(gateway gwv1beta1.Gateway, listeners []gwv1beta1.Listener, resources *common.ResourceMap, gwcc *v1alpha1.GatewayClassConfig) listenerValidationResults { var results listenerValidationResults merged := make(map[gwv1beta1.PortNumber]mergedListeners) for i, listener := range listeners { @@ -235,7 +235,15 @@ func validateListeners(gateway gwv1beta1.Gateway, listeners []gwv1beta1.Listener listener: listener, }) } - + // This list keeps track of port conflicts directly on gateways. i.e., two listeners on the same port as + // defined by the user. + seenListenerPorts := map[int]struct{}{} + // This list keeps track of port conflicts caused by privileged port mappings. + seenContainerPorts := map[int]struct{}{} + portMapping := int32(0) + if gwcc != nil { + portMapping = gwcc.Spec.MapPrivilegedContainerPorts + } for i, listener := range listeners { var result listenerValidationResult @@ -249,6 +257,10 @@ func validateListeners(gateway gwv1beta1.Gateway, listeners []gwv1beta1.Listener result.acceptedErr = errListenerUnsupportedProtocol } else if listener.Port == 20000 { // admin port result.acceptedErr = errListenerPortUnavailable + } else if _, ok := seenListenerPorts[int(listener.Port)]; ok { + result.acceptedErr = errListenerPortUnavailable + } else if _, ok := seenContainerPorts[common.ToContainerPort(listener.Port, portMapping)]; ok { + result.acceptedErr = errListenerMappedToPrivilegedPortMapping } result.routeKindErr = validateListenerAllowedRouteKinds(listener.AllowedRoutes) @@ -261,6 +273,9 @@ func validateListeners(gateway gwv1beta1.Gateway, listeners []gwv1beta1.Listener } results = append(results, result) + + seenListenerPorts[int(listener.Port)] = struct{}{} + seenContainerPorts[common.ToContainerPort(listener.Port, portMapping)] = struct{}{} } return results } diff --git a/control-plane/api-gateway/binding/validation_test.go b/control-plane/api-gateway/binding/validation_test.go index da0ed83f95..10cdf851c3 100644 --- a/control-plane/api-gateway/binding/validation_test.go +++ b/control-plane/api-gateway/binding/validation_test.go @@ -530,8 +530,10 @@ func TestValidateListeners(t *testing.T) { t.Parallel() for name, tt := range map[string]struct { - listeners []gwv1beta1.Listener - expectedAcceptedErr error + listeners []gwv1beta1.Listener + expectedAcceptedErr error + listenerIndexToTest int + mapPrivilegedContainerPorts int32 }{ "valid protocol HTTP": { listeners: []gwv1beta1.Listener{ @@ -563,9 +565,32 @@ func TestValidateListeners(t *testing.T) { }, expectedAcceptedErr: errListenerPortUnavailable, }, + "conflicted port": { + listeners: []gwv1beta1.Listener{ + {Protocol: gwv1beta1.TCPProtocolType, Port: 80}, + {Protocol: gwv1beta1.TCPProtocolType, Port: 80}, + }, + expectedAcceptedErr: errListenerPortUnavailable, + listenerIndexToTest: 1, + }, + "conflicted mapped port": { + listeners: []gwv1beta1.Listener{ + {Protocol: gwv1beta1.TCPProtocolType, Port: 80}, + {Protocol: gwv1beta1.TCPProtocolType, Port: 2080}, + }, + expectedAcceptedErr: errListenerMappedToPrivilegedPortMapping, + listenerIndexToTest: 1, + mapPrivilegedContainerPorts: 2000, + }, } { t.Run(name, func(t *testing.T) { - require.Equal(t, tt.expectedAcceptedErr, validateListeners(gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), tt.listeners, nil)[0].acceptedErr) + gwcc := &v1alpha1.GatewayClassConfig{ + Spec: v1alpha1.GatewayClassConfigSpec{ + MapPrivilegedContainerPorts: tt.mapPrivilegedContainerPorts, + }, + } + + require.Equal(t, tt.expectedAcceptedErr, validateListeners(gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), tt.listeners, nil, gwcc)[tt.listenerIndexToTest].acceptedErr) }) } } diff --git a/control-plane/api-gateway/common/helm_config.go b/control-plane/api-gateway/common/helm_config.go index d83b298d92..ecd9d42c29 100644 --- a/control-plane/api-gateway/common/helm_config.go +++ b/control-plane/api-gateway/common/helm_config.go @@ -34,6 +34,10 @@ type HelmConfig struct { // EnableOpenShift indicates whether we're deploying into an OpenShift environment // and should create SecurityContextConstraints. EnableOpenShift bool + + // MapPrivilegedServicePorts is the value which Consul will add to privileged container port values (ports < 1024) + // defined on a Gateway. + MapPrivilegedServicePorts int } type ConsulConfig struct { diff --git a/control-plane/api-gateway/common/translation.go b/control-plane/api-gateway/common/translation.go index 94241eed22..2f66749c94 100644 --- a/control-plane/api-gateway/common/translation.go +++ b/control-plane/api-gateway/common/translation.go @@ -55,11 +55,11 @@ func (t ResourceTranslator) Namespace(namespace string) string { } // ToAPIGateway translates a kuberenetes API gateway into a Consul APIGateway Config Entry. -func (t ResourceTranslator) ToAPIGateway(gateway gwv1beta1.Gateway, resources *ResourceMap) *api.APIGatewayConfigEntry { +func (t ResourceTranslator) ToAPIGateway(gateway gwv1beta1.Gateway, resources *ResourceMap, gwcc *v1alpha1.GatewayClassConfig) *api.APIGatewayConfigEntry { namespace := t.Namespace(gateway.Namespace) listeners := ConvertSliceFuncIf(gateway.Spec.Listeners, func(listener gwv1beta1.Listener) (api.APIGatewayListener, bool) { - return t.toAPIGatewayListener(gateway, listener, resources) + return t.toAPIGatewayListener(gateway, listener, resources, gwcc) }) return &api.APIGatewayConfigEntry{ @@ -81,7 +81,7 @@ var listenerProtocolMap = map[string]string{ "tcp": "tcp", } -func (t ResourceTranslator) toAPIGatewayListener(gateway gwv1beta1.Gateway, listener gwv1beta1.Listener, resources *ResourceMap) (api.APIGatewayListener, bool) { +func (t ResourceTranslator) toAPIGatewayListener(gateway gwv1beta1.Gateway, listener gwv1beta1.Listener, resources *ResourceMap, gwcc *v1alpha1.GatewayClassConfig) (api.APIGatewayListener, bool) { namespace := gateway.Namespace var certificates []api.ResourceReference @@ -104,10 +104,15 @@ func (t ResourceTranslator) toAPIGatewayListener(gateway gwv1beta1.Gateway, list } } + portMapping := int32(0) + if gwcc != nil { + portMapping = gwcc.Spec.MapPrivilegedContainerPorts + } + return api.APIGatewayListener{ Name: string(listener.Name), Hostname: DerefStringOr(listener.Hostname, ""), - Port: int(listener.Port), + Port: ToContainerPort(listener.Port, portMapping), Protocol: listenerProtocolMap[strings.ToLower(string(listener.Protocol))], TLS: api.APIGatewayTLSConfiguration{ Certificates: certificates, @@ -115,6 +120,15 @@ func (t ResourceTranslator) toAPIGatewayListener(gateway gwv1beta1.Gateway, list }, true } +func ToContainerPort(portNumber gwv1beta1.PortNumber, mapPrivilegedContainerPorts int32) int { + if portNumber >= 1024 { + // We don't care about privileged port-mapping, this is a non-privileged port + return int(portNumber) + } + + return int(portNumber) + int(mapPrivilegedContainerPorts) +} + func (t ResourceTranslator) ToHTTPRoute(route gwv1beta1.HTTPRoute, resources *ResourceMap) *api.HTTPRouteConfigEntry { namespace := t.Namespace(route.Namespace) diff --git a/control-plane/api-gateway/common/translation_test.go b/control-plane/api-gateway/common/translation_test.go index 20917151f3..daa89a698f 100644 --- a/control-plane/api-gateway/common/translation_test.go +++ b/control-plane/api-gateway/common/translation_test.go @@ -320,7 +320,7 @@ func TestTranslator_ToAPIGateway(t *testing.T) { resources.ReferenceCountCertificate(listenerOneCert) resources.ReferenceCountCertificate(listenerTwoCert) - actualConfigEntry := translator.ToAPIGateway(input, resources) + actualConfigEntry := translator.ToAPIGateway(input, resources, &v1alpha1.GatewayClassConfig{}) if diff := cmp.Diff(expectedConfigEntry, actualConfigEntry); diff != "" { t.Errorf("Translator.GatewayToAPIGateway() mismatch (-want +got):\n%s", diff) diff --git a/control-plane/api-gateway/gatekeeper/gatekeeper_test.go b/control-plane/api-gateway/gatekeeper/gatekeeper_test.go index 30cc78d464..562b139274 100644 --- a/control-plane/api-gateway/gatekeeper/gatekeeper_test.go +++ b/control-plane/api-gateway/gatekeeper/gatekeeper_test.go @@ -19,6 +19,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/intstr" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" @@ -112,6 +113,68 @@ func TestUpsert(t *testing.T) { serviceAccounts: []*corev1.ServiceAccount{}, }, }, + "create a new gateway with service and map privileged ports correctly": { + gateway: gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: gwv1beta1.GatewaySpec{ + Listeners: []gwv1beta1.Listener{ + { + Name: "Listener 1", + Port: 80, + Protocol: "TCP", + }, + { + Name: "Listener 2", + Port: 8080, + Protocol: "TCP", + }, + }, + }, + }, + gatewayClassConfig: v1alpha1.GatewayClassConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-gatewayclassconfig", + }, + Spec: v1alpha1.GatewayClassConfigSpec{ + DeploymentSpec: v1alpha1.DeploymentSpec{ + DefaultInstances: common.PointerTo(int32(3)), + MaxInstances: common.PointerTo(int32(3)), + MinInstances: common.PointerTo(int32(1)), + }, + CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, + ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), + MapPrivilegedContainerPorts: 2000, + }, + }, + helmConfig: common.HelmConfig{}, + initialResources: resources{}, + finalResources: resources{ + deployments: []*appsv1.Deployment{ + configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), + }, + roles: []*rbac.Role{}, + services: []*corev1.Service{ + configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ + { + Name: "Listener 1", + Protocol: "TCP", + Port: 80, + TargetPort: intstr.FromInt(2080), + }, + { + Name: "Listener 2", + Protocol: "TCP", + Port: 8080, + TargetPort: intstr.FromInt(8080), + }, + }, "1"), + }, + serviceAccounts: []*corev1.ServiceAccount{}, + }, + }, "create a new gateway deployment with managed Service": { gateway: gwv1beta1.Gateway{ ObjectMeta: metav1.ObjectMeta{ @@ -146,14 +209,16 @@ func TestUpsert(t *testing.T) { services: []*corev1.Service{ configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ { - Name: "Listener 1", - Protocol: "TCP", - Port: 8080, + Name: "Listener 1", + Protocol: "TCP", + Port: 8080, + TargetPort: intstr.FromInt(8080), }, { - Name: "Listener 2", - Protocol: "TCP", - Port: 8081, + Name: "Listener 2", + Protocol: "TCP", + Port: 8081, + TargetPort: intstr.FromInt(8081), }, }, "1"), }, @@ -201,14 +266,16 @@ func TestUpsert(t *testing.T) { services: []*corev1.Service{ configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ { - Name: "Listener 1", - Protocol: "TCP", - Port: 8080, + Name: "Listener 1", + Protocol: "TCP", + Port: 8080, + TargetPort: intstr.FromInt(8080), }, { - Name: "Listener 2", - Protocol: "TCP", - Port: 8081, + Name: "Listener 2", + Protocol: "TCP", + Port: 8081, + TargetPort: intstr.FromInt(8081), }, }, "1"), }, @@ -350,14 +417,16 @@ func TestUpsert(t *testing.T) { services: []*corev1.Service{ configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ { - Name: "Listener 1", - Protocol: "TCP", - Port: 8080, + Name: "Listener 1", + Protocol: "TCP", + Port: 8080, + TargetPort: intstr.FromInt(8080), }, { - Name: "Listener 2", - Protocol: "TCP", - Port: 8081, + Name: "Listener 2", + Protocol: "TCP", + Port: 8081, + TargetPort: intstr.FromInt(8081), }, }, "2"), }, @@ -436,9 +505,10 @@ func TestUpsert(t *testing.T) { services: []*corev1.Service{ configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ { - Name: "Listener 1", - Protocol: "TCP", - Port: 8080, + Name: "Listener 1", + Protocol: "TCP", + Port: 8080, + TargetPort: intstr.FromInt(8080), }, }, "2"), }, diff --git a/control-plane/api-gateway/gatekeeper/service.go b/control-plane/api-gateway/gatekeeper/service.go index d534ad50d7..a30a3df89f 100644 --- a/control-plane/api-gateway/gatekeeper/service.go +++ b/control-plane/api-gateway/gatekeeper/service.go @@ -15,6 +15,7 @@ import ( k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/intstr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" @@ -76,8 +77,9 @@ func (g *Gatekeeper) service(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClas ports = append(ports, corev1.ServicePort{ Name: string(listener.Name), // only TCP-based services are supported for now - Protocol: corev1.ProtocolTCP, - Port: int32(listener.Port), + Protocol: corev1.ProtocolTCP, + Port: int32(listener.Port), + TargetPort: intstr.FromInt(common.ToContainerPort(listener.Port, gcc.Spec.MapPrivilegedContainerPorts)), }) seenPorts[listener.Port] = struct{}{} diff --git a/control-plane/api/v1alpha1/api_gateway_types.go b/control-plane/api/v1alpha1/api_gateway_types.go index 7f0b958701..c06ac3825f 100644 --- a/control-plane/api/v1alpha1/api_gateway_types.go +++ b/control-plane/api/v1alpha1/api_gateway_types.go @@ -62,6 +62,9 @@ type GatewayClassConfigSpec struct { // The name of the OpenShift SecurityContextConstraints resource for this gateway class to use. OpenshiftSCCName string `json:"openshiftSCCName,omitempty"` + + // The value to add to privileged ports ( ports < 1024) for gateway containers + MapPrivilegedContainerPorts int32 `json:"mapPrivilegedContainerPorts,omitempty"` } // +k8s:deepcopy-gen=true diff --git a/control-plane/subcommand/gateway-resources/command.go b/control-plane/subcommand/gateway-resources/command.go index 3ad3ff7f53..6deea27a26 100644 --- a/control-plane/subcommand/gateway-resources/command.go +++ b/control-plane/subcommand/gateway-resources/command.go @@ -73,6 +73,8 @@ type Command struct { flagOpenshiftSCCName string + flagMapPrivilegedContainerPorts int + k8sClient client.Client once sync.Once @@ -128,6 +130,10 @@ func (c *Command) init() { c.flags.StringVar(&c.flagOpenshiftSCCName, "openshift-scc-name", "", "Name of security context constraint to use for gateways on Openshift.", ) + c.flags.IntVar(&c.flagMapPrivilegedContainerPorts, "map-privileged-container-ports", 0, + "The value to add to privileged container ports (< 1024) to avoid requiring addition privileges for the "+ + "gateway container.", + ) c.k8s = &flags.K8SFlags{} flags.Merge(c.flags, c.k8s.Flags()) @@ -202,7 +208,8 @@ func (c *Command) Run(args []string) int { MaxInstances: nonZeroOrNil(c.flagDeploymentMaxInstances), MinInstances: nonZeroOrNil(c.flagDeploymentMinInstances), }, - OpenshiftSCCName: c.flagOpenshiftSCCName, + OpenshiftSCCName: c.flagOpenshiftSCCName, + MapPrivilegedContainerPorts: int32(c.flagMapPrivilegedContainerPorts), }, } From a1eb32bae371bb205de5d102ee6c9057bb90fe14 Mon Sep 17 00:00:00 2001 From: Paul Glass Date: Tue, 8 Aug 2023 09:48:18 -0500 Subject: [PATCH 318/340] Support restricted PSA enforcement part 2 (#2702) --- .../framework/connhelper/connect_helper.go | 8 +-- acceptance/framework/k8s/helpers.go | 1 + .../wan-federation/wan_federation_test.go | 50 +++++++++++-------- .../create-federation-secret-job.yaml | 1 + .../ingress-gateways-deployment.yaml | 2 + .../consul/templates/partition-init-job.yaml | 1 + .../templates/sync-catalog-deployment.yaml | 1 + .../terminating-gateways-deployment.yaml | 2 + 8 files changed, 42 insertions(+), 24 deletions(-) diff --git a/acceptance/framework/connhelper/connect_helper.go b/acceptance/framework/connhelper/connect_helper.go index 2eb18c9dbb..314c0d853a 100644 --- a/acceptance/framework/connhelper/connect_helper.go +++ b/acceptance/framework/connhelper/connect_helper.go @@ -123,7 +123,7 @@ func (c *ConnectHelper) DeployClientAndServer(t *testing.T) { logger.Log(t, "creating static-server and static-client deployments") - c.setupAppNamespace(t) + c.SetupAppNamespace(t) opts := c.KubectlOptsForApp(t) if c.Cfg.EnableCNI && c.Cfg.EnableOpenshift { @@ -170,10 +170,10 @@ func (c *ConnectHelper) DeployClientAndServer(t *testing.T) { }) } -// setupAppNamespace creates a namespace where applications are deployed. This +// SetupAppNamespace creates a namespace where applications are deployed. This // does nothing if UseAppNamespace is not set. The app namespace is relevant // when testing with restricted PSA enforcement enabled. -func (c *ConnectHelper) setupAppNamespace(t *testing.T) { +func (c *ConnectHelper) SetupAppNamespace(t *testing.T) { if !c.UseAppNamespace { return } @@ -204,7 +204,7 @@ func (c *ConnectHelper) setupAppNamespace(t *testing.T) { func (c *ConnectHelper) CreateResolverRedirect(t *testing.T) { logger.Log(t, "creating resolver redirect") opts := c.KubectlOptsForApp(t) - c.setupAppNamespace(t) + c.SetupAppNamespace(t) kustomizeDir := "../fixtures/cases/resolver-redirect-virtualip" k8s.KubectlApplyK(t, opts, kustomizeDir) diff --git a/acceptance/framework/k8s/helpers.go b/acceptance/framework/k8s/helpers.go index 235d26d061..a6b4d1ca7c 100644 --- a/acceptance/framework/k8s/helpers.go +++ b/acceptance/framework/k8s/helpers.go @@ -139,6 +139,7 @@ func CopySecret(t *testing.T, sourceContext, destContext environment.TestContext secret.ResourceVersion = "" require.NoError(r, err) }) + secret.Namespace = destContext.KubectlOptions(t).Namespace _, err = destContext.KubernetesClient(t).CoreV1().Secrets(destContext.KubectlOptions(t).Namespace).Create(context.Background(), secret, metav1.CreateOptions{}) require.NoError(t, err) } diff --git a/acceptance/tests/wan-federation/wan_federation_test.go b/acceptance/tests/wan-federation/wan_federation_test.go index bae8e8e9da..8edc1f5d03 100644 --- a/acceptance/tests/wan-federation/wan_federation_test.go +++ b/acceptance/tests/wan-federation/wan_federation_test.go @@ -9,11 +9,11 @@ import ( "strconv" "testing" + "github.com/hashicorp/consul-k8s/acceptance/framework/connhelper" "github.com/hashicorp/consul-k8s/acceptance/framework/consul" "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/logger" - "github.com/hashicorp/consul/api" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -43,10 +43,6 @@ func TestWANFederation(t *testing.T) { env := suite.Environment() cfg := suite.Config() - if cfg.UseKind { - t.Skipf("skipping wan federation tests as they currently fail on Kind even though they work on other clouds.") - } - primaryContext := env.DefaultContext(t) secondaryContext := env.Context(t, 1) @@ -86,6 +82,7 @@ func TestWANFederation(t *testing.T) { federationSecret, err := primaryContext.KubernetesClient(t).CoreV1().Secrets(primaryContext.KubectlOptions(t).Namespace).Get(context.Background(), federationSecretName, metav1.GetOptions{}) require.NoError(t, err) federationSecret.ResourceVersion = "" + federationSecret.Namespace = secondaryContext.KubectlOptions(t).Namespace _, err = secondaryContext.KubernetesClient(t).CoreV1().Secrets(secondaryContext.KubectlOptions(t).Namespace).Create(context.Background(), federationSecret, metav1.CreateOptions{}) require.NoError(t, err) @@ -161,30 +158,43 @@ func TestWANFederation(t *testing.T) { k8s.KubectlDeleteK(t, secondaryContext.KubectlOptions(t), kustomizeDir) }) + primaryHelper := connhelper.ConnectHelper{ + Secure: c.secure, + ReleaseName: releaseName, + Ctx: primaryContext, + UseAppNamespace: cfg.EnableRestrictedPSAEnforcement, + Cfg: cfg, + ConsulClient: primaryClient, + } + secondaryHelper := connhelper.ConnectHelper{ + Secure: c.secure, + ReleaseName: releaseName, + Ctx: secondaryContext, + UseAppNamespace: cfg.EnableRestrictedPSAEnforcement, + Cfg: cfg, + ConsulClient: secondaryClient, + } + + // When restricted PSA enforcement is enabled on the Consul + // namespace, deploy the test apps to a different unrestricted + // namespace because they can't run in a restricted namespace. + // This creates the app namespace only if necessary. + primaryHelper.SetupAppNamespace(t) + secondaryHelper.SetupAppNamespace(t) + // Check that we can connect services over the mesh gateways logger.Log(t, "creating static-server in dc2") - k8s.DeployKustomize(t, secondaryContext.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, secondaryHelper.KubectlOptsForApp(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") logger.Log(t, "creating static-client in dc1") - k8s.DeployKustomize(t, primaryContext.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-multi-dc") + k8s.DeployKustomize(t, primaryHelper.KubectlOptsForApp(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-multi-dc") if c.secure { - logger.Log(t, "creating intention") - _, _, err = primaryClient.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{ - Kind: api.ServiceIntentions, - Name: "static-server", - Sources: []*api.SourceIntention{ - { - Name: StaticClientName, - Action: api.IntentionActionAllow, - }, - }, - }, nil) - require.NoError(t, err) + primaryHelper.CreateIntention(t) } logger.Log(t, "checking that connection is successful") - k8s.CheckStaticServerConnectionSuccessful(t, primaryContext.KubectlOptions(t), StaticClientName, "http://localhost:1234") + k8s.CheckStaticServerConnectionSuccessful(t, primaryHelper.KubectlOptsForApp(t), StaticClientName, "http://localhost:1234") }) } } diff --git a/charts/consul/templates/create-federation-secret-job.yaml b/charts/consul/templates/create-federation-secret-job.yaml index bc3e0a988b..678a2af3ba 100644 --- a/charts/consul/templates/create-federation-secret-job.yaml +++ b/charts/consul/templates/create-federation-secret-job.yaml @@ -93,6 +93,7 @@ spec: containers: - name: create-federation-secret image: "{{ .Values.global.imageK8S }}" + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} env: - name: NAMESPACE valueFrom: diff --git a/charts/consul/templates/ingress-gateways-deployment.yaml b/charts/consul/templates/ingress-gateways-deployment.yaml index 328c06ee3e..c10f1549f6 100644 --- a/charts/consul/templates/ingress-gateways-deployment.yaml +++ b/charts/consul/templates/ingress-gateways-deployment.yaml @@ -175,6 +175,7 @@ spec: # ingress-gateway-init registers the ingress gateway service with Consul. - name: ingress-gateway-init image: {{ $root.Values.global.imageK8S }} + {{- include "consul.restrictedSecurityContext" $ | nindent 8 }} env: - name: NAMESPACE valueFrom: @@ -233,6 +234,7 @@ spec: containers: - name: ingress-gateway image: {{ $root.Values.global.imageConsulDataplane | quote }} + {{- include "consul.restrictedSecurityContext" $ | nindent 8 }} {{- if (default $defaults.resources .resources) }} resources: {{ toYaml (default $defaults.resources .resources) | nindent 10 }} {{- end }} diff --git a/charts/consul/templates/partition-init-job.yaml b/charts/consul/templates/partition-init-job.yaml index db73ef783b..9209f850c8 100644 --- a/charts/consul/templates/partition-init-job.yaml +++ b/charts/consul/templates/partition-init-job.yaml @@ -81,6 +81,7 @@ spec: containers: - name: partition-init-job image: {{ .Values.global.imageK8S }} + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} env: {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 10 }} {{- if (and .Values.global.acls.bootstrapToken.secretName .Values.global.acls.bootstrapToken.secretKey) }} diff --git a/charts/consul/templates/sync-catalog-deployment.yaml b/charts/consul/templates/sync-catalog-deployment.yaml index e88adea533..a8793ef6f6 100644 --- a/charts/consul/templates/sync-catalog-deployment.yaml +++ b/charts/consul/templates/sync-catalog-deployment.yaml @@ -77,6 +77,7 @@ spec: containers: - name: sync-catalog image: "{{ default .Values.global.imageK8S .Values.syncCatalog.image }}" + {{- include "consul.restrictedSecurityContext" . | nindent 8 }} env: {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 8 }} {{- if .Values.global.acls.manageSystemACLs }} diff --git a/charts/consul/templates/terminating-gateways-deployment.yaml b/charts/consul/templates/terminating-gateways-deployment.yaml index fdf2c17d05..9433e44bc9 100644 --- a/charts/consul/templates/terminating-gateways-deployment.yaml +++ b/charts/consul/templates/terminating-gateways-deployment.yaml @@ -160,6 +160,7 @@ spec: # terminating-gateway-init registers the terminating gateway service with Consul. - name: terminating-gateway-init image: {{ $root.Values.global.imageK8S }} + {{- include "consul.restrictedSecurityContext" $ | nindent 10 }} env: - name: NAMESPACE valueFrom: @@ -218,6 +219,7 @@ spec: containers: - name: terminating-gateway image: {{ $root.Values.global.imageConsulDataplane | quote }} + {{- include "consul.restrictedSecurityContext" $ | nindent 10 }} volumeMounts: - name: consul-service mountPath: /consul/service From f3d099cf52625058e8fa6dffd938e6c02fce0b37 Mon Sep 17 00:00:00 2001 From: Nathan Coleman Date: Wed, 9 Aug 2023 15:41:43 -0400 Subject: [PATCH 319/340] NET-4413 Implement translation + validation of TLS options (#2711) * Implement validation of TLS options * Use constants for annotation keys * Add changelog entry * Implement TLS options translation * Update changelog entry * Add unit test coverage for TLS option validation * Code review feedback --- .changelog/2711.txt | 3 + control-plane/api-gateway/binding/result.go | 6 +- .../api-gateway/binding/validation.go | 124 +++++++++++++++--- .../api-gateway/binding/validation_test.go | 41 ++++++ control-plane/api-gateway/common/constants.go | 5 + .../api-gateway/common/translation.go | 12 ++ .../api-gateway/common/translation_test.go | 22 ++++ 7 files changed, 194 insertions(+), 19 deletions(-) create mode 100644 .changelog/2711.txt diff --git a/.changelog/2711.txt b/.changelog/2711.txt new file mode 100644 index 0000000000..abb0b7e4fb --- /dev/null +++ b/.changelog/2711.txt @@ -0,0 +1,3 @@ +```release-note:feature +api-gateway: translate and validate TLS configuration options, including min/max version and cipher suites, setting Gateway status appropriately +``` diff --git a/control-plane/api-gateway/binding/result.go b/control-plane/api-gateway/binding/result.go index 1953d4836c..e6c0760e7a 100644 --- a/control-plane/api-gateway/binding/result.go +++ b/control-plane/api-gateway/binding/result.go @@ -241,7 +241,11 @@ var ( // Below is where any custom generic listener validation errors should go. // We map anything under here to a custom ListenerConditionReason of Invalid on // an Accepted status type. - errListenerNoTLSPassthrough = errors.New("TLS passthrough is not supported") + errListenerNoTLSPassthrough = errors.New("TLS passthrough is not supported") + errListenerTLSCipherSuiteNotConfigurable = errors.New("tls_min_version does not allow tls_cipher_suites configuration") + errListenerUnsupportedTLSCipherSuite = errors.New("unsupported cipher suite in tls_cipher_suites") + errListenerUnsupportedTLSMaxVersion = errors.New("unsupported tls_max_version") + errListenerUnsupportedTLSMinVersion = errors.New("unsupported tls_min_version") // This custom listener validation error is used to differentiate between an errListenerPortUnavailable because of // direct port conflicts defined by the user (two listeners on the same port) vs a port conflict because we map diff --git a/control-plane/api-gateway/binding/validation.go b/control-plane/api-gateway/binding/validation.go index d5a97499ca..6029c10b24 100644 --- a/control-plane/api-gateway/binding/validation.go +++ b/control-plane/api-gateway/binding/validation.go @@ -41,6 +41,45 @@ var ( gwv1beta1.Kind("HTTPRoute"): {}, gwv1beta1.Kind("TCPRoute"): {}, } + + allSupportedTLSVersions = map[string]struct{}{ + "TLS_AUTO": {}, + "TLSv1_0": {}, + "TLSv1_1": {}, + "TLSv1_2": {}, + "TLSv1_3": {}, + } + + allTLSVersionsWithConfigurableCipherSuites = map[string]struct{}{ + // Remove "" and "TLS_AUTO" if Envoy ever sets TLS 1.3 as default minimum + "": {}, + "TLS_AUTO": {}, + "TLSv1_0": {}, + "TLSv1_1": {}, + "TLSv1_2": {}, + } + + allSupportedTLSCipherSuites = map[string]struct{}{ + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": {}, + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256": {}, + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": {}, + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256": {}, + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": {}, + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": {}, + + // NOTE: the following cipher suites are currently supported by Envoy + // but have been identified as insecure and are pending removal + // https://github.com/envoyproxy/envoy/issues/5399 + "TLS_RSA_WITH_AES_128_GCM_SHA256": {}, + "TLS_RSA_WITH_AES_128_CBC_SHA": {}, + "TLS_RSA_WITH_AES_256_GCM_SHA384": {}, + "TLS_RSA_WITH_AES_256_CBC_SHA": {}, + // https://github.com/envoyproxy/envoy/issues/5400 + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": {}, + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": {}, + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": {}, + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": {}, + } ) // validateRefs validates backend references for a route, determining whether or @@ -167,43 +206,92 @@ func (m mergedListeners) validateHostname(index int, listener gwv1beta1.Listener // validateTLS validates that the TLS configuration for a given listener is valid and that // the certificates that it references exist. func validateTLS(gateway gwv1beta1.Gateway, tls *gwv1beta1.GatewayTLSConfig, resources *common.ResourceMap) (error, error) { - namespace := gateway.Namespace - + // If there's no TLS, there's nothing to validate if tls == nil { return nil, nil } - var err error + // Validate the certificate references and then return any error + // alongside any TLS configuration error that we find below. + refsErr := validateCertificateRefs(gateway, tls.CertificateRefs, resources) + + if tls.Mode != nil && *tls.Mode == gwv1beta1.TLSModePassthrough { + return errListenerNoTLSPassthrough, refsErr + } + + if err := validateTLSOptions(tls.Options); err != nil { + return err, refsErr + } + + return nil, refsErr +} - for _, cert := range tls.CertificateRefs { - // break on the first error +func validateCertificateRefs(gateway gwv1beta1.Gateway, refs []gwv1beta1.SecretObjectReference, resources *common.ResourceMap) error { + for _, cert := range refs { + // Verify that the reference has a group and kind that we support if !common.NilOrEqual(cert.Group, "") || !common.NilOrEqual(cert.Kind, common.KindSecret) { - err = errListenerInvalidCertificateRef_NotSupported - break + return errListenerInvalidCertificateRef_NotSupported } + // Verify that the reference is within the namespace or, + // if cross-namespace, that it's allowed by a ReferenceGrant if !resources.GatewayCanReferenceSecret(gateway, cert) { - err = errRefNotPermitted - break + return errRefNotPermitted } - key := common.IndexedNamespacedNameWithDefault(cert.Name, cert.Namespace, namespace) + // Verify that the referenced resource actually exists + key := common.IndexedNamespacedNameWithDefault(cert.Name, cert.Namespace, gateway.Namespace) secret := resources.Certificate(key) - if secret == nil { - err = errListenerInvalidCertificateRef_NotFound - break + return errListenerInvalidCertificateRef_NotFound } - err = validateCertificateData(*secret) + // Verify that the referenced resource contains the data shape that we expect + if err := validateCertificateData(*secret); err != nil { + return err + } } - if tls.Mode != nil && *tls.Mode == gwv1beta1.TLSModePassthrough { - return errListenerNoTLSPassthrough, err + return nil +} + +func validateTLSOptions(options map[gwv1beta1.AnnotationKey]gwv1beta1.AnnotationValue) error { + if options == nil { + return nil + } + + tlsMinVersionValue := string(options[common.TLSMinVersionAnnotationKey]) + if tlsMinVersionValue != "" { + if _, supported := allSupportedTLSVersions[tlsMinVersionValue]; !supported { + return errListenerUnsupportedTLSMinVersion + } + } + + tlsMaxVersionValue := string(options[common.TLSMaxVersionAnnotationKey]) + if tlsMaxVersionValue != "" { + if _, supported := allSupportedTLSVersions[tlsMaxVersionValue]; !supported { + return errListenerUnsupportedTLSMaxVersion + } } - // TODO: validate tls options - return nil, err + tlsCipherSuitesValue := string(options[common.TLSCipherSuitesAnnotationKey]) + if tlsCipherSuitesValue != "" { + // If a minimum TLS version is configured, verify that it supports configuring cipher suites + if tlsMinVersionValue != "" { + if _, supported := allTLSVersionsWithConfigurableCipherSuites[tlsMinVersionValue]; !supported { + return errListenerTLSCipherSuiteNotConfigurable + } + } + + for _, tlsCipherSuiteValue := range strings.Split(tlsCipherSuitesValue, ",") { + tlsCipherSuite := strings.TrimSpace(tlsCipherSuiteValue) + if _, supported := allSupportedTLSCipherSuites[tlsCipherSuite]; !supported { + return errListenerUnsupportedTLSCipherSuite + } + } + } + + return nil } func validateCertificateData(secret corev1.Secret) error { diff --git a/control-plane/api-gateway/binding/validation_test.go b/control-plane/api-gateway/binding/validation_test.go index 10cdf851c3..1f2b143387 100644 --- a/control-plane/api-gateway/binding/validation_test.go +++ b/control-plane/api-gateway/binding/validation_test.go @@ -510,6 +510,47 @@ func TestValidateTLS(t *testing.T) { expectedResolvedRefsErr: nil, expectedAcceptedErr: nil, }, + "invalid cipher suite": { + gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), + tls: &gwv1beta1.GatewayTLSConfig{ + Options: map[gwv1beta1.AnnotationKey]gwv1beta1.AnnotationValue{ + common.TLSCipherSuitesAnnotationKey: "invalid", + }, + }, + certificates: nil, + expectedAcceptedErr: errListenerUnsupportedTLSCipherSuite, + }, + "cipher suite not configurable": { + gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), + tls: &gwv1beta1.GatewayTLSConfig{ + Options: map[gwv1beta1.AnnotationKey]gwv1beta1.AnnotationValue{ + common.TLSMinVersionAnnotationKey: "TLSv1_3", + common.TLSCipherSuitesAnnotationKey: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + }, + }, + certificates: nil, + expectedAcceptedErr: errListenerTLSCipherSuiteNotConfigurable, + }, + "invalid max version": { + gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), + tls: &gwv1beta1.GatewayTLSConfig{ + Options: map[gwv1beta1.AnnotationKey]gwv1beta1.AnnotationValue{ + common.TLSMaxVersionAnnotationKey: "invalid", + }, + }, + certificates: nil, + expectedAcceptedErr: errListenerUnsupportedTLSMaxVersion, + }, + "invalid min version": { + gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), + tls: &gwv1beta1.GatewayTLSConfig{ + Options: map[gwv1beta1.AnnotationKey]gwv1beta1.AnnotationValue{ + common.TLSMinVersionAnnotationKey: "invalid", + }, + }, + certificates: nil, + expectedAcceptedErr: errListenerUnsupportedTLSMinVersion, + }, } { t.Run(name, func(t *testing.T) { resources := common.NewResourceMap(common.ResourceTranslator{}, NewReferenceValidator(tt.grants), logrtest.NewTestLogger(t)) diff --git a/control-plane/api-gateway/common/constants.go b/control-plane/api-gateway/common/constants.go index c1ec0685a4..04701662b7 100644 --- a/control-plane/api-gateway/common/constants.go +++ b/control-plane/api-gateway/common/constants.go @@ -7,4 +7,9 @@ const ( GatewayClassControllerName = "consul.hashicorp.com/gateway-controller" AnnotationGatewayClassConfig = "consul.hashicorp.com/gateway-class-config" + + // The following annotation keys are used in the v1beta1.GatewayTLSConfig's Options on a v1beta1.Listener. + TLSCipherSuitesAnnotationKey = "api-gateway.consul.hashicorp.com/tls_cipher_suites" + TLSMaxVersionAnnotationKey = "api-gateway.consul.hashicorp.com/tls_max_version" + TLSMinVersionAnnotationKey = "api-gateway.consul.hashicorp.com/tls_min_version" ) diff --git a/control-plane/api-gateway/common/translation.go b/control-plane/api-gateway/common/translation.go index 2f66749c94..9303540e82 100644 --- a/control-plane/api-gateway/common/translation.go +++ b/control-plane/api-gateway/common/translation.go @@ -85,8 +85,17 @@ func (t ResourceTranslator) toAPIGatewayListener(gateway gwv1beta1.Gateway, list namespace := gateway.Namespace var certificates []api.ResourceReference + var cipherSuites []string + var maxVersion, minVersion string if listener.TLS != nil { + cipherSuitesVal := string(listener.TLS.Options[TLSCipherSuitesAnnotationKey]) + if cipherSuitesVal != "" { + cipherSuites = strings.Split(cipherSuitesVal, ",") + } + maxVersion = string(listener.TLS.Options[TLSMaxVersionAnnotationKey]) + minVersion = string(listener.TLS.Options[TLSMinVersionAnnotationKey]) + for _, ref := range listener.TLS.CertificateRefs { if !resources.GatewayCanReferenceSecret(gateway, ref) { return api.APIGatewayListener{}, false @@ -116,6 +125,9 @@ func (t ResourceTranslator) toAPIGatewayListener(gateway gwv1beta1.Gateway, list Protocol: listenerProtocolMap[strings.ToLower(string(listener.Protocol))], TLS: api.APIGatewayTLSConfiguration{ Certificates: certificates, + CipherSuites: cipherSuites, + MaxVersion: maxVersion, + MinVersion: minVersion, }, }, true } diff --git a/control-plane/api-gateway/common/translation_test.go b/control-plane/api-gateway/common/translation_test.go index daa89a698f..e8caad76ed 100644 --- a/control-plane/api-gateway/common/translation_test.go +++ b/control-plane/api-gateway/common/translation_test.go @@ -11,6 +11,7 @@ import ( "encoding/pem" "fmt" "math/big" + "strings" "testing" "time" @@ -134,6 +135,9 @@ func TestTranslator_ToAPIGateway(t *testing.T) { listenerOneCertK8sNamespace := "one-cert-ns" listenerOneCertConsulNamespace := "one-cert-ns" listenerOneCert := generateTestCertificate(t, "one-cert-ns", "one-cert") + listenerOneMaxVersion := "TLSv1_2" + listenerOneMinVersion := "TLSv1_3" + listenerOneCipherSuites := []string{"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256"} // listener one status listenerOneLastTransmissionTime := time.Now() @@ -157,6 +161,7 @@ func TestTranslator_ToAPIGateway(t *testing.T) { annotations map[string]string expectedGWName string listenerOneK8sCertRefs []gwv1beta1.SecretObjectReference + listenerOneTLSOptions map[gwv1beta1.AnnotationKey]gwv1beta1.AnnotationValue }{ "gw name": { annotations: make(map[string]string), @@ -167,6 +172,11 @@ func TestTranslator_ToAPIGateway(t *testing.T) { Namespace: PointerTo(gwv1beta1.Namespace(listenerOneCertK8sNamespace)), }, }, + listenerOneTLSOptions: map[gwv1beta1.AnnotationKey]gwv1beta1.AnnotationValue{ + TLSMaxVersionAnnotationKey: gwv1beta1.AnnotationValue(listenerOneMaxVersion), + TLSMinVersionAnnotationKey: gwv1beta1.AnnotationValue(listenerOneMinVersion), + TLSCipherSuitesAnnotationKey: gwv1beta1.AnnotationValue(strings.Join(listenerOneCipherSuites, ",")), + }, }, "when k8s has certs that are not referenced in consul": { annotations: make(map[string]string), @@ -181,6 +191,11 @@ func TestTranslator_ToAPIGateway(t *testing.T) { Namespace: PointerTo(gwv1beta1.Namespace(listenerOneCertK8sNamespace)), }, }, + listenerOneTLSOptions: map[gwv1beta1.AnnotationKey]gwv1beta1.AnnotationValue{ + TLSMaxVersionAnnotationKey: gwv1beta1.AnnotationValue(listenerOneMaxVersion), + TLSMinVersionAnnotationKey: gwv1beta1.AnnotationValue(listenerOneMinVersion), + TLSCipherSuitesAnnotationKey: gwv1beta1.AnnotationValue(strings.Join(listenerOneCipherSuites, ",")), + }, }, } @@ -207,6 +222,7 @@ func TestTranslator_ToAPIGateway(t *testing.T) { Protocol: gwv1beta1.ProtocolType(listenerOneProtocol), TLS: &gwv1beta1.GatewayTLSConfig{ CertificateRefs: tc.listenerOneK8sCertRefs, + Options: tc.listenerOneTLSOptions, }, }, { @@ -288,6 +304,9 @@ func TestTranslator_ToAPIGateway(t *testing.T) { Namespace: listenerOneCertConsulNamespace, }, }, + CipherSuites: listenerOneCipherSuites, + MaxVersion: listenerOneMaxVersion, + MinVersion: listenerOneMinVersion, }, }, { @@ -303,6 +322,9 @@ func TestTranslator_ToAPIGateway(t *testing.T) { Namespace: listenerTwoCertConsulNamespace, }, }, + CipherSuites: nil, + MaxVersion: "", + MinVersion: "", }, }, }, From a287fce551affef7d358394032e8997f79220c8b Mon Sep 17 00:00:00 2001 From: John Maguire Date: Wed, 9 Aug 2023 16:03:14 -0400 Subject: [PATCH 320/340] NET-4993 JWT auth basic acceptance test (#2706) * JWT auth basic acceptance test * Update to run only in enterprise mode, update comment to be correct * Remove usage of `testing.t` in retry block * Fixed last `t` in retry block in tests * Update acceptance/tests/api-gateway/api_gateway_test.go Co-authored-by: Nathan Coleman * Update acceptance/tests/api-gateway/api_gateway_test.go Co-authored-by: Nathan Coleman * Updating filenames for gw jwt cases and adding message about why this test is skipped --------- Co-authored-by: Nathan Coleman --- .../tests/api-gateway/api_gateway_test.go | 253 +++++++++++++++++- .../api-gateways/jwt-auth/api-gateway.yaml | 37 +++ .../api-gateways/jwt-auth/gateway-policy.yaml | 24 ++ .../api-gateways/jwt-auth/httproute-auth.yaml | 32 +++ .../api-gateways/jwt-auth/httproute.yaml | 19 ++ .../api-gateways/jwt-auth/jwt-provider.yaml | 9 + .../jwt-auth/jwt-route-filter.yaml | 12 + .../api-gateways/jwt-auth/kustomization.yaml | 12 + 8 files changed, 397 insertions(+), 1 deletion(-) create mode 100644 acceptance/tests/fixtures/cases/api-gateways/jwt-auth/api-gateway.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/jwt-auth/gateway-policy.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute-auth.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-provider.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-route-filter.yaml create mode 100644 acceptance/tests/fixtures/cases/api-gateways/jwt-auth/kustomization.yaml diff --git a/acceptance/tests/api-gateway/api_gateway_test.go b/acceptance/tests/api-gateway/api_gateway_test.go index 721bbf2527..df4a097b7e 100644 --- a/acceptance/tests/api-gateway/api_gateway_test.go +++ b/acceptance/tests/api-gateway/api_gateway_test.go @@ -126,7 +126,7 @@ func TestAPIGateway_Basic(t *testing.T) { // On startup, the controller can take upwards of 1m to perform // leader election so we may need to wait a long time for - // the reconcile loop to run (hence the 1m timeout here). + // the reconcile loop to run (hence the timeout here). var gatewayAddress string counter := &retry.Counter{Count: 120, Wait: 2 * time.Second} retry.RunWith(counter, t, func(r *retry.R) { @@ -288,6 +288,257 @@ func TestAPIGateway_Basic(t *testing.T) { } } +func TestAPIGateway_JWTAuth_Basic(t *testing.T) { + t.Skip("skipping this test until GW JWT auth is complete") + ctx := suite.Environment().DefaultContext(t) + cfg := suite.Config() + + if !cfg.EnableEnterprise { + t.Skipf("skipping this test because -enable-enterprise is not set") + } + + helmValues := map[string]string{ + "connectInject.enabled": "true", + "connectInject.consulNamespaces.mirroringK8S": "true", + "global.acls.manageSystemACLs": "true", // acls must be enabled for JWT auth to take place + "global.tls.enabled": "true", + "global.logLevel": "trace", + } + + releaseName := helpers.RandomName() + consulCluster := consul.NewHelmCluster(t, helmValues, ctx, cfg, releaseName) + + consulCluster.Create(t) + + // Override the default proxy config settings for this test + consulClient, _ := consulCluster.SetupConsulClient(t, true) + _, _, err := consulClient.ConfigEntries().Set(&api.ProxyConfigEntry{ + Kind: api.ProxyDefaults, + Name: api.ProxyConfigGlobal, + Config: map[string]interface{}{ + "protocol": "http", + }, + }, nil) + require.NoError(t, err) + + logger.Log(t, "creating api-gateway resources") + out, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "apply", "-k", "../fixtures/cases/api-gateways/jwt-auth") + require.NoError(t, err, out) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + // Ignore errors here because if the test ran as expected + // the custom resources will have been deleted. + k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-k", "../fixtures/cases/api-gateways/jwt-auth") + }) + + // Create certificate secret, we do this separately since + // applying the secret will make an invalid certificate that breaks other tests + logger.Log(t, "creating certificate secret") + out, err = k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "apply", "-f", "../fixtures/bases/api-gateway/certificate.yaml") + require.NoError(t, err, out) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + // Ignore errors here because if the test ran as expected + // the custom resources will have been deleted. + k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-f", "../fixtures/bases/api-gateway/certificate.yaml") + }) + + // patch certificate with data + logger.Log(t, "patching certificate secret with generated data") + certificate := generateCertificate(t, nil, "gateway.test.local") + k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "secret", "certificate", "-p", fmt.Sprintf(`{"data":{"tls.crt":"%s","tls.key":"%s"}}`, base64.StdEncoding.EncodeToString(certificate.CertPEM), base64.StdEncoding.EncodeToString(certificate.PrivateKeyPEM)), "--type=merge") + + // We use the static-client pod so that we can make calls to the api gateway + // via kubectl exec without needing a route into the cluster from the test machine. + logger.Log(t, "creating static-client pod") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-client") + + k8s.RunKubectl(t, ctx.KubectlOptions(t), "wait", "--for=condition=available", "--timeout=5m", fmt.Sprintf("deploy/%s", "static-server")) + // Grab a kubernetes client so that we can verify binding + // behavior prior to issuing requests through the gateway. + k8sClient := ctx.ControllerRuntimeClient(t) + + // On startup, the controller can take upwards of 1m to perform + // leader election so we may need to wait a long time for + // the reconcile loop to run (hence the 2m timeout here). + var ( + gatewayAddress string + gatewayClass gwv1beta1.GatewayClass + httpRoute gwv1beta1.HTTPRoute + httpRouteAuth gwv1beta1.HTTPRoute + ) + + counter := &retry.Counter{Count: 60, Wait: 2 * time.Second} + retry.RunWith(counter, t, func(r *retry.R) { + var gateway gwv1beta1.Gateway + err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway", Namespace: "default"}, &gateway) + require.NoError(r, err) + + // check our finalizers + require.Len(r, gateway.Finalizers, 1) + require.EqualValues(r, gatewayFinalizer, gateway.Finalizers[0]) + + // check our statuses + checkStatusCondition(r, gateway.Status.Conditions, trueCondition("Accepted", "Accepted")) + checkStatusCondition(r, gateway.Status.Conditions, trueCondition("ConsulAccepted", "Accepted")) + require.Len(r, gateway.Status.Listeners, 4) + + require.EqualValues(r, 1, gateway.Status.Listeners[0].AttachedRoutes) + checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, trueCondition("Accepted", "Accepted")) + checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, falseCondition("Conflicted", "NoConflicts")) + checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) + require.EqualValues(r, 1, gateway.Status.Listeners[1].AttachedRoutes) + checkStatusCondition(r, gateway.Status.Listeners[1].Conditions, trueCondition("Accepted", "Accepted")) + checkStatusCondition(r, gateway.Status.Listeners[1].Conditions, falseCondition("Conflicted", "NoConflicts")) + checkStatusCondition(r, gateway.Status.Listeners[1].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) + + // check that we have an address to use + require.Len(r, gateway.Status.Addresses, 1) + // now we know we have an address, set it so we can use it + gatewayAddress = gateway.Status.Addresses[0].Value + + // gateway class checks + err = k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway-class"}, &gatewayClass) + require.NoError(r, err) + + // check our finalizers + require.Len(r, gatewayClass.Finalizers, 1) + require.EqualValues(r, gatewayClassFinalizer, gatewayClass.Finalizers[0]) + + // http route checks + err = k8sClient.Get(context.Background(), types.NamespacedName{Name: "http-route", Namespace: "default"}, &httpRoute) + require.NoError(r, err) + + // http route checks + err = k8sClient.Get(context.Background(), types.NamespacedName{Name: "http-route-auth", Namespace: "default"}, &httpRouteAuth) + require.NoError(r, err) + + // check our finalizers + require.Len(r, httpRoute.Finalizers, 1) + require.EqualValues(r, gatewayFinalizer, httpRoute.Finalizers[0]) + + // check parent status + require.Len(r, httpRoute.Status.Parents, 1) + require.EqualValues(r, gatewayClassControllerName, httpRoute.Status.Parents[0].ControllerName) + require.EqualValues(r, "gateway", httpRoute.Status.Parents[0].ParentRef.Name) + checkStatusCondition(r, httpRoute.Status.Parents[0].Conditions, trueCondition("Accepted", "Accepted")) + checkStatusCondition(r, httpRoute.Status.Parents[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) + checkStatusCondition(r, httpRoute.Status.Parents[0].Conditions, trueCondition("ConsulAccepted", "Accepted")) + + // check our finalizers + require.Len(r, httpRouteAuth.Finalizers, 1) + require.EqualValues(r, gatewayFinalizer, httpRouteAuth.Finalizers[0]) + + // check parent status + require.Len(r, httpRouteAuth.Status.Parents, 1) + require.EqualValues(r, gatewayClassControllerName, httpRouteAuth.Status.Parents[0].ControllerName) + require.EqualValues(r, "gateway", httpRouteAuth.Status.Parents[0].ParentRef.Name) + checkStatusCondition(r, httpRouteAuth.Status.Parents[0].Conditions, trueCondition("Accepted", "Accepted")) + checkStatusCondition(r, httpRouteAuth.Status.Parents[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) + checkStatusCondition(r, httpRouteAuth.Status.Parents[0].Conditions, trueCondition("ConsulAccepted", "Accepted")) + }) + + // check that the Consul entries were created + entry, _, err := consulClient.ConfigEntries().Get(api.APIGateway, "gateway", nil) + require.NoError(t, err) + gateway := entry.(*api.APIGatewayConfigEntry) + + entry, _, err = consulClient.ConfigEntries().Get(api.HTTPRoute, "http-route", nil) + require.NoError(t, err) + consulHTTPRoute := entry.(*api.HTTPRouteConfigEntry) + + entry, _, err = consulClient.ConfigEntries().Get(api.HTTPRoute, "http-route-auth", nil) + require.NoError(t, err) + consulHTTPRouteAuth := entry.(*api.HTTPRouteConfigEntry) + + // now check the gateway status conditions + checkConsulStatusCondition(t, gateway.Status.Conditions, trueConsulCondition("Accepted", "Accepted")) + + // and the route status conditions + checkConsulStatusCondition(t, consulHTTPRoute.Status.Conditions, trueConsulCondition("Bound", "Bound")) + checkConsulStatusCondition(t, consulHTTPRouteAuth.Status.Conditions, trueConsulCondition("Bound", "Bound")) + + // finally we check that we can actually route to the service(s) via the gateway + k8sOptions := ctx.KubectlOptions(t) + targetHTTPAddress := fmt.Sprintf("http://%s/v1", gatewayAddress) + targetHTTPAddressAdmin := fmt.Sprintf("http://%s:8080/admin", gatewayAddress) + targetHTTPAddressPet := fmt.Sprintf("http://%s:8080/pet", gatewayAddress) + // valid JWT token with role of "doctor" + doctorToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJQUzI1NiIsImtpZCI6IkMtRTFuQ2p3Z0JDLVB1R00yTzQ2N0ZSRGhLeDhBa1ZjdElTQWJvM3JpZXcifQ.eyJpc3MiOiJsb2NhbCIsInJvbGUiOiJkb2N0b3IifQ.FfgpzjMf8Evh6K-fJ1cLXklfIXOm-vojVbWlPPbGVFtzxZ9hxMxoyAY_G8i36SfGrpUlp-RJ6ohMvprMrEgyRgbenu7u5kkm5iGHW-zpMus4izXRxPELBcpWOGF105HIssT2NYRstXieNR8EVzvGfLdvR0GW8ttEERgseqGvuAfdb4-aNYsysGwUUHbsZjazA6H1rZmWqHdCLOJ2ZwFsIdckO9CadnkyTILpcPUmLYyUVJdtlLGOySb0GG8c_dPML_IR5jSXCSUZt6S2JBNBNBdqukrlqpA-fIaaWft0dbWVMhv8DqPC8znult8dKvLZ1qXeU0itsqqJUyE16ihJjw" + // valid JWT token with role of "pet" + petToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJQUzI1NiIsImtpZCI6IkMtRTFuQ2p3Z0JDLVB1R00yTzQ2N0ZSRGhLeDhBa1ZjdElTQWJvM3JpZXcifQ.eyJpc3MiOiJsb2NhbCIsInJvbGUiOiJwZXQifQ.l94rJayGGTMB426HwEw5ipSjaIHjm-UWDHiBAlB_Slmi814AxAfl_0AdRwSz67UDnkoygKbvPpR5xUB03JCXNshLZuKLegWsBeQg_OJYvZGmFagl5NglBFvH7Jbta4e1eQoAxZI6Xyy1jHbu7jFBjQPVnK8EaRvWoW8Pe8a8rp_5xhub0pomhvRF6Pm5kAS4cMnxvqpVc5Oo5nO7ws_SmoNnbt2Ok14k23Zx5E2EWmGStOfbgFsdbhVbepB2DMzqv1j8jvBbwa_OxCwc_7pEOthOOxRV6L3ZjgbRSB4GumlXAOCBYXD1cRLgrMSrWB1GkefAKu8PV0Ho1px6sI9Evg" + + // check that intentions keep our connection from happening + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, targetHTTPAddress) + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, targetHTTPAddressAdmin) + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, "-H", doctorToken, targetHTTPAddressAdmin) + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, "-H", petToken, targetHTTPAddressAdmin) + + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, targetHTTPAddressPet) + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, "-H", doctorToken, targetHTTPAddressPet) + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, "-H", petToken, targetHTTPAddressPet) + + // Now we create the allow intention. + _, _, err = consulClient.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{ + Kind: api.ServiceIntentions, + Name: "static-server", + Sources: []*api.SourceIntention{ + { + Name: "gateway", + Action: api.IntentionActionAllow, + }, + }, + }, nil) + require.NoError(t, err) + + _, _, err = consulClient.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{ + Kind: api.ServiceIntentions, + Name: "static-server-protected", + Sources: []*api.SourceIntention{ + { + Name: "gateway", + Action: api.IntentionActionAllow, + }, + }, + }, nil) + require.NoError(t, err) + + // Test that we can make a call to the api gateway + logger.Log(t, "trying calls to api gateway http") + k8s.CheckStaticServerConnectionSuccessful(t, k8sOptions, StaticClientName, targetHTTPAddress) + + // ensure that overrides -> route extension -> default by making a request to the admin route with a JWT that has an issuer of "local" and a "role" of "doctor" + // we can see that: + // * the "iss" verification in the gateway override takes precedence over the "iss" verification in the route filter + // * the "role" verification in the route extension takes precedence over the "role" verification in the gateway default + // should fail because we're missing JWT + logger.Log(t, "trying calls to api gateway /admin should fail without JWT token") + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, targetHTTPAddressAdmin) + + // should fail because we use the token with the wrong role and correct issuer + logger.Log(t, "trying calls to api gateway /admin should fail with wrong role") + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, "-H", petToken, targetHTTPAddressAdmin) + + // will succeed because we use the token with the correct role and the correct issuer + logger.Log(t, "trying calls to api gateway /admin should succeed with JWT token with correct role") + k8s.CheckStaticServerConnectionSuccessful(t, k8sOptions, StaticClientName, "-H", doctorToken, targetHTTPAddressAdmin) + + // ensure that overrides -> route extension -> default by making a request to the admin route with a JWT that has an issuer of "local" and a "role" of "pet" + // the route does not define + // we can see that: + // * the "iss" verification in the gateway override takes precedence over the "iss" verification in the route filter + // * the "role" verification in the route extension takes precedence over the "role" verification in the gateway default + // should fail because we're missing JWT + logger.Log(t, "trying calls to api gateway /pet should fail without JWT token") + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, targetHTTPAddressPet) + + // should fail because we use the token with the wrong role and correct issuer + logger.Log(t, "trying calls to api gateway /pet should fail with wrong role") + k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, "-H", doctorToken, targetHTTPAddressPet) + + // will succeed because we use the token with the correct role and the correct issuer + logger.Log(t, "trying calls to api gateway /pet should succeed with JWT token with correct role") + k8s.CheckStaticServerConnectionSuccessful(t, k8sOptions, StaticClientName, "-H", petToken, targetHTTPAddressPet) +} + func checkStatusCondition(t require.TestingT, conditions []metav1.Condition, toCheck metav1.Condition) { for _, c := range conditions { if c.Type == toCheck.Type { diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/api-gateway.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/api-gateway.yaml new file mode 100644 index 0000000000..bef7e96f12 --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/api-gateway.yaml @@ -0,0 +1,37 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: gateway +spec: + gatewayClassName: gateway-class + listeners: + - protocol: HTTP + port: 8080 + name: http-auth + allowedRoutes: + namespaces: + from: "All" + - protocol: HTTP + port: 80 + name: http + allowedRoutes: + namespaces: + from: "All" + - protocol: TCP + port: 81 + name: tcp + allowedRoutes: + namespaces: + from: "All" + - protocol: HTTPS + port: 443 + name: https + tls: + certificateRefs: + - name: "certificate" + allowedRoutes: + namespaces: + from: "All" diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/gateway-policy.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/gateway-policy.yaml new file mode 100644 index 0000000000..8e47d75062 --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/gateway-policy.yaml @@ -0,0 +1,24 @@ +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ConsulGatewayPolicy +metadata: + name: my-policy +spec: + targetRef: + name: gateway + kind: Gateway + group: gateway.networking.kuberenetes.io + sectionName: http + override: + Providers: + - Provider: "local" + VerifyClaims: + - Path: + - "iss" + Value: "local" + default: + Providers: + - Provider: "local" + VerifyClaims: + - Path: + - "iss" + Value: "local" diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute-auth.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute-auth.yaml new file mode 100644 index 0000000000..4963277c55 --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute-auth.yaml @@ -0,0 +1,32 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: http-route-auth +spec: + parentRefs: + - name: gateway + sectionName: http-auth + rules: + - matches: + - path: + type: PathPrefix + value: "/admin" + backendRefs: + - name: static-server + port: 80 + filters: + - type: ExtensionRef + extensionRef: + group: consul.hashicorp.com + kind: HTTPRouteAuthFilter + name: route-jwt-auth-filter + - matches: + - path: + type: PathPrefix + value: "/pet" + backendRefs: + - name: static-server + port: 80 diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute.yaml new file mode 100644 index 0000000000..52e206a91e --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute.yaml @@ -0,0 +1,19 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: http-route +spec: + parentRefs: + - name: gateway + sectionName: http + rules: + - matches: + - path: + type: PathPrefix + value: "/v1" + backendRefs: + - name: static-server + port: 80 diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-provider.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-provider.yaml new file mode 100644 index 0000000000..37eb034d3c --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-provider.yaml @@ -0,0 +1,9 @@ +apiVersion: consul.hashicorp.com/v1alpha1 +kind: JWTProvider +metadata: + name: local +spec: + issuer: local + jsonWebKeySet: + local: + jwks: "ewogICAgImtleXMiOiBbCiAgICAgICAgewogICAgICAgICAgICAicCI6ICI5TTlWSVhJR0hpR3FlTnhseEJ2V0xFV09oUFh3dXhXZUpod01uM3dGdG9STEtfZmF6VWxjWEc1cUViLTdpMXo3VmlPUWVZRnh6WUZYTS1pbVU3OVFRa1dTVUVSazR2dHZuc2R5UnpUSnVPc3A0ZUhuWFVMSHJPOU51NkJ5bC1VeVprMzFvSnFGeGllM0pHQXlRLUM2OVF2NVFkVjFZV0hfVDkyTzk4d1hYZGMiLAogICAgICAgICAgICAia3R5IjogIlJTQSIsCiAgICAgICAgICAgICJxIjogInFIVnZBb3h0ckgxUTVza25veXNMMkhvbC1ubnU3ZlM3Mjg4clRGdE9jeG9Jb29nWXBKVTljemxwcjctSlo2bjc0TUViVHBBMHRkSUR5TEtQQ0xIN3JKTFRrZzBDZVZNQWpmY01zdkRUcWdFOHNBWE42bzd2ZjYya2hwcExYOHVCU3JxSHkyV1JhZXJsbDROU09hcmRGSkQ2MWhHSVF2cEpXRk4xazFTV3pWcyIsCiAgICAgICAgICAgICJkIjogIlp3elJsVklRZkg5ekZ6d1hOZ2hEMHhkZVctalBCbmRkWnJNZ0wwQ2JjeXZZYlg2X1c0ajlhM1dmYWpobmI2bTFILW9CWjRMczVmNXNRVTB2ZFJ2ZG1laFItUG43aWNRcUdURFNKUTYtdWVtNm15UVRWaEo2UmZiM0lINVJ2VDJTOXUzcVFDZWFadWN3aXFoZ1RCbFhnOWFfV0pwVHJYNFhPQ3JCR1ZsTng3Z2JETVJOamNEN0FnRkZ3S2p2TEZVdDRLTkZmdEJqaFF0TDFLQ2VwblNmamtvRm1RUTVlX3RSS2ozX2U1V3pNSkJkekpQejNkR2YxZEk3OF9wYmJFbmFMcWhqNWg0WUx2UU5JUUhVcURYSGx4ZDc1Qlh3aFJReE1nUDRfd1EwTFk2cVRKNGFDa2Q0RDJBTUtqMzJqeVFiVTRKTE9jQjFNMnZBRWFyc2NTU3l0USIsCiAgICAgICAgICAgICJlIjogIkFRQUIiLAogICAgICAgICAgICAidXNlIjogInNpZyIsCiAgICAgICAgICAgICJraWQiOiAiQy1FMW5DandnQkMtUHVHTTJPNDY3RlJEaEt4OEFrVmN0SVNBYm8zcmlldyIsCiAgICAgICAgICAgICJxaSI6ICJ0N2VOQjhQV21xVHdKREZLQlZKZExrZnJJT2drMFJ4MnREODBGNHB5cjhmNzRuNGlVWXFmWG1haVZtbGx2c2FlT3JlNHlIczQ4UE45NVZsZlVvS3Z6ZEJFaDNZTDFINGZTOGlYYXNzNGJiVnVuWHR4U0hMZFFPYUNZYUplSmhBbGMyUWQ4elR0NFFQWk9yRWVWLVJTYU0tN095ekkwUWtSSF9tcmk1YmRrOXMiLAogICAgICAgICAgICAiZHAiOiAiYnBLckQtVXhrRENDal81MFZLU0NFeE1Ec1Zob2VBZm1tNjMxb1o5aDhUTkZ4TUU1YVptbUJ2VzBJUG9wMm1PUF9qTW9FVWxfUG1RYUlBOEgtVEdqTFp2QTMxSlZBeFN3TU5aQzdwaVFPRjYzVnhneTZUTzlmb1hENVdndC1oLUNxU1N6T2V3eFdmUWNTMmpMcTA3NUFxOTYwTnA2SHhjbE8weUdRN1JDSlpjIiwKICAgICAgICAgICAgImFsZyI6ICJQUzI1NiIsCiAgICAgICAgICAgICJkcSI6ICJpdVZveGwwckFKSEM1c2JzbTZpZWQ3c2ZIVXIwS2Rja0hiVFBLb0lPU1BFcU5YaXBlT3BrWkdEdU55NWlDTXNyRnNHaDFrRW9kTkhZdE40ay1USm5KSDliV296SGdXbGloNnN2R1V0Zi1raFMxWC16ckxaMTJudzlyNDRBbjllWG54bjFaVXMxZm5OakltM3dtZ083algyTWxIeVlNVUZVd0RMd09xNEFPUWsiLAogICAgICAgICAgICAibiI6ICJvUmhjeUREdmp3NFZ4SHRRNTZhRDlNSmRTaWhWSk1nTHd1b2FCQVhhc0RjVDNEWVZjcENlVGxDMVBPdzdPNW1Ec2ZSWVFtcGpoendyRDVZWU8yeDE4REl4czdyNTNJdFMxRy1ybnQxQ1diVE9fUzFJT01DR2xxYzh5VWJnLUhSUkRETXQyb2V3TjJoRGtxYlBKVFJNbXpjRkpNMHRpTm1RZVVMcWViZEVYaWVUblJMT1BkMWg2ZmJycVNLS01mSXlIbGZ1WXFQc1VWSEdkMVBESGljZ3NMazFtZDhtYTNIS1hWM0hJdzZrdUV6R0hQb1gxNHo4YWF6RFFZWndUR3ZxVGlPLUdRUlVDZUJueVo4bVhyWnRmSjNqVk83UUhXcEx3MlM1VDVwVTRwcE0xQXppWTFxUDVfY3ZpOTNZT2Zrb09PalRTX3V3RENZWGFxWjB5bTJHYlEiCiAgICAgICAgfQogICAgXQp9Cg==" diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-route-filter.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-route-filter.yaml new file mode 100644 index 0000000000..e0a3128bed --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-route-filter.yaml @@ -0,0 +1,12 @@ +apiVersion: consul.hashicorp.com/v1alpha1 +kind: HTTPRouteAuthFilter +metadata: + name: example-route-jwt-filter +spec: + type: JWT + JWTProviders: + - Provider: "local" + VerifyClaims: + - Path: + - "role" + Value: "doctor" diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/kustomization.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/kustomization.yaml new file mode 100644 index 0000000000..3dc38a090c --- /dev/null +++ b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/kustomization.yaml @@ -0,0 +1,12 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ../../../bases/api-gateway + - ../../static-server-inject + - ./httproute.yaml + - ./jwt-provider.yaml + +patchesStrategicMerge: + - httproute-no-auth.yaml + - api-gateway.yaml From a86533b117bbda4711bbcb0947aab86a45e8f230 Mon Sep 17 00:00:00 2001 From: Michael Zalimeni Date: Thu, 10 Aug 2023 11:28:50 -0400 Subject: [PATCH 321/340] [NET-5217] Apply K8s node locality to services and sidecars (#2748) Apply K8s node locality to services and sidecars Locality-aware routing is based on proxy locality rather than the proxied service. Ensure we propagate locality to both when registering services. --- .changelog/2748.txt | 3 +++ .../controllers/endpoints/endpoints_controller.go | 2 ++ .../controllers/endpoints/endpoints_controller_test.go | 5 +++++ 3 files changed, 10 insertions(+) create mode 100644 .changelog/2748.txt diff --git a/.changelog/2748.txt b/.changelog/2748.txt new file mode 100644 index 0000000000..2a8c922d13 --- /dev/null +++ b/.changelog/2748.txt @@ -0,0 +1,3 @@ +```release-note:bug +control-plane: Set locality on sidecar proxies in addition to services when registering with connect-inject. +``` diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go index cdf56b187f..3f139c2662 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go @@ -535,6 +535,8 @@ func (r *Controller) createServiceRegistrations(pod corev1.Pod, serviceEndpoints Namespace: consulNS, Proxy: proxyConfig, Tags: tags, + // Sidecar locality (not proxied service locality) is used for locality-aware routing. + Locality: locality, } // A user can enable/disable tproxy for an entire namespace. diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go index 477be49e9f..2cec69dd0a 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go @@ -2021,6 +2021,10 @@ func TestReconcileCreateEndpoint(t *testing.T) { "envoy_telemetry_collector_bind_socket_dir": "/consul/connect-inject", }, }, + ServiceLocality: &api.Locality{ + Region: "us-west-1", + Zone: "us-west-1a", + }, ServiceMeta: map[string]string{ "name": "abc", "version": "2", @@ -2225,6 +2229,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { require.Equal(t, tt.expectedProxySvcInstances[i].ServicePort, instance.ServicePort) require.Equal(t, tt.expectedProxySvcInstances[i].ServiceMeta, instance.ServiceMeta) require.Equal(t, tt.expectedProxySvcInstances[i].ServiceTags, instance.ServiceTags) + require.Equal(t, tt.expectedProxySvcInstances[i].ServiceLocality, instance.ServiceLocality) if tt.nodeMeta != nil { require.Equal(t, tt.expectedProxySvcInstances[i].NodeMeta, instance.NodeMeta) } From 0100fa4f066a184de8ae11976ef70b5afa85fe39 Mon Sep 17 00:00:00 2001 From: Melisa Griffin Date: Fri, 11 Aug 2023 10:46:44 -0400 Subject: [PATCH 322/340] Adds changelog for release of 1.1.4 (#2754) --- CHANGELOG.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d60f91b67..aa47db462e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,37 @@ +## 1.1.4 (Aug 10, 2023) + +SECURITY: + +* Upgrade to use Go 1.20.6 and `x/net/http` 0.12.0. + This resolves [CVE-2023-29406](https://github.com/advisories/GHSA-f8f7-69v5-w4vx)(`net/http`). [[GH-2642](https://github.com/hashicorp/consul-k8s/issues/2642)] +* Upgrade to use Go 1.20.7 and `x/net` 0.13.0. + This resolves [CVE-2023-29409](https://nvd.nist.gov/vuln/detail/CVE-2023-29409)(`crypto/tls`) + and [CVE-2023-3978](https://nvd.nist.gov/vuln/detail/CVE-2023-3978)(`net/html`). [[GH-2710](https://github.com/hashicorp/consul-k8s/issues/2710)] + +IMPROVEMENTS: + +* Add support to provide the logLevel flag via helm for multiple low level components. Introduces the following fields +1. `global.acls.logLevel` +2. `global.tls.logLevel` +3. `global.federation.logLevel` +4. `global.gossipEncryption.logLevel` +5. `server.logLevel` +6. `client.logLevel` +7. `meshGateway.logLevel` +8. `ingressGateways.logLevel` +9. `terminatingGateways.logLevel` +10. `telemetryCollector.logLevel` [[GH-2302](https://github.com/hashicorp/consul-k8s/issues/2302)] +* control-plane: increase timeout after login for ACL replication to 60 seconds [[GH-2656](https://github.com/hashicorp/consul-k8s/issues/2656)] +* helm: adds values for `securityContext` and `annotations` on TLS and ACL init/cleanup jobs. [[GH-2525](https://github.com/hashicorp/consul-k8s/issues/2525)] +* helm: do not set container securityContexts by default on OpenShift < 4.11 [[GH-2678](https://github.com/hashicorp/consul-k8s/issues/2678)] +* helm: set container securityContexts to match the `restricted` Pod Security Standards policy to support running Consul in a namespace with restricted PSA enforcement enabled [[GH-2572](https://github.com/hashicorp/consul-k8s/issues/2572)] + +BUG FIXES: + +* control-plane: fix bug in endpoints controller when deregistering services from consul when a node is deleted. [[GH-2571](https://github.com/hashicorp/consul-k8s/issues/2571)] +* helm: fix CONSUL_LOGIN_DATACENTER for consul client-daemonset. [[GH-2652](https://github.com/hashicorp/consul-k8s/issues/2652)] +* helm: fix ui ingress manifest formatting, and exclude `ingressClass` when not defined. [[GH-2687](https://github.com/hashicorp/consul-k8s/issues/2687)] + ## 0.49.8 (July 12, 2023) IMPROVEMENTS: From 6e98cf90d890d74371214fc5b52628e4bfe96cbc Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Fri, 11 Aug 2023 14:29:29 -0400 Subject: [PATCH 323/340] Set privileged to false unless on OpenShift without CNI (#2755) * Set privileged to false unless on OpenShift without CNI --- .changelog/2755.txt | 3 + .../connect-inject/webhook/container_init.go | 10 +++- .../webhook/container_init_test.go | 57 +++++++++++++++---- 3 files changed, 56 insertions(+), 14 deletions(-) create mode 100644 .changelog/2755.txt diff --git a/.changelog/2755.txt b/.changelog/2755.txt new file mode 100644 index 0000000000..1d8cf20360 --- /dev/null +++ b/.changelog/2755.txt @@ -0,0 +1,3 @@ +```release-note:bug +control-plane: When using transparent proxy or CNI, reduced required permissions by setting privileged to false. Privileged must be true when using OpenShift without CNI. +``` diff --git a/control-plane/connect-inject/webhook/container_init.go b/control-plane/connect-inject/webhook/container_init.go index f180de88a3..88962f771e 100644 --- a/control-plane/connect-inject/webhook/container_init.go +++ b/control-plane/connect-inject/webhook/container_init.go @@ -223,6 +223,12 @@ func (w *MeshWebhook) containerInit(namespace corev1.Namespace, pod corev1.Pod, }) } + // OpenShift without CNI is the only environment where privileged must be true. + privileged := false + if w.EnableOpenShift && !w.EnableCNI { + privileged = true + } + if tproxyEnabled { if !w.EnableCNI { // Set redirect traffic config for the container so that we can apply iptables rules. @@ -243,7 +249,7 @@ func (w *MeshWebhook) containerInit(namespace corev1.Namespace, pod corev1.Pod, RunAsGroup: pointer.Int64(rootUserAndGroupID), // RunAsNonRoot overrides any setting in the Pod so that we can still run as root here as required. RunAsNonRoot: pointer.Bool(false), - Privileged: pointer.Bool(true), + Privileged: pointer.Bool(privileged), Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{netAdminCapability}, }, @@ -253,7 +259,7 @@ func (w *MeshWebhook) containerInit(namespace corev1.Namespace, pod corev1.Pod, RunAsUser: pointer.Int64(initContainersUserAndGroupID), RunAsGroup: pointer.Int64(initContainersUserAndGroupID), RunAsNonRoot: pointer.Bool(true), - Privileged: pointer.Bool(false), + Privileged: pointer.Bool(privileged), Capabilities: &corev1.Capabilities{ Drop: []corev1.Capability{"ALL"}, }, diff --git a/control-plane/connect-inject/webhook/container_init_test.go b/control-plane/connect-inject/webhook/container_init_test.go index fd89d7eba6..fa2a95dbf9 100644 --- a/control-plane/connect-inject/webhook/container_init_test.go +++ b/control-plane/connect-inject/webhook/container_init_test.go @@ -176,77 +176,104 @@ func TestHandlerContainerInit_transparentProxy(t *testing.T) { annotations map[string]string expTproxyEnabled bool namespaceLabel map[string]string + openShiftEnabled bool }{ - "enabled globally, ns not set, annotation not provided, cni disabled": { + "enabled globally, ns not set, annotation not provided, cni disabled, openshift disabled": { true, false, nil, true, nil, + false, }, - "enabled globally, ns not set, annotation is false, cni disabled": { + "enabled globally, ns not set, annotation is false, cni disabled, openshift disabled": { true, false, map[string]string{constants.KeyTransparentProxy: "false"}, false, nil, + false, }, - "enabled globally, ns not set, annotation is true, cni disabled": { + "enabled globally, ns not set, annotation is true, cni disabled, openshift disabled": { true, false, map[string]string{constants.KeyTransparentProxy: "true"}, true, nil, + false, }, - "disabled globally, ns not set, annotation not provided, cni disabled": { + "disabled globally, ns not set, annotation not provided, cni disabled, openshift disabled": { false, false, nil, false, nil, + false, }, - "disabled globally, ns not set, annotation is false, cni disabled": { + "disabled globally, ns not set, annotation is false, cni disabled, openshift disabled": { false, false, map[string]string{constants.KeyTransparentProxy: "false"}, false, nil, + false, }, - "disabled globally, ns not set, annotation is true, cni disabled": { + "disabled globally, ns not set, annotation is true, cni disabled, openshift disabled": { false, false, map[string]string{constants.KeyTransparentProxy: "true"}, true, nil, + false, }, - "disabled globally, ns enabled, annotation not set, cni disabled": { + "disabled globally, ns enabled, annotation not set, cni disabled, openshift disabled": { false, false, nil, true, map[string]string{constants.KeyTransparentProxy: "true"}, + false, }, - "enabled globally, ns disabled, annotation not set, cni disabled": { + "enabled globally, ns disabled, annotation not set, cni disabled, openshift disabled": { true, false, nil, false, map[string]string{constants.KeyTransparentProxy: "false"}, + false, }, - "disabled globally, ns enabled, annotation not set, cni enabled": { + "disabled globally, ns enabled, annotation not set, cni enabled, openshift disabled": { false, true, nil, false, map[string]string{constants.KeyTransparentProxy: "true"}, + false, }, - "enabled globally, ns not set, annotation not set, cni enabled": { + "enabled globally, ns not set, annotation not set, cni enabled, openshift disabled": { + true, + true, + nil, + false, + nil, + false, + }, + "enabled globally, ns not set, annotation not set, cni enabled, openshift enabled": { true, true, nil, false, nil, + true, + }, + "enabled globally, ns not set, annotation not set, cni disabled, openshift enabled": { + true, + false, + nil, + true, + nil, + true, }, } for name, c := range cases { @@ -255,17 +282,23 @@ func TestHandlerContainerInit_transparentProxy(t *testing.T) { EnableTransparentProxy: c.globalEnabled, EnableCNI: c.cniEnabled, ConsulConfig: &consul.Config{HTTPPort: 8500}, + EnableOpenShift: c.openShiftEnabled, } pod := minimal() pod.Annotations = c.annotations + privileged := false + if c.openShiftEnabled && !c.cniEnabled { + privileged = true + } + var expectedSecurityContext *corev1.SecurityContext if c.cniEnabled { expectedSecurityContext = &corev1.SecurityContext{ RunAsUser: pointer.Int64(initContainersUserAndGroupID), RunAsGroup: pointer.Int64(initContainersUserAndGroupID), RunAsNonRoot: pointer.Bool(true), - Privileged: pointer.Bool(false), + Privileged: pointer.Bool(privileged), Capabilities: &corev1.Capabilities{ Drop: []corev1.Capability{"ALL"}, }, @@ -275,7 +308,7 @@ func TestHandlerContainerInit_transparentProxy(t *testing.T) { RunAsUser: pointer.Int64(0), RunAsGroup: pointer.Int64(0), RunAsNonRoot: pointer.Bool(false), - Privileged: pointer.Bool(true), + Privileged: pointer.Bool(privileged), Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{netAdminCapability}, }, From b57b9369c61ee60b4f2a48a1bf0abf7a71cd8e8a Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Fri, 11 Aug 2023 16:21:09 -0400 Subject: [PATCH 324/340] Update consul-enterprise-version script to add -ent (#2756) --- .../build-support/scripts/consul-enterprise-version.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/control-plane/build-support/scripts/consul-enterprise-version.sh b/control-plane/build-support/scripts/consul-enterprise-version.sh index 6b48bb4678..37df85dfc5 100755 --- a/control-plane/build-support/scripts/consul-enterprise-version.sh +++ b/control-plane/build-support/scripts/consul-enterprise-version.sh @@ -4,8 +4,10 @@ FILE=$1 VERSION=$(yq .global.image $FILE) -if [[ !"${VERSION}" == *"consul:"* ]]; then +if [[ !"${VERSION}" == *"hashicorppreview/consul:"* ]]; then VERSION=$(echo ${VERSION} | sed "s/consul:/consul-enterprise:/g") +elif [[ !"${VERSION}" == *"hashicorp/consul:"* ]]; then + VERSION=$(echo ${VERSION} | sed "s/consul:/consul-enterprise:/g" | sed "s/$/-ent/g") fi echo "${VERSION}" From 1968df4fdc7cc54b782f450f1c5fb4f569aa9322 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Fri, 11 Aug 2023 15:35:14 -0700 Subject: [PATCH 325/340] Automate the k8s sameness tests add peering (#2725) * added fixtures * removed fixtures - intentions only gets added now if acls are enabled - payment-service-resolver is only for locality aware which isn't in scope for this PR * updated sameness tests to include peering - refactored with some helper functions for members (now TestClusters) - made names more uniform, tend more towards the cluster-01-a/cluster-02-a/etc. nomenclature * added 4 clusters to cni make target * disable proxy lifecycle --- Makefile | 4 + .../kustomization.yaml | 0 .../cluster-01-a-default-ns/sameness.yaml | 14 + .../kustomization.yaml | 5 + .../cluster-01-b-default-ns/sameness.yaml | 14 + .../kustomization.yaml | 5 + .../sameness.yaml | 7 +- .../kustomization.yaml | 5 + .../cluster-03-a-default-ns/sameness.yaml | 14 + .../sameness/override-ns/intentions.yaml | 15 - .../sameness/override-ns/kustomization.yaml | 2 - .../override-ns/payment-service-resolver.yaml | 16 - .../cluster-01-a-dialer/kustomization.yaml | 6 + .../peering-dialer-cluster-02-a.yaml | 13 + .../peering-dialer-cluster-03-a.yaml | 13 + .../cluster-01-b-dialer/kustomization.yaml | 6 + .../peering-dialer-cluster-02-a.yaml | 13 + .../peering-dialer-cluster-03-a.yaml | 13 + .../cluster-02-a-acceptor/kustomization.yaml | 6 + .../peering-acceptor-cluster-01-a.yaml | 13 + .../peering-acceptor-cluster-01-b.yaml | 13 + .../cluster-02-a-dialer/kustomization.yaml | 5 + .../peering-dialer-cluster-03-a.yaml | 13 + .../cluster-03-a-acceptor/kustomization.yaml | 7 + .../peering-acceptor-cluster-01-a.yaml | 13 + .../peering-acceptor-cluster-01-b.yaml | 13 + .../peering-acceptor-cluster-02-a.yaml | 13 + .../peering/{ => mesh}/kustomization.yaml | 0 .../sameness/peering/{ => mesh}/mesh.yaml | 0 .../cluster-01-a-acceptor/kustomization.yaml | 8 + .../sameness/cluster-01-a-acceptor/patch.yaml | 13 + .../cluster-01-b-acceptor/kustomization.yaml | 8 + .../sameness/cluster-01-b-acceptor/patch.yaml | 13 + .../cluster-02-a-acceptor/kustomization.yaml | 8 + .../sameness/cluster-02-a-acceptor/patch.yaml | 13 + .../cluster-03-a-acceptor/kustomization.yaml | 8 + .../sameness/cluster-03-a-acceptor/patch.yaml | 13 + .../ap1-partition/patch.yaml | 4 +- .../default-partition/patch.yaml | 4 +- .../kustomization.yaml | 0 .../{partition => ap1-partition}/patch.yaml | 0 .../kustomization.yaml | 0 .../{default => default-partition}/patch.yaml | 0 .../kustomization.yaml | 0 .../{default => dc1-default}/patch.yaml | 0 .../kustomization.yaml | 0 .../{partition => dc1-partition}/patch.yaml | 0 .../static-server/dc2/kustomization.yaml | 8 + .../sameness/static-server/dc2/patch.yaml | 23 + .../static-server/dc3/kustomization.yaml | 8 + .../sameness/static-server/dc3/patch.yaml | 23 + acceptance/tests/sameness/main_test.go | 2 +- acceptance/tests/sameness/sameness_test.go | 578 +++++++++++++----- 53 files changed, 812 insertions(+), 183 deletions(-) rename acceptance/tests/fixtures/bases/sameness/{default-ns => cluster-01-a-default-ns}/kustomization.yaml (100%) create mode 100644 acceptance/tests/fixtures/bases/sameness/cluster-01-a-default-ns/sameness.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/cluster-01-b-default-ns/kustomization.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/cluster-01-b-default-ns/sameness.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/cluster-02-a-default-ns/kustomization.yaml rename acceptance/tests/fixtures/bases/sameness/{default-ns => cluster-02-a-default-ns}/sameness.yaml (73%) create mode 100644 acceptance/tests/fixtures/bases/sameness/cluster-03-a-default-ns/kustomization.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/cluster-03-a-default-ns/sameness.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/override-ns/intentions.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/override-ns/payment-service-resolver.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-01-a-dialer/kustomization.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-01-a-dialer/peering-dialer-cluster-02-a.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-01-a-dialer/peering-dialer-cluster-03-a.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-01-b-dialer/kustomization.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-01-b-dialer/peering-dialer-cluster-02-a.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-01-b-dialer/peering-dialer-cluster-03-a.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-acceptor/kustomization.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-acceptor/peering-acceptor-cluster-01-a.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-acceptor/peering-acceptor-cluster-01-b.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-dialer/kustomization.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-dialer/peering-dialer-cluster-03-a.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/kustomization.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/peering-acceptor-cluster-01-a.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/peering-acceptor-cluster-01-b.yaml create mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/peering-acceptor-cluster-02-a.yaml rename acceptance/tests/fixtures/bases/sameness/peering/{ => mesh}/kustomization.yaml (100%) rename acceptance/tests/fixtures/bases/sameness/peering/{ => mesh}/mesh.yaml (100%) create mode 100644 acceptance/tests/fixtures/cases/sameness/cluster-01-a-acceptor/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/cluster-01-a-acceptor/patch.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/cluster-01-b-acceptor/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/cluster-01-b-acceptor/patch.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/cluster-02-a-acceptor/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/cluster-02-a-acceptor/patch.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/cluster-03-a-acceptor/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/cluster-03-a-acceptor/patch.yaml rename acceptance/tests/fixtures/cases/sameness/static-client/{default => ap1-partition}/kustomization.yaml (100%) rename acceptance/tests/fixtures/cases/sameness/static-client/{partition => ap1-partition}/patch.yaml (100%) rename acceptance/tests/fixtures/cases/sameness/static-client/{partition => default-partition}/kustomization.yaml (100%) rename acceptance/tests/fixtures/cases/sameness/static-client/{default => default-partition}/patch.yaml (100%) rename acceptance/tests/fixtures/cases/sameness/static-server/{default => dc1-default}/kustomization.yaml (100%) rename acceptance/tests/fixtures/cases/sameness/static-server/{default => dc1-default}/patch.yaml (100%) rename acceptance/tests/fixtures/cases/sameness/static-server/{partition => dc1-partition}/kustomization.yaml (100%) rename acceptance/tests/fixtures/cases/sameness/static-server/{partition => dc1-partition}/patch.yaml (100%) create mode 100644 acceptance/tests/fixtures/cases/sameness/static-server/dc2/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/static-server/dc2/patch.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/static-server/dc3/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/static-server/dc3/patch.yaml diff --git a/Makefile b/Makefile index e9a02a01b3..866a37fbfc 100644 --- a/Makefile +++ b/Makefile @@ -143,6 +143,10 @@ kind-cni: kind-delete make kind-cni-calico kind create cluster --config=$(CURDIR)/acceptance/framework/environment/cni-kind/kind.config --name dc2 --image $(KIND_NODE_IMAGE) make kind-cni-calico + kind create cluster --config=$(CURDIR)/acceptance/framework/environment/cni-kind/kind.config --name dc3 --image $(KIND_NODE_IMAGE) + make kind-cni-calico + kind create cluster --config=$(CURDIR)/acceptance/framework/environment/cni-kind/kind.config --name dc4 --image $(KIND_NODE_IMAGE) + make kind-cni-calico # Helper target for doing local acceptance testing kind: kind-delete diff --git a/acceptance/tests/fixtures/bases/sameness/default-ns/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/cluster-01-a-default-ns/kustomization.yaml similarity index 100% rename from acceptance/tests/fixtures/bases/sameness/default-ns/kustomization.yaml rename to acceptance/tests/fixtures/bases/sameness/cluster-01-a-default-ns/kustomization.yaml diff --git a/acceptance/tests/fixtures/bases/sameness/cluster-01-a-default-ns/sameness.yaml b/acceptance/tests/fixtures/bases/sameness/cluster-01-a-default-ns/sameness.yaml new file mode 100644 index 0000000000..9c43bb505f --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/cluster-01-a-default-ns/sameness.yaml @@ -0,0 +1,14 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: SamenessGroup +metadata: + name: group-01 +spec: + defaultForFailover: true + members: + - partition: default + - partition: ap1 + - peer: cluster-02-a + - peer: cluster-03-a \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/cluster-01-b-default-ns/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/cluster-01-b-default-ns/kustomization.yaml new file mode 100644 index 0000000000..3f9d23c28a --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/cluster-01-b-default-ns/kustomization.yaml @@ -0,0 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - sameness.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/cluster-01-b-default-ns/sameness.yaml b/acceptance/tests/fixtures/bases/sameness/cluster-01-b-default-ns/sameness.yaml new file mode 100644 index 0000000000..bf83338243 --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/cluster-01-b-default-ns/sameness.yaml @@ -0,0 +1,14 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: SamenessGroup +metadata: + name: group-01 +spec: + defaultForFailover: true + members: + - partition: ap1 + - partition: default + - peer: cluster-02-a + - peer: cluster-03-a \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/cluster-02-a-default-ns/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/cluster-02-a-default-ns/kustomization.yaml new file mode 100644 index 0000000000..3f9d23c28a --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/cluster-02-a-default-ns/kustomization.yaml @@ -0,0 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - sameness.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/default-ns/sameness.yaml b/acceptance/tests/fixtures/bases/sameness/cluster-02-a-default-ns/sameness.yaml similarity index 73% rename from acceptance/tests/fixtures/bases/sameness/default-ns/sameness.yaml rename to acceptance/tests/fixtures/bases/sameness/cluster-02-a-default-ns/sameness.yaml index 4d27ed72ae..2ed466585b 100644 --- a/acceptance/tests/fixtures/bases/sameness/default-ns/sameness.yaml +++ b/acceptance/tests/fixtures/bases/sameness/cluster-02-a-default-ns/sameness.yaml @@ -4,12 +4,11 @@ apiVersion: consul.hashicorp.com/v1alpha1 kind: SamenessGroup metadata: - name: mine + name: group-01 spec: + defaultForFailover: true members: - partition: default - - partition: ap1 - peer: cluster-01-a - peer: cluster-01-b - - peer: cluster-02-a - - peer: cluster-03-a + - peer: cluster-03-a \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/cluster-03-a-default-ns/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/cluster-03-a-default-ns/kustomization.yaml new file mode 100644 index 0000000000..3f9d23c28a --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/cluster-03-a-default-ns/kustomization.yaml @@ -0,0 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - sameness.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/cluster-03-a-default-ns/sameness.yaml b/acceptance/tests/fixtures/bases/sameness/cluster-03-a-default-ns/sameness.yaml new file mode 100644 index 0000000000..83a3c1e71a --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/cluster-03-a-default-ns/sameness.yaml @@ -0,0 +1,14 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: SamenessGroup +metadata: + name: group-01 +spec: + defaultForFailover: true + members: + - partition: default + - peer: cluster-01-a + - peer: cluster-01-b + - peer: cluster-02-a \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/override-ns/intentions.yaml b/acceptance/tests/fixtures/bases/sameness/override-ns/intentions.yaml deleted file mode 100644 index ae075c85e4..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/override-ns/intentions.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceIntentions -metadata: - name: static-server -spec: - destination: - name: static-server - sources: - - name: static-client - namespace: ns1 - samenessGroup: mine - action: allow diff --git a/acceptance/tests/fixtures/bases/sameness/override-ns/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/override-ns/kustomization.yaml index adfd1c827b..0646179949 100644 --- a/acceptance/tests/fixtures/bases/sameness/override-ns/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/sameness/override-ns/kustomization.yaml @@ -2,6 +2,4 @@ # SPDX-License-Identifier: MPL-2.0 resources: - - intentions.yaml - - payment-service-resolver.yaml - service-defaults.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/override-ns/payment-service-resolver.yaml b/acceptance/tests/fixtures/bases/sameness/override-ns/payment-service-resolver.yaml deleted file mode 100644 index 4257294c6b..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/override-ns/payment-service-resolver.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceResolver -metadata: - name: static-server -spec: - connectTimeout: 15s - failover: - '*': - samenessGroup: mine - policy: - mode: order-by-locality - regions: - - us-west-2 diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-a-dialer/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-a-dialer/kustomization.yaml new file mode 100644 index 0000000000..cf214eac6c --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-a-dialer/kustomization.yaml @@ -0,0 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - peering-dialer-cluster-02-a.yaml + - peering-dialer-cluster-03-a.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-a-dialer/peering-dialer-cluster-02-a.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-a-dialer/peering-dialer-cluster-02-a.yaml new file mode 100644 index 0000000000..d4c51553f3 --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-a-dialer/peering-dialer-cluster-02-a.yaml @@ -0,0 +1,13 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: PeeringDialer +metadata: + name: cluster-02-a +spec: + peer: + secret: + name: "cluster-02-a-cluster-01-a-peering-token" + key: "data" + backend: "kubernetes" diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-a-dialer/peering-dialer-cluster-03-a.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-a-dialer/peering-dialer-cluster-03-a.yaml new file mode 100644 index 0000000000..e6f9f9a6c9 --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-a-dialer/peering-dialer-cluster-03-a.yaml @@ -0,0 +1,13 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: PeeringDialer +metadata: + name: cluster-03-a +spec: + peer: + secret: + name: "cluster-03-a-cluster-01-a-peering-token" + key: "data" + backend: "kubernetes" diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-b-dialer/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-b-dialer/kustomization.yaml new file mode 100644 index 0000000000..cf214eac6c --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-b-dialer/kustomization.yaml @@ -0,0 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - peering-dialer-cluster-02-a.yaml + - peering-dialer-cluster-03-a.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-b-dialer/peering-dialer-cluster-02-a.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-b-dialer/peering-dialer-cluster-02-a.yaml new file mode 100644 index 0000000000..8f0f7064df --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-b-dialer/peering-dialer-cluster-02-a.yaml @@ -0,0 +1,13 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: PeeringDialer +metadata: + name: cluster-02-a +spec: + peer: + secret: + name: "cluster-02-a-cluster-01-b-peering-token" + key: "data" + backend: "kubernetes" diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-b-dialer/peering-dialer-cluster-03-a.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-b-dialer/peering-dialer-cluster-03-a.yaml new file mode 100644 index 0000000000..27cdd27ff8 --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-b-dialer/peering-dialer-cluster-03-a.yaml @@ -0,0 +1,13 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: PeeringDialer +metadata: + name: cluster-03-a +spec: + peer: + secret: + name: "cluster-03-a-cluster-01-b-peering-token" + key: "data" + backend: "kubernetes" diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-acceptor/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-acceptor/kustomization.yaml new file mode 100644 index 0000000000..4c485ee633 --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-acceptor/kustomization.yaml @@ -0,0 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - peering-acceptor-cluster-01-a.yaml + - peering-acceptor-cluster-01-b.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-acceptor/peering-acceptor-cluster-01-a.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-acceptor/peering-acceptor-cluster-01-a.yaml new file mode 100644 index 0000000000..b20b61328f --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-acceptor/peering-acceptor-cluster-01-a.yaml @@ -0,0 +1,13 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: PeeringAcceptor +metadata: + name: cluster-01-a +spec: + peer: + secret: + name: "cluster-02-a-cluster-01-a-peering-token" + key: "data" + backend: "kubernetes" \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-acceptor/peering-acceptor-cluster-01-b.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-acceptor/peering-acceptor-cluster-01-b.yaml new file mode 100644 index 0000000000..c2d5c21b37 --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-acceptor/peering-acceptor-cluster-01-b.yaml @@ -0,0 +1,13 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: PeeringAcceptor +metadata: + name: cluster-01-b +spec: + peer: + secret: + name: "cluster-02-a-cluster-01-b-peering-token" + key: "data" + backend: "kubernetes" \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-dialer/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-dialer/kustomization.yaml new file mode 100644 index 0000000000..c90eab30cc --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-dialer/kustomization.yaml @@ -0,0 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - peering-dialer-cluster-03-a.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-dialer/peering-dialer-cluster-03-a.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-dialer/peering-dialer-cluster-03-a.yaml new file mode 100644 index 0000000000..80518a04c2 --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-dialer/peering-dialer-cluster-03-a.yaml @@ -0,0 +1,13 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: PeeringDialer +metadata: + name: cluster-03-a +spec: + peer: + secret: + name: "cluster-03-a-cluster-02-a-peering-token" + key: "data" + backend: "kubernetes" diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/kustomization.yaml new file mode 100644 index 0000000000..543a846805 --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/kustomization.yaml @@ -0,0 +1,7 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - peering-acceptor-cluster-01-a.yaml + - peering-acceptor-cluster-01-b.yaml + - peering-acceptor-cluster-02-a.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/peering-acceptor-cluster-01-a.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/peering-acceptor-cluster-01-a.yaml new file mode 100644 index 0000000000..06c87e15a6 --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/peering-acceptor-cluster-01-a.yaml @@ -0,0 +1,13 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: PeeringAcceptor +metadata: + name: cluster-01-a +spec: + peer: + secret: + name: "cluster-03-a-cluster-01-a-peering-token" + key: "data" + backend: "kubernetes" \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/peering-acceptor-cluster-01-b.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/peering-acceptor-cluster-01-b.yaml new file mode 100644 index 0000000000..0a835ecef5 --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/peering-acceptor-cluster-01-b.yaml @@ -0,0 +1,13 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: PeeringAcceptor +metadata: + name: cluster-01-b +spec: + peer: + secret: + name: "cluster-03-a-cluster-01-b-peering-token" + key: "data" + backend: "kubernetes" \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/peering-acceptor-cluster-02-a.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/peering-acceptor-cluster-02-a.yaml new file mode 100644 index 0000000000..e60ea8b083 --- /dev/null +++ b/acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/peering-acceptor-cluster-02-a.yaml @@ -0,0 +1,13 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: PeeringAcceptor +metadata: + name: cluster-02-a +spec: + peer: + secret: + name: "cluster-03-a-cluster-02-a-peering-token" + key: "data" + backend: "kubernetes" \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/peering/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/peering/mesh/kustomization.yaml similarity index 100% rename from acceptance/tests/fixtures/bases/sameness/peering/kustomization.yaml rename to acceptance/tests/fixtures/bases/sameness/peering/mesh/kustomization.yaml diff --git a/acceptance/tests/fixtures/bases/sameness/peering/mesh.yaml b/acceptance/tests/fixtures/bases/sameness/peering/mesh/mesh.yaml similarity index 100% rename from acceptance/tests/fixtures/bases/sameness/peering/mesh.yaml rename to acceptance/tests/fixtures/bases/sameness/peering/mesh/mesh.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/cluster-01-a-acceptor/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/cluster-01-a-acceptor/kustomization.yaml new file mode 100644 index 0000000000..30ddacd76c --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/cluster-01-a-acceptor/kustomization.yaml @@ -0,0 +1,8 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ../../../bases/sameness/peering/acceptor + +patchesStrategicMerge: + - patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/cluster-01-a-acceptor/patch.yaml b/acceptance/tests/fixtures/cases/sameness/cluster-01-a-acceptor/patch.yaml new file mode 100644 index 0000000000..2746eeef2e --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/cluster-01-a-acceptor/patch.yaml @@ -0,0 +1,13 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: PeeringAcceptor +metadata: + name: acceptor +spec: + peer: + secret: + name: "cluster-01-a-peering-token" + key: "data" + backend: "kubernetes" \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/sameness/cluster-01-b-acceptor/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/cluster-01-b-acceptor/kustomization.yaml new file mode 100644 index 0000000000..30ddacd76c --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/cluster-01-b-acceptor/kustomization.yaml @@ -0,0 +1,8 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ../../../bases/sameness/peering/acceptor + +patchesStrategicMerge: + - patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/cluster-01-b-acceptor/patch.yaml b/acceptance/tests/fixtures/cases/sameness/cluster-01-b-acceptor/patch.yaml new file mode 100644 index 0000000000..9ca48dad0c --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/cluster-01-b-acceptor/patch.yaml @@ -0,0 +1,13 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: PeeringAcceptor +metadata: + name: acceptor +spec: + peer: + secret: + name: "cluster-01-b-peering-token" + key: "data" + backend: "kubernetes" \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/sameness/cluster-02-a-acceptor/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/cluster-02-a-acceptor/kustomization.yaml new file mode 100644 index 0000000000..30ddacd76c --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/cluster-02-a-acceptor/kustomization.yaml @@ -0,0 +1,8 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ../../../bases/sameness/peering/acceptor + +patchesStrategicMerge: + - patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/cluster-02-a-acceptor/patch.yaml b/acceptance/tests/fixtures/cases/sameness/cluster-02-a-acceptor/patch.yaml new file mode 100644 index 0000000000..4343992f8f --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/cluster-02-a-acceptor/patch.yaml @@ -0,0 +1,13 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: PeeringAcceptor +metadata: + name: acceptor +spec: + peer: + secret: + name: "cluster-02-a-peering-token" + key: "data" + backend: "kubernetes" \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/sameness/cluster-03-a-acceptor/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/cluster-03-a-acceptor/kustomization.yaml new file mode 100644 index 0000000000..6a54cd6eab --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/cluster-03-a-acceptor/kustomization.yaml @@ -0,0 +1,8 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ../../../bases/sameness/peering/acceptor + +patchesStrategicMerge: +- patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/cluster-03-a-acceptor/patch.yaml b/acceptance/tests/fixtures/cases/sameness/cluster-03-a-acceptor/patch.yaml new file mode 100644 index 0000000000..1cd49b79d7 --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/cluster-03-a-acceptor/patch.yaml @@ -0,0 +1,13 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: consul.hashicorp.com/v1alpha1 +kind: PeeringAcceptor +metadata: + name: acceptor +spec: + peer: + secret: + name: "cluster-03-a-peering-token" + key: "data" + backend: "kubernetes" \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/sameness/exported-services/ap1-partition/patch.yaml b/acceptance/tests/fixtures/cases/sameness/exported-services/ap1-partition/patch.yaml index d71e8211ba..22fa816fed 100644 --- a/acceptance/tests/fixtures/cases/sameness/exported-services/ap1-partition/patch.yaml +++ b/acceptance/tests/fixtures/cases/sameness/exported-services/ap1-partition/patch.yaml @@ -10,7 +10,7 @@ spec: - name: static-server namespace: ns2 consumers: - - samenessGroup: mine + - samenessGroup: group-01 - name: mesh-gateway consumers: - - samenessGroup: mine + - samenessGroup: group-01 diff --git a/acceptance/tests/fixtures/cases/sameness/exported-services/default-partition/patch.yaml b/acceptance/tests/fixtures/cases/sameness/exported-services/default-partition/patch.yaml index 9bb440637e..4dbacf99e1 100644 --- a/acceptance/tests/fixtures/cases/sameness/exported-services/default-partition/patch.yaml +++ b/acceptance/tests/fixtures/cases/sameness/exported-services/default-partition/patch.yaml @@ -10,7 +10,7 @@ spec: - name: static-server namespace: ns2 consumers: - - samenessGroup: mine + - samenessGroup: group-01 - name: mesh-gateway consumers: - - samenessGroup: mine + - samenessGroup: group-01 diff --git a/acceptance/tests/fixtures/cases/sameness/static-client/default/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition/kustomization.yaml similarity index 100% rename from acceptance/tests/fixtures/cases/sameness/static-client/default/kustomization.yaml rename to acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition/kustomization.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-client/partition/patch.yaml b/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition/patch.yaml similarity index 100% rename from acceptance/tests/fixtures/cases/sameness/static-client/partition/patch.yaml rename to acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition/patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-client/partition/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-client/default-partition/kustomization.yaml similarity index 100% rename from acceptance/tests/fixtures/cases/sameness/static-client/partition/kustomization.yaml rename to acceptance/tests/fixtures/cases/sameness/static-client/default-partition/kustomization.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-client/default/patch.yaml b/acceptance/tests/fixtures/cases/sameness/static-client/default-partition/patch.yaml similarity index 100% rename from acceptance/tests/fixtures/cases/sameness/static-client/default/patch.yaml rename to acceptance/tests/fixtures/cases/sameness/static-client/default-partition/patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-server/default/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-server/dc1-default/kustomization.yaml similarity index 100% rename from acceptance/tests/fixtures/cases/sameness/static-server/default/kustomization.yaml rename to acceptance/tests/fixtures/cases/sameness/static-server/dc1-default/kustomization.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-server/default/patch.yaml b/acceptance/tests/fixtures/cases/sameness/static-server/dc1-default/patch.yaml similarity index 100% rename from acceptance/tests/fixtures/cases/sameness/static-server/default/patch.yaml rename to acceptance/tests/fixtures/cases/sameness/static-server/dc1-default/patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-server/partition/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-server/dc1-partition/kustomization.yaml similarity index 100% rename from acceptance/tests/fixtures/cases/sameness/static-server/partition/kustomization.yaml rename to acceptance/tests/fixtures/cases/sameness/static-server/dc1-partition/kustomization.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-server/partition/patch.yaml b/acceptance/tests/fixtures/cases/sameness/static-server/dc1-partition/patch.yaml similarity index 100% rename from acceptance/tests/fixtures/cases/sameness/static-server/partition/patch.yaml rename to acceptance/tests/fixtures/cases/sameness/static-server/dc1-partition/patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-server/dc2/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-server/dc2/kustomization.yaml new file mode 100644 index 0000000000..c15bfe7ba7 --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/static-server/dc2/kustomization.yaml @@ -0,0 +1,8 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ../../../../bases/static-server + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/sameness/static-server/dc2/patch.yaml b/acceptance/tests/fixtures/cases/sameness/static-server/dc2/patch.yaml new file mode 100644 index 0000000000..07ac3b9aa9 --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/static-server/dc2/patch.yaml @@ -0,0 +1,23 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: static-server +spec: + template: + metadata: + annotations: + "consul.hashicorp.com/connect-inject": "true" + spec: + containers: + - name: static-server + image: docker.mirror.hashicorp.services/hashicorp/http-echo:alpine + args: + - -text="cluster-02-a" + - -listen=:8080 + ports: + - containerPort: 8080 + name: http + serviceAccountName: static-server diff --git a/acceptance/tests/fixtures/cases/sameness/static-server/dc3/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-server/dc3/kustomization.yaml new file mode 100644 index 0000000000..c15bfe7ba7 --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/static-server/dc3/kustomization.yaml @@ -0,0 +1,8 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ../../../../bases/static-server + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/sameness/static-server/dc3/patch.yaml b/acceptance/tests/fixtures/cases/sameness/static-server/dc3/patch.yaml new file mode 100644 index 0000000000..135e7b14fb --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/static-server/dc3/patch.yaml @@ -0,0 +1,23 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: static-server +spec: + template: + metadata: + annotations: + "consul.hashicorp.com/connect-inject": "true" + spec: + containers: + - name: static-server + image: docker.mirror.hashicorp.services/hashicorp/http-echo:alpine + args: + - -text="cluster-03-a" + - -listen=:8080 + ports: + - containerPort: 8080 + name: http + serviceAccountName: static-server diff --git a/acceptance/tests/sameness/main_test.go b/acceptance/tests/sameness/main_test.go index 67e6ee42b7..ded943c6f0 100644 --- a/acceptance/tests/sameness/main_test.go +++ b/acceptance/tests/sameness/main_test.go @@ -23,6 +23,6 @@ func TestMain(m *testing.M) { } else { fmt.Println(fmt.Sprintf("Skipping sameness tests because either -enable-multi-cluster is "+ "not set, the number of clusters did not match the expected count of %d, or --useKind is false. "+ - "Sameness acceptance tests are currently only suopported on Kind clusters", expectedNumberOfClusters)) + "Sameness acceptance tests are currently only supported on Kind clusters", expectedNumberOfClusters)) } } diff --git a/acceptance/tests/sameness/sameness_test.go b/acceptance/tests/sameness/sameness_test.go index 3971ccf27a..629b886d95 100644 --- a/acceptance/tests/sameness/sameness_test.go +++ b/acceptance/tests/sameness/sameness_test.go @@ -7,7 +7,9 @@ import ( "context" "fmt" "strconv" + "strings" "testing" + "time" terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/config" @@ -24,23 +26,32 @@ import ( ) const ( - primaryDatacenterPartition = "ap1" - primaryServerDatacenter = "dc1" - peer1Datacenter = "dc2" - peer2Datacenter = "dc3" - staticClientNamespace = "ns1" - staticServerNamespace = "ns2" - - keyPrimaryServer = "server" - keyPartition = "partition" - keyPeer1 = "peer1" - keyPeer2 = "peer2" + cluster01Partition = "ap1" + cluster01Datacenter = "dc1" + cluster02Datacenter = "dc2" + cluster03Datacenter = "dc3" + staticClientNamespace = "ns1" + staticServerNamespace = "ns2" + + keyCluster01a = "cluster-01-a" + keyCluster01b = "cluster-01-b" + keyCluster02a = "cluster-02-a" + keyCluster03a = "cluster-03-a" + + staticServerName = "static-server" + staticClientName = "static-client" staticServerDeployment = "deploy/static-server" staticClientDeployment = "deploy/static-client" - primaryServerClusterName = "cluster-01-a" - partitionClusterName = "cluster-01-b" + peerName1a = keyCluster01a + peerName1b = keyCluster01b + peerName2a = keyCluster02a + peerName3a = keyCluster03a + + samenessGroupName = "group-01" + + retryTimeout = 5 * time.Minute ) func TestFailover_Connect(t *testing.T) { @@ -127,19 +138,19 @@ func TestFailover_Connect(t *testing.T) { +-------------------------------------------+ */ - members := map[string]*member{ - keyPrimaryServer: {context: env.DefaultContext(t), hasServer: true}, - keyPartition: {context: env.Context(t, 1), hasServer: false}, - keyPeer1: {context: env.Context(t, 2), hasServer: true}, - keyPeer2: {context: env.Context(t, 3), hasServer: true}, + testClusters := clusters{ + keyCluster01a: {name: peerName1a, context: env.DefaultContext(t), hasServer: true, acceptors: []string{peerName2a, peerName3a}}, + keyCluster01b: {name: peerName1b, context: env.Context(t, 1), partition: cluster01Partition, hasServer: false, acceptors: []string{peerName2a, peerName3a}}, + keyCluster02a: {name: peerName2a, context: env.Context(t, 2), hasServer: true, acceptors: []string{peerName3a}}, + keyCluster03a: {name: peerName3a, context: env.Context(t, 3), hasServer: true}, } // Setup Namespaces. - for _, v := range members { + for _, v := range testClusters { createNamespaces(t, cfg, v.context) } - // Create the Default Cluster. + // Create the cluster-01-a. commonHelmValues := map[string]string{ "global.peering.enabled": "true", @@ -150,7 +161,7 @@ func TestFailover_Connect(t *testing.T) { "global.adminPartitions.enabled": "true", - "global.logLevel": "debug", + "global.logLevel": "warn", "global.acls.manageSystemACLs": strconv.FormatBool(c.ACLsEnabled), @@ -161,10 +172,11 @@ func TestFailover_Connect(t *testing.T) { "meshGateway.replicas": "1", "dns.enabled": "true", + "connectInject.sidecarProxy.lifecycle.defaultEnabled": "false", } defaultPartitionHelmValues := map[string]string{ - "global.datacenter": primaryServerDatacenter, + "global.datacenter": cluster01Datacenter, } // On Kind, there are no load balancers but since all clusters @@ -180,39 +192,39 @@ func TestFailover_Connect(t *testing.T) { helpers.MergeMaps(defaultPartitionHelmValues, commonHelmValues) releaseName := helpers.RandomName() - members[keyPrimaryServer].helmCluster = consul.NewHelmCluster(t, defaultPartitionHelmValues, members[keyPrimaryServer].context, cfg, releaseName) - members[keyPrimaryServer].helmCluster.Create(t) + testClusters[keyCluster01a].helmCluster = consul.NewHelmCluster(t, defaultPartitionHelmValues, testClusters[keyCluster01a].context, cfg, releaseName) + testClusters[keyCluster01a].helmCluster.Create(t) // Get the TLS CA certificate and key secret from the server cluster and apply it to the client cluster. caCertSecretName := fmt.Sprintf("%s-consul-ca-cert", releaseName) logger.Logf(t, "retrieving ca cert secret %s from the server cluster and applying to the client cluster", caCertSecretName) - k8s.CopySecret(t, members[keyPrimaryServer].context, members[keyPartition].context, caCertSecretName) + k8s.CopySecret(t, testClusters[keyCluster01a].context, testClusters[keyCluster01b].context, caCertSecretName) - // Create Secondary Partition Cluster which will apply the primary datacenter. + // Create Secondary Partition Cluster (cluster-01-b) which will apply the primary (dc1) datacenter. partitionToken := fmt.Sprintf("%s-consul-partitions-acl-token", releaseName) if c.ACLsEnabled { logger.Logf(t, "retrieving partition token secret %s from the server cluster and applying to the client cluster", partitionToken) - k8s.CopySecret(t, members[keyPrimaryServer].context, members[keyPartition].context, partitionToken) + k8s.CopySecret(t, testClusters[keyCluster01a].context, testClusters[keyCluster01b].context, partitionToken) } partitionServiceName := fmt.Sprintf("%s-consul-expose-servers", releaseName) - partitionSvcAddress := k8s.ServiceHost(t, cfg, members[keyPrimaryServer].context, partitionServiceName) + partitionSvcAddress := k8s.ServiceHost(t, cfg, testClusters[keyCluster01a].context, partitionServiceName) - k8sAuthMethodHost := k8s.KubernetesAPIServerHost(t, cfg, members[keyPartition].context) + k8sAuthMethodHost := k8s.KubernetesAPIServerHost(t, cfg, testClusters[keyCluster01b].context) secondaryPartitionHelmValues := map[string]string{ "global.enabled": "false", - "global.datacenter": primaryServerDatacenter, + "global.datacenter": cluster01Datacenter, - "global.adminPartitions.name": primaryDatacenterPartition, + "global.adminPartitions.name": cluster01Partition, "global.tls.caCert.secretName": caCertSecretName, "global.tls.caCert.secretKey": "tls.crt", "externalServers.enabled": "true", "externalServers.hosts[0]": partitionSvcAddress, - "externalServers.tlsServerName": fmt.Sprintf("server.%s.consul", primaryServerDatacenter), + "externalServers.tlsServerName": fmt.Sprintf("server.%s.consul", cluster01Datacenter), "global.server.enabled": "false", } @@ -231,12 +243,12 @@ func TestFailover_Connect(t *testing.T) { } helpers.MergeMaps(secondaryPartitionHelmValues, commonHelmValues) - members[keyPartition].helmCluster = consul.NewHelmCluster(t, secondaryPartitionHelmValues, members[keyPartition].context, cfg, releaseName) - members[keyPartition].helmCluster.Create(t) + testClusters[keyCluster01b].helmCluster = consul.NewHelmCluster(t, secondaryPartitionHelmValues, testClusters[keyCluster01b].context, cfg, releaseName) + testClusters[keyCluster01b].helmCluster.Create(t) - // Create Peer 1 Cluster. + // Create cluster-02-a Cluster. PeerOneHelmValues := map[string]string{ - "global.datacenter": peer1Datacenter, + "global.datacenter": cluster02Datacenter, } if cfg.UseKind { @@ -246,12 +258,12 @@ func TestFailover_Connect(t *testing.T) { } helpers.MergeMaps(PeerOneHelmValues, commonHelmValues) - members[keyPeer1].helmCluster = consul.NewHelmCluster(t, PeerOneHelmValues, members[keyPeer1].context, cfg, releaseName) - members[keyPeer1].helmCluster.Create(t) + testClusters[keyCluster02a].helmCluster = consul.NewHelmCluster(t, PeerOneHelmValues, testClusters[keyCluster02a].context, cfg, releaseName) + testClusters[keyCluster02a].helmCluster.Create(t) - // Create Peer 2 Cluster. + // Create cluster-03-a Cluster. PeerTwoHelmValues := map[string]string{ - "global.datacenter": peer2Datacenter, + "global.datacenter": cluster03Datacenter, } if cfg.UseKind { @@ -261,138 +273,298 @@ func TestFailover_Connect(t *testing.T) { } helpers.MergeMaps(PeerTwoHelmValues, commonHelmValues) - members[keyPeer2].helmCluster = consul.NewHelmCluster(t, PeerTwoHelmValues, members[keyPeer2].context, cfg, releaseName) - members[keyPeer2].helmCluster.Create(t) + testClusters[keyCluster03a].helmCluster = consul.NewHelmCluster(t, PeerTwoHelmValues, testClusters[keyCluster03a].context, cfg, releaseName) + testClusters[keyCluster03a].helmCluster.Create(t) // Create a ProxyDefaults resource to configure services to use the mesh // gateways and set server and client opts. - for k, v := range members { + for k, v := range testClusters { logger.Logf(t, "applying resources on %s", v.context.KubectlOptions(t).ContextName) // Client will use the client namespace. - members[k].clientOpts = &terratestk8s.KubectlOptions{ + testClusters[k].clientOpts = &terratestk8s.KubectlOptions{ ContextName: v.context.KubectlOptions(t).ContextName, ConfigPath: v.context.KubectlOptions(t).ConfigPath, Namespace: staticClientNamespace, } // Server will use the server namespace. - members[k].serverOpts = &terratestk8s.KubectlOptions{ + testClusters[k].serverOpts = &terratestk8s.KubectlOptions{ ContextName: v.context.KubectlOptions(t).ContextName, ConfigPath: v.context.KubectlOptions(t).ConfigPath, Namespace: staticServerNamespace, } // Sameness Defaults need to be applied first so that the sameness group exists. - applyResources(t, cfg, "../fixtures/bases/mesh-gateway", members[k].context.KubectlOptions(t)) - applyResources(t, cfg, "../fixtures/bases/sameness/default-ns", members[k].context.KubectlOptions(t)) - applyResources(t, cfg, "../fixtures/bases/sameness/override-ns", members[k].serverOpts) + applyResources(t, cfg, "../fixtures/bases/mesh-gateway", v.context.KubectlOptions(t)) + applyResources(t, cfg, "../fixtures/bases/sameness/override-ns", v.serverOpts) // Only assign a client if the cluster is running a Consul server. if v.hasServer { - members[k].client, _ = members[k].helmCluster.SetupConsulClient(t, c.ACLsEnabled) + testClusters[k].client, _ = testClusters[k].helmCluster.SetupConsulClient(t, c.ACLsEnabled) } } - // TODO: Add further setup for peering, right now the rest of this test will only cover Partitions - // Create static server deployments. - logger.Log(t, "creating static-server and static-client deployments") - k8s.DeployKustomize(t, members[keyPrimaryServer].serverOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, - "../fixtures/cases/sameness/static-server/default") - k8s.DeployKustomize(t, members[keyPartition].serverOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, - "../fixtures/cases/sameness/static-server/partition") + // Assign the client default partition client to the partition + testClusters[keyCluster01b].client = testClusters[keyCluster01a].client - // Create static client deployments. - k8s.DeployKustomize(t, members[keyPrimaryServer].clientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, - "../fixtures/cases/sameness/static-client/default") - k8s.DeployKustomize(t, members[keyPartition].clientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, - "../fixtures/cases/sameness/static-client/partition") + // Apply Mesh resource to default partition and peers + for _, v := range testClusters { + if v.hasServer { + applyResources(t, cfg, "../fixtures/bases/sameness/peering/mesh", v.context.KubectlOptions(t)) + } + } - // Verify that both static-server and static-client have been injected and now have 2 containers in server cluster. - // Also get the server IP - for _, labelSelector := range []string{"app=static-server", "app=static-client"} { - podList, err := members[keyPrimaryServer].context.KubernetesClient(t).CoreV1().Pods(metav1.NamespaceAll).List(context.Background(), - metav1.ListOptions{LabelSelector: labelSelector}) - require.NoError(t, err) - require.Len(t, podList.Items, 1) - require.Len(t, podList.Items[0].Spec.Containers, 2) - if labelSelector == "app=static-server" { - ip := &podList.Items[0].Status.PodIP - require.NotNil(t, ip) - logger.Logf(t, "default-static-server-ip: %s", *ip) - members[keyPrimaryServer].staticServerIP = ip + // Peering/Dialer relationship + /* + cluster-01-a cluster-02-a + Dialer -> 2a 1a -> acceptor + Dialer -> 3a 1b -> acceptor + Dialer -> 3a + + cluster-01-b cluster-03-a + Dialer -> 2a 1a -> acceptor + Dialer -> 3a 1b -> acceptor + 2a -> acceptor + */ + for _, v := range []*cluster{testClusters[keyCluster02a], testClusters[keyCluster03a]} { + logger.Logf(t, "creating acceptor on %s", v.name) + // Create an acceptor token on the cluster + applyResources(t, cfg, fmt.Sprintf("../fixtures/bases/sameness/peering/%s-acceptor", v.name), v.context.KubectlOptions(t)) + + // Copy secrets to the necessary peers to be used for dialing later + for _, vv := range testClusters { + if isAcceptor(v.name, vv.acceptors) { + acceptorSecretName := getPeeringAcceptorSecret(t, cfg, v, vv.name) + logger.Logf(t, "acceptor %s created on %s", acceptorSecretName, v.name) + + logger.Logf(t, "copying acceptor token %s from %s to %s", acceptorSecretName, v.name, vv.name) + copySecret(t, cfg, v.context, vv.context, acceptorSecretName) + } } + } - podList, err = members[keyPartition].context.KubernetesClient(t).CoreV1().Pods(metav1.NamespaceAll).List(context.Background(), - metav1.ListOptions{LabelSelector: labelSelector}) - require.NoError(t, err) - require.Len(t, podList.Items, 1) - require.Len(t, podList.Items[0].Spec.Containers, 2) - if labelSelector == "app=static-server" { - ip := &podList.Items[0].Status.PodIP - require.NotNil(t, ip) - logger.Logf(t, "partition-static-server-ip: %s", *ip) - members[keyPartition].staticServerIP = ip + // Create the dialers + for _, v := range []*cluster{testClusters[keyCluster01a], testClusters[keyCluster01b], testClusters[keyCluster02a]} { + applyResources(t, cfg, fmt.Sprintf("../fixtures/bases/sameness/peering/%s-dialer", v.name), v.context.KubectlOptions(t)) + } + + // If ACLs are enabled, we need to create the intentions + if c.ACLsEnabled { + intention := &api.ServiceIntentionsConfigEntry{ + Name: staticServerName, + Kind: api.ServiceIntentions, + Namespace: staticServerNamespace, + Sources: []*api.SourceIntention{ + { + Name: staticClientName, + Namespace: staticClientNamespace, + SamenessGroup: samenessGroupName, + Action: api.IntentionActionAllow, + }, + }, + } + + for _, v := range testClusters { + logger.Logf(t, "creating intentions on server %s", v.name) + _, _, err := v.client.ConfigEntries().Set(intention, &api.WriteOptions{Partition: v.partition}) + require.NoError(t, err) } } logger.Log(t, "creating exported services") - applyResources(t, cfg, "../fixtures/cases/sameness/exported-services/default-partition", members[keyPrimaryServer].context.KubectlOptions(t)) - applyResources(t, cfg, "../fixtures/cases/sameness/exported-services/ap1-partition", members[keyPartition].context.KubectlOptions(t)) + for _, v := range testClusters { + if v.hasServer { + applyResources(t, cfg, "../fixtures/cases/sameness/exported-services/default-partition", v.context.KubectlOptions(t)) + } else { + applyResources(t, cfg, "../fixtures/cases/sameness/exported-services/ap1-partition", v.context.KubectlOptions(t)) + } + } + + // Create sameness group after exporting the services, this will reduce flakiness in an automated test + for _, v := range testClusters { + applyResources(t, cfg, fmt.Sprintf("../fixtures/bases/sameness/%s-default-ns", v.name), v.context.KubectlOptions(t)) + } // Setup DNS. - dnsService, err := members[keyPrimaryServer].context.KubernetesClient(t).CoreV1().Services("default").Get(context.Background(), fmt.Sprintf("%s-%s", releaseName, "consul-dns"), metav1.GetOptions{}) - require.NoError(t, err) - dnsIP := dnsService.Spec.ClusterIP - logger.Logf(t, "dnsIP: %s", dnsIP) + for _, v := range testClusters { + dnsService, err := v.context.KubernetesClient(t).CoreV1().Services("default").Get(context.Background(), fmt.Sprintf("%s-%s", releaseName, "consul-dns"), metav1.GetOptions{}) + require.NoError(t, err) + v.dnsIP = &dnsService.Spec.ClusterIP + logger.Logf(t, "%s dnsIP: %s", v.name, *v.dnsIP) + } // Setup Prepared Query. definition := &api.PreparedQueryDefinition{ Name: "my-query", Service: api.ServiceQuery{ - Service: "static-server", - SamenessGroup: "mine", + Service: staticServerName, + SamenessGroup: samenessGroupName, Namespace: staticServerNamespace, OnlyPassing: false, }, } - resp, _, err := members[keyPrimaryServer].client.PreparedQuery().Create(definition, &api.WriteOptions{}) - require.NoError(t, err) - logger.Logf(t, "PQ ID: %s", resp) - - logger.Log(t, "all infrastructure up and running") - logger.Log(t, "verifying failover scenarios") - - const dnsLookup = "static-server.service.ns2.ns.mine.sg.consul" - const dnsPQLookup = "my-query.query.consul" - - // Verify initial server. - serviceFailoverCheck(t, primaryServerClusterName, members[keyPrimaryServer]) - // Verify initial dns. - dnsFailoverCheck(t, releaseName, dnsIP, dnsLookup, members[keyPrimaryServer], members[keyPrimaryServer]) + for k, v := range testClusters { + if v.hasServer { + pqID, _, err := v.client.PreparedQuery().Create(definition, &api.WriteOptions{}) + require.NoError(t, err) + logger.Logf(t, "%s PQ ID: %s", v.name, pqID) + testClusters[k].pqID = &pqID + testClusters[k].pqName = &definition.Name + } + } - // Verify initial dns with PQ. - dnsFailoverCheck(t, releaseName, dnsIP, dnsPQLookup, members[keyPrimaryServer], members[keyPrimaryServer]) + // Create static server/client after the rest of the config is setup for a more stable testing experience + // Create static server deployments. + logger.Log(t, "creating static-server and static-client deployments") + k8s.DeployKustomize(t, testClusters[keyCluster01a].serverOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, + "../fixtures/cases/sameness/static-server/dc1-default") + k8s.DeployKustomize(t, testClusters[keyCluster01b].serverOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, + "../fixtures/cases/sameness/static-server/dc1-partition") + k8s.DeployKustomize(t, testClusters[keyCluster02a].serverOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, + "../fixtures/cases/sameness/static-server/dc2") + k8s.DeployKustomize(t, testClusters[keyCluster03a].serverOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, + "../fixtures/cases/sameness/static-server/dc3") - // Scale down static-server on the server, will fail over to partition. - k8s.KubectlScale(t, members[keyPrimaryServer].serverOpts, staticServerDeployment, 0) + // Create static client deployments. + k8s.DeployKustomize(t, testClusters[keyCluster01a].clientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, + "../fixtures/cases/sameness/static-client/default-partition") + k8s.DeployKustomize(t, testClusters[keyCluster02a].clientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, + "../fixtures/cases/sameness/static-client/default-partition") + k8s.DeployKustomize(t, testClusters[keyCluster03a].clientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, + "../fixtures/cases/sameness/static-client/default-partition") + k8s.DeployKustomize(t, testClusters[keyCluster01b].clientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, + "../fixtures/cases/sameness/static-client/ap1-partition") + + // Verify that both static-server and static-client have been injected and now have 2 containers in each cluster. + // Also get the server IP + testClusters.setServerIP(t) - // Verify failover to partition. - serviceFailoverCheck(t, partitionClusterName, members[keyPrimaryServer]) + // Everything should be up and running now + testClusters.verifyServerUpState(t) + logger.Log(t, "all infrastructure up and running") - // Verify dns failover to partition. - dnsFailoverCheck(t, releaseName, dnsIP, dnsLookup, members[keyPrimaryServer], members[keyPartition]) + // Verify all the failover Scenarios + logger.Log(t, "verifying failover scenarios") - // Verify prepared query failover. - dnsFailoverCheck(t, releaseName, dnsIP, dnsPQLookup, members[keyPrimaryServer], members[keyPartition]) + subCases := []struct { + name string + server *cluster + failovers []struct { + failoverServer *cluster + expectedPQ expectedPQ + } + checkDNSPQ bool + }{ + { + name: "cluster-01-a perspective", // This matches the diagram at the beginning of the test + server: testClusters[keyCluster01a], + failovers: []struct { + failoverServer *cluster + expectedPQ expectedPQ + }{ + {failoverServer: testClusters[keyCluster01a], expectedPQ: expectedPQ{partition: "default", peerName: "", namespace: "ns2"}}, + {failoverServer: testClusters[keyCluster01b], expectedPQ: expectedPQ{partition: "ap1", peerName: "", namespace: "ns2"}}, + {failoverServer: testClusters[keyCluster02a], expectedPQ: expectedPQ{partition: "default", peerName: testClusters[keyCluster02a].name, namespace: "ns2"}}, + {failoverServer: testClusters[keyCluster03a], expectedPQ: expectedPQ{partition: "default", peerName: testClusters[keyCluster03a].name, namespace: "ns2"}}, + }, + checkDNSPQ: true, + }, + { + name: "cluster-01-b partition perspective", + server: testClusters[keyCluster01b], + failovers: []struct { + failoverServer *cluster + expectedPQ expectedPQ + }{ + {failoverServer: testClusters[keyCluster01b], expectedPQ: expectedPQ{partition: "ap1", peerName: "", namespace: "ns2"}}, + {failoverServer: testClusters[keyCluster01a], expectedPQ: expectedPQ{partition: "default", peerName: "", namespace: "ns2"}}, + {failoverServer: testClusters[keyCluster02a], expectedPQ: expectedPQ{partition: "default", peerName: testClusters[keyCluster02a].name, namespace: "ns2"}}, + {failoverServer: testClusters[keyCluster03a], expectedPQ: expectedPQ{partition: "default", peerName: testClusters[keyCluster03a].name, namespace: "ns2"}}, + }, + checkDNSPQ: false, + }, + { + name: "cluster-02-a perspective", + server: testClusters[keyCluster02a], + failovers: []struct { + failoverServer *cluster + expectedPQ expectedPQ + }{ + {failoverServer: testClusters[keyCluster02a], expectedPQ: expectedPQ{partition: "default", peerName: "", namespace: "ns2"}}, + {failoverServer: testClusters[keyCluster01a], expectedPQ: expectedPQ{partition: "default", peerName: testClusters[keyCluster01a].name, namespace: "ns2"}}, + {failoverServer: testClusters[keyCluster01b], expectedPQ: expectedPQ{partition: "default", peerName: testClusters[keyCluster01b].name, namespace: "ns2"}}, + {failoverServer: testClusters[keyCluster03a], expectedPQ: expectedPQ{partition: "default", peerName: testClusters[keyCluster03a].name, namespace: "ns2"}}, + }, + checkDNSPQ: true, + }, + { + name: "cluster-03-a perspective", + server: testClusters[keyCluster03a], + failovers: []struct { + failoverServer *cluster + expectedPQ expectedPQ + }{ + {failoverServer: testClusters[keyCluster03a], expectedPQ: expectedPQ{partition: "default", peerName: "", namespace: "ns2"}}, + {failoverServer: testClusters[keyCluster01a], expectedPQ: expectedPQ{partition: "default", peerName: testClusters[keyCluster01a].name, namespace: "ns2"}}, + {failoverServer: testClusters[keyCluster01b], expectedPQ: expectedPQ{partition: "default", peerName: testClusters[keyCluster01b].name, namespace: "ns2"}}, + {failoverServer: testClusters[keyCluster02a], expectedPQ: expectedPQ{partition: "default", peerName: testClusters[keyCluster02a].name, namespace: "ns2"}}, + }, + checkDNSPQ: true, + }, + } - logger.Log(t, "tests complete") + for _, sc := range subCases { + t.Run(sc.name, func(t *testing.T) { + // Reset the scale of all servers + testClusters.resetScale(t) + testClusters.verifyServerUpState(t) + // We're resetting the scale, so make sure we have all the new IP addresses saved + testClusters.setServerIP(t) + + for _, v := range sc.failovers { + // Verify Failover (If this is the first check, then just verifying we're starting with the right server) + logger.Log(t, "checking service failover") + serviceFailoverCheck(t, sc.server, v.failoverServer.name) + + // Verify DNS + if sc.checkDNSPQ { + logger.Log(t, "verifying dns") + dnsFailoverCheck(t, cfg, releaseName, *sc.server.dnsIP, sc.server, v.failoverServer) + + // Verify PQ + logger.Log(t, "verifying prepared query") + preparedQueryFailoverCheck(t, releaseName, *sc.server.dnsIP, v.expectedPQ, sc.server, v.failoverServer) + } else { + // We currently skip running DNS and PQ tests for a couple of reasons + // 1. The admin partition does not contain a server, so DNS service will not resolve on the admin partition cluster + // 2. A workaround to perform the DNS and PQ queries on the primary datacenter cluster by specifying the admin partition + // e.g kubectl --context kind-dc1 --namespace ns1 exec -i deploy/static-client -c static-client \ + // -- dig @test-3lmypr-consul-dns.default static-server.service.ns2.ns.mine.sg.ap1.ap.consul + // is not possible at the moment due to a bug. The workaround will be used once this bug is fixed. + logger.Logf(t, "skipping DNS and PQ checks for %s", sc.name) + } + + // Scale down static-server on the current failover, will fail over to the next. + logger.Logf(t, "scaling server down on %s", v.failoverServer.name) + k8s.KubectlScale(t, v.failoverServer.serverOpts, staticServerDeployment, 0) + } + }) + } }) } } -type member struct { +type expectedPQ struct { + partition string + peerName string + namespace string +} + +type cluster struct { + name string + partition string context environment.TestContext helmCluster *consul.HelmCluster client *api.Client @@ -400,6 +572,56 @@ type member struct { serverOpts *terratestk8s.KubectlOptions clientOpts *terratestk8s.KubectlOptions staticServerIP *string + pqID *string + pqName *string + dnsIP *string + acceptors []string +} + +type clusters map[string]*cluster + +func (c clusters) resetScale(t *testing.T) { + for _, v := range c { + k8s.KubectlScale(t, v.serverOpts, staticServerDeployment, 1) + } +} + +// setServerIP makes sure everything is up and running and then saves the +// static-server IP to the appropriate cluster. IP addresses can change when +// services are scaled up and down. +func (c clusters) setServerIP(t *testing.T) { + for _, labelSelector := range []string{"app=static-server", "app=static-client"} { + for k, v := range c { + podList, err := v.context.KubernetesClient(t).CoreV1().Pods(metav1.NamespaceAll).List(context.Background(), + metav1.ListOptions{LabelSelector: labelSelector}) + require.NoError(t, err) + require.Len(t, podList.Items, 1) + require.Len(t, podList.Items[0].Spec.Containers, 2) + if labelSelector == "app=static-server" { + ip := &podList.Items[0].Status.PodIP + require.NotNil(t, ip) + logger.Logf(t, "%s-static-server-ip: %s", v.name, *ip) + c[k].staticServerIP = ip + } + } + } +} + +// verifyServerUpState will verify that the static-servers are all up and running as +// expected by curling them from their local datacenters. +func (c clusters) verifyServerUpState(t *testing.T) { + logger.Logf(t, "verifying that static-servers are up") + for _, v := range c { + // Query using a client and expect its own name, no failover should occur + serviceFailoverCheck(t, v, v.name) + } +} + +func copySecret(t *testing.T, cfg *config.TestConfig, sourceContext, destContext environment.TestContext, secretName string) { + k8s.CopySecret(t, sourceContext, destContext, secretName) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + k8s.RunKubectl(t, destContext.KubectlOptions(t), "delete", "secret", secretName) + }) } func createNamespaces(t *testing.T, cfg *config.TestConfig, context environment.TestContext) { @@ -421,37 +643,111 @@ func applyResources(t *testing.T, cfg *config.TestConfig, kustomizeDir string, o // serviceFailoverCheck verifies that the server failed over as expected by checking that curling the `static-server` // using the `static-client` responds with the expected cluster name. Each static-server responds with a uniquue // name so that we can verify failover occured as expected. -func serviceFailoverCheck(t *testing.T, expectedClusterName string, server *member) { - retry.Run(t, func(r *retry.R) { - resp, err := k8s.RunKubectlAndGetOutputE(t, server.clientOpts, "exec", "-i", - staticClientDeployment, "-c", "static-client", "--", "curl", "localhost:8080") +func serviceFailoverCheck(t *testing.T, server *cluster, expectedName string) { + timer := &retry.Timer{Timeout: retryTimeout, Wait: 5 * time.Second} + var resp string + var err error + retry.RunWith(timer, t, func(r *retry.R) { + resp, err = k8s.RunKubectlAndGetOutputE(t, server.clientOpts, "exec", "-i", + staticClientDeployment, "-c", staticClientName, "--", "curl", "localhost:8080") require.NoError(r, err) - assert.Contains(r, resp, expectedClusterName) - logger.Log(t, resp) + assert.Contains(r, resp, expectedName) }) + logger.Log(t, resp) } -func dnsFailoverCheck(t *testing.T, releaseName string, dnsIP string, dnsQuery string, server, failover *member) { - retry.Run(t, func(r *retry.R) { - logs, err := k8s.RunKubectlAndGetOutputE(t, server.clientOpts, "exec", "-i", - staticClientDeployment, "-c", "static-client", "--", "dig", fmt.Sprintf("@%s-consul-dns.default", releaseName), dnsQuery) - require.NoError(r, err) +// preparedQueryFailoverCheck verifies that failover occurs when executing the prepared query. It also assures that +// executing the prepared query via DNS also provides expected results. +func preparedQueryFailoverCheck(t *testing.T, releaseName string, dnsIP string, epq expectedPQ, server, failover *cluster) { + timer := &retry.Timer{Timeout: retryTimeout, Wait: 5 * time.Second} + resp, _, err := server.client.PreparedQuery().Execute(*server.pqID, &api.QueryOptions{Namespace: staticServerNamespace, Partition: server.partition}) + require.NoError(t, err) + require.Len(t, resp.Nodes, 1) + + assert.Equal(t, epq.partition, resp.Nodes[0].Service.Partition) + assert.Equal(t, epq.peerName, resp.Nodes[0].Service.PeerName) + assert.Equal(t, epq.namespace, resp.Nodes[0].Service.Namespace) + assert.Equal(t, *failover.staticServerIP, resp.Nodes[0].Service.Address) + + // Verify that dns lookup is successful, there is no guarantee that the ip address is unique, so for PQ this is + // just verifying that we can query using DNS and that the ip address is correct. It does not however prove + // that failover occured, that is left to client `Execute` + dnsPQLookup := []string{fmt.Sprintf("%s.query.consul", *server.pqName)} + retry.RunWith(timer, t, func(r *retry.R) { + logs := dnsQuery(t, releaseName, dnsPQLookup, server, failover) + assert.Contains(r, logs, fmt.Sprintf("SERVER: %s", dnsIP)) + assert.Contains(r, logs, "ANSWER SECTION:") + assert.Contains(r, logs, *failover.staticServerIP) + }) +} - // When the `dig` request is successful, a section of its response looks like the following: - // - // ;; ANSWER SECTION: - // static-server.service.mine.sg.ns2.ns.consul. 0 IN A - // - // ;; Query time: 2 msec - // ;; SERVER: #() - // ;; WHEN: Mon Aug 10 15:02:40 UTC 2020 - // ;; MSG SIZE rcvd: 98 - // - // We assert on the existence of the ANSWER SECTION, The consul-server IPs being present in - // the ANSWER SECTION and the DNS IP mentioned in the SERVER: field +// DNS failover check verifies that failover occurred when querying the DNS. +func dnsFailoverCheck(t *testing.T, cfg *config.TestConfig, releaseName string, dnsIP string, server, failover *cluster) { + timer := &retry.Timer{Timeout: retryTimeout, Wait: 5 * time.Second} + dnsLookup := []string{fmt.Sprintf("static-server.service.ns2.ns.%s.sg.consul", samenessGroupName), "+tcp", "SRV"} + retry.RunWith(timer, t, func(r *retry.R) { + logs := dnsQuery(t, releaseName, dnsLookup, server, failover) assert.Contains(r, logs, fmt.Sprintf("SERVER: %s", dnsIP)) assert.Contains(r, logs, "ANSWER SECTION:") assert.Contains(r, logs, *failover.staticServerIP) + + // Additional checks + // When accessing the SRV record for DNS we can get more information. In the case of Kind, + // the context can be used to determine that failover occured to the expected kubernetes cluster + // hosting Consul + assert.Contains(r, logs, "ADDITIONAL SECTION:") + expectedName := failover.context.KubectlOptions(t).ContextName + if cfg.UseKind { + expectedName = strings.Replace(expectedName, "kind-", "", -1) + } + assert.Contains(r, logs, expectedName) + }) +} + +// dnsQuery performs a dns query with the provided query string. +func dnsQuery(t *testing.T, releaseName string, dnsQuery []string, server, failover *cluster) string { + timer := &retry.Timer{Timeout: retryTimeout, Wait: 1 * time.Second} + var logs string + retry.RunWith(timer, t, func(r *retry.R) { + args := []string{"exec", "-i", + staticClientDeployment, "-c", staticClientName, "--", "dig", fmt.Sprintf("@%s-consul-dns.default", + releaseName)} + args = append(args, dnsQuery...) + var err error + logs, err = k8s.RunKubectlAndGetOutputE(t, server.clientOpts, args...) + require.NoError(r, err) }) + logger.Logf(t, "%s: %s", failover.name, logs) + return logs +} + +// isAcceptor iterates through the provided acceptor list of cluster names and determines if +// any match the provided name. Returns true if a match is found, false otherwise. +func isAcceptor(name string, acceptorList []string) bool { + for _, v := range acceptorList { + if name == v { + return true + } + } + return false +} + +// getPeeringAcceptorSecret assures that the secret is created and retrieves the secret from the provided acceptor. +func getPeeringAcceptorSecret(t *testing.T, cfg *config.TestConfig, server *cluster, acceptorName string) string { + // Ensure the secrets are created. + var acceptorSecretName string + timer := &retry.Timer{Timeout: retryTimeout, Wait: 1 * time.Second} + retry.RunWith(timer, t, func(r *retry.R) { + var err error + acceptorSecretName, err = k8s.RunKubectlAndGetOutputE(t, server.context.KubectlOptions(t), "get", "peeringacceptor", acceptorName, "-o", "jsonpath={.status.secret.name}") + require.NoError(r, err) + require.NotEmpty(r, acceptorSecretName) + }) + + helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + k8s.RunKubectl(t, server.context.KubectlOptions(t), "delete", "secret", acceptorSecretName) + }) + + return acceptorSecretName } From 6e9f4731e76642dbb5d0869cdf1aa3fa40e1681a Mon Sep 17 00:00:00 2001 From: Melisa Griffin Date: Mon, 14 Aug 2023 11:12:59 -0400 Subject: [PATCH 326/340] Updates changelog to include 1.0.9 (#2758) --- CHANGELOG.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa47db462e..52529c952a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,36 @@ +## 1.0.9 (Aug 10, 2023) + +SECURITY: + +* Upgrade to use Go 1.19.11 and `x/net/http` 0.12.0. + This resolves [CVE-2023-29406](https://github.com/advisories/GHSA-f8f7-69v5-w4vx)(`net/http`). [[GH-2650](https://github.com/hashicorp/consul-k8s/issues/2650)] +* Upgrade to use Go 1.19.12 and `x/net` 0.13.0. + This resolves [CVE-2023-29409](https://nvd.nist.gov/vuln/detail/CVE-2023-29409)(`crypto/tls`) + and [CVE-2023-3978](https://nvd.nist.gov/vuln/detail/CVE-2023-3978)(`net/html`). [[GH-2717](https://github.com/hashicorp/consul-k8s/issues/2717)] + +IMPROVEMENTS: + +* Add support to provide the logLevel flag via helm for multiple low level components. Introduces the following fields +1. `global.acls.logLevel` +2. `global.tls.logLevel` +3. `global.federation.logLevel` +4. `global.gossipEncryption.logLevel` +5. `server.logLevel` +6. `client.logLevel` +7. `meshGateway.logLevel` +8. `ingressGateways.logLevel` +9. `terminatingGateways.logLevel` [[GH-2302](https://github.com/hashicorp/consul-k8s/issues/2302)] +* control-plane: increase timeout after login for ACL replication to 60 seconds [[GH-2656](https://github.com/hashicorp/consul-k8s/issues/2656)] +* helm: adds values for `securityContext` and `annotations` on TLS and ACL init/cleanup jobs. [[GH-2525](https://github.com/hashicorp/consul-k8s/issues/2525)] +* helm: do not set container securityContexts by default on OpenShift < 4.11 [[GH-2678](https://github.com/hashicorp/consul-k8s/issues/2678)] +* helm: set container securityContexts to match the `restricted` Pod Security Standards policy to support running Consul in a namespace with restricted PSA enforcement enabled [[GH-2572](https://github.com/hashicorp/consul-k8s/issues/2572)] + +BUG FIXES: + +* control-plane: fix bug in endpoints controller when deregistering services from consul when a node is deleted. [[GH-2571](https://github.com/hashicorp/consul-k8s/issues/2571)] +* helm: fix CONSUL_LOGIN_DATACENTER for consul client-daemonset. [[GH-2652](https://github.com/hashicorp/consul-k8s/issues/2652)] +* helm: fix ui ingress manifest formatting, and exclude `ingressClass` when not defined. [[GH-2687](https://github.com/hashicorp/consul-k8s/issues/2687)] + ## 1.1.4 (Aug 10, 2023) SECURITY: From ab00c033f386f5f3fdc1614092c1494ca36e6a1b Mon Sep 17 00:00:00 2001 From: Melisa Griffin Date: Tue, 15 Aug 2023 09:37:16 -0400 Subject: [PATCH 327/340] Adds changelog for 1.2.1, reorders 1.1.4 and 1.0.9 (#2768) --- CHANGELOG.md | 71 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 64 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52529c952a..d9a4d76bef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,28 @@ -## 1.0.9 (Aug 10, 2023) +## 1.2.1 (Aug 10, 2023) +BREAKING CHANGES: + +* control-plane: All policies managed by consul-k8s will now be updated on upgrade. If you previously edited the policies after install, your changes will be overwritten. [[GH-2392](https://github.com/hashicorp/consul-k8s/issues/2392)] SECURITY: -* Upgrade to use Go 1.19.11 and `x/net/http` 0.12.0. - This resolves [CVE-2023-29406](https://github.com/advisories/GHSA-f8f7-69v5-w4vx)(`net/http`). [[GH-2650](https://github.com/hashicorp/consul-k8s/issues/2650)] -* Upgrade to use Go 1.19.12 and `x/net` 0.13.0. +* Upgrade to use Go 1.20.6 and `x/net/http` 0.12.0. + This resolves [CVE-2023-29406](https://github.com/advisories/GHSA-f8f7-69v5-w4vx)(`net/http`). [[GH-2642](https://github.com/hashicorp/consul-k8s/issues/2642)] +* Upgrade to use Go 1.20.7 and `x/net` 0.13.0. This resolves [CVE-2023-29409](https://nvd.nist.gov/vuln/detail/CVE-2023-29409)(`crypto/tls`) - and [CVE-2023-3978](https://nvd.nist.gov/vuln/detail/CVE-2023-3978)(`net/html`). [[GH-2717](https://github.com/hashicorp/consul-k8s/issues/2717)] + and [CVE-2023-3978](https://nvd.nist.gov/vuln/detail/CVE-2023-3978)(`net/html`). [[GH-2710](https://github.com/hashicorp/consul-k8s/issues/2710)] + +FEATURES: + +* Add support for configuring graceful shutdown proxy lifecycle management settings. [[GH-2233](https://github.com/hashicorp/consul-k8s/issues/2233)] +* api-gateway: adds ability to map privileged ports on Gateway listeners to unprivileged ports so that containers do not require additional privileges [[GH-2707](https://github.com/hashicorp/consul-k8s/issues/2707)] +* api-gateway: support deploying to OpenShift 4.11 [[GH-2184](https://github.com/hashicorp/consul-k8s/issues/2184)] +* helm: Adds `acls.resources` field which can be configured to override the `resource` settings for the `server-acl-init` and `server-acl-init-cleanup` Jobs. [[GH-2416](https://github.com/hashicorp/consul-k8s/issues/2416)] +* sync-catalog: add ability to support weighted loadbalancing by service annotation `consul.hashicorp.com/service-weight: ` [[GH-2293](https://github.com/hashicorp/consul-k8s/issues/2293)] IMPROVEMENTS: +* (Consul Enterprise) Add support to provide inputs via helm for audit log related configuration [[GH-2370](https://github.com/hashicorp/consul-k8s/issues/2370)] +* (api-gateway) make API gateway controller less verbose [[GH-2524](https://github.com/hashicorp/consul-k8s/issues/2524)] * Add support to provide the logLevel flag via helm for multiple low level components. Introduces the following fields 1. `global.acls.logLevel` 2. `global.tls.logLevel` @@ -19,17 +32,28 @@ IMPROVEMENTS: 6. `client.logLevel` 7. `meshGateway.logLevel` 8. `ingressGateways.logLevel` -9. `terminatingGateways.logLevel` [[GH-2302](https://github.com/hashicorp/consul-k8s/issues/2302)] +9. `terminatingGateways.logLevel` +10. `telemetryCollector.logLevel` [[GH-2302](https://github.com/hashicorp/consul-k8s/issues/2302)] * control-plane: increase timeout after login for ACL replication to 60 seconds [[GH-2656](https://github.com/hashicorp/consul-k8s/issues/2656)] * helm: adds values for `securityContext` and `annotations` on TLS and ACL init/cleanup jobs. [[GH-2525](https://github.com/hashicorp/consul-k8s/issues/2525)] -* helm: do not set container securityContexts by default on OpenShift < 4.11 [[GH-2678](https://github.com/hashicorp/consul-k8s/issues/2678)] * helm: set container securityContexts to match the `restricted` Pod Security Standards policy to support running Consul in a namespace with restricted PSA enforcement enabled [[GH-2572](https://github.com/hashicorp/consul-k8s/issues/2572)] +* helm: update `imageConsulDataplane` value to `hashicorp/consul-dataplane:1.2.0` [[GH-2476](https://github.com/hashicorp/consul-k8s/issues/2476)] +* helm: update `image` value to `hashicorp/consul:1.16.0` [[GH-2476](https://github.com/hashicorp/consul-k8s/issues/2476)] BUG FIXES: +* api-gateway: Fix creation of invalid Kubernetes Service when multiple Gateway listeners have the same port. [[GH-2413](https://github.com/hashicorp/consul-k8s/issues/2413)] +* api-gateway: fix helm install when setting copyAnnotations or nodeSelector [[GH-2597](https://github.com/hashicorp/consul-k8s/issues/2597)] +* api-gateway: fixes bug where envoy will silently reject RSA keys less than 2048 bits in length when not in FIPS mode, and + will reject keys that are not 2048, 3072, or 4096 bits in length in FIPS mode. We now validate + and reject invalid certs earlier. [[GH-2478](https://github.com/hashicorp/consul-k8s/issues/2478)] +* api-gateway: set route condition appropriately when parent ref includes non-existent section name [[GH-2420](https://github.com/hashicorp/consul-k8s/issues/2420)] +* control-plane: Always update ACL policies upon upgrade. [[GH-2392](https://github.com/hashicorp/consul-k8s/issues/2392)] * control-plane: fix bug in endpoints controller when deregistering services from consul when a node is deleted. [[GH-2571](https://github.com/hashicorp/consul-k8s/issues/2571)] * helm: fix CONSUL_LOGIN_DATACENTER for consul client-daemonset. [[GH-2652](https://github.com/hashicorp/consul-k8s/issues/2652)] * helm: fix ui ingress manifest formatting, and exclude `ingressClass` when not defined. [[GH-2687](https://github.com/hashicorp/consul-k8s/issues/2687)] +* transparent-proxy: Fix issue where connect-inject lacked sufficient `mesh:write` privileges in some deployments, + which prevented virtual IPs from persisting properly. [[GH-2520](https://github.com/hashicorp/consul-k8s/issues/2520)] ## 1.1.4 (Aug 10, 2023) @@ -65,6 +89,39 @@ BUG FIXES: * helm: fix CONSUL_LOGIN_DATACENTER for consul client-daemonset. [[GH-2652](https://github.com/hashicorp/consul-k8s/issues/2652)] * helm: fix ui ingress manifest formatting, and exclude `ingressClass` when not defined. [[GH-2687](https://github.com/hashicorp/consul-k8s/issues/2687)] +## 1.0.9 (Aug 10, 2023) + +SECURITY: + +* Upgrade to use Go 1.19.11 and `x/net/http` 0.12.0. + This resolves [CVE-2023-29406](https://github.com/advisories/GHSA-f8f7-69v5-w4vx)(`net/http`). [[GH-2650](https://github.com/hashicorp/consul-k8s/issues/2650)] +* Upgrade to use Go 1.19.12 and `x/net` 0.13.0. + This resolves [CVE-2023-29409](https://nvd.nist.gov/vuln/detail/CVE-2023-29409)(`crypto/tls`) + and [CVE-2023-3978](https://nvd.nist.gov/vuln/detail/CVE-2023-3978)(`net/html`). [[GH-2717](https://github.com/hashicorp/consul-k8s/issues/2717)] + +IMPROVEMENTS: + +* Add support to provide the logLevel flag via helm for multiple low level components. Introduces the following fields +1. `global.acls.logLevel` +2. `global.tls.logLevel` +3. `global.federation.logLevel` +4. `global.gossipEncryption.logLevel` +5. `server.logLevel` +6. `client.logLevel` +7. `meshGateway.logLevel` +8. `ingressGateways.logLevel` +9. `terminatingGateways.logLevel` [[GH-2302](https://github.com/hashicorp/consul-k8s/issues/2302)] +* control-plane: increase timeout after login for ACL replication to 60 seconds [[GH-2656](https://github.com/hashicorp/consul-k8s/issues/2656)] +* helm: adds values for `securityContext` and `annotations` on TLS and ACL init/cleanup jobs. [[GH-2525](https://github.com/hashicorp/consul-k8s/issues/2525)] +* helm: do not set container securityContexts by default on OpenShift < 4.11 [[GH-2678](https://github.com/hashicorp/consul-k8s/issues/2678)] +* helm: set container securityContexts to match the `restricted` Pod Security Standards policy to support running Consul in a namespace with restricted PSA enforcement enabled [[GH-2572](https://github.com/hashicorp/consul-k8s/issues/2572)] + +BUG FIXES: + +* control-plane: fix bug in endpoints controller when deregistering services from consul when a node is deleted. [[GH-2571](https://github.com/hashicorp/consul-k8s/issues/2571)] +* helm: fix CONSUL_LOGIN_DATACENTER for consul client-daemonset. [[GH-2652](https://github.com/hashicorp/consul-k8s/issues/2652)] +* helm: fix ui ingress manifest formatting, and exclude `ingressClass` when not defined. [[GH-2687](https://github.com/hashicorp/consul-k8s/issues/2687)] + ## 0.49.8 (July 12, 2023) IMPROVEMENTS: From 8a5eff010a6ee72a72f9e83ea86bc4e52bbf4b2d Mon Sep 17 00:00:00 2001 From: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Date: Wed, 16 Aug 2023 12:36:38 -0700 Subject: [PATCH 328/340] Mw/net 4260 add tproxy coverage (#2776) * add additional tproxy static-client - this doesn't specify an upstream so that tproxy will be able to handle routing * add tproxy coverage - add control-flow to handle using the virtual host name when tproxy is enabled --- .../ap1-partition-tproxy/kustomization.yaml | 8 +++ .../ap1-partition-tproxy/patch.yaml | 21 ++++++++ .../kustomization.yaml | 8 +++ .../default-partition-tproxy/patch.yaml | 21 ++++++++ acceptance/tests/sameness/sameness_test.go | 49 ++++++++++++++----- 5 files changed, 95 insertions(+), 12 deletions(-) create mode 100644 acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition-tproxy/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition-tproxy/patch.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/static-client/default-partition-tproxy/kustomization.yaml create mode 100644 acceptance/tests/fixtures/cases/sameness/static-client/default-partition-tproxy/patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition-tproxy/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition-tproxy/kustomization.yaml new file mode 100644 index 0000000000..227f223c9f --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition-tproxy/kustomization.yaml @@ -0,0 +1,8 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ../../../../bases/static-client + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition-tproxy/patch.yaml b/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition-tproxy/patch.yaml new file mode 100644 index 0000000000..68f3c8dd91 --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition-tproxy/patch.yaml @@ -0,0 +1,21 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: static-client +spec: + template: + metadata: + annotations: + 'consul.hashicorp.com/connect-inject': 'true' + spec: + containers: + - name: static-client + image: anubhavmishra/tiny-tools:latest + # Just spin & wait forever, we'll use `kubectl exec` to demo + command: ['/bin/sh', '-c', '--'] + args: ['while true; do sleep 30; done;'] + # If ACLs are enabled, the serviceAccountName must match the Consul service name. + serviceAccountName: static-client diff --git a/acceptance/tests/fixtures/cases/sameness/static-client/default-partition-tproxy/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-client/default-partition-tproxy/kustomization.yaml new file mode 100644 index 0000000000..227f223c9f --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/static-client/default-partition-tproxy/kustomization.yaml @@ -0,0 +1,8 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ../../../../bases/static-client + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/sameness/static-client/default-partition-tproxy/patch.yaml b/acceptance/tests/fixtures/cases/sameness/static-client/default-partition-tproxy/patch.yaml new file mode 100644 index 0000000000..e53ef7b509 --- /dev/null +++ b/acceptance/tests/fixtures/cases/sameness/static-client/default-partition-tproxy/patch.yaml @@ -0,0 +1,21 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: static-client +spec: + template: + metadata: + annotations: + 'consul.hashicorp.com/connect-inject': 'true' + spec: + containers: + - name: static-client + image: anubhavmishra/tiny-tools:latest + # Just spin & wait forever, we'll use `kubectl exec` to demo + command: ['/bin/sh', '-c', '--'] + args: ['while true; do sleep 30; done;'] + # If ACLs are enabled, the serviceAccountName must match the Consul service name. + serviceAccountName: static-client \ No newline at end of file diff --git a/acceptance/tests/sameness/sameness_test.go b/acceptance/tests/sameness/sameness_test.go index 629b886d95..b45f771773 100644 --- a/acceptance/tests/sameness/sameness_test.go +++ b/acceptance/tests/sameness/sameness_test.go @@ -428,21 +428,30 @@ func TestFailover_Connect(t *testing.T) { "../fixtures/cases/sameness/static-server/dc3") // Create static client deployments. + staticClientKustomizeDirDefault := "../fixtures/cases/sameness/static-client/default-partition" + staticClientKustomizeDirAP1 := "../fixtures/cases/sameness/static-client/ap1-partition" + + // If transparent proxy is enabled create clients without explicit upstreams + if cfg.EnableTransparentProxy { + staticClientKustomizeDirDefault = fmt.Sprintf("%s-%s", staticClientKustomizeDirDefault, "tproxy") + staticClientKustomizeDirAP1 = fmt.Sprintf("%s-%s", staticClientKustomizeDirAP1, "tproxy") + } + k8s.DeployKustomize(t, testClusters[keyCluster01a].clientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, - "../fixtures/cases/sameness/static-client/default-partition") + staticClientKustomizeDirDefault) k8s.DeployKustomize(t, testClusters[keyCluster02a].clientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, - "../fixtures/cases/sameness/static-client/default-partition") + staticClientKustomizeDirDefault) k8s.DeployKustomize(t, testClusters[keyCluster03a].clientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, - "../fixtures/cases/sameness/static-client/default-partition") + staticClientKustomizeDirDefault) k8s.DeployKustomize(t, testClusters[keyCluster01b].clientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, - "../fixtures/cases/sameness/static-client/ap1-partition") + staticClientKustomizeDirAP1) // Verify that both static-server and static-client have been injected and now have 2 containers in each cluster. // Also get the server IP testClusters.setServerIP(t) // Everything should be up and running now - testClusters.verifyServerUpState(t) + testClusters.verifyServerUpState(t, cfg.EnableTransparentProxy) logger.Log(t, "all infrastructure up and running") // Verify all the failover Scenarios @@ -514,19 +523,23 @@ func TestFailover_Connect(t *testing.T) { checkDNSPQ: true, }, } - for _, sc := range subCases { t.Run(sc.name, func(t *testing.T) { // Reset the scale of all servers testClusters.resetScale(t) - testClusters.verifyServerUpState(t) + testClusters.verifyServerUpState(t, cfg.EnableTransparentProxy) // We're resetting the scale, so make sure we have all the new IP addresses saved testClusters.setServerIP(t) for _, v := range sc.failovers { // Verify Failover (If this is the first check, then just verifying we're starting with the right server) logger.Log(t, "checking service failover") - serviceFailoverCheck(t, sc.server, v.failoverServer.name) + + if cfg.EnableTransparentProxy { + serviceFailoverCheck(t, sc.server, v.failoverServer.name, fmt.Sprintf("http://static-server.virtual.ns2.ns.%s.ap.consul", sc.server.fullTextPartition())) + } else { + serviceFailoverCheck(t, sc.server, v.failoverServer.name, "localhost:8080") + } // Verify DNS if sc.checkDNSPQ { @@ -578,6 +591,14 @@ type cluster struct { acceptors []string } +func (c cluster) fullTextPartition() string { + if c.partition == "" { + return "default" + } else { + return c.partition + } +} + type clusters map[string]*cluster func (c clusters) resetScale(t *testing.T) { @@ -609,11 +630,15 @@ func (c clusters) setServerIP(t *testing.T) { // verifyServerUpState will verify that the static-servers are all up and running as // expected by curling them from their local datacenters. -func (c clusters) verifyServerUpState(t *testing.T) { +func (c clusters) verifyServerUpState(t *testing.T, isTproxyEnabled bool) { logger.Logf(t, "verifying that static-servers are up") for _, v := range c { // Query using a client and expect its own name, no failover should occur - serviceFailoverCheck(t, v, v.name) + if isTproxyEnabled { + serviceFailoverCheck(t, v, v.name, fmt.Sprintf("http://static-server.virtual.ns2.ns.%s.ap.consul", v.fullTextPartition())) + } else { + serviceFailoverCheck(t, v, v.name, "localhost:8080") + } } } @@ -643,13 +668,13 @@ func applyResources(t *testing.T, cfg *config.TestConfig, kustomizeDir string, o // serviceFailoverCheck verifies that the server failed over as expected by checking that curling the `static-server` // using the `static-client` responds with the expected cluster name. Each static-server responds with a uniquue // name so that we can verify failover occured as expected. -func serviceFailoverCheck(t *testing.T, server *cluster, expectedName string) { +func serviceFailoverCheck(t *testing.T, server *cluster, expectedName string, curlAddress string) { timer := &retry.Timer{Timeout: retryTimeout, Wait: 5 * time.Second} var resp string var err error retry.RunWith(timer, t, func(r *retry.R) { resp, err = k8s.RunKubectlAndGetOutputE(t, server.clientOpts, "exec", "-i", - staticClientDeployment, "-c", staticClientName, "--", "curl", "localhost:8080") + staticClientDeployment, "-c", staticClientName, "--", "curl", curlAddress) require.NoError(r, err) assert.Contains(r, resp, expectedName) }) From 48184c6fd2b7fdb1f3dfcbca886de0c56c1fa4af Mon Sep 17 00:00:00 2001 From: Michael Zalimeni Date: Thu, 17 Aug 2023 17:00:05 -0400 Subject: [PATCH 329/340] [NET-2880] Add `PrioritizeByLocality` to `ProxyDefaults` CRD (#2784) Add `PrioritizeByLocality` to `ProxyDefaults` CRD In addition to service resolver, add this field to the CRD for proxy defaults for parity with Consul config options. --- .changelog/2357.txt | 3 -- .changelog/2784.txt | 3 ++ .../consul/templates/crd-proxydefaults.yaml | 9 +++++ .../api/v1alpha1/proxydefaults_types.go | 27 +++++++------ .../api/v1alpha1/proxydefaults_types_test.go | 25 ++++++++++++ .../api/v1alpha1/serviceresolver_types.go | 38 +------------------ .../v1alpha1/serviceresolver_types_test.go | 10 ++--- control-plane/api/v1alpha1/shared_types.go | 31 +++++++++++++++ .../api/v1alpha1/zz_generated.deepcopy.go | 37 ++++++++++-------- .../consul.hashicorp.com_proxydefaults.yaml | 9 +++++ 10 files changed, 120 insertions(+), 72 deletions(-) delete mode 100644 .changelog/2357.txt create mode 100644 .changelog/2784.txt diff --git a/.changelog/2357.txt b/.changelog/2357.txt deleted file mode 100644 index 7cc35f595a..0000000000 --- a/.changelog/2357.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:feature -Add the `PrioritizeByLocality` field to the `ServiceResolver` CRD. -``` diff --git a/.changelog/2784.txt b/.changelog/2784.txt new file mode 100644 index 0000000000..5b11ca3d43 --- /dev/null +++ b/.changelog/2784.txt @@ -0,0 +1,3 @@ +```release-note:feature +Add the `PrioritizeByLocality` field to the `ServiceResolver` and `ProxyDefaults` CRDs. +``` diff --git a/charts/consul/templates/crd-proxydefaults.yaml b/charts/consul/templates/crd-proxydefaults.yaml index 362672c1c1..1e931dd888 100644 --- a/charts/consul/templates/crd-proxydefaults.yaml +++ b/charts/consul/templates/crd-proxydefaults.yaml @@ -160,6 +160,15 @@ spec: type: string type: array type: object + prioritizeByLocality: + description: PrioritizeByLocality contains the configuration for + locality aware routing. + properties: + mode: + description: Mode specifies the behavior of PrioritizeByLocality + routing. Valid values are "", "none", and "failover". + type: string + type: object meshGateway: description: MeshGateway controls the default mesh gateway configuration for this service. diff --git a/control-plane/api/v1alpha1/proxydefaults_types.go b/control-plane/api/v1alpha1/proxydefaults_types.go index 1100cd107a..7b1529b941 100644 --- a/control-plane/api/v1alpha1/proxydefaults_types.go +++ b/control-plane/api/v1alpha1/proxydefaults_types.go @@ -95,6 +95,9 @@ type ProxyDefaultsSpec struct { EnvoyExtensions EnvoyExtensions `json:"envoyExtensions,omitempty"` // FailoverPolicy specifies the exact mechanism used for failover. FailoverPolicy *FailoverPolicy `json:"failoverPolicy,omitempty"` + // PrioritizeByLocality controls whether the locality of services within the + // local partition will be used to prioritize connectivity. + PrioritizeByLocality *PrioritizeByLocality `json:"prioritizeByLocality,omitempty"` } func (in *ProxyDefaults) GetObjectMeta() metav1.ObjectMeta { @@ -179,17 +182,18 @@ func (in *ProxyDefaults) SetLastSyncedTime(time *metav1.Time) { func (in *ProxyDefaults) ToConsul(datacenter string) capi.ConfigEntry { consulConfig := in.convertConfig() return &capi.ProxyConfigEntry{ - Kind: in.ConsulKind(), - Name: in.ConsulName(), - MeshGateway: in.Spec.MeshGateway.toConsul(), - Expose: in.Spec.Expose.toConsul(), - Config: consulConfig, - TransparentProxy: in.Spec.TransparentProxy.toConsul(), - MutualTLSMode: in.Spec.MutualTLSMode.toConsul(), - AccessLogs: in.Spec.AccessLogs.toConsul(), - EnvoyExtensions: in.Spec.EnvoyExtensions.toConsul(), - FailoverPolicy: in.Spec.FailoverPolicy.toConsul(), - Meta: meta(datacenter), + Kind: in.ConsulKind(), + Name: in.ConsulName(), + MeshGateway: in.Spec.MeshGateway.toConsul(), + Expose: in.Spec.Expose.toConsul(), + Config: consulConfig, + TransparentProxy: in.Spec.TransparentProxy.toConsul(), + MutualTLSMode: in.Spec.MutualTLSMode.toConsul(), + AccessLogs: in.Spec.AccessLogs.toConsul(), + EnvoyExtensions: in.Spec.EnvoyExtensions.toConsul(), + FailoverPolicy: in.Spec.FailoverPolicy.toConsul(), + PrioritizeByLocality: in.Spec.PrioritizeByLocality.toConsul(), + Meta: meta(datacenter), } } @@ -228,6 +232,7 @@ func (in *ProxyDefaults) Validate(_ common.ConsulMeta) error { allErrs = append(allErrs, in.Spec.Expose.validate(path.Child("expose"))...) allErrs = append(allErrs, in.Spec.EnvoyExtensions.validate(path.Child("envoyExtensions"))...) allErrs = append(allErrs, in.Spec.FailoverPolicy.validate(path.Child("failoverPolicy"))...) + allErrs = append(allErrs, in.Spec.PrioritizeByLocality.validate(path.Child("prioritizeByLocality"))...) if len(allErrs) > 0 { return apierrors.NewInvalid( diff --git a/control-plane/api/v1alpha1/proxydefaults_types_test.go b/control-plane/api/v1alpha1/proxydefaults_types_test.go index 07f894f322..6a965fce3a 100644 --- a/control-plane/api/v1alpha1/proxydefaults_types_test.go +++ b/control-plane/api/v1alpha1/proxydefaults_types_test.go @@ -98,6 +98,9 @@ func TestProxyDefaults_MatchesConsul(t *testing.T) { Mode: "sequential", Regions: []string{"us-west-1"}, }, + PrioritizeByLocality: &PrioritizeByLocality{ + Mode: "failover", + }, }, }, Theirs: &capi.ProxyConfigEntry{ @@ -161,6 +164,9 @@ func TestProxyDefaults_MatchesConsul(t *testing.T) { Mode: "sequential", Regions: []string{"us-west-1"}, }, + PrioritizeByLocality: &capi.ServiceResolverPrioritizeByLocality{ + Mode: "failover", + }, }, Matches: true, }, @@ -318,6 +324,9 @@ func TestProxyDefaults_ToConsul(t *testing.T) { Mode: "sequential", Regions: []string{"us-west-1"}, }, + PrioritizeByLocality: &PrioritizeByLocality{ + Mode: "none", + }, }, }, Exp: &capi.ProxyConfigEntry{ @@ -382,6 +391,9 @@ func TestProxyDefaults_ToConsul(t *testing.T) { Mode: "sequential", Regions: []string{"us-west-1"}, }, + PrioritizeByLocality: &capi.ServiceResolverPrioritizeByLocality{ + Mode: "none", + }, Meta: map[string]string{ common.SourceKey: common.SourceValue, common.DatacenterKey: "datacenter", @@ -652,6 +664,19 @@ func TestProxyDefaults_Validate(t *testing.T) { }, expectedErrMsg: `proxydefaults.consul.hashicorp.com "global" is invalid: spec.failoverPolicy.mode: Invalid value: "wrong-mode": must be one of "", "sequential", "order-by-locality"`, }, + "prioritize by locality invalid": { + input: &ProxyDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "global", + }, + Spec: ProxyDefaultsSpec{ + PrioritizeByLocality: &PrioritizeByLocality{ + Mode: "wrong-mode", + }, + }, + }, + expectedErrMsg: `proxydefaults.consul.hashicorp.com "global" is invalid: spec.prioritizeByLocality.mode: Invalid value: "wrong-mode": must be one of "", "none", "failover"`, + }, "multi-error": { input: &ProxyDefaults{ ObjectMeta: metav1.ObjectMeta{ diff --git a/control-plane/api/v1alpha1/serviceresolver_types.go b/control-plane/api/v1alpha1/serviceresolver_types.go index 714cd94c2a..3a8e907222 100644 --- a/control-plane/api/v1alpha1/serviceresolver_types.go +++ b/control-plane/api/v1alpha1/serviceresolver_types.go @@ -84,14 +84,7 @@ type ServiceResolverSpec struct { LoadBalancer *LoadBalancer `json:"loadBalancer,omitempty"` // PrioritizeByLocality controls whether the locality of services within the // local partition will be used to prioritize connectivity. - PrioritizeByLocality *ServiceResolverPrioritizeByLocality `json:"prioritizeByLocality,omitempty"` -} - -type ServiceResolverPrioritizeByLocality struct { - // Mode specifies the type of prioritization that will be performed - // when selecting nodes in the local partition. - // Valid values are: "" (default "none"), "none", and "failover". - Mode string `json:"mode,omitempty"` + PrioritizeByLocality *PrioritizeByLocality `json:"prioritizeByLocality,omitempty"` } type ServiceResolverRedirect struct { @@ -536,16 +529,6 @@ func (in *ServiceResolverFailover) toConsul() *capi.ServiceResolverFailover { } } -func (in *ServiceResolverPrioritizeByLocality) toConsul() *capi.ServiceResolverPrioritizeByLocality { - if in == nil { - return nil - } - - return &capi.ServiceResolverPrioritizeByLocality{ - Mode: in.Mode, - } -} - func (in ServiceResolverFailoverTarget) toConsul() capi.ServiceResolverFailoverTarget { return capi.ServiceResolverFailoverTarget{ Service: in.Service, @@ -655,25 +638,6 @@ func (in *ServiceResolverFailover) isEmpty() bool { return in.Service == "" && in.ServiceSubset == "" && in.Namespace == "" && len(in.Datacenters) == 0 && len(in.Targets) == 0 && in.Policy == nil && in.SamenessGroup == "" } -func (in *ServiceResolverPrioritizeByLocality) validate(path *field.Path) field.ErrorList { - var errs field.ErrorList - - if in == nil { - return nil - } - - switch in.Mode { - case "": - case "none": - case "failover": - default: - asJSON, _ := json.Marshal(in) - errs = append(errs, field.Invalid(path, string(asJSON), - "mode must be one of '', 'none', or 'failover'")) - } - return errs -} - func (in *ServiceResolverFailover) validate(path *field.Path, consulMeta common.ConsulMeta) field.ErrorList { var errs field.ErrorList if in.isEmpty() { diff --git a/control-plane/api/v1alpha1/serviceresolver_types_test.go b/control-plane/api/v1alpha1/serviceresolver_types_test.go index 11751aaa2a..3fbc2b4fce 100644 --- a/control-plane/api/v1alpha1/serviceresolver_types_test.go +++ b/control-plane/api/v1alpha1/serviceresolver_types_test.go @@ -66,7 +66,7 @@ func TestServiceResolver_MatchesConsul(t *testing.T) { Datacenter: "redirect_datacenter", Peer: "redirect_peer", }, - PrioritizeByLocality: &ServiceResolverPrioritizeByLocality{ + PrioritizeByLocality: &PrioritizeByLocality{ Mode: "failover", }, Failover: map[string]ServiceResolverFailover{ @@ -285,7 +285,7 @@ func TestServiceResolver_ToConsul(t *testing.T) { Datacenter: "redirect_datacenter", Partition: "redirect_partition", }, - PrioritizeByLocality: &ServiceResolverPrioritizeByLocality{ + PrioritizeByLocality: &PrioritizeByLocality{ Mode: "none", }, Failover: map[string]ServiceResolverFailover{ @@ -898,20 +898,20 @@ func TestServiceResolver_Validate(t *testing.T) { "spec.failover[failB].namespace: Invalid value: \"namespace-b\": Consul Enterprise namespaces must be enabled to set failover.namespace", }, }, - "prioritize by locality none": { + "prioritize by locality invalid": { input: &ServiceResolver{ ObjectMeta: metav1.ObjectMeta{ Name: "foo", }, Spec: ServiceResolverSpec{ - PrioritizeByLocality: &ServiceResolverPrioritizeByLocality{ + PrioritizeByLocality: &PrioritizeByLocality{ Mode: "bad", }, }, }, namespacesEnabled: false, expectedErrMsgs: []string{ - "mode must be one of '', 'none', or 'failover'", + "serviceresolver.consul.hashicorp.com \"foo\" is invalid: spec.prioritizeByLocality.mode: Invalid value: \"bad\": must be one of \"\", \"none\", \"failover\"", }, }, } diff --git a/control-plane/api/v1alpha1/shared_types.go b/control-plane/api/v1alpha1/shared_types.go index aa19c339da..148376a393 100644 --- a/control-plane/api/v1alpha1/shared_types.go +++ b/control-plane/api/v1alpha1/shared_types.go @@ -310,6 +310,37 @@ func (in *FailoverPolicy) validate(path *field.Path) field.ErrorList { return errs } +// PrioritizeByLocality controls whether the locality of services within the +// local partition will be used to prioritize connectivity. +type PrioritizeByLocality struct { + // Mode specifies the type of prioritization that will be performed + // when selecting nodes in the local partition. + // Valid values are: "" (default "none"), "none", and "failover". + Mode string `json:"mode,omitempty"` +} + +func (in *PrioritizeByLocality) toConsul() *capi.ServiceResolverPrioritizeByLocality { + if in == nil { + return nil + } + + return &capi.ServiceResolverPrioritizeByLocality{ + Mode: in.Mode, + } +} + +func (in *PrioritizeByLocality) validate(path *field.Path) field.ErrorList { + var errs field.ErrorList + if in == nil { + return nil + } + modes := []string{"", "none", "failover"} + if !sliceContains(modes, in.Mode) { + errs = append(errs, field.Invalid(path.Child("mode"), in.Mode, notInSliceMessage(modes))) + } + return errs +} + func notInSliceMessage(slice []string) string { return fmt.Sprintf(`must be one of "%s"`, strings.Join(slice, `", "`)) } diff --git a/control-plane/api/v1alpha1/zz_generated.deepcopy.go b/control-plane/api/v1alpha1/zz_generated.deepcopy.go index 05000031ad..5de7c61c4e 100644 --- a/control-plane/api/v1alpha1/zz_generated.deepcopy.go +++ b/control-plane/api/v1alpha1/zz_generated.deepcopy.go @@ -1947,6 +1947,21 @@ func (in *PeeringMeshConfig) DeepCopy() *PeeringMeshConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PrioritizeByLocality) DeepCopyInto(out *PrioritizeByLocality) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrioritizeByLocality. +func (in *PrioritizeByLocality) DeepCopy() *PrioritizeByLocality { + if in == nil { + return nil + } + out := new(PrioritizeByLocality) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ProxyDefaults) DeepCopyInto(out *ProxyDefaults) { *out = *in @@ -2043,6 +2058,11 @@ func (in *ProxyDefaultsSpec) DeepCopyInto(out *ProxyDefaultsSpec) { *out = new(FailoverPolicy) (*in).DeepCopyInto(*out) } + if in.PrioritizeByLocality != nil { + in, out := &in.PrioritizeByLocality, &out.PrioritizeByLocality + *out = new(PrioritizeByLocality) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyDefaultsSpec. @@ -2618,21 +2638,6 @@ func (in *ServiceResolverList) DeepCopyObject() runtime.Object { return nil } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ServiceResolverPrioritizeByLocality) DeepCopyInto(out *ServiceResolverPrioritizeByLocality) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceResolverPrioritizeByLocality. -func (in *ServiceResolverPrioritizeByLocality) DeepCopy() *ServiceResolverPrioritizeByLocality { - if in == nil { - return nil - } - out := new(ServiceResolverPrioritizeByLocality) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServiceResolverRedirect) DeepCopyInto(out *ServiceResolverRedirect) { *out = *in @@ -2679,7 +2684,7 @@ func (in *ServiceResolverSpec) DeepCopyInto(out *ServiceResolverSpec) { } if in.PrioritizeByLocality != nil { in, out := &in.PrioritizeByLocality, &out.PrioritizeByLocality - *out = new(ServiceResolverPrioritizeByLocality) + *out = new(PrioritizeByLocality) **out = **in } } diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml index 7084980bf0..25e61a51ee 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml @@ -156,6 +156,15 @@ spec: type: string type: array type: object + prioritizeByLocality: + description: PrioritizeByLocality contains the configuration for + locality aware routing. + properties: + mode: + description: Mode specifies the behavior of PrioritizeByLocality + routing. Valid values are "", "none", and "failover". + type: string + type: object meshGateway: description: MeshGateway controls the default mesh gateway configuration for this service. From 9f089ec53f201eefb6ca8d87da35dab4138c3db1 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Fri, 18 Aug 2023 11:12:07 -0400 Subject: [PATCH 330/340] AKS 1.24 is deprecated, update to latest 1.25 patch (#2792) --- charts/consul/test/terraform/aks/main.tf | 2 +- charts/consul/test/terraform/aks/variables.tf | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/charts/consul/test/terraform/aks/main.tf b/charts/consul/test/terraform/aks/main.tf index 2683bdc1a7..f9dc36a51c 100644 --- a/charts/consul/test/terraform/aks/main.tf +++ b/charts/consul/test/terraform/aks/main.tf @@ -55,7 +55,7 @@ resource "azurerm_kubernetes_cluster" "default" { location = azurerm_resource_group.default[count.index].location resource_group_name = azurerm_resource_group.default[count.index].name dns_prefix = "consul-k8s-${random_id.suffix[count.index].dec}" - kubernetes_version = "1.24.10" + kubernetes_version = var.kubernetes_version role_based_access_control_enabled = true // We're setting the network plugin and other network properties explicitly diff --git a/charts/consul/test/terraform/aks/variables.tf b/charts/consul/test/terraform/aks/variables.tf index 554d1b0965..9115517029 100644 --- a/charts/consul/test/terraform/aks/variables.tf +++ b/charts/consul/test/terraform/aks/variables.tf @@ -6,6 +6,11 @@ variable "location" { description = "The location to launch this AKS cluster in." } +variable "kubernetes_version" { + default = "1.25.17" + description = "Kubernetes version supported on AKS (1.25.17 Released August 2nd, 2023)" +} + variable "client_id" { default = "" description = "The client ID of the service principal to be used by Kubernetes when creating Azure resources like load balancers." From e5ad4475cccc64f5142e434ea165a5f822ca0eec Mon Sep 17 00:00:00 2001 From: sarahalsmiller <100602640+sarahalsmiller@users.noreply.github.com> Date: Fri, 18 Aug 2023 14:54:45 -0500 Subject: [PATCH 331/340] Net 4889 implement retry feature on the api gateway (#2735) * squash, add support for retry loops and timeouts to api-gateway NET-4889, NET-4890 * Update .changelog/2735.txt Co-authored-by: Andrew Stucki * clean up extra files * delete custom struct, just use client.Object * delete * revert kustomization * lint cleanups * fix merge reversion, last bit of cleanup --------- Co-authored-by: Andrew Stucki --- .changelog/2735.txt | 3 + .../templates/connect-inject-clusterrole.yaml | 2 + .../templates/crd-routeretryfilters.yaml | 117 ++++++++++ .../templates/crd-routetimeoutfilters.yaml | 115 ++++++++++ control-plane/PROJECT | 18 ++ control-plane/api-gateway/binding/result.go | 15 +- .../api-gateway/binding/route_binding.go | 26 +++ .../api-gateway/binding/validation.go | 61 ++++++ control-plane/api-gateway/common/diff.go | 13 +- control-plane/api-gateway/common/helpers.go | 19 ++ control-plane/api-gateway/common/resources.go | 37 ++++ .../api-gateway/common/translation.go | 56 ++++- .../api-gateway/common/translation_test.go | 203 +++++++++++++++++- .../controllers/gateway_controller.go | 115 +++++++++- .../api-gateway/controllers/index.go | 54 +++++ .../api/v1alpha1/routeretryfilter_types.go | 55 +++++ .../api/v1alpha1/routetimeoutfilter_types.go | 52 +++++ .../api/v1alpha1/zz_generated.deepcopy.go | 168 +++++++++++++++ ...onsul.hashicorp.com_routeretryfilters.yaml | 112 ++++++++++ ...sul.hashicorp.com_routetimeoutfilters.yaml | 110 ++++++++++ control-plane/go.mod | 4 +- control-plane/go.sum | 8 +- 22 files changed, 1330 insertions(+), 33 deletions(-) create mode 100644 .changelog/2735.txt create mode 100644 charts/consul/templates/crd-routeretryfilters.yaml create mode 100644 charts/consul/templates/crd-routetimeoutfilters.yaml create mode 100644 control-plane/api/v1alpha1/routeretryfilter_types.go create mode 100644 control-plane/api/v1alpha1/routetimeoutfilter_types.go create mode 100644 control-plane/config/crd/bases/consul.hashicorp.com_routeretryfilters.yaml create mode 100644 control-plane/config/crd/bases/consul.hashicorp.com_routetimeoutfilters.yaml diff --git a/.changelog/2735.txt b/.changelog/2735.txt new file mode 100644 index 0000000000..8b74b5552d --- /dev/null +++ b/.changelog/2735.txt @@ -0,0 +1,3 @@ +```release-note:feature +api-gateway: add RouteRetryFilter and RouteTimeoutFilter CRDs +``` \ No newline at end of file diff --git a/charts/consul/templates/connect-inject-clusterrole.yaml b/charts/consul/templates/connect-inject-clusterrole.yaml index f1f6b3878f..c95b7c143a 100644 --- a/charts/consul/templates/connect-inject-clusterrole.yaml +++ b/charts/consul/templates/connect-inject-clusterrole.yaml @@ -28,6 +28,8 @@ rules: - meshservices - samenessgroups - controlplanerequestlimits + - routeretryfilters + - routetimeoutfilters {{- if .Values.global.peering.enabled }} - peeringacceptors - peeringdialers diff --git a/charts/consul/templates/crd-routeretryfilters.yaml b/charts/consul/templates/crd-routeretryfilters.yaml new file mode 100644 index 0000000000..3c69a5a2ae --- /dev/null +++ b/charts/consul/templates/crd-routeretryfilters.yaml @@ -0,0 +1,117 @@ +{{- if .Values.connectInject.enabled }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: routeretryfilters.consul.hashicorp.com + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd +spec: + group: consul.hashicorp.com + names: + kind: RouteRetryFilter + listKind: RouteRetryFilterList + plural: routeretryfilters + singular: routeretryfilter + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: RouteRetryFilter is the Schema for the routeretryfilters API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: RouteRetryFilterSpec defines the desired state of RouteRetryFilter + properties: + numRetries: + format: int32 + minimum: 0 + type: integer + retryOn: + items: + type: string + type: array + retryOnConnectFailure: + type: boolean + retryOnStatusCodes: + items: + format: int32 + type: integer + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/consul/templates/crd-routetimeoutfilters.yaml b/charts/consul/templates/crd-routetimeoutfilters.yaml new file mode 100644 index 0000000000..992d21b35b --- /dev/null +++ b/charts/consul/templates/crd-routetimeoutfilters.yaml @@ -0,0 +1,115 @@ +{{- if .Values.connectInject.enabled }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: routetimeoutfilters.consul.hashicorp.com + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd +spec: + group: consul.hashicorp.com + names: + kind: RouteTimeoutFilter + listKind: RouteTimeoutFilterList + plural: routetimeoutfilters + singular: routetimeoutfilter + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: RouteTimeoutFilter is the Schema for the httproutetimeoutfilters + API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: RouteTimeoutFilterSpec defines the desired state of RouteTimeoutFilter + properties: + idleTimeout: + description: A Duration represents the elapsed time between two instants + as an int64 nanosecond count. The representation limits the largest + representable duration to approximately 290 years. + format: int64 + type: integer + requestTimeout: + description: A Duration represents the elapsed time between two instants + as an int64 nanosecond count. The representation limits the largest + representable duration to approximately 290 years. + format: int64 + type: integer + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/control-plane/PROJECT b/control-plane/PROJECT index 5338cff047..5a4e24d0f8 100644 --- a/control-plane/PROJECT +++ b/control-plane/PROJECT @@ -108,4 +108,22 @@ resources: kind: ControlPlaneRequestLimit path: github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1beta1 + namespaced: true + controller: true + domain: hashicorp.com + group: consul + kind: RouteRetryFilter + path: github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1 + version: v1alpha1 +- api: + crdVersion: v1beta1 + namespaced: true + controller: true + domain: hashicorp.com + group: consul + kind: RouteTimeoutFilter + path: github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1 + version: v1alpha1 version: "3" diff --git a/control-plane/api-gateway/binding/result.go b/control-plane/api-gateway/binding/result.go index e6c0760e7a..0ba4a257fd 100644 --- a/control-plane/api-gateway/binding/result.go +++ b/control-plane/api-gateway/binding/result.go @@ -25,7 +25,6 @@ var errRefNotPermitted = errors.New("reference not permitted due to lack of Refe var ( // Each of the below are specified in the Gateway spec under RouteConditionReason - // the general usage is that each error is specified as errRoute* where * corresponds // to the RouteConditionReason given in the spec. If a reason is overloaded and can // be used with two different types of things (i.e. something is not found or it's not supported) // then we distinguish those two usages with errRoute*_Usage. @@ -35,6 +34,8 @@ var ( errRouteInvalidKind = errors.New("invalid backend kind") errRouteBackendNotFound = errors.New("backend not found") errRouteNoMatchingParent = errors.New("no matching parent") + errInvalidExternalRefType = errors.New("invalid externalref filter kind") + errExternalRefNotFound = errors.New("ref not found") ) // routeValidationResult holds the result of validating a route globally, in other @@ -176,16 +177,20 @@ func (b bindResults) Condition() metav1.Condition { // if we only have a single binding error, we can get more specific if len(b) == 1 { for _, result := range b { - switch result.err { - case errRouteNoMatchingListenerHostname: + switch { + case errors.Is(result.err, errRouteNoMatchingListenerHostname): // if we have a hostname mismatch error, then use the more specific reason reason = "NoMatchingListenerHostname" - case errRefNotPermitted: + case errors.Is(result.err, errRefNotPermitted): // or if we have a ref not permitted, then use that reason = "RefNotPermitted" - case errRouteNoMatchingParent: + case errors.Is(result.err, errRouteNoMatchingParent): // or if the route declares a parent that we can't find reason = "NoMatchingParent" + case errors.Is(result.err, errExternalRefNotFound): + reason = "FilterNotFound" + case errors.Is(result.err, errInvalidExternalRefType): + reason = "UnsupportedValue" } } } diff --git a/control-plane/api-gateway/binding/route_binding.go b/control-plane/api-gateway/binding/route_binding.go index 8b2e66e761..75a70c2974 100644 --- a/control-plane/api-gateway/binding/route_binding.go +++ b/control-plane/api-gateway/binding/route_binding.go @@ -163,6 +163,31 @@ func (r *Binder) bindRoute(route client.Object, boundCount map[gwv1beta1.Section parent: ref, results: result, }) + + httproute, ok := route.(*gwv1beta1.HTTPRoute) + if ok { + if !externalRefsOnRouteAllExist(httproute, r.config.Resources) { + results = append(results, parentBindResult{ + parent: ref, + results: []bindResult{ + { + err: errExternalRefNotFound, + }, + }, + }) + } + + if !externalRefsKindAllowedOnRoute(httproute) { + results = append(results, parentBindResult{ + parent: ref, + results: []bindResult{ + { + err: errInvalidExternalRefType, + }, + }, + }) + } + } } updated := false @@ -294,6 +319,7 @@ func (r *Binder) mutateRouteWithBindingResults(snapshot *Snapshot, object client for parent := range parents.Iter() { new.Parents = append(new.Parents, parent.(api.ResourceReference)) } + return new }) case *gwv1alpha2.TCPRoute: diff --git a/control-plane/api-gateway/binding/validation.go b/control-plane/api-gateway/binding/validation.go index 6029c10b24..a2726b8bb1 100644 --- a/control-plane/api-gateway/binding/validation.go +++ b/control-plane/api-gateway/binding/validation.go @@ -431,6 +431,67 @@ func routeAllowedForListenerHostname(hostname *gwv1beta1.Hostname, hostnames []g return false } +// externalRefsOnRouteAllExist checks to make sure that all external filters referenced by the route exist in the resource map. +func externalRefsOnRouteAllExist(route *gwv1beta1.HTTPRoute, resources *common.ResourceMap) bool { + for _, rule := range route.Spec.Rules { + for _, filter := range rule.Filters { + if filter.Type != gwv1beta1.HTTPRouteFilterExtensionRef { + continue + } + + if !resources.ExternalFilterExists(*filter.ExtensionRef, route.Namespace) { + return false + } + + } + + for _, backendRef := range rule.BackendRefs { + for _, filter := range backendRef.Filters { + if filter.Type != gwv1beta1.HTTPRouteFilterExtensionRef { + continue + } + + if !resources.ExternalFilterExists(*filter.ExtensionRef, route.Namespace) { + return false + } + } + + } + } + + return true +} + +// externalRefsKindAllowedOnRoute makes sure that all externalRefs reference a kind supported by gatewaycontroller. +func externalRefsKindAllowedOnRoute(route *gwv1beta1.HTTPRoute) bool { + for _, rule := range route.Spec.Rules { + if !filtersAllAllowedType(rule.Filters) { + return false + } + + //same thing but for backendref + for _, backendRef := range rule.BackendRefs { + if !filtersAllAllowedType(backendRef.Filters) { + return false + } + } + } + return true +} + +func filtersAllAllowedType(filters []gwv1beta1.HTTPRouteFilter) bool { + for _, filter := range filters { + if filter.ExtensionRef == nil { + continue + } + + if !common.FilterIsExternalFilter(filter) { + return false + } + } + return true +} + // hostnameMatch checks that an individual hostname matches another hostname for // compatibility. func hostnamesMatch(a gwv1alpha2.Hostname, b gwv1beta1.Hostname) bool { diff --git a/control-plane/api-gateway/common/diff.go b/control-plane/api-gateway/common/diff.go index b58bf23901..a50581ca31 100644 --- a/control-plane/api-gateway/common/diff.go +++ b/control-plane/api-gateway/common/diff.go @@ -149,7 +149,9 @@ func (e entryComparator) httpRouteRulesEqual(a, b api.HTTPRouteRule) bool { return slices.EqualFunc(a.Filters.Headers, b.Filters.Headers, e.httpHeaderFiltersEqual) && bothNilOrEqualFunc(a.Filters.URLRewrite, b.Filters.URLRewrite, e.urlRewritesEqual) && slices.EqualFunc(a.Matches, b.Matches, e.httpMatchesEqual) && - slices.EqualFunc(a.Services, b.Services, e.httpServicesEqual) + slices.EqualFunc(a.Services, b.Services, e.httpServicesEqual) && + bothNilOrEqualFunc(a.Filters.RetryFilter, b.Filters.RetryFilter, e.retryFiltersEqual) && + bothNilOrEqualFunc(a.Filters.TimeoutFilter, b.Filters.TimeoutFilter, e.timeoutFiltersEqual) } func (e entryComparator) httpServicesEqual(a, b api.HTTPService) bool { @@ -190,6 +192,15 @@ func (e entryComparator) urlRewritesEqual(a, b api.URLRewrite) bool { return a.Path == b.Path } +func (e entryComparator) retryFiltersEqual(a, b api.RetryFilter) bool { + return BothNilOrEqual(a.NumRetries, b.NumRetries) && BothNilOrEqual(a.RetryOnConnectFailure, b.RetryOnConnectFailure) && + slices.Equal(a.RetryOn, b.RetryOn) && slices.Equal(a.RetryOnStatusCodes, b.RetryOnStatusCodes) +} + +func (e entryComparator) timeoutFiltersEqual(a, b api.TimeoutFilter) bool { + return a.RequestTimeout == b.RequestTimeout && a.IdleTimeout == b.IdleTimeout +} + func tcpRoutesEqual(a, b *api.TCPRouteConfigEntry) bool { if a == nil || b == nil { return false diff --git a/control-plane/api-gateway/common/helpers.go b/control-plane/api-gateway/common/helpers.go index b0eeb46510..f2ac883571 100644 --- a/control-plane/api-gateway/common/helpers.go +++ b/control-plane/api-gateway/common/helpers.go @@ -4,6 +4,7 @@ package common import ( + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "github.com/hashicorp/consul/api" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -27,6 +28,24 @@ func NilOrEqual[T ~string](v *T, check string) bool { return v == nil || string(*v) == check } +func FilterIsExternalFilter(filter gwv1beta1.HTTPRouteFilter) bool { + if filter.Type != gwv1beta1.HTTPRouteFilterExtensionRef { + return false + } + + if !DerefEqual(&filter.ExtensionRef.Group, v1alpha1.ConsulHashicorpGroup) { + return false + } + + switch filter.ExtensionRef.Kind { + case v1alpha1.RouteRetryFilterKind, v1alpha1.RouteTimeoutFilterKind: + return true + } + + return false + +} + func IndexedNamespacedNameWithDefault[T ~string, U ~string, V ~string](t T, u *U, v V) types.NamespacedName { return types.NamespacedName{ Namespace: DerefStringOr(u, v), diff --git a/control-plane/api-gateway/common/resources.go b/control-plane/api-gateway/common/resources.go index d412c01eee..40fe74bf8d 100644 --- a/control-plane/api-gateway/common/resources.go +++ b/control-plane/api-gateway/common/resources.go @@ -115,6 +115,7 @@ type ResourceMap struct { tcpRouteGateways map[api.ResourceReference]*tcpRoute httpRouteGateways map[api.ResourceReference]*httpRoute gatewayResources map[api.ResourceReference]*resourceSet + externalFilters map[corev1.ObjectReference]client.Object // consul resources for a gateway consulTCPRoutes map[api.ResourceReference]*consulTCPRoute @@ -364,6 +365,42 @@ func (s *ResourceMap) ReferenceCountHTTPRoute(route gwv1beta1.HTTPRoute) { s.httpRouteGateways[consulKey] = set } +func localObjectReferenceToObjectReference(filterRef gwv1beta1.LocalObjectReference, namespace string) corev1.ObjectReference { + return corev1.ObjectReference{ + Kind: string(filterRef.Kind), + Name: string(filterRef.Name), + Namespace: namespace, + } +} + +func objectToObjectReference(object client.Object) corev1.ObjectReference { + return corev1.ObjectReference{ + Kind: object.GetObjectKind().GroupVersionKind().Kind, + Name: object.GetName(), + Namespace: object.GetNamespace(), + } +} + +func (s *ResourceMap) AddExternalFilter(filter client.Object) { + if s.externalFilters == nil { + s.externalFilters = make(map[corev1.ObjectReference]client.Object) + } + + key := objectToObjectReference(filter) + s.externalFilters[key] = filter +} + +func (s *ResourceMap) GetExternalFilter(filterRef gwv1beta1.LocalObjectReference, namespace string) (client.Object, bool) { + key := localObjectReferenceToObjectReference(filterRef, namespace) + filter, ok := s.externalFilters[key] + return filter, ok +} + +func (s *ResourceMap) ExternalFilterExists(filterRef gwv1beta1.LocalObjectReference, namespace string) bool { + _, ok := s.GetExternalFilter(filterRef, namespace) + return ok +} + func (s *ResourceMap) ReferenceCountTCPRoute(route gwv1alpha2.TCPRoute) { key := client.ObjectKeyFromObject(&route) consulKey := NormalizeMeta(s.toConsulReference(api.TCPRoute, key)) diff --git a/control-plane/api-gateway/common/translation.go b/control-plane/api-gateway/common/translation.go index 9303540e82..d3627b11b4 100644 --- a/control-plane/api-gateway/common/translation.go +++ b/control-plane/api-gateway/common/translation.go @@ -151,7 +151,7 @@ func (t ResourceTranslator) ToHTTPRoute(route gwv1beta1.HTTPRoute, resources *Re return t.translateHTTPRouteRule(route, rule, resources) }) - return &api.HTTPRouteConfigEntry{ + configEntry := api.HTTPRouteConfigEntry{ Kind: api.HTTPRoute, Name: route.Name, Namespace: namespace, @@ -163,10 +163,13 @@ func (t ResourceTranslator) ToHTTPRoute(route gwv1beta1.HTTPRoute, resources *Re Hostnames: hostnames, Rules: rules, } + + return &configEntry } func (t ResourceTranslator) translateHTTPRouteRule(route gwv1beta1.HTTPRoute, rule gwv1beta1.HTTPRouteRule, resources *ResourceMap) (api.HTTPRouteRule, bool) { services := ConvertSliceFuncIf(rule.BackendRefs, func(ref gwv1beta1.HTTPBackendRef) (api.HTTPService, bool) { + return t.translateHTTPBackendRef(route, ref, resources) }) @@ -175,7 +178,7 @@ func (t ResourceTranslator) translateHTTPRouteRule(route gwv1beta1.HTTPRoute, ru } matches := ConvertSliceFunc(rule.Matches, t.translateHTTPMatch) - filters := t.translateHTTPFilters(rule.Filters) + filters := t.translateHTTPFilters(rule.Filters, resources, route.Namespace) return api.HTTPRouteRule{ Services: services, @@ -193,9 +196,8 @@ func (t ResourceTranslator) translateHTTPBackendRef(route gwv1beta1.HTTPRoute, r isServiceRef := NilOrEqual(ref.Group, "") && NilOrEqual(ref.Kind, "Service") if isServiceRef && resources.HasService(id) && resources.HTTPRouteCanReferenceBackend(route, ref.BackendRef) { - filters := t.translateHTTPFilters(ref.Filters) + filters := t.translateHTTPFilters(ref.Filters, resources, route.Namespace) service := resources.Service(id) - return api.HTTPService{ Name: service.Name, Namespace: service.Namespace, @@ -207,7 +209,7 @@ func (t ResourceTranslator) translateHTTPBackendRef(route gwv1beta1.HTTPRoute, r isMeshServiceRef := DerefEqual(ref.Group, v1alpha1.ConsulHashicorpGroup) && DerefEqual(ref.Kind, v1alpha1.MeshServiceKind) if isMeshServiceRef && resources.HasMeshService(id) && resources.HTTPRouteCanReferenceBackend(route, ref.BackendRef) { - filters := t.translateHTTPFilters(ref.Filters) + filters := t.translateHTTPFilters(ref.Filters, resources, route.Namespace) service := resources.MeshService(id) return api.HTTPService{ @@ -273,12 +275,14 @@ func (t ResourceTranslator) translateHTTPQueryMatch(match gwv1beta1.HTTPQueryPar } } -func (t ResourceTranslator) translateHTTPFilters(filters []gwv1beta1.HTTPRouteFilter) api.HTTPFilters { +func (t ResourceTranslator) translateHTTPFilters(filters []gwv1beta1.HTTPRouteFilter, resourceMap *ResourceMap, namespace string) api.HTTPFilters { var urlRewrite *api.URLRewrite consulFilter := api.HTTPHeaderFilter{ Add: make(map[string]string), Set: make(map[string]string), } + var retryFilter *api.RetryFilter + var timeoutFilter *api.TimeoutFilter for _, filter := range filters { if filter.RequestHeaderModifier != nil { @@ -299,10 +303,46 @@ func (t ResourceTranslator) translateHTTPFilters(filters []gwv1beta1.HTTPRouteFi filter.URLRewrite.Path.Type == gwv1beta1.PrefixMatchHTTPPathModifier { urlRewrite = &api.URLRewrite{Path: DerefStringOr(filter.URLRewrite.Path.ReplacePrefixMatch, "")} } + + if filter.ExtensionRef != nil { + //get crd from resources map + crdFilter, exists := resourceMap.GetExternalFilter(*filter.ExtensionRef, namespace) + if !exists { + // this should never be the case because we only translate a route if it's actually valid, and if we're missing filters during the validation step, then we won't get here + continue + } + switch filter.ExtensionRef.Kind { + case v1alpha1.RouteRetryFilterKind: + + retryFilterCRD := crdFilter.(*v1alpha1.RouteRetryFilter) + //new filter that needs to be appended + + retryFilter = &api.RetryFilter{ + NumRetries: retryFilterCRD.Spec.NumRetries, + RetryOn: retryFilterCRD.Spec.RetryOn, + RetryOnStatusCodes: retryFilterCRD.Spec.RetryOnStatusCodes, + RetryOnConnectFailure: retryFilterCRD.Spec.RetryOnConnectFailure, + } + + case v1alpha1.RouteTimeoutFilterKind: + + timeoutFilterCRD := crdFilter.(*v1alpha1.RouteTimeoutFilter) + //new filter that needs to be appended + + timeoutFilter = &api.TimeoutFilter{ + RequestTimeout: timeoutFilterCRD.Spec.RequestTimeout, + IdleTimeout: timeoutFilterCRD.Spec.IdleTimeout, + } + + } + } + } return api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{consulFilter}, - URLRewrite: urlRewrite, + Headers: []api.HTTPHeaderFilter{consulFilter}, + URLRewrite: urlRewrite, + RetryFilter: retryFilter, + TimeoutFilter: timeoutFilter, } } diff --git a/control-plane/api-gateway/common/translation_test.go b/control-plane/api-gateway/common/translation_test.go index e8caad76ed..d562845fc8 100644 --- a/control-plane/api-gateway/common/translation_test.go +++ b/control-plane/api-gateway/common/translation_test.go @@ -10,7 +10,9 @@ import ( "crypto/x509/pkix" "encoding/pem" "fmt" + "k8s.io/utils/pointer" "math/big" + "sigs.k8s.io/controller-runtime/pkg/client" "strings" "testing" "time" @@ -354,10 +356,12 @@ func TestTranslator_ToAPIGateway(t *testing.T) { func TestTranslator_ToHTTPRoute(t *testing.T) { t.Parallel() type args struct { - k8sHTTPRoute gwv1beta1.HTTPRoute - services []types.NamespacedName - meshServices []v1alpha1.MeshService + k8sHTTPRoute gwv1beta1.HTTPRoute + services []types.NamespacedName + meshServices []v1alpha1.MeshService + externalFilters []client.Object } + tests := map[string]struct { args args want api.HTTPRouteConfigEntry @@ -1210,6 +1214,193 @@ func TestTranslator_ToHTTPRoute(t *testing.T) { Namespace: "k8s-ns", }, }, + "test with external filters": { + args: args{ + k8sHTTPRoute: gwv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "k8s-http-route", + Namespace: "k8s-ns", + Annotations: map[string]string{}, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + { + Namespace: PointerTo(gwv1beta1.Namespace("k8s-gw-ns")), + Name: gwv1beta1.ObjectName("api-gw"), + Kind: PointerTo(gwv1beta1.Kind("Gateway")), + SectionName: PointerTo(gwv1beta1.SectionName("listener-1")), + }, + }, + }, + Hostnames: []gwv1beta1.Hostname{ + "host-name.example.com", + "consul.io", + }, + Rules: []gwv1beta1.HTTPRouteRule{ + { + Matches: []gwv1beta1.HTTPRouteMatch{ + { + Path: &gwv1beta1.HTTPPathMatch{ + Type: PointerTo(gwv1beta1.PathMatchPathPrefix), + Value: PointerTo("/v1"), + }, + Headers: []gwv1beta1.HTTPHeaderMatch{ + { + Type: PointerTo(gwv1beta1.HeaderMatchExact), + Name: "my header match", + Value: "the value", + }, + }, + QueryParams: []gwv1beta1.HTTPQueryParamMatch{ + { + Type: PointerTo(gwv1beta1.QueryParamMatchExact), + Name: "search", + Value: "term", + }, + }, + Method: PointerTo(gwv1beta1.HTTPMethodGet), + }, + }, + Filters: []gwv1beta1.HTTPRouteFilter{ + { + ExtensionRef: &gwv1beta1.LocalObjectReference{ + Name: "test", + Kind: v1alpha1.RouteRetryFilterKind, + Group: "consul.hashicorp.com/v1alpha1", + }, + }, + }, + BackendRefs: []gwv1beta1.HTTPBackendRef{ + { + BackendRef: gwv1beta1.BackendRef{ + BackendObjectReference: gwv1beta1.BackendObjectReference{ + Name: "service one", + Namespace: PointerTo(gwv1beta1.Namespace("other")), + }, + Weight: PointerTo(int32(45)), + }, + Filters: []gwv1beta1.HTTPRouteFilter{ + { + ExtensionRef: &gwv1beta1.LocalObjectReference{ + Name: "test", + Kind: v1alpha1.RouteRetryFilterKind, + Group: "consul.hashicorp.com/v1alpha1", + }, + }, + }, + }, + }, + }, + }, + }, + }, + services: []types.NamespacedName{ + {Name: "service one", Namespace: "other"}, + }, + externalFilters: []client.Object{ + &v1alpha1.RouteRetryFilter{ + TypeMeta: metav1.TypeMeta{ + Kind: v1alpha1.RouteRetryFilterKind, + APIVersion: "consul.hashicorp.com/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "k8s-ns", + }, + Spec: v1alpha1.RouteRetryFilterSpec{ + NumRetries: pointer.Uint32(3), + RetryOn: []string{"cancelled"}, + RetryOnStatusCodes: []uint32{500, 502}, + RetryOnConnectFailure: pointer.Bool(false), + }, + }, + + &v1alpha1.RouteRetryFilter{ + TypeMeta: metav1.TypeMeta{ + Kind: v1alpha1.RouteRetryFilterKind, + APIVersion: "consul.hashicorp.com/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "other-namespace-even-though-same-name", + }, + Spec: v1alpha1.RouteRetryFilterSpec{ + NumRetries: pointer.Uint32(3), + RetryOn: []string{"don't"}, + RetryOnStatusCodes: []uint32{404}, + RetryOnConnectFailure: pointer.Bool(true), + }, + }, + }, + }, + want: api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, + Name: "k8s-http-route", + Rules: []api.HTTPRouteRule{ + { + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{{Add: map[string]string{}, Set: map[string]string{}}}, + URLRewrite: nil, + RetryFilter: &api.RetryFilter{ + NumRetries: pointer.Uint32(3), + RetryOn: []string{"cancelled"}, + RetryOnStatusCodes: []uint32{500, 502}, + RetryOnConnectFailure: pointer.Bool(false), + }, + }, + Matches: []api.HTTPMatch{ + { + Headers: []api.HTTPHeaderMatch{ + { + Match: api.HTTPHeaderMatchExact, + Name: "my header match", + Value: "the value", + }, + }, + Method: api.HTTPMatchMethodGet, + Path: api.HTTPPathMatch{ + Match: api.HTTPPathMatchPrefix, + Value: "/v1", + }, + Query: []api.HTTPQueryMatch{ + { + Match: api.HTTPQueryMatchExact, + Name: "search", + Value: "term", + }, + }, + }, + }, + Services: []api.HTTPService{ + { + Name: "service one", + Weight: 45, + Filters: api.HTTPFilters{ + Headers: []api.HTTPHeaderFilter{{Add: map[string]string{}, Set: map[string]string{}}}, + RetryFilter: &api.RetryFilter{ + NumRetries: pointer.Uint32(3), + RetryOn: []string{"cancelled"}, + RetryOnStatusCodes: []uint32{500, 502}, + RetryOnConnectFailure: pointer.Bool(false), + }, + }, + Namespace: "other", + }, + }, + }, + }, + Hostnames: []string{ + "host-name.example.com", + "consul.io", + }, + Meta: map[string]string{ + constants.MetaKeyKubeNS: "k8s-ns", + constants.MetaKeyKubeName: "k8s-http-route", + }, + Namespace: "k8s-ns", + }, + }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { @@ -1226,6 +1417,10 @@ func TestTranslator_ToHTTPRoute(t *testing.T) { resources.AddMeshService(service) } + for _, filterToAdd := range tc.args.externalFilters { + resources.AddExternalFilter(filterToAdd) + } + got := tr.ToHTTPRoute(tc.args.k8sHTTPRoute, resources) if diff := cmp.Diff(&tc.want, got); diff != "" { t.Errorf("Translator.ToHTTPRoute() mismatch (-want +got):\n%s", diff) @@ -1444,7 +1639,7 @@ func TestResourceTranslator_translateHTTPFilters(t1 *testing.T) { ConsulPartition: tt.fields.ConsulPartition, Datacenter: tt.fields.Datacenter, } - assert.Equalf(t1, tt.want, t.translateHTTPFilters(tt.args.filters), "translateHTTPFilters(%v)", tt.args.filters) + assert.Equalf(t1, tt.want, t.translateHTTPFilters(tt.args.filters, nil, ""), "translateHTTPFilters(%v)", tt.args.filters) }) } } diff --git a/control-plane/api-gateway/controllers/gateway_controller.go b/control-plane/api-gateway/controllers/gateway_controller.go index 66347adea4..f7ef3af83b 100644 --- a/control-plane/api-gateway/controllers/gateway_controller.go +++ b/control-plane/api-gateway/controllers/gateway_controller.go @@ -5,6 +5,7 @@ package controllers import ( "context" + "fmt" "reflect" "strconv" "strings" @@ -456,6 +457,14 @@ func SetupGatewayControllerWithManager(ctx context.Context, mgr ctrl.Manager, co // Subscribe to changes from Consul for InlineCertificates &source.Channel{Source: c.Subscribe(ctx, api.InlineCertificate, r.transformConsulInlineCertificate(ctx)).Events()}, &handler.EnqueueRequestForObject{}, + ). + Watches( + source.NewKindWithCache((&v1alpha1.RouteRetryFilter{}), mgr.GetCache()), + handler.EnqueueRequestsFromMapFunc(r.transformRouteRetryFilter(ctx)), + ). + Watches( + source.NewKindWithCache((&v1alpha1.RouteTimeoutFilter{}), mgr.GetCache()), + handler.EnqueueRequestsFromMapFunc(r.transformRouteTimeoutFilter(ctx)), ).Complete(r) } @@ -572,6 +581,20 @@ func (r *GatewayController) transformConsulHTTPRoute(ctx context.Context) func(e } } +// transformRouteRetryFilter will return a list of routes that need to be reconciled. +func (r *GatewayController) transformRouteRetryFilter(ctx context.Context) func(object client.Object) []reconcile.Request { + return func(o client.Object) []reconcile.Request { + return r.gatewaysForRoutesReferencing(ctx, "", HTTPRoute_RouteRetryFilterIndex, client.ObjectKeyFromObject(o).String()) + } +} + +// transformTimeoutRetryFilter will return a list of routes that need to be reconciled. +func (r *GatewayController) transformRouteTimeoutFilter(ctx context.Context) func(object client.Object) []reconcile.Request { + return func(o client.Object) []reconcile.Request { + return r.gatewaysForRoutesReferencing(ctx, "", HTTPRoute_RouteTimeoutFilterIndex, client.ObjectKeyFromObject(o).String()) + } +} + func (r *GatewayController) transformConsulTCPRoute(ctx context.Context) func(entry api.ConfigEntry) []types.NamespacedName { return func(entry api.ConfigEntry) []types.NamespacedName { parents := mapset.NewSet() @@ -660,15 +683,17 @@ func (r *GatewayController) transformEndpoints(ctx context.Context) func(o clien func (r *GatewayController) gatewaysForRoutesReferencing(ctx context.Context, tcpIndex, httpIndex, key string) []reconcile.Request { requestSet := make(map[types.NamespacedName]struct{}) - tcpRouteList := &gwv1alpha2.TCPRouteList{} - if err := r.Client.List(ctx, tcpRouteList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(tcpIndex, key), - }); err != nil { - r.Log.Error(err, "unable to list TCPRoutes") - } - for _, route := range tcpRouteList.Items { - for _, ref := range common.ParentRefs(common.BetaGroup, common.KindGateway, route.Namespace, route.Spec.ParentRefs) { - requestSet[ref] = struct{}{} + if tcpIndex != "" { + tcpRouteList := &gwv1alpha2.TCPRouteList{} + if err := r.Client.List(ctx, tcpRouteList, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(tcpIndex, key), + }); err != nil { + r.Log.Error(err, "unable to list TCPRoutes") + } + for _, route := range tcpRouteList.Items { + for _, ref := range common.ParentRefs(common.BetaGroup, common.KindGateway, route.Namespace, route.Spec.ParentRefs) { + requestSet[ref] = struct{}{} + } } } @@ -777,11 +802,81 @@ func (c *GatewayController) getRelatedHTTPRoutes(ctx context.Context, gateway ty for _, route := range list.Items { resources.ReferenceCountHTTPRoute(route) + + _, err := c.getExternalFiltersForHTTPRoute(ctx, route, resources) + if err != nil { + c.Log.Error(err, "unable to list HTTPRoute ExternalFilters") + return nil, err + } } return list.Items, nil } +func (c *GatewayController) getExternalFiltersForHTTPRoute(ctx context.Context, route gwv1beta1.HTTPRoute, resources *common.ResourceMap) ([]interface{}, error) { + var externalFilters []interface{} + for _, rule := range route.Spec.Rules { + ruleFilters, err := c.filterFiltersForExternalRefs(ctx, route, rule.Filters, resources) + if err != nil { + return nil, err + } + externalFilters = append(externalFilters, ruleFilters...) + + for _, backendRef := range rule.BackendRefs { + backendRefFilter, err := c.filterFiltersForExternalRefs(ctx, route, backendRef.Filters, resources) + if err != nil { + return nil, err + } + + externalFilters = append(externalFilters, backendRefFilter...) + } + } + + return externalFilters, nil +} + +func (c *GatewayController) filterFiltersForExternalRefs(ctx context.Context, route gwv1beta1.HTTPRoute, filters []gwv1beta1.HTTPRouteFilter, resources *common.ResourceMap) ([]interface{}, error) { + var externalFilters []interface{} + + for _, filter := range filters { + var externalFilter client.Object + + //check to see if we need to grab this filter + if filter.ExtensionRef == nil { + continue + } + switch kind := filter.ExtensionRef.Kind; kind { + case v1alpha1.RouteRetryFilterKind: + externalFilter = &v1alpha1.RouteRetryFilter{} + case v1alpha1.RouteTimeoutFilterKind: + externalFilter = &v1alpha1.RouteTimeoutFilter{} + default: + continue + } + + //get object from API + err := c.Client.Get(ctx, client.ObjectKey{ + Name: string(filter.ExtensionRef.Name), + Namespace: route.Namespace, + }, externalFilter) + + if err != nil { + if k8serrors.IsNotFound(err) { + c.Log.Info(fmt.Sprintf("externalref %s:%s not found: %v", filter.ExtensionRef.Kind, filter.ExtensionRef.Name, err)) + //ignore, the validation call should mark this route as error + continue + } else { + return nil, err + } + } + + //add external ref (or error) to resource map for this route + resources.AddExternalFilter(externalFilter) + externalFilters = append(externalFilters, externalFilter) + } + return externalFilters, nil +} + func (c *GatewayController) getRelatedTCPRoutes(ctx context.Context, gateway types.NamespacedName, resources *common.ResourceMap) ([]gwv1alpha2.TCPRoute, error) { var list gwv1alpha2.TCPRouteList @@ -945,6 +1040,7 @@ func (c *GatewayController) fetchMeshService(ctx context.Context, resources *com } func (c *GatewayController) fetchServicesForEndpoints(ctx context.Context, resources *common.ResourceMap, key types.NamespacedName) error { + if shouldIgnore(key.Namespace, c.denyK8sNamespacesSet, c.allowK8sNamespacesSet) { return nil } @@ -972,6 +1068,7 @@ func (c *GatewayController) fetchServicesForEndpoints(ctx context.Context, resou } resources.AddService(key, serviceName(pod, endpoints)) + } } } diff --git a/control-plane/api-gateway/controllers/index.go b/control-plane/api-gateway/controllers/index.go index 7fd13de1de..131ec383e7 100644 --- a/control-plane/api-gateway/controllers/index.go +++ b/control-plane/api-gateway/controllers/index.go @@ -28,6 +28,8 @@ const ( TCPRoute_MeshServiceIndex = "__tcproute_referencing_mesh_service" MeshService_PeerIndex = "__meshservice_referencing_peer" Secret_GatewayIndex = "__secret_referencing_gateway" + HTTPRoute_RouteRetryFilterIndex = "__httproute_referencing_retryfilter" + HTTPRoute_RouteTimeoutFilterIndex = "__httproute_referencing_timeoutfilter" ) // RegisterFieldIndexes registers all of the field indexes for the API gateway controllers. @@ -104,6 +106,16 @@ var indexes = []index{ target: &v1alpha1.MeshService{}, indexerFunc: peersForMeshService, }, + { + name: HTTPRoute_RouteRetryFilterIndex, + target: &gwv1beta1.HTTPRoute{}, + indexerFunc: filtersForHTTPRoute, + }, + { + name: HTTPRoute_RouteTimeoutFilterIndex, + target: &gwv1beta1.HTTPRoute{}, + indexerFunc: filtersForHTTPRoute, + }, } // gatewayClassConfigForGatewayClass creates an index of every GatewayClassConfig referenced by a GatewayClass. @@ -271,3 +283,45 @@ func gatewaysForRoute(namespace string, refs []gwv1beta1.ParentReference, status } return references } + +func filtersForHTTPRoute(o client.Object) []string { + route := o.(*gwv1beta1.HTTPRoute) + filters := []string{} + var nilString *string + + for _, rule := range route.Spec.Rules { + FILTERS_LOOP: + for _, filter := range rule.Filters { + if common.FilterIsExternalFilter(filter) { + //TODO this seems like its type agnostic, so this might just work without having to make + //multiple index functions per custom filter type? + + //index external filters + filter := common.IndexedNamespacedNameWithDefault(string(filter.ExtensionRef.Name), nilString, route.Namespace).String() + for _, member := range filters { + if member == filter { + continue FILTERS_LOOP + } + } + filters = append(filters, filter) + } + } + + //same thing but over the backend refs + BACKEND_LOOP: + for _, ref := range rule.BackendRefs { + for _, filter := range ref.Filters { + if common.FilterIsExternalFilter(filter) { + filter := common.IndexedNamespacedNameWithDefault(string(filter.ExtensionRef.Name), nilString, route.Namespace).String() + for _, member := range filters { + if member == filter { + continue BACKEND_LOOP + } + } + filters = append(filters, filter) + } + } + } + } + return filters +} diff --git a/control-plane/api/v1alpha1/routeretryfilter_types.go b/control-plane/api/v1alpha1/routeretryfilter_types.go new file mode 100644 index 0000000000..79fa85c608 --- /dev/null +++ b/control-plane/api/v1alpha1/routeretryfilter_types.go @@ -0,0 +1,55 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func init() { + SchemeBuilder.Register(&RouteRetryFilter{}, &RouteRetryFilterList{}) +} + +const RouteRetryFilterKind = "RouteRetryFilter" + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// RouteRetryFilter is the Schema for the routeretryfilters API +// +kubebuilder:printcolumn:name="Synced",type="string",JSONPath=".status.conditions[?(@.type==\"Synced\")].status",description="The sync status of the resource with Consul" +// +kubebuilder:printcolumn:name="Last Synced",type="date",JSONPath=".status.lastSyncedTime",description="The last successful synced time of the resource with Consul" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="The age of the resource" +type RouteRetryFilter struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec RouteRetryFilterSpec `json:"spec,omitempty"` + Status `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// RouteRetryFilterList contains a list of RouteRetryFilter. +type RouteRetryFilterList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []RouteRetryFilter `json:"items"` +} + +// RouteRetryFilterSpec defines the desired state of RouteRetryFilter. +type RouteRetryFilterSpec struct { + // +kubebuilder:validation:Minimum:=0 + // +kubebuilder:validation:Optional + NumRetries *uint32 `json:"numRetries"` + // +kubebuilder:validation:Optional + RetryOn []string `json:"retryOn"` + // +kubebuilder:validation:Optional + RetryOnStatusCodes []uint32 `json:"retryOnStatusCodes"` + // +kubebuilder:validation:Optional + RetryOnConnectFailure *bool `json:"retryOnConnectFailure"` +} + +func (h *RouteRetryFilter) GetNamespace() string { + return h.Namespace +} diff --git a/control-plane/api/v1alpha1/routetimeoutfilter_types.go b/control-plane/api/v1alpha1/routetimeoutfilter_types.go new file mode 100644 index 0000000000..59b25f62ea --- /dev/null +++ b/control-plane/api/v1alpha1/routetimeoutfilter_types.go @@ -0,0 +1,52 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "time" +) + +func init() { + SchemeBuilder.Register(&RouteTimeoutFilter{}, &RouteTimeoutFilterList{}) +} + +const RouteTimeoutFilterKind = "RouteTimeoutFilter" + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// RouteTimeoutFilter is the Schema for the httproutetimeoutfilters API +// +kubebuilder:printcolumn:name="Synced",type="string",JSONPath=".status.conditions[?(@.type==\"Synced\")].status",description="The sync status of the resource with Consul" +// +kubebuilder:printcolumn:name="Last Synced",type="date",JSONPath=".status.lastSyncedTime",description="The last successful synced time of the resource with Consul" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="The age of the resource" +type RouteTimeoutFilter struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec RouteTimeoutFilterSpec `json:"spec,omitempty"` + Status `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// RouteTimeoutFilterList contains a list of RouteTimeoutFilter. +type RouteTimeoutFilterList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []RouteTimeoutFilter `json:"items"` +} + +// RouteTimeoutFilterSpec defines the desired state of RouteTimeoutFilter. +type RouteTimeoutFilterSpec struct { + // +kubebuilder:validation:Optional + RequestTimeout time.Duration `json:"requestTimeout"` + + // +kubebuilder:validation:Optional + IdleTimeout time.Duration `json:"idleTimeout"` +} + +func (h *RouteTimeoutFilter) GetNamespace() string { + return h.Namespace +} diff --git a/control-plane/api/v1alpha1/zz_generated.deepcopy.go b/control-plane/api/v1alpha1/zz_generated.deepcopy.go index 5de7c61c4e..ccd69c4281 100644 --- a/control-plane/api/v1alpha1/zz_generated.deepcopy.go +++ b/control-plane/api/v1alpha1/zz_generated.deepcopy.go @@ -2140,6 +2140,174 @@ func (in *RingHashConfig) DeepCopy() *RingHashConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RouteRetryFilter) DeepCopyInto(out *RouteRetryFilter) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteRetryFilter. +func (in *RouteRetryFilter) DeepCopy() *RouteRetryFilter { + if in == nil { + return nil + } + out := new(RouteRetryFilter) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RouteRetryFilter) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RouteRetryFilterList) DeepCopyInto(out *RouteRetryFilterList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]RouteRetryFilter, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteRetryFilterList. +func (in *RouteRetryFilterList) DeepCopy() *RouteRetryFilterList { + if in == nil { + return nil + } + out := new(RouteRetryFilterList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RouteRetryFilterList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RouteRetryFilterSpec) DeepCopyInto(out *RouteRetryFilterSpec) { + *out = *in + if in.NumRetries != nil { + in, out := &in.NumRetries, &out.NumRetries + *out = new(uint32) + **out = **in + } + if in.RetryOn != nil { + in, out := &in.RetryOn, &out.RetryOn + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.RetryOnStatusCodes != nil { + in, out := &in.RetryOnStatusCodes, &out.RetryOnStatusCodes + *out = make([]uint32, len(*in)) + copy(*out, *in) + } + if in.RetryOnConnectFailure != nil { + in, out := &in.RetryOnConnectFailure, &out.RetryOnConnectFailure + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteRetryFilterSpec. +func (in *RouteRetryFilterSpec) DeepCopy() *RouteRetryFilterSpec { + if in == nil { + return nil + } + out := new(RouteRetryFilterSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RouteTimeoutFilter) DeepCopyInto(out *RouteTimeoutFilter) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteTimeoutFilter. +func (in *RouteTimeoutFilter) DeepCopy() *RouteTimeoutFilter { + if in == nil { + return nil + } + out := new(RouteTimeoutFilter) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RouteTimeoutFilter) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RouteTimeoutFilterList) DeepCopyInto(out *RouteTimeoutFilterList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]RouteTimeoutFilter, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteTimeoutFilterList. +func (in *RouteTimeoutFilterList) DeepCopy() *RouteTimeoutFilterList { + if in == nil { + return nil + } + out := new(RouteTimeoutFilterList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RouteTimeoutFilterList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RouteTimeoutFilterSpec) DeepCopyInto(out *RouteTimeoutFilterSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteTimeoutFilterSpec. +func (in *RouteTimeoutFilterSpec) DeepCopy() *RouteTimeoutFilterSpec { + if in == nil { + return nil + } + out := new(RouteTimeoutFilterSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SamenessGroup) DeepCopyInto(out *SamenessGroup) { *out = *in diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_routeretryfilters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_routeretryfilters.yaml new file mode 100644 index 0000000000..5928864ac5 --- /dev/null +++ b/control-plane/config/crd/bases/consul.hashicorp.com_routeretryfilters.yaml @@ -0,0 +1,112 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: routeretryfilters.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: RouteRetryFilter + listKind: RouteRetryFilterList + plural: routeretryfilters + singular: routeretryfilter + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: RouteRetryFilter is the Schema for the routeretryfilters API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: RouteRetryFilterSpec defines the desired state of RouteRetryFilter + properties: + numRetries: + format: int32 + minimum: 0 + type: integer + retryOn: + items: + type: string + type: array + retryOnConnectFailure: + type: boolean + retryOnStatusCodes: + items: + format: int32 + type: integer + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_routetimeoutfilters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_routetimeoutfilters.yaml new file mode 100644 index 0000000000..e671ecd9b9 --- /dev/null +++ b/control-plane/config/crd/bases/consul.hashicorp.com_routetimeoutfilters.yaml @@ -0,0 +1,110 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: routetimeoutfilters.consul.hashicorp.com +spec: + group: consul.hashicorp.com + names: + kind: RouteTimeoutFilter + listKind: RouteTimeoutFilterList + plural: routetimeoutfilters + singular: routetimeoutfilter + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: RouteTimeoutFilter is the Schema for the httproutetimeoutfilters + API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: RouteTimeoutFilterSpec defines the desired state of RouteTimeoutFilter + properties: + idleTimeout: + description: A Duration represents the elapsed time between two instants + as an int64 nanosecond count. The representation limits the largest + representable duration to approximately 290 years. + format: int64 + type: integer + requestTimeout: + description: A Duration represents the elapsed time between two instants + as an int64 nanosecond count. The representation limits the largest + representable duration to approximately 290 years. + format: int64 + type: integer + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/control-plane/go.mod b/control-plane/go.mod index 2dcd78848c..1a36ef889c 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -10,8 +10,8 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83383d github.com/hashicorp/consul-server-connection-manager v0.1.3 - github.com/hashicorp/consul/api v1.22.0-rc1 - github.com/hashicorp/consul/sdk v0.14.0-rc1 + github.com/hashicorp/consul/api v1.10.1-0.20230802160219-cf2bf7fdecdf + github.com/hashicorp/consul/sdk v0.14.0 github.com/hashicorp/go-bexpr v0.1.11 github.com/hashicorp/go-discover v0.0.0-20230519164032-214571b6a530 github.com/hashicorp/go-hclog v1.5.0 diff --git a/control-plane/go.sum b/control-plane/go.sum index dec3ba9eb4..70c7220dd6 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -264,12 +264,12 @@ github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83 github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83383d/go.mod h1:IHIHMzkoMwlv6rLsgwcoFBVYupR7/1pKEOHBMjD4L0k= github.com/hashicorp/consul-server-connection-manager v0.1.3 h1:fxsZ15XBNNWhV26yBVdCcnxHwSRgf9wqHGS2ZVCQIhc= github.com/hashicorp/consul-server-connection-manager v0.1.3/go.mod h1:Md2IGKaFJ4ek9GUA0pW1S2R60wpquMOUs27GiD9kZd0= -github.com/hashicorp/consul/api v1.22.0-rc1 h1:ePmGqndeMgaI38KUbSA/CqTzeEAIogXyWnfNJzglo70= -github.com/hashicorp/consul/api v1.22.0-rc1/go.mod h1:wtduXtbAqSGtBdi3tyA5SSAYGAG51rBejV9SEUBciMY= +github.com/hashicorp/consul/api v1.10.1-0.20230802160219-cf2bf7fdecdf h1:XCfEJhwSx188jLPjctsGoOfG7OueuXaqceR0HwHAH1s= +github.com/hashicorp/consul/api v1.10.1-0.20230802160219-cf2bf7fdecdf/go.mod h1:TqtEfVYyzax2gaBwU1vsCGFcysmK9g9UANUWCd8qMBw= github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3usQMXZF6ziK3SzXU= github.com/hashicorp/consul/proto-public v0.1.0/go.mod h1:vs2KkuWwtjkIgA5ezp4YKPzQp4GitV+q/+PvksrA92k= -github.com/hashicorp/consul/sdk v0.14.0-rc1 h1:PuETOfN0uxl28i0Pq6rK7TBCrIl7psMbL0YTSje4KvM= -github.com/hashicorp/consul/sdk v0.14.0-rc1/go.mod h1:gHYeuDa0+0qRAD6Wwr6yznMBvBwHKoxSBoW5l73+saE= +github.com/hashicorp/consul/sdk v0.14.0 h1:Hly+BMNMssVzoWddbBnBFi3W+Fzytvm0haSkihhj3GU= +github.com/hashicorp/consul/sdk v0.14.0/go.mod h1:gHYeuDa0+0qRAD6Wwr6yznMBvBwHKoxSBoW5l73+saE= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= From 4d40591d4eb93b07f0eb7770623cff457a082890 Mon Sep 17 00:00:00 2001 From: Thomas Eckert Date: Mon, 21 Aug 2023 13:07:50 -0400 Subject: [PATCH 332/340] Update Kustomize to use `patches` instead of `patchesStrategicMerge` (#2786) * Fix Kustomization for cases * Fix patches in config * Update `Contributing` --- CONTRIBUTING.md | 2 +- .../api-gateways/jwt-auth/kustomization.yaml | 16 +++++++++------- .../default-partition-default/kustomization.yaml | 9 +++++---- .../default-partition-ns1/kustomization.yaml | 9 +++++---- .../kustomization.yaml | 9 +++++---- .../secondary-partition-ns1/kustomization.yaml | 9 +++++---- .../default-namespace/kustomization.yaml | 9 +++++---- .../cases/crd-peers/default/kustomization.yaml | 9 +++++---- .../non-default-namespace/kustomization.yaml | 9 +++++---- .../fixtures/cases/crds-ent/kustomization.yaml | 6 ++++-- .../cluster-01-a-acceptor/kustomization.yaml | 9 +++++---- .../cluster-01-b-acceptor/kustomization.yaml | 9 +++++---- .../cluster-02-a-acceptor/kustomization.yaml | 9 +++++---- .../cluster-03-a-acceptor/kustomization.yaml | 9 +++++---- .../ap1-partition/kustomization.yaml | 9 +++++---- .../default-partition/kustomization.yaml | 9 +++++---- .../ap1-partition-tproxy/kustomization.yaml | 9 +++++---- .../ap1-partition/kustomization.yaml | 9 +++++---- .../default-partition-tproxy/kustomization.yaml | 9 +++++---- .../default-partition/kustomization.yaml | 9 +++++---- .../static-server/dc1-default/kustomization.yaml | 9 +++++---- .../dc1-partition/kustomization.yaml | 9 +++++---- .../static-server/dc2/kustomization.yaml | 9 +++++---- .../static-server/dc3/kustomization.yaml | 9 +++++---- .../kustomization.yaml | 9 +++++---- .../static-client-inject/kustomization.yaml | 9 +++++---- .../static-client-multi-dc/kustomization.yaml | 9 +++++---- .../static-client-namespaces/kustomization.yaml | 9 +++++---- .../kustomization.yaml | 9 +++++---- .../kustomization.yaml | 9 +++++---- .../kustomization.yaml | 9 +++++---- .../default-ns-partition/kustomization.yaml | 9 +++++---- .../ns-default-partition/kustomization.yaml | 9 +++++---- .../ns-partition/kustomization.yaml | 9 +++++---- .../default-namespace/kustomization.yaml | 9 +++++---- .../default/kustomization.yaml | 9 +++++---- .../non-default-namespace/kustomization.yaml | 9 +++++---- .../static-client-tproxy/kustomization.yaml | 9 +++++---- .../static-server-inject/kustomization.yaml | 9 +++++---- .../static-server-openshift/kustomization.yaml | 9 +++++---- control-plane/config/crd/kustomization.yaml | 5 ++++- 41 files changed, 203 insertions(+), 159 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c1e3446e8d..4eca76a1dd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -376,7 +376,7 @@ rebase the branch on main, fixing any conflicts along the way before the code ca ### Generating YAML 1. Run `make ctrl-manifests` to generate the CRD and webhook YAML. -1. Uncomment your CRD in `control-plane/config/crd/kustomization` under `patchesStrategicMerge:` +1. Uncomment your CRD in `control-plane/config/crd/kustomization` under `patches:` 1. Update the sample, e.g. `control-plane/config/samples/consul_v1alpha1_ingressgateway.yaml` to a valid resource that can be used for testing: ```yaml diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/kustomization.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/kustomization.yaml index 3dc38a090c..9730a1a4ac 100644 --- a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/kustomization.yaml @@ -2,11 +2,13 @@ # SPDX-License-Identifier: MPL-2.0 resources: - - ../../../bases/api-gateway - - ../../static-server-inject - - ./httproute.yaml - - ./jwt-provider.yaml +- ../../../bases/api-gateway +- ../../static-server-inject +- ./httproute.yaml +- ./jwt-provider.yaml -patchesStrategicMerge: - - httproute-no-auth.yaml - - api-gateway.yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +patches: +- patch: httproute-no-auth.yaml +- path: api-gateway.yaml diff --git a/acceptance/tests/fixtures/cases/crd-partitions/default-partition-default/kustomization.yaml b/acceptance/tests/fixtures/cases/crd-partitions/default-partition-default/kustomization.yaml index a175d8ece0..f3d0bca3ce 100644 --- a/acceptance/tests/fixtures/cases/crd-partitions/default-partition-default/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crd-partitions/default-partition-default/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../bases/exportedservices-default - -patchesStrategicMerge: -- patch.yaml +- ../../../bases/exportedservices-default +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/crd-partitions/default-partition-ns1/kustomization.yaml b/acceptance/tests/fixtures/cases/crd-partitions/default-partition-ns1/kustomization.yaml index a175d8ece0..f3d0bca3ce 100644 --- a/acceptance/tests/fixtures/cases/crd-partitions/default-partition-ns1/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crd-partitions/default-partition-ns1/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../bases/exportedservices-default - -patchesStrategicMerge: -- patch.yaml +- ../../../bases/exportedservices-default +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-default/kustomization.yaml b/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-default/kustomization.yaml index bb16f51e64..77c6bd3fae 100644 --- a/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-default/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-default/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../bases/exportedservices-secondary - -patchesStrategicMerge: -- patch.yaml +- ../../../bases/exportedservices-secondary +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-ns1/kustomization.yaml b/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-ns1/kustomization.yaml index bb16f51e64..77c6bd3fae 100644 --- a/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-ns1/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-ns1/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../bases/exportedservices-secondary - -patchesStrategicMerge: -- patch.yaml +- ../../../bases/exportedservices-secondary +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/crd-peers/default-namespace/kustomization.yaml b/acceptance/tests/fixtures/cases/crd-peers/default-namespace/kustomization.yaml index a175d8ece0..f3d0bca3ce 100644 --- a/acceptance/tests/fixtures/cases/crd-peers/default-namespace/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crd-peers/default-namespace/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../bases/exportedservices-default - -patchesStrategicMerge: -- patch.yaml +- ../../../bases/exportedservices-default +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/crd-peers/default/kustomization.yaml b/acceptance/tests/fixtures/cases/crd-peers/default/kustomization.yaml index a175d8ece0..f3d0bca3ce 100644 --- a/acceptance/tests/fixtures/cases/crd-peers/default/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crd-peers/default/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../bases/exportedservices-default - -patchesStrategicMerge: -- patch.yaml +- ../../../bases/exportedservices-default +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/crd-peers/non-default-namespace/kustomization.yaml b/acceptance/tests/fixtures/cases/crd-peers/non-default-namespace/kustomization.yaml index a175d8ece0..f3d0bca3ce 100644 --- a/acceptance/tests/fixtures/cases/crd-peers/non-default-namespace/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crd-peers/non-default-namespace/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../bases/exportedservices-default - -patchesStrategicMerge: -- patch.yaml +- ../../../bases/exportedservices-default +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/crds-ent/kustomization.yaml b/acceptance/tests/fixtures/cases/crds-ent/kustomization.yaml index 69d972b417..3fd5556ebd 100644 --- a/acceptance/tests/fixtures/cases/crds-ent/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crds-ent/kustomization.yaml @@ -1,7 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - ../../bases/crds-oss -patchesStrategicMerge: -- exportedservices.yaml +patches: +- path: exportedservices.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/cluster-01-a-acceptor/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/cluster-01-a-acceptor/kustomization.yaml index 30ddacd76c..08c7c9b818 100644 --- a/acceptance/tests/fixtures/cases/sameness/cluster-01-a-acceptor/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/sameness/cluster-01-a-acceptor/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../bases/sameness/peering/acceptor - -patchesStrategicMerge: - - patch.yaml +- ../../../bases/sameness/peering/acceptor +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/cluster-01-b-acceptor/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/cluster-01-b-acceptor/kustomization.yaml index 30ddacd76c..08c7c9b818 100644 --- a/acceptance/tests/fixtures/cases/sameness/cluster-01-b-acceptor/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/sameness/cluster-01-b-acceptor/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../bases/sameness/peering/acceptor - -patchesStrategicMerge: - - patch.yaml +- ../../../bases/sameness/peering/acceptor +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/cluster-02-a-acceptor/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/cluster-02-a-acceptor/kustomization.yaml index 30ddacd76c..08c7c9b818 100644 --- a/acceptance/tests/fixtures/cases/sameness/cluster-02-a-acceptor/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/sameness/cluster-02-a-acceptor/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../bases/sameness/peering/acceptor - -patchesStrategicMerge: - - patch.yaml +- ../../../bases/sameness/peering/acceptor +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/cluster-03-a-acceptor/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/cluster-03-a-acceptor/kustomization.yaml index 6a54cd6eab..08c7c9b818 100644 --- a/acceptance/tests/fixtures/cases/sameness/cluster-03-a-acceptor/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/sameness/cluster-03-a-acceptor/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../bases/sameness/peering/acceptor - -patchesStrategicMerge: -- patch.yaml +- ../../../bases/sameness/peering/acceptor +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/exported-services/ap1-partition/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/exported-services/ap1-partition/kustomization.yaml index 2a2f47a332..d25bed6eee 100644 --- a/acceptance/tests/fixtures/cases/sameness/exported-services/ap1-partition/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/sameness/exported-services/ap1-partition/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../../bases/sameness/exportedservices-ap1 - -patchesStrategicMerge: -- patch.yaml +- ../../../../bases/sameness/exportedservices-ap1 +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/exported-services/default-partition/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/exported-services/default-partition/kustomization.yaml index 05de6151fc..7f4ab4ba7c 100644 --- a/acceptance/tests/fixtures/cases/sameness/exported-services/default-partition/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/sameness/exported-services/default-partition/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../../bases/exportedservices-default - -patchesStrategicMerge: -- patch.yaml +- ../../../../bases/exportedservices-default +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition-tproxy/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition-tproxy/kustomization.yaml index 227f223c9f..096edd19ed 100644 --- a/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition-tproxy/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition-tproxy/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../../bases/static-client - -patchesStrategicMerge: - - patch.yaml \ No newline at end of file +- ../../../../bases/static-client +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition/kustomization.yaml index 227f223c9f..096edd19ed 100644 --- a/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../../bases/static-client - -patchesStrategicMerge: - - patch.yaml \ No newline at end of file +- ../../../../bases/static-client +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-client/default-partition-tproxy/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-client/default-partition-tproxy/kustomization.yaml index 227f223c9f..096edd19ed 100644 --- a/acceptance/tests/fixtures/cases/sameness/static-client/default-partition-tproxy/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/sameness/static-client/default-partition-tproxy/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../../bases/static-client - -patchesStrategicMerge: - - patch.yaml \ No newline at end of file +- ../../../../bases/static-client +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-client/default-partition/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-client/default-partition/kustomization.yaml index 227f223c9f..096edd19ed 100644 --- a/acceptance/tests/fixtures/cases/sameness/static-client/default-partition/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/sameness/static-client/default-partition/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../../bases/static-client - -patchesStrategicMerge: - - patch.yaml \ No newline at end of file +- ../../../../bases/static-client +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-server/dc1-default/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-server/dc1-default/kustomization.yaml index c15bfe7ba7..e03603d26d 100644 --- a/acceptance/tests/fixtures/cases/sameness/static-server/dc1-default/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/sameness/static-server/dc1-default/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../../bases/static-server - -patchesStrategicMerge: - - patch.yaml \ No newline at end of file +- ../../../../bases/static-server +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-server/dc1-partition/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-server/dc1-partition/kustomization.yaml index c15bfe7ba7..e03603d26d 100644 --- a/acceptance/tests/fixtures/cases/sameness/static-server/dc1-partition/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/sameness/static-server/dc1-partition/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../../bases/static-server - -patchesStrategicMerge: - - patch.yaml \ No newline at end of file +- ../../../../bases/static-server +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-server/dc2/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-server/dc2/kustomization.yaml index c15bfe7ba7..e03603d26d 100644 --- a/acceptance/tests/fixtures/cases/sameness/static-server/dc2/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/sameness/static-server/dc2/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../../bases/static-server - -patchesStrategicMerge: - - patch.yaml \ No newline at end of file +- ../../../../bases/static-server +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-server/dc3/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-server/dc3/kustomization.yaml index c15bfe7ba7..e03603d26d 100644 --- a/acceptance/tests/fixtures/cases/sameness/static-server/dc3/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/sameness/static-server/dc3/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../../bases/static-server - -patchesStrategicMerge: - - patch.yaml \ No newline at end of file +- ../../../../bases/static-server +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/static-client-inject-multiport/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-inject-multiport/kustomization.yaml index 4d4a53b87f..564d02a68f 100644 --- a/acceptance/tests/fixtures/cases/static-client-inject-multiport/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-inject-multiport/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../bases/static-client - -patchesStrategicMerge: - - patch.yaml \ No newline at end of file +- ../../bases/static-client +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/static-client-inject/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-inject/kustomization.yaml index 4d4a53b87f..564d02a68f 100644 --- a/acceptance/tests/fixtures/cases/static-client-inject/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-inject/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../bases/static-client - -patchesStrategicMerge: - - patch.yaml \ No newline at end of file +- ../../bases/static-client +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/static-client-multi-dc/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-multi-dc/kustomization.yaml index 4d4a53b87f..564d02a68f 100644 --- a/acceptance/tests/fixtures/cases/static-client-multi-dc/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-multi-dc/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../bases/static-client - -patchesStrategicMerge: - - patch.yaml \ No newline at end of file +- ../../bases/static-client +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/static-client-namespaces/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-namespaces/kustomization.yaml index 97a00c6466..564d02a68f 100644 --- a/acceptance/tests/fixtures/cases/static-client-namespaces/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-namespaces/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../bases/static-client - -patchesStrategicMerge: - - patch.yaml +- ../../bases/static-client +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/static-client-openshift-inject/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-openshift-inject/kustomization.yaml index 4d4a53b87f..564d02a68f 100644 --- a/acceptance/tests/fixtures/cases/static-client-openshift-inject/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-openshift-inject/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../bases/static-client - -patchesStrategicMerge: - - patch.yaml \ No newline at end of file +- ../../bases/static-client +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/static-client-openshift-tproxy/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-openshift-tproxy/kustomization.yaml index 4d4a53b87f..564d02a68f 100644 --- a/acceptance/tests/fixtures/cases/static-client-openshift-tproxy/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-openshift-tproxy/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../bases/static-client - -patchesStrategicMerge: - - patch.yaml \ No newline at end of file +- ../../bases/static-client +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-default-partition/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-default-partition/kustomization.yaml index 38bc36bffd..0ae44380dd 100644 --- a/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-default-partition/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-default-partition/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../bases/static-client - -patchesStrategicMerge: - - patch.yaml \ No newline at end of file +- ../../../bases/static-client +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-partition/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-partition/kustomization.yaml index 38bc36bffd..0ae44380dd 100644 --- a/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-partition/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-partition/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../bases/static-client - -patchesStrategicMerge: - - patch.yaml \ No newline at end of file +- ../../../bases/static-client +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/static-client-partitions/ns-default-partition/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-partitions/ns-default-partition/kustomization.yaml index 38bc36bffd..0ae44380dd 100644 --- a/acceptance/tests/fixtures/cases/static-client-partitions/ns-default-partition/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-partitions/ns-default-partition/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../bases/static-client - -patchesStrategicMerge: - - patch.yaml \ No newline at end of file +- ../../../bases/static-client +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/static-client-partitions/ns-partition/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-partitions/ns-partition/kustomization.yaml index 38bc36bffd..0ae44380dd 100644 --- a/acceptance/tests/fixtures/cases/static-client-partitions/ns-partition/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-partitions/ns-partition/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../bases/static-client - -patchesStrategicMerge: - - patch.yaml \ No newline at end of file +- ../../../bases/static-client +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/static-client-peers/default-namespace/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-peers/default-namespace/kustomization.yaml index 38bc36bffd..0ae44380dd 100644 --- a/acceptance/tests/fixtures/cases/static-client-peers/default-namespace/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-peers/default-namespace/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../bases/static-client - -patchesStrategicMerge: - - patch.yaml \ No newline at end of file +- ../../../bases/static-client +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/static-client-peers/default/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-peers/default/kustomization.yaml index 38bc36bffd..0ae44380dd 100644 --- a/acceptance/tests/fixtures/cases/static-client-peers/default/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-peers/default/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../bases/static-client - -patchesStrategicMerge: - - patch.yaml \ No newline at end of file +- ../../../bases/static-client +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/static-client-peers/non-default-namespace/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-peers/non-default-namespace/kustomization.yaml index 38bc36bffd..0ae44380dd 100644 --- a/acceptance/tests/fixtures/cases/static-client-peers/non-default-namespace/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-peers/non-default-namespace/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../../bases/static-client - -patchesStrategicMerge: - - patch.yaml \ No newline at end of file +- ../../../bases/static-client +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/static-client-tproxy/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-tproxy/kustomization.yaml index 4d4a53b87f..564d02a68f 100644 --- a/acceptance/tests/fixtures/cases/static-client-tproxy/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-tproxy/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../bases/static-client - -patchesStrategicMerge: - - patch.yaml \ No newline at end of file +- ../../bases/static-client +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/static-server-inject/kustomization.yaml b/acceptance/tests/fixtures/cases/static-server-inject/kustomization.yaml index bc50c78adf..bd2c22ff5f 100644 --- a/acceptance/tests/fixtures/cases/static-server-inject/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-server-inject/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../bases/static-server - -patchesStrategicMerge: - - patch.yaml \ No newline at end of file +- ../../bases/static-server +patches: +- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/static-server-openshift/kustomization.yaml b/acceptance/tests/fixtures/cases/static-server-openshift/kustomization.yaml index bc50c78adf..bd2c22ff5f 100644 --- a/acceptance/tests/fixtures/cases/static-server-openshift/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-server-openshift/kustomization.yaml @@ -1,8 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - - ../../bases/static-server - -patchesStrategicMerge: - - patch.yaml \ No newline at end of file +- ../../bases/static-server +patches: +- path: patch.yaml diff --git a/control-plane/config/crd/kustomization.yaml b/control-plane/config/crd/kustomization.yaml index 2c8358a48b..cbb41bc60c 100644 --- a/control-plane/config/crd/kustomization.yaml +++ b/control-plane/config/crd/kustomization.yaml @@ -1,6 +1,9 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + # This kustomization.yaml is not intended to be run by itself, # since it depends on service name and namespace that are out of this kustomize package. # It should be run by config/default @@ -8,7 +11,7 @@ resources: - bases/consul.hashicorp.com_controlplanerequestlimits.yaml #+kubebuilder:scaffold:crdkustomizeresource -patchesStrategicMerge: +patches: # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. # patches here are for enabling the conversion webhook for each CRD #- patches/webhook_in_controlplanerequestlimits.yaml From fd19813e725df80cc316eb5de8eb2e3c42fa5fb4 Mon Sep 17 00:00:00 2001 From: Michael Zalimeni Date: Mon, 21 Aug 2023 14:08:13 -0400 Subject: [PATCH 333/340] [NET-4498] Test locality propagation to services from k8s (#2791) Test locality propagation to services from k8s Verify that we propagate locality (region and zone) from standard k8s annotations to services registered by consul-k8s. This will later be expanded to exercise multi-cluster locality-based failover. --- acceptance/framework/k8s/kubectl.go | 8 ++ acceptance/tests/sameness/sameness_test.go | 106 +++++++++++++++++---- 2 files changed, 95 insertions(+), 19 deletions(-) diff --git a/acceptance/framework/k8s/kubectl.go b/acceptance/framework/k8s/kubectl.go index acfedbdb3d..2e37b9bd0a 100644 --- a/acceptance/framework/k8s/kubectl.go +++ b/acceptance/framework/k8s/kubectl.go @@ -122,6 +122,14 @@ func KubectlScale(t *testing.T, options *k8s.KubectlOptions, deployment string, require.NoError(t, err) } +// KubectlLabel takes an object and applies the given label to it. +// Example: `KubectlLabel(t, options, "node", nodeId, corev1.LabelTopologyRegion, "us-east-1")`. +func KubectlLabel(t *testing.T, options *k8s.KubectlOptions, objectType string, objectId string, key string, value string) { + // `kubectl label` doesn't support timeouts + _, err := RunKubectlAndGetOutputE(t, options, "label", objectType, objectId, "--overwrite", fmt.Sprintf("%s=%s", key, value)) + require.NoError(t, err) +} + // RunKubectl runs an arbitrary kubectl command provided via args and ignores the output. // If there's an error running the command, fail the test. func RunKubectl(t *testing.T, options *k8s.KubectlOptions, args ...string) { diff --git a/acceptance/tests/sameness/sameness_test.go b/acceptance/tests/sameness/sameness_test.go index b45f771773..5e1c268169 100644 --- a/acceptance/tests/sameness/sameness_test.go +++ b/acceptance/tests/sameness/sameness_test.go @@ -4,7 +4,7 @@ package sameness import ( - "context" + ctx "context" "fmt" "strconv" "strings" @@ -22,6 +22,7 @@ import ( "github.com/hashicorp/consul/sdk/testutil/retry" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -51,6 +52,10 @@ const ( samenessGroupName = "group-01" + cluster01Region = "us-east-1" + cluster02Region = "us-west-1" + cluster03Region = "us-east-2" + retryTimeout = 5 * time.Minute ) @@ -116,8 +121,8 @@ func TestFailover_Connect(t *testing.T) { | | +------------------+ | | | | | +------------------+ | | | Admin Partitions: Default | | | | | | | | Name: cluster-01-a | | | | | Admin Partitions: Default | - | | | | | | | Name: cluster-02-a | - | +-----------------------------+ | | | | | + | | Region: us-east-1 | | | | | Name: cluster-02-a | + | +-----------------------------+ | | | | Region: us-west-1 | | | | | +-----------------------------------+ | Failover 1| | Failover 3 | | +-------------------------------+ | | | +-----------------------------------+ @@ -132,17 +137,17 @@ func TestFailover_Connect(t *testing.T) { | | +------------------+ | | | | | | Admin Partitions: ap1 | | | Admin Partitions: Default | | | Name: cluster-01-b | | | Name: cluster-03-a | - | | | | | | + | | Region: us-east-1 | | | Region: us-east-2 | | +-------------------------------+ | | | | | +-----------------------------------+ +-------------------------------------------+ */ testClusters := clusters{ - keyCluster01a: {name: peerName1a, context: env.DefaultContext(t), hasServer: true, acceptors: []string{peerName2a, peerName3a}}, - keyCluster01b: {name: peerName1b, context: env.Context(t, 1), partition: cluster01Partition, hasServer: false, acceptors: []string{peerName2a, peerName3a}}, - keyCluster02a: {name: peerName2a, context: env.Context(t, 2), hasServer: true, acceptors: []string{peerName3a}}, - keyCluster03a: {name: peerName3a, context: env.Context(t, 3), hasServer: true}, + keyCluster01a: {name: peerName1a, context: env.DefaultContext(t), hasServer: true, acceptors: []string{peerName2a, peerName3a}, locality: localityForRegion(cluster01Region)}, + keyCluster01b: {name: peerName1b, context: env.Context(t, 1), partition: cluster01Partition, hasServer: false, acceptors: []string{peerName2a, peerName3a}, locality: localityForRegion(cluster01Region)}, + keyCluster02a: {name: peerName2a, context: env.Context(t, 2), hasServer: true, acceptors: []string{peerName3a}, locality: localityForRegion(cluster02Region)}, + keyCluster03a: {name: peerName3a, context: env.Context(t, 3), hasServer: true, locality: localityForRegion(cluster03Region)}, } // Setup Namespaces. @@ -315,6 +320,11 @@ func TestFailover_Connect(t *testing.T) { } } + // Apply locality to clusters + for _, v := range testClusters { + setK8sNodeLocality(t, v.context, v) + } + // Peering/Dialer relationship /* cluster-01-a cluster-02-a @@ -388,7 +398,7 @@ func TestFailover_Connect(t *testing.T) { // Setup DNS. for _, v := range testClusters { - dnsService, err := v.context.KubernetesClient(t).CoreV1().Services("default").Get(context.Background(), fmt.Sprintf("%s-%s", releaseName, "consul-dns"), metav1.GetOptions{}) + dnsService, err := v.context.KubernetesClient(t).CoreV1().Services("default").Get(ctx.Background(), fmt.Sprintf("%s-%s", releaseName, "consul-dns"), metav1.GetOptions{}) require.NoError(t, err) v.dnsIP = &dnsService.Spec.ClusterIP logger.Logf(t, "%s dnsIP: %s", v.name, *v.dnsIP) @@ -454,6 +464,15 @@ func TestFailover_Connect(t *testing.T) { testClusters.verifyServerUpState(t, cfg.EnableTransparentProxy) logger.Log(t, "all infrastructure up and running") + // Verify locality is set on services based on node labels previously applied. + // + // This is currently the only locality testing we do for k8s and ensures that single-partition + // locality-aware routing will function in consul-k8s. In the future, this test will be expanded + // to test multi-cluster locality-based failover with sameness groups. + for _, v := range testClusters { + checkLocalities(t, v) + } + // Verify all the failover Scenarios logger.Log(t, "verifying failover scenarios") @@ -536,9 +555,9 @@ func TestFailover_Connect(t *testing.T) { logger.Log(t, "checking service failover") if cfg.EnableTransparentProxy { - serviceFailoverCheck(t, sc.server, v.failoverServer.name, fmt.Sprintf("http://static-server.virtual.ns2.ns.%s.ap.consul", sc.server.fullTextPartition())) + serviceTargetCheck(t, sc.server, v.failoverServer.name, fmt.Sprintf("http://static-server.virtual.ns2.ns.%s.ap.consul", sc.server.fullTextPartition())) } else { - serviceFailoverCheck(t, sc.server, v.failoverServer.name, "localhost:8080") + serviceTargetCheck(t, sc.server, v.failoverServer.name, "localhost:8080") } // Verify DNS @@ -578,6 +597,7 @@ type expectedPQ struct { type cluster struct { name string partition string + locality api.Locality context environment.TestContext helmCluster *consul.HelmCluster client *api.Client @@ -613,7 +633,7 @@ func (c clusters) resetScale(t *testing.T) { func (c clusters) setServerIP(t *testing.T) { for _, labelSelector := range []string{"app=static-server", "app=static-client"} { for k, v := range c { - podList, err := v.context.KubernetesClient(t).CoreV1().Pods(metav1.NamespaceAll).List(context.Background(), + podList, err := v.context.KubernetesClient(t).CoreV1().Pods(metav1.NamespaceAll).List(ctx.Background(), metav1.ListOptions{LabelSelector: labelSelector}) require.NoError(t, err) require.Len(t, podList.Items, 1) @@ -635,9 +655,9 @@ func (c clusters) verifyServerUpState(t *testing.T, isTproxyEnabled bool) { for _, v := range c { // Query using a client and expect its own name, no failover should occur if isTproxyEnabled { - serviceFailoverCheck(t, v, v.name, fmt.Sprintf("http://static-server.virtual.ns2.ns.%s.ap.consul", v.fullTextPartition())) + serviceTargetCheck(t, v, v.name, fmt.Sprintf("http://static-server.virtual.ns2.ns.%s.ap.consul", v.fullTextPartition())) } else { - serviceFailoverCheck(t, v, v.name, "localhost:8080") + serviceTargetCheck(t, v, v.name, "localhost:8080") } } } @@ -665,16 +685,28 @@ func applyResources(t *testing.T, cfg *config.TestConfig, kustomizeDir string, o }) } -// serviceFailoverCheck verifies that the server failed over as expected by checking that curling the `static-server` -// using the `static-client` responds with the expected cluster name. Each static-server responds with a uniquue -// name so that we can verify failover occured as expected. -func serviceFailoverCheck(t *testing.T, server *cluster, expectedName string, curlAddress string) { +// setK8sNodeLocality labels the k8s node corresponding to the given cluster with standard labels indicating the +// locality of that node. These are propagated by connect-inject to registered Consul services. +func setK8sNodeLocality(t *testing.T, context environment.TestContext, c *cluster) { + nodeList, err := context.KubernetesClient(t).CoreV1().Nodes().List(ctx.Background(), metav1.ListOptions{}) + require.NoError(t, err) + // Get the name of the (only) node from the Kind cluster. + node := nodeList.Items[0].Name + k8s.KubectlLabel(t, context.KubectlOptions(t), "node", node, corev1.LabelTopologyRegion, c.locality.Region) + k8s.KubectlLabel(t, context.KubectlOptions(t), "node", node, corev1.LabelTopologyZone, c.locality.Zone) +} + +// serviceTargetCheck verifies that curling the `static-server` using the `static-client` responds with the expected +// cluster name. Each static-server responds with a unique name so that we can verify failover occured as expected. +func serviceTargetCheck(t *testing.T, server *cluster, expectedName string, curlAddress string) { timer := &retry.Timer{Timeout: retryTimeout, Wait: 5 * time.Second} var resp string var err error retry.RunWith(timer, t, func(r *retry.R) { + // Use -s/--silent and -S/--show-error flags w/ curl to reduce noise during retries. + // This silences extra output like the request progress bar, but preserves errors. resp, err = k8s.RunKubectlAndGetOutputE(t, server.clientOpts, "exec", "-i", - staticClientDeployment, "-c", staticClientName, "--", "curl", curlAddress) + staticClientDeployment, "-c", staticClientName, "--", "curl", "-sS", curlAddress) require.NoError(r, err) assert.Contains(r, resp, expectedName) }) @@ -776,3 +808,39 @@ func getPeeringAcceptorSecret(t *testing.T, cfg *config.TestConfig, server *clus return acceptorSecretName } + +// localityForRegion returns the full api.Locality to use in tests for a given region string. +func localityForRegion(r string) api.Locality { + return api.Locality{ + Region: r, + Zone: r + "a", + } +} + +// checkLocalities checks the given cluster for `static-client` and `static-server` instances matching the locality +// expected for the cluster. +func checkLocalities(t *testing.T, c *cluster) { + for ns, svcs := range map[string][]string{ + staticClientNamespace: { + staticClientName, + staticClientName + "-sidecar-proxy", + }, + staticServerNamespace: { + staticServerName, + staticServerName + "-sidecar-proxy", + }, + } { + for _, svc := range svcs { + cs := getCatalogService(t, c, svc, ns, c.partition) + assert.NotNil(t, cs.ServiceLocality, "service %s in %s did not have locality set", svc, c.name) + assert.Equal(t, c.locality, *cs.ServiceLocality, "locality for service %s in %s did not match expected", svc, c.name) + } + } +} + +func getCatalogService(t *testing.T, c *cluster, svc, ns, partition string) *api.CatalogService { + resp, _, err := c.client.Catalog().Service(svc, "", &api.QueryOptions{Namespace: ns, Partition: partition}) + require.NoError(t, err) + assert.NotEmpty(t, resp, "did not find service %s in cluster %s (partition=%s ns=%s)", svc, c.name, partition, ns) + return resp[0] +} From 13f42c26c38ee3ed7e0344a58f8dd70d003d7884 Mon Sep 17 00:00:00 2001 From: Curt Bushko Date: Mon, 21 Aug 2023 16:18:23 -0400 Subject: [PATCH 334/340] Use Kubernetes 1.25 on AKS (#2801) --- charts/consul/test/terraform/aks/variables.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/consul/test/terraform/aks/variables.tf b/charts/consul/test/terraform/aks/variables.tf index 9115517029..bb8d48251a 100644 --- a/charts/consul/test/terraform/aks/variables.tf +++ b/charts/consul/test/terraform/aks/variables.tf @@ -7,8 +7,8 @@ variable "location" { } variable "kubernetes_version" { - default = "1.25.17" - description = "Kubernetes version supported on AKS (1.25.17 Released August 2nd, 2023)" + default = "1.25" + description = "Kubernetes version supported on AKS" } variable "client_id" { From 4c95f8ff8d2041f4eec39ad53d6060ccd39e93b8 Mon Sep 17 00:00:00 2001 From: sarahalsmiller <100602640+sarahalsmiller@users.noreply.github.com> Date: Mon, 21 Aug 2023 15:30:06 -0500 Subject: [PATCH 335/340] Point mod to main to fix build errors (#2805) point mod to main to fix build errors --- control-plane/go.mod | 4 ++-- control-plane/go.sum | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/control-plane/go.mod b/control-plane/go.mod index 1a36ef889c..c199f954d6 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -10,8 +10,8 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83383d github.com/hashicorp/consul-server-connection-manager v0.1.3 - github.com/hashicorp/consul/api v1.10.1-0.20230802160219-cf2bf7fdecdf - github.com/hashicorp/consul/sdk v0.14.0 + github.com/hashicorp/consul/api v1.10.1-0.20230821180813-217d305b38d5 + github.com/hashicorp/consul/sdk v0.14.1 github.com/hashicorp/go-bexpr v0.1.11 github.com/hashicorp/go-discover v0.0.0-20230519164032-214571b6a530 github.com/hashicorp/go-hclog v1.5.0 diff --git a/control-plane/go.sum b/control-plane/go.sum index 70c7220dd6..36797f0e3d 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -266,10 +266,18 @@ github.com/hashicorp/consul-server-connection-manager v0.1.3 h1:fxsZ15XBNNWhV26y github.com/hashicorp/consul-server-connection-manager v0.1.3/go.mod h1:Md2IGKaFJ4ek9GUA0pW1S2R60wpquMOUs27GiD9kZd0= github.com/hashicorp/consul/api v1.10.1-0.20230802160219-cf2bf7fdecdf h1:XCfEJhwSx188jLPjctsGoOfG7OueuXaqceR0HwHAH1s= github.com/hashicorp/consul/api v1.10.1-0.20230802160219-cf2bf7fdecdf/go.mod h1:TqtEfVYyzax2gaBwU1vsCGFcysmK9g9UANUWCd8qMBw= +github.com/hashicorp/consul/api v1.10.1-0.20230821140749-587663dbcbd1 h1:HulZABqJoIf1NLkWkXRqH1Vl/OLKiJQBEuuFk/XezuI= +github.com/hashicorp/consul/api v1.10.1-0.20230821140749-587663dbcbd1/go.mod h1:NZJGRFYruc/80wYowkPFCp1LbGmJC9L8izrwfyVx/Wg= +github.com/hashicorp/consul/api v1.10.1-0.20230821180813-217d305b38d5 h1:TTTgXv9YeaRnODyFP1k2b2Nq5RIGrUUgI5SkDhuSNwM= +github.com/hashicorp/consul/api v1.10.1-0.20230821180813-217d305b38d5/go.mod h1:NZJGRFYruc/80wYowkPFCp1LbGmJC9L8izrwfyVx/Wg= +github.com/hashicorp/consul/api v1.24.0 h1:u2XyStA2j0jnCiVUU7Qyrt8idjRn4ORhK6DlvZ3bWhA= +github.com/hashicorp/consul/api v1.24.0/go.mod h1:NZJGRFYruc/80wYowkPFCp1LbGmJC9L8izrwfyVx/Wg= github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3usQMXZF6ziK3SzXU= github.com/hashicorp/consul/proto-public v0.1.0/go.mod h1:vs2KkuWwtjkIgA5ezp4YKPzQp4GitV+q/+PvksrA92k= github.com/hashicorp/consul/sdk v0.14.0 h1:Hly+BMNMssVzoWddbBnBFi3W+Fzytvm0haSkihhj3GU= github.com/hashicorp/consul/sdk v0.14.0/go.mod h1:gHYeuDa0+0qRAD6Wwr6yznMBvBwHKoxSBoW5l73+saE= +github.com/hashicorp/consul/sdk v0.14.1 h1:ZiwE2bKb+zro68sWzZ1SgHF3kRMBZ94TwOCFRF4ylPs= +github.com/hashicorp/consul/sdk v0.14.1/go.mod h1:vFt03juSzocLRFo59NkeQHHmQa6+g7oU0pfzdI1mUhg= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= From 3c07c5d6cacad7d06087f5f7c96f7722a0b7f062 Mon Sep 17 00:00:00 2001 From: Derek Menteer <105233703+hashi-derek@users.noreply.github.com> Date: Tue, 22 Aug 2023 11:08:56 -0500 Subject: [PATCH 336/340] Fix peer test flakes. (#2812) This commit fixes an issue where the peering tests would flake due to the fact that we were concurrently modifying a global map. It also adds in retry logic so that the consul servers have sufficient time to initialize before attempting to generate peering tokens. --- .../peering_acceptor_controller_test.go | 19 +++++++---- .../peering/peering_dialer_controller_test.go | 34 ++++++++++++------- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/control-plane/connect-inject/controllers/peering/peering_acceptor_controller_test.go b/control-plane/connect-inject/controllers/peering/peering_acceptor_controller_test.go index 5a0d37135a..601be1a833 100644 --- a/control-plane/connect-inject/controllers/peering/peering_acceptor_controller_test.go +++ b/control-plane/connect-inject/controllers/peering/peering_acceptor_controller_test.go @@ -22,7 +22,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/kubernetes/scheme" "k8s.io/utils/pointer" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -501,7 +500,8 @@ func TestReconcile_CreateUpdatePeeringAcceptor(t *testing.T) { // Create fake k8s client k8sObjects := append(tt.k8sObjects(), &ns) - s := scheme.Scheme + s := runtime.NewScheme() + corev1.AddToScheme(s) s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PeeringAcceptor{}, &v1alpha1.PeeringAcceptorList{}) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(k8sObjects...).Build() @@ -622,7 +622,8 @@ func TestReconcile_DeletePeeringAcceptor(t *testing.T) { k8sObjects := []runtime.Object{&ns, acceptor} // Add peering types to the scheme. - s := scheme.Scheme + s := runtime.NewScheme() + corev1.AddToScheme(s) s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PeeringAcceptor{}, &v1alpha1.PeeringAcceptorList{}) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(k8sObjects...).Build() @@ -768,7 +769,8 @@ func TestReconcile_VersionAnnotation(t *testing.T) { // Create fake k8s client k8sObjects := []runtime.Object{acceptor, secret, ns} - s := scheme.Scheme + s := runtime.NewScheme() + corev1.AddToScheme(s) s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PeeringAcceptor{}, &v1alpha1.PeeringAcceptorList{}) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(k8sObjects...).Build() @@ -1080,7 +1082,8 @@ func TestAcceptorUpdateStatus(t *testing.T) { k8sObjects = append(k8sObjects, tt.peeringAcceptor) // Add peering types to the scheme. - s := scheme.Scheme + s := runtime.NewScheme() + corev1.AddToScheme(s) s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PeeringAcceptor{}, &v1alpha1.PeeringAcceptorList{}) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(k8sObjects...).Build() // Create the peering acceptor controller. @@ -1192,7 +1195,8 @@ func TestAcceptorUpdateStatusError(t *testing.T) { k8sObjects = append(k8sObjects, tt.acceptor) // Add peering types to the scheme. - s := scheme.Scheme + s := runtime.NewScheme() + corev1.AddToScheme(s) s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PeeringAcceptor{}, &v1alpha1.PeeringAcceptorList{}) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(k8sObjects...).Build() // Create the peering acceptor controller. @@ -1476,7 +1480,8 @@ func TestAcceptor_RequestsForPeeringTokens(t *testing.T) { for name, tt := range cases { t.Run(name, func(t *testing.T) { - s := scheme.Scheme + s := runtime.NewScheme() + corev1.AddToScheme(s) s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PeeringAcceptor{}, &v1alpha1.PeeringAcceptorList{}) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(tt.secret, &tt.acceptors).Build() controller := AcceptorController{ diff --git a/control-plane/connect-inject/controllers/peering/peering_dialer_controller_test.go b/control-plane/connect-inject/controllers/peering/peering_dialer_controller_test.go index 8d999cbf2a..c142cd9eee 100644 --- a/control-plane/connect-inject/controllers/peering/peering_dialer_controller_test.go +++ b/control-plane/connect-inject/controllers/peering/peering_dialer_controller_test.go @@ -24,7 +24,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/kubernetes/scheme" "k8s.io/utils/pointer" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -280,9 +279,11 @@ func TestReconcile_CreateUpdatePeeringDialer(t *testing.T) { var encodedPeeringToken string if tt.peeringName != "" { // Create the initial token. - baseToken, _, err := acceptorClient.Peerings().GenerateToken(context.Background(), api.PeeringGenerateTokenRequest{PeerName: tt.peeringName}, nil) - require.NoError(t, err) - encodedPeeringToken = baseToken.PeeringToken + retry.Run(t, func(r *retry.R) { + baseToken, _, err := acceptorClient.Peerings().GenerateToken(context.Background(), api.PeeringGenerateTokenRequest{PeerName: tt.peeringName}, nil) + require.NoError(r, err) + encodedPeeringToken = baseToken.PeeringToken + }) } // If the peering is not supposed to already exist in Consul, then create a secret with the generated token. @@ -314,7 +315,8 @@ func TestReconcile_CreateUpdatePeeringDialer(t *testing.T) { secret.SetResourceVersion("latest-version") k8sObjects = append(k8sObjects, secret) } - s := scheme.Scheme + s := runtime.NewScheme() + corev1.AddToScheme(s) s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PeeringDialer{}, &v1alpha1.PeeringDialerList{}) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(k8sObjects...).Build() @@ -482,8 +484,11 @@ func TestReconcile_VersionAnnotationPeeringDialer(t *testing.T) { // Create a peering connection in Consul by generating and establishing a peering connection before calling // Reconcile(). // Generate a token. - generatedToken, _, err := acceptorClient.Peerings().GenerateToken(context.Background(), api.PeeringGenerateTokenRequest{PeerName: "peering"}, nil) - require.NoError(t, err) + var generatedToken *api.PeeringGenerateTokenResponse + retry.Run(t, func(r *retry.R) { + generatedToken, _, err = acceptorClient.Peerings().GenerateToken(context.Background(), api.PeeringGenerateTokenRequest{PeerName: "peering"}, nil) + require.NoError(r, err) + }) // Create test consul server. var testServerCfg *testutil.TestServerConfig @@ -524,7 +529,8 @@ func TestReconcile_VersionAnnotationPeeringDialer(t *testing.T) { secret.SetResourceVersion("latest-version") k8sObjects = append(k8sObjects, secret) - s := scheme.Scheme + s := runtime.NewScheme() + corev1.AddToScheme(s) s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PeeringDialer{}, &v1alpha1.PeeringDialerList{}) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(k8sObjects...).Build() @@ -740,7 +746,8 @@ func TestReconcileDeletePeeringDialer(t *testing.T) { k8sObjects := []runtime.Object{ns, dialer} // Add peering types to the scheme. - s := scheme.Scheme + s := runtime.NewScheme() + corev1.AddToScheme(s) s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PeeringDialer{}, &v1alpha1.PeeringDialerList{}) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(k8sObjects...).Build() @@ -881,7 +888,8 @@ func TestDialerUpdateStatus(t *testing.T) { k8sObjects = append(k8sObjects, tt.peeringDialer) // Add peering types to the scheme. - s := scheme.Scheme + s := runtime.NewScheme() + corev1.AddToScheme(s) s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PeeringDialer{}, &v1alpha1.PeeringDialerList{}) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(k8sObjects...).Build() // Create the peering dialer controller. @@ -993,7 +1001,8 @@ func TestDialerUpdateStatusError(t *testing.T) { k8sObjects = append(k8sObjects, tt.dialer) // Add peering types to the scheme. - s := scheme.Scheme + s := runtime.NewScheme() + corev1.AddToScheme(s) s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PeeringDialer{}, &v1alpha1.PeeringDialerList{}) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(k8sObjects...).Build() // Create the peering dialer controller. @@ -1277,7 +1286,8 @@ func TestDialer_RequestsForPeeringTokens(t *testing.T) { for name, tt := range cases { t.Run(name, func(t *testing.T) { - s := scheme.Scheme + s := runtime.NewScheme() + corev1.AddToScheme(s) s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PeeringDialer{}, &v1alpha1.PeeringDialerList{}) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(tt.secret, &tt.dialers).Build() controller := PeeringDialerController{ From 950e96b724276a0b610c61b554b7144da26a9256 Mon Sep 17 00:00:00 2001 From: Derek Menteer Date: Tue, 22 Aug 2023 08:28:40 -0500 Subject: [PATCH 337/340] Fix issue where tokens had missing pod name. Prior to this commit, tokens descriptions would have a missing pod name and would have the form: {pod: "default/"} This poses issues for the endpoints controller, which will try to parse the metadata and use it to clean up the token. Without the pod name, consul-k8s will continually leak tokens. --- .../webhook/consul_dataplane_sidecar.go | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go b/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go index 68f57ed061..ad2e07fffa 100644 --- a/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go +++ b/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go @@ -98,6 +98,29 @@ func (w *MeshWebhook) consulDataplaneSidecar(namespace corev1.Namespace, pod cor Name: "DP_SERVICE_NODE_NAME", Value: "$(NODE_NAME)-virtual", }, + // The pod name isn't known currently, so we must rely on the environment variable to fill it in rather than using args. + { + Name: "POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{FieldPath: "metadata.name"}, + }, + }, + { + Name: "POD_NAMESPACE", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{FieldPath: "metadata.namespace"}, + }, + }, + { + Name: "DP_CREDENTIAL_LOGIN_META", + Value: "pod=$(POD_NAMESPACE)/$(POD_NAME)", + }, + // This entry exists to support certain versions of consul dataplane, where environment variable entries + // utilize this numbered notation to indicate individual KV pairs in a map. + { + Name: "DP_CREDENTIAL_LOGIN_META1", + Value: "pod=$(POD_NAMESPACE)/$(POD_NAME)", + }, }, VolumeMounts: []corev1.VolumeMount{ { @@ -208,7 +231,7 @@ func (w *MeshWebhook) getContainerSidecarArgs(namespace corev1.Namespace, mpi mu "-credential-type=login", "-login-auth-method="+w.AuthMethod, "-login-bearer-token-path="+bearerTokenFile, - "-login-meta="+fmt.Sprintf("pod=%s/%s", namespace.Name, pod.Name), + // We don't know the pod name at this time, so we must use environment variables to populate the login-meta instead. ) if w.EnableNamespaces { if w.EnableK8SNSMirroring { From 354b985a00c318b94bc5289b77b4707af0eca10a Mon Sep 17 00:00:00 2001 From: Derek Menteer Date: Tue, 22 Aug 2023 08:44:52 -0500 Subject: [PATCH 338/340] Add acceptance test. --- acceptance/tests/connect/connect_inject_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/acceptance/tests/connect/connect_inject_test.go b/acceptance/tests/connect/connect_inject_test.go index 7c7f17fc0a..ff8b5bf2df 100644 --- a/acceptance/tests/connect/connect_inject_test.go +++ b/acceptance/tests/connect/connect_inject_test.go @@ -152,6 +152,18 @@ func TestConnectInject_CleanupKilledPods(t *testing.T) { } } }) + // Ensure the token is cleaned up + if secure { + retry.Run(t, func(r *retry.R) { + tokens, _, err := consulClient.ACL().TokenList(nil) + require.NoError(r, err) + for _, t := range tokens { + if strings.Contains(t.Description, podName) { + r.Errorf("Found a token that was supposed to be deleted for pod %v", podName) + } + } + }) + } }) } } From 2bff8ea99537b2dda09083acf589c42414eb30ca Mon Sep 17 00:00:00 2001 From: Derek Menteer Date: Tue, 22 Aug 2023 15:38:31 +0000 Subject: [PATCH 339/340] backport of commit 2734b31ebb6c27f0989926aced7d9aa736874ac5 --- .changelog/1914.txt | 3 - .changelog/1920.txt | 3 - .changelog/1976.txt | 3 + .changelog/2029.txt | 3 - .changelog/2030.txt | 3 - .changelog/2048.txt | 3 - .changelog/2075.txt | 3 - .changelog/2086.txt | 3 - .changelog/2093.txt | 3 - .changelog/2097.txt | 3 - .changelog/2100.txt | 3 - .changelog/{2102.txt => 2108.txt} | 13 +- .changelog/2124.txt | 3 - .changelog/2134.txt | 3 - .changelog/2140.txt | 4 + .changelog/2143.txt | 4 - .changelog/2152.txt | 3 - .changelog/2159.txt | 3 + .changelog/2165.txt | 3 - .changelog/2166.txt | 3 - .changelog/2170.txt | 2 - .changelog/2183.txt | 3 - .changelog/2184.txt | 3 - .changelog/2195.txt | 3 - .changelog/2205.txt | 3 - .changelog/2209.txt | 3 - .changelog/2213.txt | 3 - .changelog/2225.txt | 3 + .changelog/2265.txt | 2 +- .changelog/2302.txt | 1 - .changelog/2304.txt | 3 - .changelog/2346.txt | 3 - .changelog/2413.txt | 3 - .changelog/2420.txt | 3 - .changelog/2476.txt | 7 - .changelog/2478.txt | 5 - .changelog/2520.txt | 4 - .changelog/2524.txt | 3 - .changelog/2597.txt | 3 - .changelog/{2642.txt => 2650.txt} | 2 +- .changelog/2678.txt | 3 + .changelog/2707.txt | 3 - .changelog/2711.txt | 3 - .changelog/{2710.txt => 2717.txt} | 2 +- .changelog/2735.txt | 3 - .changelog/2748.txt | 3 - .changelog/2784.txt | 3 - .copywrite.hcl | 16 - .github/ISSUE_TEMPLATE/config.yml | 3 - .github/pull_request_template.md | 5 +- .github/workflows/backport-checker.yml | 2 - .github/workflows/backport.yml | 6 +- .github/workflows/build.yml | 204 +- .github/workflows/changelog-checker.yml | 4 +- .github/workflows/jira-issues.yaml | 12 +- .github/workflows/jira-pr.yaml | 12 +- .github/workflows/merge.yml | 2 +- .github/workflows/nightly-acceptance.yml | 2 +- .../nightly-api-gateway-conformance.yml | 27 - .github/workflows/nightly-cleanup.yml | 26 - .github/workflows/pr.yml | 2 +- .../workflows/weekly-acceptance-0-49-x.yml | 2 +- .github/workflows/weekly-acceptance-1-0-x.yml | 2 +- .github/workflows/weekly-acceptance-1-1-x.yml | 2 +- .github/workflows/weekly-acceptance-1-2-x.yml | 30 - .go-version | 2 +- .golangci.yml | 3 - .release/ci.hcl | 3 - .release/release-metadata.hcl | 3 - .release/security-scan.hcl | 3 - CHANGELOG.md | 371 +-- CONTRIBUTING.md | 247 +- Makefile | 52 +- README.md | 2 +- .../aks_acceptance_test_packages.yaml | 3 - .../eks_acceptance_test_packages.yaml | 3 - .../gke_acceptance_test_packages.yaml | 3 - acceptance/ci-inputs/kind-inputs.yaml | 5 +- .../kind_acceptance_test_packages.yaml | 12 +- acceptance/framework/cli/cli.go | 3 - acceptance/framework/config/config.go | 10 - acceptance/framework/config/config_test.go | 3 - .../framework/connhelper/connect_helper.go | 33 +- acceptance/framework/consul/cli_cluster.go | 22 +- acceptance/framework/consul/cluster.go | 5 +- acceptance/framework/consul/helm_cluster.go | 82 +- .../framework/consul/helm_cluster_test.go | 8 - .../cni-kind/custom-resources.yaml | 3 - .../environment/cni-kind/tigera-operator.yaml | 3 - .../framework/environment/environment.go | 39 +- acceptance/framework/flags/flags.go | 20 - acceptance/framework/flags/flags_test.go | 3 - acceptance/framework/helpers/helpers.go | 11 +- acceptance/framework/helpers/helpers_test.go | 3 - acceptance/framework/k8s/debug.go | 3 - acceptance/framework/k8s/deploy.go | 20 +- acceptance/framework/k8s/helpers.go | 9 +- acceptance/framework/k8s/kubectl.go | 28 +- acceptance/framework/logger/logger.go | 3 - .../framework/portforward/port_forward.go | 5 +- acceptance/framework/suite/suite.go | 3 - acceptance/framework/vault/helpers.go | 17 - acceptance/framework/vault/vault_cluster.go | 45 +- acceptance/go.mod | 133 +- acceptance/go.sum | 595 ++-- .../api_gateway_external_servers_test.go | 133 - .../api_gateway_gatewayclassconfig_test.go | 213 -- .../api-gateway/api_gateway_lifecycle_test.go | 444 --- .../api-gateway/api_gateway_tenancy_test.go | 404 --- .../tests/api-gateway/api_gateway_test.go | 588 ---- acceptance/tests/api-gateway/example_test.go | 64 + acceptance/tests/api-gateway/main_test.go | 27 +- acceptance/tests/basic/basic_test.go | 3 - acceptance/tests/basic/main_test.go | 3 - acceptance/tests/cli/cli_install_test.go | 45 +- acceptance/tests/cli/cli_upgrade_test.go | 3 - acceptance/tests/cli/main_test.go | 3 - acceptance/tests/cloud/basic_test.go | 286 -- acceptance/tests/cloud/main_test.go | 18 - acceptance/tests/cloud/remote_dev_test.go | 264 -- .../config_entries_namespaces_test.go | 155 +- .../config-entries/config_entries_test.go | 128 +- acceptance/tests/config-entries/main_test.go | 3 - .../connect/connect_external_servers_test.go | 9 +- .../connect/connect_inject_namespaces_test.go | 17 +- .../tests/connect/connect_inject_test.go | 40 +- .../connect/connect_proxy_lifecycle_test.go | 3 +- acceptance/tests/connect/main_test.go | 3 - .../tests/connect/permissive_mtls_test.go | 98 - .../tests/consul-dns/consul_dns_test.go | 7 +- acceptance/tests/consul-dns/main_test.go | 3 - acceptance/tests/example/example_test.go | 5 +- acceptance/tests/example/main_test.go | 3 - .../bases/api-gateway/apigateway.yaml | 31 - .../bases/api-gateway/certificate.yaml | 11 - .../bases/api-gateway/gatewayclass.yaml | 13 - .../bases/api-gateway/gatewayclassconfig.yaml | 7 - .../fixtures/bases/api-gateway/httproute.yaml | 10 - .../bases/api-gateway/kustomization.yaml | 9 - .../bases/api-gateway/meshservice.yaml | 9 - .../bases/cloud/hcp-mock/deployment.yaml | 29 - .../bases/cloud/hcp-mock/kustomization.yaml | 10 - .../bases/cloud/hcp-mock/service.yaml | 17 - .../bases/cloud/hcp-mock/serviceaccount.yaml | 7 - .../bases/cloud/service-intentions/acl.yaml | 15 - .../service-intentions/kustomization.yaml | 5 - .../crds-oss/controlplanerequestlimit.yaml | 50 - .../bases/crds-oss/exportedservices.yaml | 13 - .../bases/crds-oss/ingressgateway.yaml | 3 - .../fixtures/bases/crds-oss/jwtprovider.yaml | 30 - .../bases/crds-oss/kustomization.yaml | 24 +- .../tests/fixtures/bases/crds-oss/mesh.yaml | 3 - .../bases/crds-oss/proxydefaults.yaml | 14 - .../bases/crds-oss/servicedefaults.yaml | 19 +- .../bases/crds-oss/serviceexports.yaml | 10 + .../bases/crds-oss/serviceintentions.yaml | 3 - .../bases/crds-oss/serviceresolver.yaml | 5 +- .../bases/crds-oss/servicerouter.yaml | 3 - .../bases/crds-oss/servicesplitter.yaml | 3 - .../bases/crds-oss/terminatinggateway.yaml | 3 - .../exportedservices-default.yaml | 3 - .../kustomization.yaml | 3 - .../exportedservices-secondary.yaml | 3 - .../kustomization.yaml | 3 - .../fixtures/bases/intention/intention.yaml | 3 - .../bases/intention/kustomization.yaml | 3 - .../bases/mesh-gateway/kustomization.yaml | 3 - .../bases/mesh-gateway/proxydefaults.yaml | 3 - .../bases/mesh-peering/kustomization.yaml | 3 - .../bases/mesh-peering/meshpeering.yaml | 3 - .../multiport-app/anyuid-scc-rolebinding.yaml | 3 - .../bases/multiport-app/deployment.yaml | 3 - .../bases/multiport-app/kustomization.yaml | 3 - .../privileged-scc-rolebinding.yaml | 3 - .../bases/multiport-app/psp-rolebinding.yaml | 3 - .../fixtures/bases/multiport-app/secret.yaml | 3 - .../fixtures/bases/multiport-app/service.yaml | 3 - .../bases/multiport-app/serviceaccount.yaml | 3 - .../bases/openshift/network-attachment.yaml | 3 - .../bases/peering/peering-acceptor.yaml | 3 - .../bases/peering/peering-dialer.yaml | 3 - .../bases/resolver-redirect/intention.yaml | 24 - .../resolver-redirect/kustomization.yaml | 8 - .../bases/resolver-redirect/resolver.yaml | 10 - .../bases/resolver-redirect/service.yaml | 15 - .../resolver-redirect/serviceaccount.yaml | 7 - .../kustomization.yaml | 5 - .../cluster-01-a-default-ns/sameness.yaml | 14 - .../kustomization.yaml | 5 - .../cluster-01-b-default-ns/sameness.yaml | 14 - .../kustomization.yaml | 5 - .../cluster-02-a-default-ns/sameness.yaml | 14 - .../kustomization.yaml | 5 - .../cluster-03-a-default-ns/sameness.yaml | 14 - .../exportedservices-ap1.yaml | 9 - .../exportedservices-ap1/kustomization.yaml | 5 - .../sameness/override-ns/kustomization.yaml | 5 - .../override-ns/service-defaults.yaml | 9 - .../cluster-01-a-dialer/kustomization.yaml | 6 - .../peering-dialer-cluster-02-a.yaml | 13 - .../peering-dialer-cluster-03-a.yaml | 13 - .../cluster-01-b-dialer/kustomization.yaml | 6 - .../peering-dialer-cluster-02-a.yaml | 13 - .../peering-dialer-cluster-03-a.yaml | 13 - .../cluster-02-a-acceptor/kustomization.yaml | 6 - .../peering-acceptor-cluster-01-a.yaml | 13 - .../peering-acceptor-cluster-01-b.yaml | 13 - .../cluster-02-a-dialer/kustomization.yaml | 5 - .../peering-dialer-cluster-03-a.yaml | 13 - .../cluster-03-a-acceptor/kustomization.yaml | 7 - .../peering-acceptor-cluster-01-a.yaml | 13 - .../peering-acceptor-cluster-01-b.yaml | 13 - .../peering-acceptor-cluster-02-a.yaml | 13 - .../sameness/peering/mesh/kustomization.yaml | 5 - .../bases/sameness/peering/mesh/mesh.yaml | 10 - .../static-client/anyuid-scc-rolebinding.yaml | 3 - .../bases/static-client/deployment.yaml | 3 - .../bases/static-client/kustomization.yaml | 3 - .../privileged-scc-rolebinding.yaml | 3 - .../bases/static-client/psp-rolebinding.yaml | 3 - .../fixtures/bases/static-client/service.yaml | 3 - .../bases/static-client/serviceaccount.yaml | 3 - .../bases/static-metrics-app/deployment.yaml | 3 - .../static-metrics-app/kustomization.yaml | 3 - .../bases/static-metrics-app/service.yaml | 3 - .../anyuid-scc-rolebinding.yaml | 3 - .../bases/static-server-https/configmap.yaml | 3 - .../bases/static-server-https/deployment.yaml | 3 - .../static-server-https/kustomization.yaml | 3 - .../privileged-scc-rolebinding.yaml | 3 - .../static-server-https/psp-rolebinding.yaml | 3 - .../bases/static-server-https/service.yaml | 3 - .../static-server-https/serviceaccount.yaml | 3 - .../anyuid-scc-rolebinding.yaml | 14 - .../bases/static-server-tcp/deployment.yaml | 49 - .../static-server-tcp/kustomization.yaml | 11 - .../privileged-scc-rolebinding.yaml | 14 - .../static-server-tcp/psp-rolebinding.yaml | 14 - .../bases/static-server-tcp/service.yaml | 15 - .../static-server-tcp/serviceaccount.yaml | 7 - .../static-server-tcp/servicedefaults.yaml | 10 - .../static-server/anyuid-scc-rolebinding.yaml | 3 - .../bases/static-server/deployment.yaml | 7 +- .../bases/static-server/kustomization.yaml | 3 - .../privileged-scc-rolebinding.yaml | 3 - .../bases/static-server/psp-rolebinding.yaml | 3 - .../fixtures/bases/static-server/service.yaml | 3 - .../bases/static-server/serviceaccount.yaml | 3 - .../api-gateways/certificate/certificate.yaml | 11 - .../certificate/kustomization.yaml | 5 - .../dc1-to-dc2-resolver/kustomization.yaml | 5 - .../dc1-to-dc2-resolver/serviceresolver.yaml | 11 - .../dc2-to-dc1-resolver/kustomization.yaml | 5 - .../dc2-to-dc1-resolver/serviceresolver.yaml | 11 - .../cases/api-gateways/gateway/gateway.yaml | 20 - .../api-gateways/gateway/kustomization.yaml | 5 - .../api-gateways/httproute/kustomization.yaml | 5 - .../cases/api-gateways/httproute/route.yaml | 8 - .../api-gateways/jwt-auth/api-gateway.yaml | 37 - .../api-gateways/jwt-auth/gateway-policy.yaml | 24 - .../api-gateways/jwt-auth/httproute-auth.yaml | 32 - .../api-gateways/jwt-auth/httproute.yaml | 19 - .../api-gateways/jwt-auth/jwt-provider.yaml | 9 - .../jwt-auth/jwt-route-filter.yaml | 12 - .../api-gateways/jwt-auth/kustomization.yaml | 14 - .../api-gateways/mesh/kustomization.yaml | 5 - .../api-gateways/mesh/proxydefaults.yaml | 12 - .../peer-resolver/kustomization.yaml | 5 - .../peer-resolver/serviceresolver.yaml | 12 - .../api-gateways/resolver/kustomization.yaml | 5 - .../resolver/serviceresolver.yaml | 12 - .../cases/api-gateways/tcproute/route.yaml | 14 - .../kustomization.yaml | 12 +- .../default-partition-default/patch.yaml | 3 - .../default-partition-ns1/kustomization.yaml | 12 +- .../default-partition-ns1/patch.yaml | 3 - .../kustomization.yaml | 12 +- .../secondary-partition-default/patch.yaml | 3 - .../kustomization.yaml | 12 +- .../secondary-partition-ns1/patch.yaml | 3 - .../default-namespace/kustomization.yaml | 12 +- .../crd-peers/default-namespace/patch.yaml | 3 - .../crd-peers/default/kustomization.yaml | 12 +- .../cases/crd-peers/default/patch.yaml | 3 - .../non-default-namespace/kustomization.yaml | 12 +- .../non-default-namespace/patch.yaml | 3 - .../cases/crds-ent/exportedservices.yaml | 7 +- .../cases/crds-ent/kustomization.yaml | 10 +- .../mesh-config-permissive-allowed.yaml | 9 - ...ice-defaults-static-server-permissive.yaml | 10 - ...service-defaults-static-server-strict.yaml | 10 - .../kustomization.yaml | 5 - .../cluster-01-a-acceptor/kustomization.yaml | 9 - .../sameness/cluster-01-a-acceptor/patch.yaml | 13 - .../cluster-01-b-acceptor/kustomization.yaml | 9 - .../sameness/cluster-01-b-acceptor/patch.yaml | 13 - .../cluster-02-a-acceptor/kustomization.yaml | 9 - .../sameness/cluster-02-a-acceptor/patch.yaml | 13 - .../cluster-03-a-acceptor/kustomization.yaml | 9 - .../sameness/cluster-03-a-acceptor/patch.yaml | 13 - .../ap1-partition/kustomization.yaml | 9 - .../ap1-partition/patch.yaml | 16 - .../default-partition/kustomization.yaml | 9 - .../default-partition/patch.yaml | 16 - .../ap1-partition-tproxy/kustomization.yaml | 9 - .../ap1-partition-tproxy/patch.yaml | 21 - .../ap1-partition/kustomization.yaml | 9 - .../static-client/ap1-partition/patch.yaml | 22 - .../kustomization.yaml | 9 - .../default-partition-tproxy/patch.yaml | 21 - .../default-partition/kustomization.yaml | 9 - .../default-partition/patch.yaml | 22 - .../dc1-default/kustomization.yaml | 9 - .../static-server/dc1-default/patch.yaml | 23 - .../dc1-partition/kustomization.yaml | 9 - .../static-server/dc1-partition/patch.yaml | 23 - .../static-server/dc2/kustomization.yaml | 9 - .../sameness/static-server/dc2/patch.yaml | 23 - .../static-server/dc3/kustomization.yaml | 9 - .../sameness/static-server/dc3/patch.yaml | 23 - .../kustomization.yaml | 12 +- .../static-client-inject-multiport/patch.yaml | 3 - .../static-client-inject/kustomization.yaml | 12 +- .../cases/static-client-inject/patch.yaml | 3 - .../static-client-multi-dc/kustomization.yaml | 12 +- .../cases/static-client-multi-dc/patch.yaml | 3 - .../kustomization.yaml | 12 +- .../cases/static-client-namespaces/patch.yaml | 3 - .../kustomization.yaml | 9 +- .../kustomization.yaml | 9 +- .../kustomization.yaml | 12 +- .../default-ns-default-partition/patch.yaml | 3 - .../default-ns-partition/kustomization.yaml | 12 +- .../default-ns-partition/patch.yaml | 3 - .../ns-default-partition/kustomization.yaml | 12 +- .../ns-default-partition/patch.yaml | 3 - .../ns-partition/kustomization.yaml | 12 +- .../ns-partition/patch.yaml | 3 - .../default-namespace/kustomization.yaml | 12 +- .../default-namespace/patch.yaml | 3 - .../default/kustomization.yaml | 12 +- .../static-client-peers/default/patch.yaml | 3 - .../non-default-namespace/kustomization.yaml | 12 +- .../non-default-namespace/patch.yaml | 3 - .../static-client-tproxy/kustomization.yaml | 12 +- .../cases/static-client-tproxy/patch.yaml | 3 - .../static-server-inject/kustomization.yaml | 12 +- .../cases/static-server-inject/patch.yaml | 3 - .../kustomization.yaml | 9 +- .../ingress_gateway_namespaces_test.go | 15 +- .../ingress-gateway/ingress_gateway_test.go | 7 +- acceptance/tests/ingress-gateway/main_test.go | 3 - acceptance/tests/metrics/main_test.go | 3 - acceptance/tests/metrics/metrics_test.go | 11 +- acceptance/tests/partitions/main_test.go | 3 - .../partitions/partitions_connect_test.go | 55 +- .../partitions/partitions_gateway_test.go | 360 --- .../tests/partitions/partitions_sync_test.go | 13 +- acceptance/tests/peering/main_test.go | 3 - .../peering_connect_namespaces_test.go | 31 +- .../tests/peering/peering_connect_test.go | 27 +- .../tests/peering/peering_gateway_test.go | 290 -- acceptance/tests/sameness/main_test.go | 28 - acceptance/tests/sameness/sameness_test.go | 846 ------ acceptance/tests/snapshot-agent/main_test.go | 3 - .../snapshot_agent_k8s_secret_test.go | 3 - .../snapshot_agent_vault_test.go | 3 - acceptance/tests/sync/main_test.go | 3 - .../sync/sync_catalog_namespaces_test.go | 7 +- acceptance/tests/sync/sync_catalog_test.go | 9 +- .../tests/terminating-gateway/common.go | 3 - .../tests/terminating-gateway/main_test.go | 3 - .../terminating_gateway_destinations_test.go | 7 +- .../terminating_gateway_namespaces_test.go | 17 +- .../terminating_gateway_test.go | 7 +- acceptance/tests/vault/main_test.go | 3 - .../tests/vault/vault_namespaces_test.go | 11 +- .../tests/vault/vault_partitions_test.go | 3 - acceptance/tests/vault/vault_test.go | 88 +- .../tests/vault/vault_tls_auto_reload_test.go | 11 +- acceptance/tests/vault/vault_wan_fed_test.go | 13 +- acceptance/tests/wan-federation/main_test.go | 3 - .../wan_federation_gateway_test.go | 241 -- .../wan-federation/wan_federation_test.go | 9 +- charts/consul/.helmignore | 1 - charts/consul/Chart.yaml | 17 +- charts/consul/addons/gen.sh | 3 - charts/consul/addons/values/prometheus.yaml | 3 - charts/consul/templates/_helpers.tpl | 90 +- .../templates/connect-inject-clusterrole.yaml | 106 +- .../templates/connect-inject-deployment.yaml | 1 - ...t-inject-mutatingwebhookconfiguration.yaml | 63 - .../crd-controlplanerequestlimits.yaml | 203 -- .../templates/crd-exportedservices.yaml | 8 +- .../templates/crd-gatewayclassconfigs.yaml | 164 -- .../consul/templates/crd-gatewayclasses.yaml | 330 --- charts/consul/templates/crd-gateways.yaml | 884 ------ charts/consul/templates/crd-grpcroutes.yaml | 768 ------ charts/consul/templates/crd-httproutes.yaml | 1916 ------------- charts/consul/templates/crd-jwtproviders.yaml | 265 -- charts/consul/templates/crd-meshes.yaml | 5 - charts/consul/templates/crd-meshservices.yaml | 64 - .../consul/templates/crd-proxydefaults.yaml | 86 - .../consul/templates/crd-referencegrants.yaml | 211 -- .../templates/crd-routeretryfilters.yaml | 117 - .../templates/crd-routetimeoutfilters.yaml | 115 - .../consul/templates/crd-samenessgroups.yaml | 137 - .../consul/templates/crd-servicedefaults.yaml | 68 +- .../templates/crd-serviceintentions.yaml | 82 +- .../templates/crd-serviceresolvers.yaml | 38 - charts/consul/templates/crd-tcproutes.yaml | 284 -- charts/consul/templates/crd-tlsroutes.yaml | 294 -- charts/consul/templates/crd-udproutes.yaml | 284 -- .../gateway-cleanup-clusterrole.yaml | 35 - .../gateway-cleanup-clusterrolebinding.yaml | 20 - .../consul/templates/gateway-cleanup-job.yaml | 65 - .../gateway-cleanup-podsecuritypolicy.yaml | 32 - .../gateway-cleanup-serviceaccount.yaml | 13 - .../gateway-resources-clusterrole.yaml | 37 - .../gateway-resources-clusterrolebinding.yaml | 20 - .../templates/gateway-resources-job.yaml | 122 - .../gateway-resources-podsecuritypolicy.yaml | 32 - .../gateway-resources-serviceaccount.yaml | 13 - .../consul/templates/server-acl-init-job.yaml | 55 +- .../consul/templates/server-clusterrole.yaml | 16 - .../templates/server-clusterrolebinding.yaml | 18 - .../templates/server-config-configmap.yaml | 8 - .../consul/templates/server-statefulset.yaml | 51 +- .../telemetry-collector-configmap.yaml | 18 - .../telemetry-collector-deployment.yaml | 391 --- ...telemetry-collector-podsecuritypolicy.yaml | 40 - .../templates/telemetry-collector-role.yaml | 21 - .../telemetry-collector-rolebinding.yaml | 21 - .../telemetry-collector-service.yaml | 24 - .../telemetry-collector-serviceaccount.yaml | 23 - .../webhook-cert-manager-clusterrole.yaml | 2 +- ...bhook-cert-manager-clusterrolebinding.yaml | 2 +- .../webhook-cert-manager-configmap.yaml | 2 +- .../webhook-cert-manager-deployment.yaml | 2 +- ...ebhook-cert-manager-podsecuritypolicy.yaml | 2 +- .../webhook-cert-manager-serviceaccount.yaml | 2 +- charts/consul/test/docker/Test.dockerfile | 3 - charts/consul/test/terraform/eks/main.tf | 15 +- charts/consul/test/terraform/eks/outputs.tf | 2 +- charts/consul/test/terraform/eks/variables.tf | 2 +- charts/consul/test/terraform/gke/main.tf | 2 +- charts/consul/test/terraform/gke/outputs.tf | 2 +- charts/consul/test/terraform/gke/variables.tf | 2 +- .../consul/test/terraform/openshift/main.tf | 3 - .../test/terraform/openshift/oc-login.sh | 3 - .../test/terraform/openshift/outputs.tf | 3 - .../test/terraform/openshift/variables.tf | 3 - charts/consul/test/unit/_helpers.bash | 3 - .../test/unit/connect-inject-clusterrole.bats | 37 +- .../test/unit/connect-inject-deployment.bats | 37 +- ...t-inject-mutatingwebhookconfiguration.bats | 4 +- .../unit/crd-controlplanerequestlimits.bats | 26 - .../test/unit/crd-exportedservices.bats | 2 +- .../test/unit/crd-gatewayclassconfigs.bats | 20 - .../consul/test/unit/crd-gatewayclasses.bats | 28 - charts/consul/test/unit/crd-gateways.bats | 28 - charts/consul/test/unit/crd-grpcroutes.bats | 28 - charts/consul/test/unit/crd-httproutes.bats | 28 - charts/consul/test/unit/crd-meshservices.bats | 21 - charts/consul/test/unit/crd-tcproutes.bats | 28 - charts/consul/test/unit/crd-tlsroutes.bats | 28 - charts/consul/test/unit/crd-udproutes.bats | 28 - .../unit/gateway-cleanup-clusterrole.bats | 33 - .../gateway-cleanup-clusterrolebinding.bats | 23 - .../consul/test/unit/gateway-cleanup-job.bats | 45 - .../gateway-cleanup-podsecuritypolicy.bats | 41 - .../unit/gateway-cleanup-serviceaccount.bats | 23 - .../unit/gateway-resources-clusterrole.bats | 33 - .../gateway-resources-clusterrolebinding.bats | 23 - .../test/unit/gateway-resources-job.bats | 164 -- .../gateway-resources-podsecuritypolicy.bats | 41 - .../gateway-resources-serviceaccount.bats | 23 - charts/consul/test/unit/helpers.bats | 2 +- .../consul/test/unit/server-acl-init-job.bats | 148 +- .../test/unit/server-config-configmap.bats | 94 +- .../consul/test/unit/server-statefulset.bats | 90 +- .../unit/telemetry-collector-configmap.bats | 29 - .../unit/telemetry-collector-deployment.bats | 1133 -------- ...telemetry-collector-podsecuritypolicy.bats | 21 - .../test/unit/telemetry-collector-role.bats | 21 - .../unit/telemetry-collector-rolebinding.bats | 21 - .../unit/telemetry-collector-service.bats | 86 - .../telemetry-collector-serviceaccount.bats | 84 - .../webhook-cert-manager-clusterrole.bats | 5 +- ...bhook-cert-manager-clusterrolebinding.bats | 5 +- .../unit/webhook-cert-manager-configmap.bats | 5 +- .../unit/webhook-cert-manager-deployment.bats | 5 +- ...ebhook-cert-manager-podsecuritypolicy.bats | 5 +- .../webhook-cert-manager-serviceaccount.bats | 5 +- charts/consul/values.yaml | 337 +-- charts/demo/Chart.yaml | 3 - charts/demo/templates/frontend.yaml | 3 - charts/demo/templates/intentions.yaml | 14 - charts/demo/templates/nginx.yaml | 3 - charts/demo/templates/payments.yaml | 3 - charts/demo/templates/postgres.yaml | 3 - charts/demo/templates/products-api.yaml | 3 - charts/demo/templates/public-api.yaml | 3 - charts/demo/values.yaml | 3 - charts/embed_chart.go | 4 - charts/go.mod | 2 +- cli/cmd/config/command.go | 3 - cli/cmd/config/read/command.go | 3 - cli/cmd/config/read/command_test.go | 3 - cli/cmd/install/install.go | 5 +- cli/cmd/install/install_test.go | 21 +- cli/cmd/proxy/command.go | 3 - cli/cmd/proxy/list/command.go | 3 - cli/cmd/proxy/list/command_test.go | 3 - cli/cmd/proxy/loglevel/command.go | 349 --- cli/cmd/proxy/loglevel/command_test.go | 296 -- cli/cmd/proxy/read/command.go | 45 +- cli/cmd/proxy/read/command_test.go | 117 +- .../http.go => cmd/proxy/read/config.go} | 63 +- .../proxy/read/config_test.go} | 60 +- .../proxy/read/envoy_types.go} | 5 +- cli/cmd/proxy/read/filters.go | 17 +- cli/cmd/proxy/read/filters_test.go | 49 +- cli/cmd/proxy/read/format.go | 20 +- cli/cmd/proxy/read/format_test.go | 21 +- .../proxy/read}/test_clusters.json | 0 .../proxy/read}/test_config_dump.json | 0 cli/cmd/status/status.go | 3 - cli/cmd/status/status_test.go | 3 - cli/cmd/troubleshoot/command.go | 29 - cli/cmd/troubleshoot/proxy/proxy.go | 285 -- cli/cmd/troubleshoot/proxy/proxy_test.go | 75 - cli/cmd/troubleshoot/upstreams/upstreams.go | 277 -- .../troubleshoot/upstreams/upstreams_test.go | 130 - cli/cmd/uninstall/uninstall.go | 3 - cli/cmd/uninstall/uninstall_test.go | 3 - cli/cmd/upgrade/upgrade.go | 6 +- cli/cmd/upgrade/upgrade_test.go | 7 +- cli/cmd/version/version.go | 3 - cli/commands.go | 28 +- cli/common/base.go | 3 - cli/common/diff.go | 3 - cli/common/diff_test.go | 3 - cli/common/envoy/logger_params.go | 169 -- cli/common/envoy/logger_params_test.go | 175 -- .../envoy/testdata/fetch_debug_levels.txt | 59 - cli/common/error.go | 3 - cli/common/flag/doc.go | 3 - cli/common/flag/flag.go | 3 - cli/common/flag/flag_bool.go | 3 - cli/common/flag/flag_enum.go | 3 - cli/common/flag/flag_enum_single.go | 3 - cli/common/flag/flag_float.go | 3 - cli/common/flag/flag_int.go | 3 - cli/common/flag/flag_string.go | 3 - cli/common/flag/flag_string_map.go | 3 - cli/common/flag/flag_string_slice.go | 3 - cli/common/flag/flag_string_slice_test.go | 3 - cli/common/flag/flag_time.go | 3 - cli/common/flag/flag_var.go | 3 - cli/common/flag/set.go | 3 - cli/common/flag/set_test.go | 3 - cli/common/portforward.go | 3 - cli/common/portforward_test.go | 3 - cli/common/terminal/basic.go | 3 - cli/common/terminal/doc.go | 3 - cli/common/terminal/table.go | 21 +- cli/common/terminal/ui.go | 3 - cli/common/usage.go | 3 - cli/common/utils.go | 3 - cli/common/utils_test.go | 3 - cli/config/config.go | 3 - cli/go.mod | 94 +- cli/go.sum | 423 +-- cli/helm/action.go | 3 - cli/helm/chart.go | 7 +- cli/helm/chart_test.go | 9 +- cli/helm/install.go | 3 - cli/helm/install_test.go | 3 - cli/helm/mock.go | 3 - cli/helm/test_fixtures/consul/Chart.yaml | 3 - .../test_fixtures/consul/templates/foo.yaml | 3 - cli/helm/test_fixtures/consul/values.yaml | 3 - cli/helm/upgrade.go | 3 - cli/helm/upgrade_test.go | 3 - cli/helm/values.go | 15 +- cli/main.go | 3 - cli/preset/cloud_preset.go | 19 +- cli/preset/cloud_preset_test.go | 59 +- cli/preset/demo.go | 5 +- cli/preset/preset.go | 3 - cli/preset/preset_test.go | 3 - cli/preset/quickstart.go | 5 +- cli/preset/secure.go | 5 +- cli/release/release.go | 3 - cli/release/release_test.go | 3 - cli/validation/kubernetes.go | 3 - cli/validation/kubernetes_test.go | 3 - cli/version/fips_build.go | 30 - cli/version/non_fips_build.go | 15 - cli/version/version.go | 11 +- control-plane/Dockerfile | 16 +- control-plane/PROJECT | 49 - .../api-gateway/binding/annotations.go | 37 - .../api-gateway/binding/annotations_test.go | 207 -- control-plane/api-gateway/binding/binder.go | 389 --- .../api-gateway/binding/binder_test.go | 2384 ----------------- .../api-gateway/binding/reference_grant.go | 148 - .../binding/reference_grant_test.go | 454 ---- .../api-gateway/binding/registration.go | 92 - .../api-gateway/binding/registration_test.go | 83 - control-plane/api-gateway/binding/result.go | 564 ---- .../api-gateway/binding/result_test.go | 70 - .../api-gateway/binding/route_binding.go | 513 ---- control-plane/api-gateway/binding/setter.go | 132 - .../api-gateway/binding/setter_test.go | 42 - control-plane/api-gateway/binding/snapshot.go | 66 - .../api-gateway/binding/validation.go | 559 ---- .../api-gateway/binding/validation_test.go | 862 ------ control-plane/api-gateway/cache/consul.go | 526 ---- .../api-gateway/cache/consul_test.go | 2027 -------------- control-plane/api-gateway/cache/gateway.go | 140 - control-plane/api-gateway/cache/kubernetes.go | 32 - .../api-gateway/cache/subscription.go | 30 - control-plane/api-gateway/common/constants.go | 15 - control-plane/api-gateway/common/diff.go | 274 -- .../api-gateway/common/finalizers.go | 60 - .../api-gateway/common/helm_config.go | 65 - control-plane/api-gateway/common/helpers.go | 237 -- .../api-gateway/common/helpers_test.go | 175 -- control-plane/api-gateway/common/labels.go | 39 - control-plane/api-gateway/common/reference.go | 184 -- control-plane/api-gateway/common/resources.go | 647 ----- control-plane/api-gateway/common/secrets.go | 123 - .../api-gateway/common/secrets_test.go | 108 - .../api-gateway/common/translation.go | 446 --- .../api-gateway/common/translation_test.go | 1645 ------------ .../api-gateway/controllers/finalizer.go | 44 - .../api-gateway/controllers/finalizer_test.go | 84 - .../controllers/gateway_controller.go | 1166 -------- .../gateway_controller_integration_test.go | 1325 --------- .../controllers/gateway_controller_test.go | 642 ----- .../controllers/gatewayclass_controller.go | 271 -- .../gatewayclass_controller_test.go | 276 -- .../gatewayclassconfig_controller.go | 139 - .../gatewayclassconfig_controller_test.go | 123 - .../api-gateway/controllers/index.go | 327 --- .../api-gateway/controllers/index_test.go | 13 - .../api-gateway/gatekeeper/dataplane.go | 176 -- .../api-gateway/gatekeeper/deployment.go | 234 -- .../api-gateway/gatekeeper/gatekeeper.go | 103 - .../api-gateway/gatekeeper/gatekeeper_test.go | 1299 --------- control-plane/api-gateway/gatekeeper/init.go | 197 -- control-plane/api-gateway/gatekeeper/role.go | 94 - .../api-gateway/gatekeeper/rolebinding.go | 90 - .../api-gateway/gatekeeper/service.go | 154 -- .../api-gateway/gatekeeper/serviceaccount.go | 80 - control-plane/api/common/common.go | 24 +- control-plane/api/common/configentry.go | 3 - .../api/common/configentry_webhook.go | 3 - .../api/common/configentry_webhook_test.go | 3 - .../api/v1alpha1/api_gateway_types.go | 141 - .../api/v1alpha1/api_gateway_types_test.go | 49 - .../controlplanerequestlimit_types.go | 271 -- .../controlplanerequestlimit_types_test.go | 569 ---- .../controlplanerequestlimit_webhook.go | 83 - .../controlplanerequestlimit_webhook_test.go | 145 - .../api/v1alpha1/exportedservices_types.go | 43 +- .../v1alpha1/exportedservices_types_test.go | 122 +- .../api/v1alpha1/exportedservices_webhook.go | 3 - .../v1alpha1/exportedservices_webhook_test.go | 5 +- .../api/v1alpha1/groupversion_info.go | 3 - .../api/v1alpha1/ingressgateway_types.go | 3 - .../api/v1alpha1/ingressgateway_types_test.go | 3 - .../api/v1alpha1/ingressgateway_webhook.go | 3 - .../api/v1alpha1/jwtprovider_types.go | 663 ----- .../api/v1alpha1/jwtprovider_types_test.go | 730 ----- .../api/v1alpha1/jwtprovider_webhook.go | 61 - control-plane/api/v1alpha1/mesh_types.go | 17 +- control-plane/api/v1alpha1/mesh_types_test.go | 7 - control-plane/api/v1alpha1/mesh_webhook.go | 3 - .../api/v1alpha1/mesh_webhook_test.go | 3 - .../api/v1alpha1/peeringacceptor_types.go | 3 - .../v1alpha1/peeringacceptor_types_test.go | 3 - .../api/v1alpha1/peeringacceptor_webhook.go | 3 - .../v1alpha1/peeringacceptor_webhook_test.go | 3 - .../api/v1alpha1/peeringdialer_types.go | 3 - .../api/v1alpha1/peeringdialer_types_test.go | 3 - .../api/v1alpha1/peeringdialer_webhook.go | 3 - .../v1alpha1/peeringdialer_webhook_test.go | 3 - .../api/v1alpha1/proxydefaults_types.go | 140 +- .../api/v1alpha1/proxydefaults_types_test.go | 339 +-- .../api/v1alpha1/proxydefaults_webhook.go | 3 - .../v1alpha1/proxydefaults_webhook_test.go | 5 +- .../api/v1alpha1/routeretryfilter_types.go | 55 - .../api/v1alpha1/routetimeoutfilter_types.go | 52 - .../api/v1alpha1/samenessgroup_types.go | 265 -- .../api/v1alpha1/samenessgroup_types_test.go | 390 --- .../api/v1alpha1/samenessgroup_webhook.go | 61 - .../api/v1alpha1/servicedefaults_types.go | 90 +- .../v1alpha1/servicedefaults_types_test.go | 299 +-- .../api/v1alpha1/servicedefaults_webhook.go | 3 - .../api/v1alpha1/serviceintentions_types.go | 176 +- .../v1alpha1/serviceintentions_types_test.go | 368 +-- .../api/v1alpha1/serviceintentions_webhook.go | 3 - .../serviceintentions_webhook_test.go | 3 - .../api/v1alpha1/serviceresolver_types.go | 241 +- .../v1alpha1/serviceresolver_types_test.go | 668 +---- .../api/v1alpha1/serviceresolver_webhook.go | 3 - .../api/v1alpha1/servicerouter_types.go | 3 - .../api/v1alpha1/servicerouter_types_test.go | 3 - .../api/v1alpha1/servicerouter_webhook.go | 3 - .../api/v1alpha1/servicesplitter_types.go | 3 - .../v1alpha1/servicesplitter_types_test.go | 3 - .../api/v1alpha1/servicesplitter_webhook.go | 3 - control-plane/api/v1alpha1/shared_types.go | 165 -- control-plane/api/v1alpha1/status.go | 3 - .../api/v1alpha1/terminatinggateway_types.go | 3 - .../v1alpha1/terminatinggateway_types_test.go | 3 - .../v1alpha1/terminatinggateway_webhook.go | 3 - .../api/v1alpha1/zz_generated.deepcopy.go | 1784 ++---------- .../build-support/controller/README.md | 5 + .../controller/boilerplate.go.txt | 0 .../build-support/functions/00-vars.sh | 3 - .../build-support/functions/10-util.sh | 4 +- .../build-support/functions/20-build.sh | 17 +- .../build-support/functions/40-publish.sh | 3 - .../build-support/scripts/build-local.sh | 10 - .../scripts/consul-enterprise-version.sh | 1 + .../build-support/scripts/functions.sh | 3 - .../scripts/terraformfmtcheck.sh | 3 - .../build-support/scripts/version.sh | 3 - control-plane/catalog/to-consul/annotation.go | 3 - control-plane/catalog/to-consul/resource.go | 9 +- .../catalog/to-consul/resource_test.go | 3 - control-plane/catalog/to-consul/service_id.go | 3 - control-plane/catalog/to-consul/syncer.go | 3 - .../catalog/to-consul/syncer_ent_test.go | 3 - .../catalog/to-consul/syncer_test.go | 3 - control-plane/catalog/to-consul/testing.go | 3 - control-plane/catalog/to-k8s/sink.go | 3 - control-plane/catalog/to-k8s/sink_test.go | 3 - control-plane/catalog/to-k8s/source.go | 3 - control-plane/catalog/to-k8s/source_test.go | 3 - control-plane/catalog/to-k8s/testing.go | 3 - control-plane/cni/config/config.go | 3 - control-plane/cni/go.mod | 18 +- control-plane/cni/go.sum | 30 +- control-plane/cni/main.go | 3 - control-plane/cni/main_test.go | 3 - control-plane/commands.go | 17 - ...shicorp.com_controlplanerequestlimits.yaml | 198 -- ...consul.hashicorp.com_exportedservices.yaml | 11 +- ...sul.hashicorp.com_gatewayclassconfigs.yaml | 146 - .../consul.hashicorp.com_ingressgateways.yaml | 3 - .../consul.hashicorp.com_jwtproviders.yaml | 260 -- .../bases/consul.hashicorp.com_meshes.yaml | 8 - .../consul.hashicorp.com_meshservices.yaml | 59 - ...consul.hashicorp.com_peeringacceptors.yaml | 3 - .../consul.hashicorp.com_peeringdialers.yaml | 3 - .../consul.hashicorp.com_proxydefaults.yaml | 89 - ...onsul.hashicorp.com_routeretryfilters.yaml | 112 - ...sul.hashicorp.com_routetimeoutfilters.yaml | 110 - .../consul.hashicorp.com_samenessgroups.yaml | 132 - .../consul.hashicorp.com_servicedefaults.yaml | 71 +- ...onsul.hashicorp.com_serviceintentions.yaml | 85 +- ...consul.hashicorp.com_serviceresolvers.yaml | 41 - .../consul.hashicorp.com_servicerouters.yaml | 3 - ...consul.hashicorp.com_servicesplitters.yaml | 3 - ...sul.hashicorp.com_terminatinggateways.yaml | 3 - ...ewayclasses.gateway.networking.k8s.io.yaml | 323 --- .../gateways.gateway.networking.k8s.io.yaml | 877 ------ .../grpcroutes.gateway.networking.k8s.io.yaml | 761 ------ .../httproutes.gateway.networking.k8s.io.yaml | 1909 ------------- .../config/crd/external/kustomization.yaml | 10 - ...rencegrants.gateway.networking.k8s.io.yaml | 203 -- .../tcproutes.gateway.networking.k8s.io.yaml | 276 -- .../tlsroutes.gateway.networking.k8s.io.yaml | 286 -- .../udproutes.gateway.networking.k8s.io.yaml | 276 -- control-plane/config/crd/kustomization.yaml | 27 - control-plane/config/crd/kustomizeconfig.yaml | 20 - control-plane/config/rbac/role.yaml | 63 - control-plane/config/webhook/manifests.yaml | 66 - control-plane/connect-inject/common/common.go | 3 - .../connect-inject/common/common_test.go | 3 - .../constants/annotations_and_labels.go | 3 - .../connect-inject/constants/constants.go | 12 - .../endpoints/consul_client_health_checks.go | 3 - .../consul_client_health_checks_test.go | 3 - .../endpoints/endpoints_controller.go | 74 +- .../endpoints_controller_ent_test.go | 21 +- .../endpoints/endpoints_controller_test.go | 215 +- .../peering/peering_acceptor_controller.go | 3 - .../peering_acceptor_controller_test.go | 3 - .../peering/peering_dialer_controller.go | 3 - .../peering/peering_dialer_controller_test.go | 3 - .../metrics/metrics_configuration.go | 3 - .../metrics/metrics_configuration_test.go | 3 - .../webhook/consul_dataplane_sidecar.go | 3 - .../webhook/consul_dataplane_sidecar_test.go | 3 - .../connect-inject/webhook/container_env.go | 3 - .../webhook/container_env_test.go | 3 - .../connect-inject/webhook/container_init.go | 3 - .../webhook/container_init_test.go | 3 - .../webhook/container_volume.go | 3 - control-plane/connect-inject/webhook/dns.go | 3 - .../connect-inject/webhook/dns_test.go | 3 - .../webhook/health_checks_test.go | 3 - .../connect-inject/webhook/heath_checks.go | 3 - .../connect-inject/webhook/mesh_webhook.go | 3 - .../webhook/mesh_webhook_ent_test.go | 41 +- .../webhook/mesh_webhook_test.go | 3 - .../webhook/redirect_traffic.go | 3 - .../webhook/redirect_traffic_test.go | 3 - control-plane/consul/consul.go | 3 - control-plane/consul/consul_test.go | 3 - .../configentry_controller.go | 84 +- .../configentry_controller_ent_test.go | 759 ++++++ .../configentry_controller_test.go | 363 +-- .../exportedservices_controller.go | 5 +- .../exportedservices_controller_ent_test.go | 31 +- .../ingressgateway_controller.go | 5 +- .../mesh_controller.go | 5 +- .../proxydefaults_controller.go | 5 +- .../servicedefaults_controller.go | 5 +- .../serviceintentions_controller.go | 5 +- .../serviceresolver_controller.go | 5 +- .../servicerouter_controller.go | 5 +- .../servicesplitter_controller.go | 5 +- .../terminatinggateway_controller.go | 5 +- .../configentry_controller_ent_test.go | 1387 ---------- .../controlplanerequestlimit_controller.go | 43 - .../controllers/jwtprovider_controller.go | 43 - .../controllers/samenessgroups_controller.go | 45 - control-plane/go.mod | 98 +- control-plane/go.sum | 342 ++- .../hack/lint-api-new-client/main.go | 3 - control-plane/helper/cert/notify.go | 3 - control-plane/helper/cert/notify_test.go | 3 - control-plane/helper/cert/source.go | 3 - control-plane/helper/cert/source_gen.go | 3 - control-plane/helper/cert/source_gen_test.go | 3 - control-plane/helper/cert/tls_util.go | 3 - control-plane/helper/coalesce/coalesce.go | 3 - .../helper/coalesce/coalesce_test.go | 3 - control-plane/helper/controller/controller.go | 3 - .../helper/controller/controller_test.go | 3 - control-plane/helper/controller/resource.go | 3 - control-plane/helper/controller/testing.go | 3 - control-plane/helper/go-discover/discover.go | 3 - .../helper/go-discover/discover_test.go | 3 - .../helper/go-discover/mocks/mock_provider.go | 3 - .../mutating_webhook_configuration.go | 3 - .../mutating_webhook_configuration_test.go | 3 - control-plane/helper/parsetags/parsetags.go | 3 - .../helper/parsetags/parsetags_test.go | 3 - control-plane/helper/test/test_util.go | 3 - control-plane/main.go | 6 +- control-plane/namespaces/namespaces.go | 3 - control-plane/namespaces/namespaces_test.go | 3 - control-plane/subcommand/acl-init/command.go | 3 - .../subcommand/acl-init/command_test.go | 3 - control-plane/subcommand/auth.go | 3 - control-plane/subcommand/common/common.go | 3 - .../subcommand/common/common_test.go | 22 +- control-plane/subcommand/common/test_util.go | 3 - .../subcommand/connect-init/command.go | 28 +- .../connect-init/command_ent_test.go | 3 - .../subcommand/connect-init/command_test.go | 7 +- .../subcommand/consul-logout/command.go | 3 - .../subcommand/consul-logout/command_test.go | 9 +- .../create-federation-secret/command.go | 3 - .../create-federation-secret/command_test.go | 21 +- .../delete-completed-job/command.go | 3 - .../delete-completed-job/command_test.go | 3 - .../subcommand/fetch-server-region/command.go | 158 -- .../fetch-server-region/command_test.go | 114 - control-plane/subcommand/flags/consul.go | 3 - control-plane/subcommand/flags/consul_test.go | 3 - .../subcommand/flags/flag_map_value.go | 3 - .../subcommand/flags/flag_map_value_test.go | 3 - .../subcommand/flags/flag_slice_value.go | 3 - .../subcommand/flags/flag_slice_value_test.go | 3 - control-plane/subcommand/flags/flags.go | 3 - control-plane/subcommand/flags/http.go | 3 - control-plane/subcommand/flags/http_test.go | 3 - control-plane/subcommand/flags/k8s.go | 3 - control-plane/subcommand/flags/mapset.go | 3 - control-plane/subcommand/flags/usage.go | 3 - control-plane/subcommand/flags/usage_test.go | 3 - .../subcommand/gateway-cleanup/command.go | 193 -- .../gateway-cleanup/command_test.go | 83 - .../subcommand/gateway-resources/command.go | 365 --- .../gateway-resources/command_test.go | 255 -- .../get-consul-client-ca/command.go | 3 - .../get-consul-client-ca/command_test.go | 14 +- .../gossip-encryption-autogenerate/command.go | 3 - .../command_test.go | 3 - .../subcommand/inject-connect/command.go | 192 +- .../subcommand/inject-connect/command_test.go | 3 - .../subcommand/install-cni/binary.go | 3 - .../subcommand/install-cni/binary_test.go | 3 - .../subcommand/install-cni/cniconfig.go | 3 - .../subcommand/install-cni/cniconfig_test.go | 3 - .../subcommand/install-cni/command.go | 3 - .../subcommand/install-cni/command_test.go | 3 - .../subcommand/install-cni/kubeconfig.go | 3 - .../subcommand/install-cni/kubeconfig_test.go | 3 - .../subcommand/partition-init/command.go | 3 - .../partition-init/command_ent_test.go | 3 - .../server-acl-init/anonymous_token.go | 3 - .../subcommand/server-acl-init/command.go | 153 +- .../server-acl-init/command_ent_test.go | 6 +- .../server-acl-init/command_test.go | 460 ++-- .../server-acl-init/connect_inject.go | 3 - .../server-acl-init/connect_inject_test.go | 3 - .../server-acl-init/create_or_update.go | 3 - .../server-acl-init/create_or_update_test.go | 3 - .../server-acl-init/k8s_secrets_backend.go | 65 - .../subcommand/server-acl-init/rules.go | 8 +- .../subcommand/server-acl-init/rules_test.go | 32 +- .../server-acl-init/secrets_backend.go | 21 - .../subcommand/server-acl-init/servers.go | 57 +- .../test_fake_secrets_backend.go | 23 - .../server-acl-init/vault_secrets_backend.go | 69 - .../subcommand/sync-catalog/command.go | 3 - .../sync-catalog/command_ent_test.go | 3 - .../subcommand/sync-catalog/command_test.go | 3 - control-plane/subcommand/tls-init/command.go | 3 - .../subcommand/tls-init/command_test.go | 3 - control-plane/subcommand/version/command.go | 3 - .../webhook-cert-manager/command.go | 3 - .../webhook-cert-manager/command_test.go | 3 - .../webhook-cert-manager/mocks/mocks.go | 3 - control-plane/version/fips_build.go | 30 - control-plane/version/non_fips_build.go | 15 - control-plane/version/version.go | 11 +- docs/admin-partitions-with-acls.md | 98 + hack/aws-acceptance-test-cleanup/go.mod | 2 +- hack/aws-acceptance-test-cleanup/main.go | 128 - hack/copy-crds-to-chart/go.mod | 2 +- hack/copy-crds-to-chart/main.go | 119 +- hack/helm-reference-gen/doc_node.go | 3 - .../fixtures/full-values.yaml | 3 - hack/helm-reference-gen/go.mod | 2 +- hack/helm-reference-gen/main.go | 10 +- hack/helm-reference-gen/main_test.go | 3 - hack/helm-reference-gen/parse_error.go | 3 - 948 files changed, 3772 insertions(+), 61291 deletions(-) delete mode 100644 .changelog/1914.txt delete mode 100644 .changelog/1920.txt create mode 100644 .changelog/1976.txt delete mode 100644 .changelog/2029.txt delete mode 100644 .changelog/2030.txt delete mode 100644 .changelog/2048.txt delete mode 100644 .changelog/2075.txt delete mode 100644 .changelog/2086.txt delete mode 100644 .changelog/2093.txt delete mode 100644 .changelog/2097.txt delete mode 100644 .changelog/2100.txt rename .changelog/{2102.txt => 2108.txt} (76%) delete mode 100644 .changelog/2124.txt delete mode 100644 .changelog/2134.txt create mode 100644 .changelog/2140.txt delete mode 100644 .changelog/2143.txt delete mode 100644 .changelog/2152.txt create mode 100644 .changelog/2159.txt delete mode 100644 .changelog/2165.txt delete mode 100644 .changelog/2166.txt delete mode 100644 .changelog/2170.txt delete mode 100644 .changelog/2183.txt delete mode 100644 .changelog/2184.txt delete mode 100644 .changelog/2195.txt delete mode 100644 .changelog/2205.txt delete mode 100644 .changelog/2209.txt delete mode 100644 .changelog/2213.txt create mode 100644 .changelog/2225.txt delete mode 100644 .changelog/2304.txt delete mode 100644 .changelog/2346.txt delete mode 100644 .changelog/2413.txt delete mode 100644 .changelog/2420.txt delete mode 100644 .changelog/2476.txt delete mode 100644 .changelog/2478.txt delete mode 100644 .changelog/2520.txt delete mode 100644 .changelog/2524.txt delete mode 100644 .changelog/2597.txt rename .changelog/{2642.txt => 2650.txt} (70%) create mode 100644 .changelog/2678.txt delete mode 100644 .changelog/2707.txt delete mode 100644 .changelog/2711.txt rename .changelog/{2710.txt => 2717.txt} (81%) delete mode 100644 .changelog/2735.txt delete mode 100644 .changelog/2748.txt delete mode 100644 .changelog/2784.txt delete mode 100644 .copywrite.hcl delete mode 100644 .github/workflows/nightly-api-gateway-conformance.yml delete mode 100644 .github/workflows/nightly-cleanup.yml delete mode 100644 .github/workflows/weekly-acceptance-1-2-x.yml delete mode 100644 acceptance/tests/api-gateway/api_gateway_external_servers_test.go delete mode 100644 acceptance/tests/api-gateway/api_gateway_gatewayclassconfig_test.go delete mode 100644 acceptance/tests/api-gateway/api_gateway_lifecycle_test.go delete mode 100644 acceptance/tests/api-gateway/api_gateway_tenancy_test.go delete mode 100644 acceptance/tests/api-gateway/api_gateway_test.go create mode 100644 acceptance/tests/api-gateway/example_test.go delete mode 100644 acceptance/tests/cloud/basic_test.go delete mode 100644 acceptance/tests/cloud/main_test.go delete mode 100644 acceptance/tests/cloud/remote_dev_test.go delete mode 100644 acceptance/tests/connect/permissive_mtls_test.go delete mode 100644 acceptance/tests/fixtures/bases/api-gateway/apigateway.yaml delete mode 100644 acceptance/tests/fixtures/bases/api-gateway/certificate.yaml delete mode 100644 acceptance/tests/fixtures/bases/api-gateway/gatewayclass.yaml delete mode 100644 acceptance/tests/fixtures/bases/api-gateway/gatewayclassconfig.yaml delete mode 100644 acceptance/tests/fixtures/bases/api-gateway/httproute.yaml delete mode 100644 acceptance/tests/fixtures/bases/api-gateway/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/bases/api-gateway/meshservice.yaml delete mode 100644 acceptance/tests/fixtures/bases/cloud/hcp-mock/deployment.yaml delete mode 100644 acceptance/tests/fixtures/bases/cloud/hcp-mock/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/bases/cloud/hcp-mock/service.yaml delete mode 100644 acceptance/tests/fixtures/bases/cloud/hcp-mock/serviceaccount.yaml delete mode 100644 acceptance/tests/fixtures/bases/cloud/service-intentions/acl.yaml delete mode 100644 acceptance/tests/fixtures/bases/cloud/service-intentions/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/bases/crds-oss/controlplanerequestlimit.yaml delete mode 100644 acceptance/tests/fixtures/bases/crds-oss/exportedservices.yaml delete mode 100644 acceptance/tests/fixtures/bases/crds-oss/jwtprovider.yaml create mode 100644 acceptance/tests/fixtures/bases/crds-oss/serviceexports.yaml delete mode 100644 acceptance/tests/fixtures/bases/resolver-redirect/intention.yaml delete mode 100644 acceptance/tests/fixtures/bases/resolver-redirect/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/bases/resolver-redirect/resolver.yaml delete mode 100644 acceptance/tests/fixtures/bases/resolver-redirect/service.yaml delete mode 100644 acceptance/tests/fixtures/bases/resolver-redirect/serviceaccount.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/cluster-01-a-default-ns/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/cluster-01-a-default-ns/sameness.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/cluster-01-b-default-ns/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/cluster-01-b-default-ns/sameness.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/cluster-02-a-default-ns/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/cluster-02-a-default-ns/sameness.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/cluster-03-a-default-ns/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/cluster-03-a-default-ns/sameness.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/exportedservices-ap1/exportedservices-ap1.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/exportedservices-ap1/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/override-ns/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/override-ns/service-defaults.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-01-a-dialer/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-01-a-dialer/peering-dialer-cluster-02-a.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-01-a-dialer/peering-dialer-cluster-03-a.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-01-b-dialer/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-01-b-dialer/peering-dialer-cluster-02-a.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-01-b-dialer/peering-dialer-cluster-03-a.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-acceptor/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-acceptor/peering-acceptor-cluster-01-a.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-acceptor/peering-acceptor-cluster-01-b.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-dialer/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-dialer/peering-dialer-cluster-03-a.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/peering-acceptor-cluster-01-a.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/peering-acceptor-cluster-01-b.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/peering-acceptor-cluster-02-a.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/peering/mesh/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/bases/sameness/peering/mesh/mesh.yaml delete mode 100644 acceptance/tests/fixtures/bases/static-server-tcp/anyuid-scc-rolebinding.yaml delete mode 100644 acceptance/tests/fixtures/bases/static-server-tcp/deployment.yaml delete mode 100644 acceptance/tests/fixtures/bases/static-server-tcp/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/bases/static-server-tcp/privileged-scc-rolebinding.yaml delete mode 100644 acceptance/tests/fixtures/bases/static-server-tcp/psp-rolebinding.yaml delete mode 100644 acceptance/tests/fixtures/bases/static-server-tcp/service.yaml delete mode 100644 acceptance/tests/fixtures/bases/static-server-tcp/serviceaccount.yaml delete mode 100644 acceptance/tests/fixtures/bases/static-server-tcp/servicedefaults.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/certificate/certificate.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/certificate/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/dc1-to-dc2-resolver/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/dc1-to-dc2-resolver/serviceresolver.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/dc2-to-dc1-resolver/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/dc2-to-dc1-resolver/serviceresolver.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/gateway/gateway.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/gateway/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/httproute/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/httproute/route.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/jwt-auth/api-gateway.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/jwt-auth/gateway-policy.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute-auth.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-provider.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-route-filter.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/jwt-auth/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/mesh/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/mesh/proxydefaults.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/peer-resolver/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/peer-resolver/serviceresolver.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/resolver/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/resolver/serviceresolver.yaml delete mode 100644 acceptance/tests/fixtures/cases/api-gateways/tcproute/route.yaml delete mode 100644 acceptance/tests/fixtures/cases/permissive-mtls/mesh-config-permissive-allowed.yaml delete mode 100644 acceptance/tests/fixtures/cases/permissive-mtls/service-defaults-static-server-permissive.yaml delete mode 100644 acceptance/tests/fixtures/cases/permissive-mtls/service-defaults-static-server-strict.yaml delete mode 100644 acceptance/tests/fixtures/cases/resolver-redirect-virtualip/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/cluster-01-a-acceptor/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/cluster-01-a-acceptor/patch.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/cluster-01-b-acceptor/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/cluster-01-b-acceptor/patch.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/cluster-02-a-acceptor/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/cluster-02-a-acceptor/patch.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/cluster-03-a-acceptor/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/cluster-03-a-acceptor/patch.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/exported-services/ap1-partition/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/exported-services/ap1-partition/patch.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/exported-services/default-partition/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/exported-services/default-partition/patch.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition-tproxy/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition-tproxy/patch.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition/patch.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/static-client/default-partition-tproxy/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/static-client/default-partition-tproxy/patch.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/static-client/default-partition/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/static-client/default-partition/patch.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/static-server/dc1-default/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/static-server/dc1-default/patch.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/static-server/dc1-partition/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/static-server/dc1-partition/patch.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/static-server/dc2/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/static-server/dc2/patch.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/static-server/dc3/kustomization.yaml delete mode 100644 acceptance/tests/fixtures/cases/sameness/static-server/dc3/patch.yaml delete mode 100644 acceptance/tests/partitions/partitions_gateway_test.go delete mode 100644 acceptance/tests/peering/peering_gateway_test.go delete mode 100644 acceptance/tests/sameness/main_test.go delete mode 100644 acceptance/tests/sameness/sameness_test.go delete mode 100644 acceptance/tests/wan-federation/wan_federation_gateway_test.go delete mode 100644 charts/consul/templates/crd-controlplanerequestlimits.yaml delete mode 100644 charts/consul/templates/crd-gatewayclassconfigs.yaml delete mode 100644 charts/consul/templates/crd-gatewayclasses.yaml delete mode 100644 charts/consul/templates/crd-gateways.yaml delete mode 100644 charts/consul/templates/crd-grpcroutes.yaml delete mode 100644 charts/consul/templates/crd-httproutes.yaml delete mode 100644 charts/consul/templates/crd-jwtproviders.yaml delete mode 100644 charts/consul/templates/crd-meshservices.yaml delete mode 100644 charts/consul/templates/crd-referencegrants.yaml delete mode 100644 charts/consul/templates/crd-routeretryfilters.yaml delete mode 100644 charts/consul/templates/crd-routetimeoutfilters.yaml delete mode 100644 charts/consul/templates/crd-samenessgroups.yaml delete mode 100644 charts/consul/templates/crd-tcproutes.yaml delete mode 100644 charts/consul/templates/crd-tlsroutes.yaml delete mode 100644 charts/consul/templates/crd-udproutes.yaml delete mode 100644 charts/consul/templates/gateway-cleanup-clusterrole.yaml delete mode 100644 charts/consul/templates/gateway-cleanup-clusterrolebinding.yaml delete mode 100644 charts/consul/templates/gateway-cleanup-job.yaml delete mode 100644 charts/consul/templates/gateway-cleanup-podsecuritypolicy.yaml delete mode 100644 charts/consul/templates/gateway-cleanup-serviceaccount.yaml delete mode 100644 charts/consul/templates/gateway-resources-clusterrole.yaml delete mode 100644 charts/consul/templates/gateway-resources-clusterrolebinding.yaml delete mode 100644 charts/consul/templates/gateway-resources-job.yaml delete mode 100644 charts/consul/templates/gateway-resources-podsecuritypolicy.yaml delete mode 100644 charts/consul/templates/gateway-resources-serviceaccount.yaml delete mode 100644 charts/consul/templates/server-clusterrole.yaml delete mode 100644 charts/consul/templates/server-clusterrolebinding.yaml delete mode 100644 charts/consul/templates/telemetry-collector-configmap.yaml delete mode 100644 charts/consul/templates/telemetry-collector-deployment.yaml delete mode 100644 charts/consul/templates/telemetry-collector-podsecuritypolicy.yaml delete mode 100644 charts/consul/templates/telemetry-collector-role.yaml delete mode 100644 charts/consul/templates/telemetry-collector-rolebinding.yaml delete mode 100644 charts/consul/templates/telemetry-collector-service.yaml delete mode 100644 charts/consul/templates/telemetry-collector-serviceaccount.yaml delete mode 100644 charts/consul/test/unit/crd-controlplanerequestlimits.bats delete mode 100644 charts/consul/test/unit/crd-gatewayclassconfigs.bats delete mode 100644 charts/consul/test/unit/crd-gatewayclasses.bats delete mode 100644 charts/consul/test/unit/crd-gateways.bats delete mode 100644 charts/consul/test/unit/crd-grpcroutes.bats delete mode 100644 charts/consul/test/unit/crd-httproutes.bats delete mode 100644 charts/consul/test/unit/crd-meshservices.bats delete mode 100644 charts/consul/test/unit/crd-tcproutes.bats delete mode 100644 charts/consul/test/unit/crd-tlsroutes.bats delete mode 100644 charts/consul/test/unit/crd-udproutes.bats delete mode 100644 charts/consul/test/unit/gateway-cleanup-clusterrole.bats delete mode 100644 charts/consul/test/unit/gateway-cleanup-clusterrolebinding.bats delete mode 100644 charts/consul/test/unit/gateway-cleanup-job.bats delete mode 100644 charts/consul/test/unit/gateway-cleanup-podsecuritypolicy.bats delete mode 100644 charts/consul/test/unit/gateway-cleanup-serviceaccount.bats delete mode 100644 charts/consul/test/unit/gateway-resources-clusterrole.bats delete mode 100644 charts/consul/test/unit/gateway-resources-clusterrolebinding.bats delete mode 100644 charts/consul/test/unit/gateway-resources-job.bats delete mode 100644 charts/consul/test/unit/gateway-resources-podsecuritypolicy.bats delete mode 100644 charts/consul/test/unit/gateway-resources-serviceaccount.bats delete mode 100644 charts/consul/test/unit/telemetry-collector-configmap.bats delete mode 100755 charts/consul/test/unit/telemetry-collector-deployment.bats delete mode 100644 charts/consul/test/unit/telemetry-collector-podsecuritypolicy.bats delete mode 100644 charts/consul/test/unit/telemetry-collector-role.bats delete mode 100644 charts/consul/test/unit/telemetry-collector-rolebinding.bats delete mode 100755 charts/consul/test/unit/telemetry-collector-service.bats delete mode 100644 charts/consul/test/unit/telemetry-collector-serviceaccount.bats delete mode 100644 cli/cmd/proxy/loglevel/command.go delete mode 100644 cli/cmd/proxy/loglevel/command_test.go rename cli/{common/envoy/http.go => cmd/proxy/read/config.go} (90%) rename cli/{common/envoy/http_test.go => cmd/proxy/read/config_test.go} (92%) rename cli/{common/envoy/types.go => cmd/proxy/read/envoy_types.go} (99%) rename cli/{common/envoy/testdata => cmd/proxy/read}/test_clusters.json (100%) rename cli/{common/envoy/testdata => cmd/proxy/read}/test_config_dump.json (100%) delete mode 100644 cli/cmd/troubleshoot/command.go delete mode 100644 cli/cmd/troubleshoot/proxy/proxy.go delete mode 100644 cli/cmd/troubleshoot/proxy/proxy_test.go delete mode 100644 cli/cmd/troubleshoot/upstreams/upstreams.go delete mode 100644 cli/cmd/troubleshoot/upstreams/upstreams_test.go delete mode 100644 cli/common/envoy/logger_params.go delete mode 100644 cli/common/envoy/logger_params_test.go delete mode 100644 cli/common/envoy/testdata/fetch_debug_levels.txt delete mode 100644 cli/version/fips_build.go delete mode 100644 cli/version/non_fips_build.go delete mode 100644 control-plane/api-gateway/binding/annotations.go delete mode 100644 control-plane/api-gateway/binding/annotations_test.go delete mode 100644 control-plane/api-gateway/binding/binder.go delete mode 100644 control-plane/api-gateway/binding/binder_test.go delete mode 100644 control-plane/api-gateway/binding/reference_grant.go delete mode 100644 control-plane/api-gateway/binding/reference_grant_test.go delete mode 100644 control-plane/api-gateway/binding/registration.go delete mode 100644 control-plane/api-gateway/binding/registration_test.go delete mode 100644 control-plane/api-gateway/binding/result.go delete mode 100644 control-plane/api-gateway/binding/result_test.go delete mode 100644 control-plane/api-gateway/binding/route_binding.go delete mode 100644 control-plane/api-gateway/binding/setter.go delete mode 100644 control-plane/api-gateway/binding/setter_test.go delete mode 100644 control-plane/api-gateway/binding/snapshot.go delete mode 100644 control-plane/api-gateway/binding/validation.go delete mode 100644 control-plane/api-gateway/binding/validation_test.go delete mode 100644 control-plane/api-gateway/cache/consul.go delete mode 100644 control-plane/api-gateway/cache/consul_test.go delete mode 100644 control-plane/api-gateway/cache/gateway.go delete mode 100644 control-plane/api-gateway/cache/kubernetes.go delete mode 100644 control-plane/api-gateway/cache/subscription.go delete mode 100644 control-plane/api-gateway/common/constants.go delete mode 100644 control-plane/api-gateway/common/diff.go delete mode 100644 control-plane/api-gateway/common/finalizers.go delete mode 100644 control-plane/api-gateway/common/helm_config.go delete mode 100644 control-plane/api-gateway/common/helpers.go delete mode 100644 control-plane/api-gateway/common/helpers_test.go delete mode 100644 control-plane/api-gateway/common/labels.go delete mode 100644 control-plane/api-gateway/common/reference.go delete mode 100644 control-plane/api-gateway/common/resources.go delete mode 100644 control-plane/api-gateway/common/secrets.go delete mode 100644 control-plane/api-gateway/common/secrets_test.go delete mode 100644 control-plane/api-gateway/common/translation.go delete mode 100644 control-plane/api-gateway/common/translation_test.go delete mode 100644 control-plane/api-gateway/controllers/finalizer.go delete mode 100644 control-plane/api-gateway/controllers/finalizer_test.go delete mode 100644 control-plane/api-gateway/controllers/gateway_controller.go delete mode 100644 control-plane/api-gateway/controllers/gateway_controller_integration_test.go delete mode 100644 control-plane/api-gateway/controllers/gateway_controller_test.go delete mode 100644 control-plane/api-gateway/controllers/gatewayclass_controller.go delete mode 100644 control-plane/api-gateway/controllers/gatewayclass_controller_test.go delete mode 100644 control-plane/api-gateway/controllers/gatewayclassconfig_controller.go delete mode 100644 control-plane/api-gateway/controllers/gatewayclassconfig_controller_test.go delete mode 100644 control-plane/api-gateway/controllers/index.go delete mode 100644 control-plane/api-gateway/controllers/index_test.go delete mode 100644 control-plane/api-gateway/gatekeeper/dataplane.go delete mode 100644 control-plane/api-gateway/gatekeeper/deployment.go delete mode 100644 control-plane/api-gateway/gatekeeper/gatekeeper.go delete mode 100644 control-plane/api-gateway/gatekeeper/gatekeeper_test.go delete mode 100644 control-plane/api-gateway/gatekeeper/init.go delete mode 100644 control-plane/api-gateway/gatekeeper/role.go delete mode 100644 control-plane/api-gateway/gatekeeper/rolebinding.go delete mode 100644 control-plane/api-gateway/gatekeeper/service.go delete mode 100644 control-plane/api-gateway/gatekeeper/serviceaccount.go delete mode 100644 control-plane/api/v1alpha1/api_gateway_types.go delete mode 100644 control-plane/api/v1alpha1/api_gateway_types_test.go delete mode 100644 control-plane/api/v1alpha1/controlplanerequestlimit_types.go delete mode 100644 control-plane/api/v1alpha1/controlplanerequestlimit_types_test.go delete mode 100644 control-plane/api/v1alpha1/controlplanerequestlimit_webhook.go delete mode 100644 control-plane/api/v1alpha1/controlplanerequestlimit_webhook_test.go delete mode 100644 control-plane/api/v1alpha1/jwtprovider_types.go delete mode 100644 control-plane/api/v1alpha1/jwtprovider_types_test.go delete mode 100644 control-plane/api/v1alpha1/jwtprovider_webhook.go delete mode 100644 control-plane/api/v1alpha1/routeretryfilter_types.go delete mode 100644 control-plane/api/v1alpha1/routetimeoutfilter_types.go delete mode 100644 control-plane/api/v1alpha1/samenessgroup_types.go delete mode 100644 control-plane/api/v1alpha1/samenessgroup_types_test.go delete mode 100644 control-plane/api/v1alpha1/samenessgroup_webhook.go create mode 100644 control-plane/build-support/controller/README.md create mode 100644 control-plane/build-support/controller/boilerplate.go.txt delete mode 100644 control-plane/config/crd/bases/consul.hashicorp.com_controlplanerequestlimits.yaml delete mode 100644 control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml delete mode 100644 control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml delete mode 100644 control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml delete mode 100644 control-plane/config/crd/bases/consul.hashicorp.com_routeretryfilters.yaml delete mode 100644 control-plane/config/crd/bases/consul.hashicorp.com_routetimeoutfilters.yaml delete mode 100644 control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml delete mode 100644 control-plane/config/crd/external/gatewayclasses.gateway.networking.k8s.io.yaml delete mode 100644 control-plane/config/crd/external/gateways.gateway.networking.k8s.io.yaml delete mode 100644 control-plane/config/crd/external/grpcroutes.gateway.networking.k8s.io.yaml delete mode 100644 control-plane/config/crd/external/httproutes.gateway.networking.k8s.io.yaml delete mode 100644 control-plane/config/crd/external/kustomization.yaml delete mode 100644 control-plane/config/crd/external/referencegrants.gateway.networking.k8s.io.yaml delete mode 100644 control-plane/config/crd/external/tcproutes.gateway.networking.k8s.io.yaml delete mode 100644 control-plane/config/crd/external/tlsroutes.gateway.networking.k8s.io.yaml delete mode 100644 control-plane/config/crd/external/udproutes.gateway.networking.k8s.io.yaml delete mode 100644 control-plane/config/crd/kustomization.yaml delete mode 100644 control-plane/config/crd/kustomizeconfig.yaml rename control-plane/{controllers => controller}/configentry_controller.go (84%) create mode 100644 control-plane/controller/configentry_controller_ent_test.go rename control-plane/{controllers => controller}/configentry_controller_test.go (85%) rename control-plane/{controllers => controller}/exportedservices_controller.go (94%) rename control-plane/{controllers => controller}/exportedservices_controller_ent_test.go (93%) rename control-plane/{controllers => controller}/ingressgateway_controller.go (94%) rename control-plane/{controllers => controller}/mesh_controller.go (93%) rename control-plane/{controllers => controller}/proxydefaults_controller.go (93%) rename control-plane/{controllers => controller}/servicedefaults_controller.go (94%) rename control-plane/{controllers => controller}/serviceintentions_controller.go (94%) rename control-plane/{controllers => controller}/serviceresolver_controller.go (94%) rename control-plane/{controllers => controller}/servicerouter_controller.go (94%) rename control-plane/{controllers => controller}/servicesplitter_controller.go (94%) rename control-plane/{controllers => controller}/terminatinggateway_controller.go (94%) delete mode 100644 control-plane/controllers/configentry_controller_ent_test.go delete mode 100644 control-plane/controllers/controlplanerequestlimit_controller.go delete mode 100644 control-plane/controllers/jwtprovider_controller.go delete mode 100644 control-plane/controllers/samenessgroups_controller.go delete mode 100644 control-plane/subcommand/fetch-server-region/command.go delete mode 100644 control-plane/subcommand/fetch-server-region/command_test.go delete mode 100644 control-plane/subcommand/gateway-cleanup/command.go delete mode 100644 control-plane/subcommand/gateway-cleanup/command_test.go delete mode 100644 control-plane/subcommand/gateway-resources/command.go delete mode 100644 control-plane/subcommand/gateway-resources/command_test.go delete mode 100644 control-plane/subcommand/server-acl-init/k8s_secrets_backend.go delete mode 100644 control-plane/subcommand/server-acl-init/secrets_backend.go delete mode 100644 control-plane/subcommand/server-acl-init/test_fake_secrets_backend.go delete mode 100644 control-plane/subcommand/server-acl-init/vault_secrets_backend.go delete mode 100644 control-plane/version/fips_build.go delete mode 100644 control-plane/version/non_fips_build.go create mode 100644 docs/admin-partitions-with-acls.md diff --git a/.changelog/1914.txt b/.changelog/1914.txt deleted file mode 100644 index 3179f3b0b3..0000000000 --- a/.changelog/1914.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:bug -control-plane: fix issue where consul-connect-injector acl token was unintentionally being deleted and not recreated when a container was restarted due to a livenessProbe failure. -``` \ No newline at end of file diff --git a/.changelog/1920.txt b/.changelog/1920.txt deleted file mode 100644 index 4b1f151fe4..0000000000 --- a/.changelog/1920.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:improvement -helm: When the `global.acls.bootstrapToken` field is set and the content of the secret is empty, the bootstrap ACL token is written to that secret after bootstrapping ACLs. This applies to both the Vault and Consul secrets backends. -``` diff --git a/.changelog/1976.txt b/.changelog/1976.txt new file mode 100644 index 0000000000..65024aa6f9 --- /dev/null +++ b/.changelog/1976.txt @@ -0,0 +1,3 @@ +```release-note:security +upgrade to use Go 1.19.6. This resolves vulnerabilities CVE-2022-41724 in crypto/tls and CVE-2022-41723 in net/http. +``` \ No newline at end of file diff --git a/.changelog/2029.txt b/.changelog/2029.txt deleted file mode 100644 index c864419eba..0000000000 --- a/.changelog/2029.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:bug -api-gateway: fix ACL issue where when adminPartitions and ACLs are enabled, API Gateway Controller is unable to create a new namespace in Consul -``` \ No newline at end of file diff --git a/.changelog/2030.txt b/.changelog/2030.txt deleted file mode 100644 index 46516d9513..0000000000 --- a/.changelog/2030.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:improvement -helm: add failover policy field to service resolver and proxy default CRDs -``` \ No newline at end of file diff --git a/.changelog/2048.txt b/.changelog/2048.txt deleted file mode 100644 index 5796ce2397..0000000000 --- a/.changelog/2048.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:improvement -helm: add samenessGroup CRD -``` \ No newline at end of file diff --git a/.changelog/2075.txt b/.changelog/2075.txt deleted file mode 100644 index 2f0f0344eb..0000000000 --- a/.changelog/2075.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:improvement -helm: add samenessGroup field to exported services CRD -``` \ No newline at end of file diff --git a/.changelog/2086.txt b/.changelog/2086.txt deleted file mode 100644 index d4e43a630d..0000000000 --- a/.changelog/2086.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:improvement -helm: add samenessGroup field to service resolver CRD -``` \ No newline at end of file diff --git a/.changelog/2093.txt b/.changelog/2093.txt deleted file mode 100644 index 20c657e566..0000000000 --- a/.changelog/2093.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:improvement -control-plane: set agent localities on Consul servers to the server node's `topology.kubernetes.io/region` label. -``` diff --git a/.changelog/2097.txt b/.changelog/2097.txt deleted file mode 100644 index 60e99a8515..0000000000 --- a/.changelog/2097.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:improvement -helm: add samenessGroup field to source intention CRD -``` \ No newline at end of file diff --git a/.changelog/2100.txt b/.changelog/2100.txt deleted file mode 100644 index 4fece0991c..0000000000 --- a/.changelog/2100.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:feature -crd: Add `mutualTLSMode` to the ProxyDefaults and ServiceDefaults CRDs and `allowEnablingPermissiveMutualTLS` to the Mesh CRD to support configuring permissive mutual TLS. -``` diff --git a/.changelog/2102.txt b/.changelog/2108.txt similarity index 76% rename from .changelog/2102.txt rename to .changelog/2108.txt index 7adf361d2d..1c10ef62d9 100644 --- a/.changelog/2102.txt +++ b/.changelog/2108.txt @@ -1,5 +1,5 @@ ```release-note:security -Upgrade to use Go 1.20.4. +Upgrade to use Go 1.19.9. This resolves vulnerabilities [CVE-2023-24537](https://github.com/advisories/GHSA-9f7g-gqwh-jpf5)(`go/scanner`), [CVE-2023-24538](https://github.com/advisories/GHSA-v4m2-x4rp-hv22)(`html/template`), [CVE-2023-24534](https://github.com/advisories/GHSA-8v5j-pwr7-w5f8)(`net/textproto`) and @@ -9,13 +9,4 @@ Also, `golang.org/x/net` has been updated to v0.7.0 to resolve CVEs [CVE-2022-41 ), [CVE-2022-27664](https://github.com/advisories/GHSA-69cg-p879-7622) and [CVE-2022-41723 ](https://github.com/advisories/GHSA-vvpx-j8f3-3w6h .) -``` - -```release-note:improvement -cli: update minimum go version for project to 1.20. -``` - -```release-note:improvement -control-plane: update minimum go version for project to 1.20. -``` - +``` \ No newline at end of file diff --git a/.changelog/2124.txt b/.changelog/2124.txt deleted file mode 100644 index b65c23db2e..0000000000 --- a/.changelog/2124.txt +++ /dev/null @@ -1,3 +0,0 @@ -``release-note:improvement -control-plane: Transparent proxy enhancements for failover and virtual Services -``` diff --git a/.changelog/2134.txt b/.changelog/2134.txt deleted file mode 100644 index 980e7ba666..0000000000 --- a/.changelog/2134.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:feature -Add support for consul-telemetry-collector to forward envoy metrics to an otelhttp compatible receiver or HCP -``` \ No newline at end of file diff --git a/.changelog/2140.txt b/.changelog/2140.txt new file mode 100644 index 0000000000..d6c5efeffb --- /dev/null +++ b/.changelog/2140.txt @@ -0,0 +1,4 @@ +```release-note:improvement +helm: update `imageConsulDataplane` value to `hashicorp/consul-dataplane:1.0.2`, `image` value to `hashicorp/consul:1.14.7`, +and `imageEnvoy` to `envoyproxy/envoy:v1.24.7`. +``` diff --git a/.changelog/2143.txt b/.changelog/2143.txt deleted file mode 100644 index 8f58328f3d..0000000000 --- a/.changelog/2143.txt +++ /dev/null @@ -1,4 +0,0 @@ - -```release-note:feature -consul-telemetry-collector: Configure envoy proxy config during registration when consul-telemetry-collector is enabled. -``` diff --git a/.changelog/2152.txt b/.changelog/2152.txt deleted file mode 100644 index 2f0743a9d8..0000000000 --- a/.changelog/2152.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:feature -api-gateway: Add API Gateway for Consul on Kubernetes leveraging Consul native API Gateway configuration. -``` diff --git a/.changelog/2159.txt b/.changelog/2159.txt new file mode 100644 index 0000000000..9b970bf3f4 --- /dev/null +++ b/.changelog/2159.txt @@ -0,0 +1,3 @@ +```release-note:bug +control-plane: fix issue with json tags of service defaults fields EnforcingConsecutive5xx, MaxEjectionPercent and BaseEjectionTime. +``` \ No newline at end of file diff --git a/.changelog/2165.txt b/.changelog/2165.txt deleted file mode 100644 index 15c4bdb1e0..0000000000 --- a/.changelog/2165.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:improvement -control-plane: add FIPS support -``` \ No newline at end of file diff --git a/.changelog/2166.txt b/.changelog/2166.txt deleted file mode 100644 index b2392bd7d5..0000000000 --- a/.changelog/2166.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:feature -Add support for configuring Consul server-side rate limiting -``` diff --git a/.changelog/2170.txt b/.changelog/2170.txt deleted file mode 100644 index 6d10ae1097..0000000000 --- a/.changelog/2170.txt +++ /dev/null @@ -1,2 +0,0 @@ -```release-note:feature -Add support for configuring global level server rate limiting. diff --git a/.changelog/2183.txt b/.changelog/2183.txt deleted file mode 100644 index d54983a8f4..0000000000 --- a/.changelog/2183.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:security -Fix Prometheus CVEs by bumping controller-runtime. -``` diff --git a/.changelog/2184.txt b/.changelog/2184.txt deleted file mode 100644 index bdcb6039fd..0000000000 --- a/.changelog/2184.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:feature -api-gateway: support deploying to OpenShift 4.11 -``` diff --git a/.changelog/2195.txt b/.changelog/2195.txt deleted file mode 100644 index e7475f88e0..0000000000 --- a/.changelog/2195.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:improvement -consul-telemetry-collector: add acceptance tests for consul telemetry collector component. -``` diff --git a/.changelog/2205.txt b/.changelog/2205.txt deleted file mode 100644 index 6a66970cfc..0000000000 --- a/.changelog/2205.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:improvement -cli: update cloud preset to enable telemetry collector -``` \ No newline at end of file diff --git a/.changelog/2209.txt b/.changelog/2209.txt deleted file mode 100644 index 72a59064e4..0000000000 --- a/.changelog/2209.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:feature -helm: Add `JWTProvider` CRD for configuring the `jwt-provider` config entry. -``` diff --git a/.changelog/2213.txt b/.changelog/2213.txt deleted file mode 100644 index c09c2e0397..0000000000 --- a/.changelog/2213.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:feature -helm: Update the ServiceIntentions CRD to support `JWT` fields. -``` diff --git a/.changelog/2225.txt b/.changelog/2225.txt new file mode 100644 index 0000000000..fcbb89a54b --- /dev/null +++ b/.changelog/2225.txt @@ -0,0 +1,3 @@ +```release-note:security +Bump `controller-runtime` to address CVEs in dependencies. +``` diff --git a/.changelog/2265.txt b/.changelog/2265.txt index 1cf6813c94..35643ce272 100644 --- a/.changelog/2265.txt +++ b/.changelog/2265.txt @@ -1,3 +1,3 @@ ```release-note:improvement (Consul Enterprise) Add support to provide inputs via helm for audit log related configuration -``` +``` \ No newline at end of file diff --git a/.changelog/2302.txt b/.changelog/2302.txt index 7bf7e6b0f6..9d36d912d3 100644 --- a/.changelog/2302.txt +++ b/.changelog/2302.txt @@ -9,5 +9,4 @@ Add support to provide the logLevel flag via helm for multiple low level compone 7. `meshGateway.logLevel` 8. `ingressGateways.logLevel` 9. `terminatingGateways.logLevel` -10. `telemetryCollector.logLevel` ``` diff --git a/.changelog/2304.txt b/.changelog/2304.txt deleted file mode 100644 index c977da5acd..0000000000 --- a/.changelog/2304.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:improvement -helm: Kubernetes v1.27 is now supported. Minimum tested version of Kubernetes is now v1.24. -``` diff --git a/.changelog/2346.txt b/.changelog/2346.txt deleted file mode 100644 index fb062ee0fb..0000000000 --- a/.changelog/2346.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:feature -Set locality on services registered with connect-inject. -``` diff --git a/.changelog/2413.txt b/.changelog/2413.txt deleted file mode 100644 index 89755b23a7..0000000000 --- a/.changelog/2413.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:bug -api-gateway: Fix creation of invalid Kubernetes Service when multiple Gateway listeners have the same port. -``` diff --git a/.changelog/2420.txt b/.changelog/2420.txt deleted file mode 100644 index 86776497c4..0000000000 --- a/.changelog/2420.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:bug -api-gateway: set route condition appropriately when parent ref includes non-existent section name -``` diff --git a/.changelog/2476.txt b/.changelog/2476.txt deleted file mode 100644 index e57889cabe..0000000000 --- a/.changelog/2476.txt +++ /dev/null @@ -1,7 +0,0 @@ -```release-note:improvement -helm: update `imageConsulDataplane` value to `hashicorp/consul-dataplane:1.2.0` -``` - -```release-note:improvement -helm: update `image` value to `hashicorp/consul:1.16.0` -``` \ No newline at end of file diff --git a/.changelog/2478.txt b/.changelog/2478.txt deleted file mode 100644 index ccbbb71ec8..0000000000 --- a/.changelog/2478.txt +++ /dev/null @@ -1,5 +0,0 @@ -```release-note:bug -api-gateway: fixes bug where envoy will silently reject RSA keys less than 2048 bits in length when not in FIPS mode, and -will reject keys that are not 2048, 3072, or 4096 bits in length in FIPS mode. We now validate -and reject invalid certs earlier. -``` diff --git a/.changelog/2520.txt b/.changelog/2520.txt deleted file mode 100644 index 96d03dc093..0000000000 --- a/.changelog/2520.txt +++ /dev/null @@ -1,4 +0,0 @@ -```release-note:bug -transparent-proxy: Fix issue where connect-inject lacked sufficient `mesh:write` privileges in some deployments, -which prevented virtual IPs from persisting properly. -``` diff --git a/.changelog/2524.txt b/.changelog/2524.txt deleted file mode 100644 index 5d634e68e1..0000000000 --- a/.changelog/2524.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:improvement -(api-gateway) make API gateway controller less verbose -``` \ No newline at end of file diff --git a/.changelog/2597.txt b/.changelog/2597.txt deleted file mode 100644 index 83cc369b6d..0000000000 --- a/.changelog/2597.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:bug -api-gateway: fix helm install when setting copyAnnotations or nodeSelector -``` diff --git a/.changelog/2642.txt b/.changelog/2650.txt similarity index 70% rename from .changelog/2642.txt rename to .changelog/2650.txt index 5278ed705c..229410a2bf 100644 --- a/.changelog/2642.txt +++ b/.changelog/2650.txt @@ -1,4 +1,4 @@ ```release-note:security -Upgrade to use Go 1.20.6 and `x/net/http` 0.12.0. +Upgrade to use Go 1.19.11 and `x/net/http` 0.12.0. This resolves [CVE-2023-29406](https://github.com/advisories/GHSA-f8f7-69v5-w4vx)(`net/http`). ``` diff --git a/.changelog/2678.txt b/.changelog/2678.txt new file mode 100644 index 0000000000..97e7707c41 --- /dev/null +++ b/.changelog/2678.txt @@ -0,0 +1,3 @@ +```release-note:improvement +helm: do not set container securityContexts by default on OpenShift < 4.11 +``` diff --git a/.changelog/2707.txt b/.changelog/2707.txt deleted file mode 100644 index 370aaa7c17..0000000000 --- a/.changelog/2707.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:feature -api-gateway: adds ability to map privileged ports on Gateway listeners to unprivileged ports so that containers do not require additional privileges -``` diff --git a/.changelog/2711.txt b/.changelog/2711.txt deleted file mode 100644 index abb0b7e4fb..0000000000 --- a/.changelog/2711.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:feature -api-gateway: translate and validate TLS configuration options, including min/max version and cipher suites, setting Gateway status appropriately -``` diff --git a/.changelog/2710.txt b/.changelog/2717.txt similarity index 81% rename from .changelog/2710.txt rename to .changelog/2717.txt index 1d37b32dfb..a4557ecaaf 100644 --- a/.changelog/2710.txt +++ b/.changelog/2717.txt @@ -1,5 +1,5 @@ ```release-note:security -Upgrade to use Go 1.20.7 and `x/net` 0.13.0. +Upgrade to use Go 1.19.12 and `x/net` 0.13.0. This resolves [CVE-2023-29409](https://nvd.nist.gov/vuln/detail/CVE-2023-29409)(`crypto/tls`) and [CVE-2023-3978](https://nvd.nist.gov/vuln/detail/CVE-2023-3978)(`net/html`). ``` diff --git a/.changelog/2735.txt b/.changelog/2735.txt deleted file mode 100644 index 8b74b5552d..0000000000 --- a/.changelog/2735.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:feature -api-gateway: add RouteRetryFilter and RouteTimeoutFilter CRDs -``` \ No newline at end of file diff --git a/.changelog/2748.txt b/.changelog/2748.txt deleted file mode 100644 index 2a8c922d13..0000000000 --- a/.changelog/2748.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:bug -control-plane: Set locality on sidecar proxies in addition to services when registering with connect-inject. -``` diff --git a/.changelog/2784.txt b/.changelog/2784.txt deleted file mode 100644 index 5b11ca3d43..0000000000 --- a/.changelog/2784.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:feature -Add the `PrioritizeByLocality` field to the `ServiceResolver` and `ProxyDefaults` CRDs. -``` diff --git a/.copywrite.hcl b/.copywrite.hcl deleted file mode 100644 index 9c2e0c0310..0000000000 --- a/.copywrite.hcl +++ /dev/null @@ -1,16 +0,0 @@ -schema_version = 1 - -project { - license = "MPL-2.0" - copyright_year = 2018 - - # (OPTIONAL) A list of globs that should not have copyright/license headers. - # Supports doublestar glob patterns for more flexibility in defining which - # files or folders should be ignored - header_ignore = [ - - # ignoring charts templates as adding copyright headers breaks all tests - "charts/consul/templates/**", - - ] -} diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index eb998e2cd9..e22e28a48a 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - blank_issues_enabled: false contact_links: - name: Consul Community Support diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index f5d211b137..b615e69dd1 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -9,6 +9,7 @@ How I expect reviewers to test this PR: Checklist: - [ ] Tests added -- [ ] [CHANGELOG entry added](https://github.com/hashicorp/consul-k8s/blob/main/CONTRIBUTING.md#adding-a-changelog-entry) - +- [ ] CHANGELOG entry added + > HashiCorp engineers only, community PRs should not add a changelog entry. + > Entries should use present tense (e.g. Add support for...) diff --git a/.github/workflows/backport-checker.yml b/.github/workflows/backport-checker.yml index a70790c0c0..5bcac5a38e 100644 --- a/.github/workflows/backport-checker.yml +++ b/.github/workflows/backport-checker.yml @@ -1,5 +1,3 @@ -# Copyright (c) HashiCorp, Inc. - # This workflow checks that there is either a 'pr/no-backport' label applied to a PR # or there is a backport/.txt file associated with a PR for a backport label diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index dadde6a651..48883e24fa 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -1,10 +1,8 @@ -# Copyright (c) HashiCorp, Inc. - --- name: Backport Assistant Runner on: - pull_request_target: + pull_request: types: - closed - labeled @@ -13,7 +11,7 @@ jobs: backport: if: github.event.pull_request.merged runs-on: ubuntu-latest - container: hashicorpdev/backport-assistant:0.3.4 + container: hashicorpdev/backport-assistant:0.3.3 steps: - name: Run Backport Assistant run: backport-assistant backport -merge-method=squash -gh-automerge diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 414c875b26..4fd9ae3cfd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,5 +1,3 @@ -# Copyright (c) HashiCorp, Inc. - name: build on: workflow_dispatch: @@ -21,7 +19,7 @@ jobs: outputs: go-version: ${{ steps.get-go-version.outputs.go-version }} steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@v3 - name: Determine Go version id: get-go-version # We use .go-version as our source of truth for current Go @@ -35,7 +33,7 @@ jobs: outputs: product-version: ${{ steps.get-product-version.outputs.product-version }} steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@v3 - name: get product version id: get-product-version run: | @@ -49,7 +47,7 @@ jobs: filepath: ${{ steps.generate-metadata-file.outputs.filepath }} steps: - name: "Checkout directory" - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@v3 - name: Generate metadata file id: generate-metadata-file uses: hashicorp/actions-generate-metadata@v1 @@ -57,14 +55,14 @@ jobs: version: ${{ needs.get-product-version.outputs.product-version }} product: ${{ env.PKG_NAME }} repositoryOwner: "hashicorp" - - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + - uses: actions/upload-artifact@v3 with: name: metadata.json path: ${{ steps.generate-metadata-file.outputs.filepath }} build: needs: [get-go-version, get-product-version] - runs-on: ubuntu-20.04 # the GLIBC is too high on 22.04 + runs-on: ubuntu-latest strategy: matrix: include: @@ -79,28 +77,20 @@ jobs: - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s.exe" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "darwin", goarch: "amd64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "darwin", goarch: "arm64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s" } - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto", fips: "+fips1402", pkg_suffix: "-fips" } - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "arm64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto CC=aarch64-linux-gnu-gcc", fips: "+fips1402", pkg_suffix: "-fips" } - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s.exe", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=cngcrypto", fips: "+fips1402" } - - # control-plane + # control-plane - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "freebsd", goarch: "386", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "freebsd", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "386", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "arm", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "arm64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } - # solaris is only built for the control plane + # solaris is only built for the control plane - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "solaris", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "386", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane.exe" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane.exe" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "darwin", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "darwin", goarch: "arm64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto", fips: "+fips1402", pkg_suffix: "-fips" } - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "arm64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto CC=aarch64-linux-gnu-gcc", fips: "+fips1402", pkg_suffix: "-fips" } - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane.exe", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=cngcrypto", fips: "+fips1402" } - - # consul-cni + # consul-cni - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "freebsd", goarch: "386", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "freebsd", goarch: "amd64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "386", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni" } @@ -112,41 +102,18 @@ jobs: - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni.exe" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "darwin", goarch: "amd64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni" } - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "darwin", goarch: "arm64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni" } - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto", fips: "+fips1402", pkg_suffix: "-fips" } - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "arm64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=boringcrypto CC=aarch64-linux-gnu-gcc", fips: "+fips1402", pkg_suffix: "-fips" } - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "windows", goarch: "amd64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni.exe", gotags: "fips", env: "CGO_ENABLED=1 GOEXPERIMENT=cngcrypto", fips: "+fips1402" } - fail-fast: true - name: Go ${{ matrix.go }} ${{ matrix.goos }} ${{ matrix.goarch }} ${{ matrix.component }} ${{ matrix.fips }} build + name: Go ${{ matrix.go }} ${{ matrix.goos }} ${{ matrix.goarch }} ${{ matrix.component }} build steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@v3 - name: Setup go - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1 + uses: actions/setup-go@v3 with: go-version: ${{ matrix.go }} - - name: Replace Go for Windows FIPS with Microsoft Go - if: ${{ matrix.fips == '+fips1402' && matrix.goos == 'windows' }} - run: | - # Uninstall standard Go and use microsoft/go instead - rm -rf /home/runner/actions-runner/_work/_tool/go - curl https://aka.ms/golang/release/latest/go${{ matrix.go }}-1.linux-amd64.tar.gz -Lo go${{ matrix.go }}.linux-amd64.tar.gz - tar -C $HOME -xf go${{ matrix.go }}.linux-amd64.tar.gz - chmod +x $HOME/go/bin - export PATH=$HOME/go/bin:$PATH - if [ $(which go) != "$HOME/go/bin/go" ]; then - echo "Unable to verify microsoft/go toolchain" - exit 1 - fi - - - name: Install cross-compiler for FIPS on arm - if: ${{ matrix.fips == '+fips1402' && matrix.goarch == 'arm64' }} - run: | - sudo apt-get update --allow-releaseinfo-change-suite --allow-releaseinfo-change-version && sudo apt-get install -y gcc-aarch64-linux-gnu - - name: Build env: GOOS: ${{ matrix.goos }} @@ -161,23 +128,23 @@ jobs: export GIT_IMPORT=github.com/hashicorp/consul-k8s/${{ matrix.component }}/version export GOLDFLAGS="-X ${GIT_IMPORT}.GitCommit=${GIT_COMMIT}${GIT_DIRTY} -X ${GIT_IMPORT}.GitDescribe=${{ needs.get-product-version.outputs.product-version }}" - ${{ matrix.env }} go build -o dist/${{ matrix.bin_name }} -ldflags "${GOLDFLAGS}" -tags=${{ matrix.gotags }} . - zip -r -j out/${{ matrix.pkg_name }}_${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip dist/ + CGO_ENABLED=0 go build -o dist/${{ matrix.bin_name }} -ldflags "${GOLDFLAGS}" . + zip -r -j out/${{ matrix.pkg_name }}_${{ needs.get-product-version.outputs.product-version }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip dist/ - name: Upload built binaries - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + uses: actions/upload-artifact@v3 with: - name: ${{ matrix.pkg_name }}_${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip - path: ${{ matrix.component}}/out/${{ matrix.pkg_name }}_${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip + name: ${{ matrix.pkg_name }}_${{ needs.get-product-version.outputs.product-version }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip + path: ${{ matrix.component}}/out/${{ matrix.pkg_name }}_${{ needs.get-product-version.outputs.product-version }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip - name: Package rpm and deb files if: ${{ matrix.goos == 'linux' && matrix.component == 'cli' && matrix.goarch == 'amd64'}} uses: hashicorp/actions-packaging-linux@v1 with: - name: consul-k8s${{ matrix.pkg_suffix }} + name: consul-k8s description: "consul-k8s provides a cli interface to first-class integrations between Consul and Kubernetes." arch: ${{ matrix.goarch }} - version: ${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }} + version: ${{ needs.get-product-version.outputs.product-version }} maintainer: "HashiCorp" homepage: "https://github.com/hashicorp/consul-k8s" license: "MPL-2.0" @@ -193,7 +160,7 @@ jobs: - name: Test rpm package if: ${{ matrix.goos == 'linux' && matrix.component == 'cli' && matrix.goarch == 'amd64'}} - uses: addnab/docker-run-action@4f65fabd2431ebc8d299f8e5a018d79a769ae185 # v3 + uses: addnab/docker-run-action@v3 with: image: registry.access.redhat.com/ubi9/ubi:latest options: -v ${{ github.workspace }}:/work @@ -202,7 +169,7 @@ jobs: cd /work rpm -ivh out/${{ env.RPM_PACKAGE }} CONSUL_K8S_VERSION="$(consul-k8s version | awk '{print $2}')" - VERSION="v${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }}" + VERSION="v${{ needs.get-product-version.outputs.product-version }}" if [ "${VERSION}" != "${CONSUL_K8S_VERSION}" ]; then echo "Test FAILED, expected: ${VERSION}, got: ${CONSUL_K8S_VERSION}" exit 1 @@ -210,7 +177,7 @@ jobs: echo "Test PASSED, expected: ${VERSION}, got: ${CONSUL_K8S_VERSION}" - name: Upload rpm package - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + uses: actions/upload-artifact@v3 if: ${{ matrix.goos == 'linux' && matrix.component == 'cli' && matrix.goarch == 'amd64'}} with: name: ${{ env.RPM_PACKAGE }} @@ -218,7 +185,7 @@ jobs: - name: Test debian package if: ${{ matrix.goos == 'linux' && matrix.component == 'cli' && matrix.goarch == 'amd64'}} - uses: addnab/docker-run-action@4f65fabd2431ebc8d299f8e5a018d79a769ae185 # v3 + uses: addnab/docker-run-action@v3 with: image: ubuntu:latest options: -v ${{ github.workspace }}:/work @@ -227,7 +194,7 @@ jobs: cd /work apt install ./out/${{ env.DEB_PACKAGE }} CONSUL_K8S_VERSION="$(consul-k8s version | awk '{print $2}')" - VERSION="v${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }}" + VERSION="v${{ needs.get-product-version.outputs.product-version }}" if [ "${VERSION}" != "${CONSUL_K8S_VERSION}" ]; then echo "Test FAILED, expected: ${VERSION}, got: ${CONSUL_K8S_VERSION}" exit 1 @@ -235,43 +202,36 @@ jobs: echo "Test PASSED, expected: ${VERSION}, got: ${CONSUL_K8S_VERSION}" - name: Upload debian packages - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + uses: actions/upload-artifact@v3 if: ${{ matrix.goos == 'linux' && matrix.component == 'cli' && matrix.goarch == 'amd64'}} with: name: ${{ env.DEB_PACKAGE }} path: out/${{ env.DEB_PACKAGE }} build-docker: - name: Docker ${{ matrix.goarch }} ${{ matrix.fips }} default release build + name: Docker ${{ matrix.arch }} default release build needs: [get-product-version, build] runs-on: ubuntu-latest strategy: matrix: - include: - - { goos: "linux", goarch: "arm" } - - { goos: "linux", goarch: "arm64" } - - { goos: "linux", goarch: "386" } - - { goos: "linux", goarch: "amd64" } - - { goos: "linux", goarch: "amd64", fips: "+fips1402" } - - { goos: "linux", goarch: "arm64", fips: "+fips1402" } + arch: ["arm", "arm64", "386", "amd64"] env: repo: ${{ github.event.repository.name }} - version: ${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }} + version: ${{ needs.get-product-version.outputs.product-version }} steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + - uses: actions/checkout@v3 + - uses: actions/download-artifact@v3 with: - name: consul-cni_${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }}_${{ matrix.goos}}_${{ matrix.goarch }}.zip - path: control-plane/dist/cni/${{ matrix.goos}}/${{ matrix.goarch }} + name: consul-cni_${{ needs.get-product-version.outputs.product-version }}_linux_${{ matrix.arch }}.zip + path: control-plane/dist/cni/linux/${{ matrix.arch }} - name: extract consul-cni zip env: - ZIP_LOCATION: control-plane/dist/cni/${{ matrix.goos}}/${{ matrix.goarch }} + ZIP_LOCATION: control-plane/dist/cni/linux/${{ matrix.arch }} run: | cd "${ZIP_LOCATION}" unzip -j *.zip - name: Docker Build (Action) uses: hashicorp/actions-docker-build@v1 - if: ${{ !matrix.fips }} with: smoke_test: | TEST_VERSION="$(docker run "${IMAGE_NAME}" consul-k8s-control-plane version | awk '{print $2}')" @@ -282,7 +242,7 @@ jobs: echo "Test PASSED" version: ${{ env.version }} target: release-default - arch: ${{ matrix.goarch }} + arch: ${{ matrix.arch }} pkg_name: consul-k8s-control-plane_${{ env.version }} bin_name: consul-k8s-control-plane workdir: control-plane @@ -292,46 +252,21 @@ jobs: hashicorppreview/${{ env.repo }}-control-plane:${{ env.version }} docker.io/hashicorppreview/${{ env.repo }}-control-plane:${{ env.version }}-${{ github.sha }} - - name: Docker FIPS Build (Action) - uses: hashicorp/actions-docker-build@v1 - if: ${{ matrix.fips }} - with: - smoke_test: | - TEST_VERSION="$(docker run "${IMAGE_NAME}" consul-k8s-control-plane version | awk '{print $2}')" - if [ "${TEST_VERSION}" != "v${version}" ]; then - echo "Test FAILED" - exit 1 - fi - echo "Test PASSED" - version: ${{ env.version }} - target: release-default-fips # duplicate target to distinguish FIPS builds in CRT machinery - arch: ${{ matrix.goarch }} - pkg_name: consul-k8s-control-plane_${{ env.version }} - bin_name: consul-k8s-control-plane - workdir: control-plane - tags: | - docker.io/hashicorp/${{ env.repo }}-control-plane-fips:${{ env.version }} - dev_tags: | - hashicorppreview/${{ env.repo }}-control-plane-fips:${{ env.version }} - docker.io/hashicorppreview/${{ env.repo }}-control-plane-fips:${{ env.version }}-${{ github.sha }} - build-docker-ubi-redhat-registry: - name: Docker ${{ matrix.arch }} ${{ matrix.fips }} UBI build for RedHat Registry + name: Docker ${{ matrix.arch }} UBI build for RedHat Registry needs: [get-product-version, build] runs-on: ubuntu-latest strategy: matrix: - include: - - { arch: "amd64" } - - { arch: "amd64", fips: "+fips1402" } + arch: ["amd64"] env: repo: ${{ github.event.repository.name }} - version: ${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }} + version: ${{ needs.get-product-version.outputs.product-version }} steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + - uses: actions/checkout@v3 + - uses: actions/download-artifact@v3 with: - name: consul-cni_${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }}_linux_${{ matrix.arch }}.zip + name: consul-cni_${{ needs.get-product-version.outputs.product-version }}_linux_${{ matrix.arch }}.zip path: control-plane/dist/cni/linux/${{ matrix.arch }} - name: extract consul-cni zip env: @@ -342,9 +277,7 @@ jobs: - name: Copy LICENSE run: cp LICENSE ./control-plane - - name: Docker Build (Action) - if: ${{ !matrix.fips }} - uses: hashicorp/actions-docker-build@v1 + - uses: hashicorp/actions-docker-build@v1 with: smoke_test: | TEST_VERSION="$(docker run "${IMAGE_NAME}" consul-k8s-control-plane version | awk '{print $2}')" @@ -360,41 +293,22 @@ jobs: bin_name: consul-k8s-control-plane workdir: control-plane redhat_tag: quay.io/redhat-isv-containers/611ca2f89a9b407267837100:${{env.version}}-ubi - - name: Docker FIPS Build (Action) - if: ${{ matrix.fips }} - uses: hashicorp/actions-docker-build@v1 - with: - smoke_test: | - TEST_VERSION="$(docker run "${IMAGE_NAME}" consul-k8s-control-plane version | awk '{print $2}')" - if [ "${TEST_VERSION}" != "v${version}" ]; then - echo "Test FAILED" - exit 1 - fi - echo "Test PASSED" - version: ${{ env.version }} - target: ubi-fips # duplicate target to distinguish FIPS builds in CRT machinery - arch: ${{ matrix.arch }} - pkg_name: consul-k8s-control-plane_${{ env.version }} - bin_name: consul-k8s-control-plane - workdir: control-plane - redhat_tag: quay.io/redhat-isv-containers/6486b1beabfc4e51588c0416:${{env.version}}-ubi # this is different than the non-FIPS one build-docker-ubi-dockerhub: - name: Docker ${{ matrix.arch }} ${{ matrix.fips }} UBI build for DockerHub + name: Docker ${{ matrix.arch }} UBI build for DockerHub needs: [ get-product-version, build ] runs-on: ubuntu-latest strategy: matrix: arch: [ "amd64" ] - fips: [ "+fips1402", "" ] env: repo: ${{ github.event.repository.name }} - version: ${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }} + version: ${{ needs.get-product-version.outputs.product-version }} steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + - uses: actions/checkout@v3 + - uses: actions/download-artifact@v3 with: - name: consul-cni_${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }}_linux_${{ matrix.arch }}.zip + name: consul-cni_${{ needs.get-product-version.outputs.product-version }}_linux_${{ matrix.arch }}.zip path: control-plane/dist/cni/linux/${{ matrix.arch }} - name: extract consul-cni zip env: @@ -405,9 +319,7 @@ jobs: - name: Copy LICENSE run: cp LICENSE ./control-plane - - name: Docker Build (Action) - uses: hashicorp/actions-docker-build@v1 - if: ${{ !matrix.fips }} + - uses: hashicorp/actions-docker-build@v1 with: smoke_test: | TEST_VERSION="$(docker run "${IMAGE_NAME}" consul-k8s-control-plane version | awk '{print $2}')" @@ -426,26 +338,4 @@ jobs: docker.io/hashicorp/${{ env.repo }}-control-plane:${{ env.version }}-ubi dev_tags: | hashicorppreview/${{ env.repo }}-control-plane:${{ env.version }}-ubi - docker.io/hashicorppreview/${{ env.repo }}-control-plane:${{ env.version }}-ubi-${{ github.sha }} - - name: Docker FIPS Build (Action) - uses: hashicorp/actions-docker-build@v1 - if: ${{ matrix.fips }} - with: - smoke_test: | - TEST_VERSION="$(docker run "${IMAGE_NAME}" consul-k8s-control-plane version | awk '{print $2}')" - if [ "${TEST_VERSION}" != "v${version}" ]; then - echo "Test FAILED" - exit 1 - fi - echo "Test PASSED" - version: ${{ env.version }} - target: ubi-fips # duplicate target to distinguish FIPS builds in CRT machinery - arch: ${{ matrix.arch }} - pkg_name: consul-k8s-control-plane_${{ env.version }} - bin_name: consul-k8s-control-plane - workdir: control-plane - tags: | - docker.io/hashicorp/${{ env.repo }}-control-plane-fips:${{ env.version }}-ubi - dev_tags: | - hashicorppreview/${{ env.repo }}-control-plane-fips:${{ env.version }}-ubi - docker.io/hashicorppreview/${{ env.repo }}-control-plane-fips:${{ env.version }}-ubi-${{ github.sha }} + docker.io/hashicorppreview/${{ env.repo }}-control-plane:${{ env.version }}-ubi-${{ github.sha }} diff --git a/.github/workflows/changelog-checker.yml b/.github/workflows/changelog-checker.yml index 40c9b17c68..d40bdfbd6a 100644 --- a/.github/workflows/changelog-checker.yml +++ b/.github/workflows/changelog-checker.yml @@ -1,5 +1,3 @@ -# Copyright (c) HashiCorp, Inc. - # This workflow checks that there is either a 'pr/no-changelog' label applied to a PR # or there is a .changelog/.txt file associated with a PR for a changelog entry @@ -21,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@v2 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 # by default the checkout action doesn't checkout all branches diff --git a/.github/workflows/jira-issues.yaml b/.github/workflows/jira-issues.yaml index bddc69c83f..dc743e9328 100644 --- a/.github/workflows/jira-issues.yaml +++ b/.github/workflows/jira-issues.yaml @@ -15,7 +15,7 @@ jobs: name: Jira Community Issue sync steps: - name: Login - uses: atlassian/gajira-login@ca13f8850ea309cf44a6e4e0c49d9aa48ac3ca4c # v3 + uses: atlassian/gajira-login@v3.0.0 env: JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} @@ -38,7 +38,7 @@ jobs: - name: Create ticket if an issue is filed, or if PR not by a team member is opened if: github.event.action == 'opened' - uses: tomhjp/gh-action-jira-create@3ed1789cad3521292e591a7cfa703215ec1348bf # v0.2.1 + uses: tomhjp/gh-action-jira-create@v0.2.0 with: project: NET issuetype: "${{ steps.set-ticket-type.outputs.TYPE }}" @@ -58,28 +58,28 @@ jobs: - name: Search if: github.event.action != 'opened' id: search - uses: tomhjp/gh-action-jira-search@04700b457f317c3e341ce90da5a3ff4ce058f2fa # v0.2.2 + uses: tomhjp/gh-action-jira-search@v0.2.1 with: # cf[10089] is Issue Link (use JIRA API to retrieve) jql: 'issuetype = "${{ steps.set-ticket-type.outputs.TYPE }}" and cf[10089] = "${{ github.event.issue.html_url || github.event.pull_request.html_url }}"' - name: Sync comment if: github.event.action == 'created' && steps.search.outputs.issue - uses: tomhjp/gh-action-jira-comment@6eb6b9ead70221916b6badd118c24535ed220bd9 # v0.2.0 + uses: tomhjp/gh-action-jira-comment@v0.1.0 with: issue: ${{ steps.search.outputs.issue }} comment: "${{ github.actor }} ${{ github.event.review.state || 'commented' }}:\n\n${{ github.event.comment.body || github.event.review.body }}\n\n${{ github.event.comment.html_url || github.event.review.html_url }}" - name: Close ticket if: ( github.event.action == 'closed' || github.event.action == 'deleted' ) && steps.search.outputs.issue - uses: atlassian/gajira-transition@4749176faf14633954d72af7a44d7f2af01cc92b # v3 + uses: atlassian/gajira-transition@v2.0.1 with: issue: ${{ steps.search.outputs.issue }} transition: "Closed" - name: Reopen ticket if: github.event.action == 'reopened' && steps.search.outputs.issue - uses: atlassian/gajira-transition@4749176faf14633954d72af7a44d7f2af01cc92b # v3 + uses: atlassian/gajira-transition@v2.0.1 with: issue: ${{ steps.search.outputs.issue }} transition: "To Do" diff --git a/.github/workflows/jira-pr.yaml b/.github/workflows/jira-pr.yaml index 078365ac88..c07a92ee77 100644 --- a/.github/workflows/jira-pr.yaml +++ b/.github/workflows/jira-pr.yaml @@ -13,7 +13,7 @@ jobs: name: Jira sync steps: - name: Login - uses: atlassian/gajira-login@ca13f8850ea309cf44a6e4e0c49d9aa48ac3ca4c # v3 + uses: atlassian/gajira-login@v3.0.0 env: JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} @@ -52,7 +52,7 @@ jobs: - name: Create ticket if an issue is filed, or if PR not by a team member is opened if: ( github.event.action == 'opened' && steps.is-team-member.outputs.MESSAGE == 'false' ) - uses: tomhjp/gh-action-jira-create@3ed1789cad3521292e591a7cfa703215ec1348bf # v0.2.1 + uses: tomhjp/gh-action-jira-create@v0.2.0 with: project: NET issuetype: "${{ steps.set-ticket-type.outputs.TYPE }}" @@ -72,28 +72,28 @@ jobs: - name: Search if: github.event.action != 'opened' id: search - uses: tomhjp/gh-action-jira-search@04700b457f317c3e341ce90da5a3ff4ce058f2fa # v0.2.2 + uses: tomhjp/gh-action-jira-search@v0.2.1 with: # cf[10089] is Issue Link (use JIRA API to retrieve) jql: 'issuetype = "${{ steps.set-ticket-type.outputs.TYPE }}" and cf[10089] = "${{ github.event.issue.html_url || github.event.pull_request.html_url }}"' - name: Sync comment if: github.event.action == 'created' && steps.search.outputs.issue - uses: tomhjp/gh-action-jira-comment@6eb6b9ead70221916b6badd118c24535ed220bd9 # v0.2.0 + uses: tomhjp/gh-action-jira-comment@v0.1.0 with: issue: ${{ steps.search.outputs.issue }} comment: "${{ github.actor }} ${{ github.event.review.state || 'commented' }}:\n\n${{ github.event.comment.body || github.event.review.body }}\n\n${{ github.event.comment.html_url || github.event.review.html_url }}" - name: Close ticket if: ( github.event.action == 'closed' || github.event.action == 'deleted' ) && steps.search.outputs.issue - uses: atlassian/gajira-transition@4749176faf14633954d72af7a44d7f2af01cc92b # v3 + uses: atlassian/gajira-transition@v2.0.1 with: issue: ${{ steps.search.outputs.issue }} transition: "Closed" - name: Reopen ticket if: github.event.action == 'reopened' && steps.search.outputs.issue - uses: atlassian/gajira-transition@4749176faf14633954d72af7a44d7f2af01cc92b # v3 + uses: atlassian/gajira-transition@v2.0.1 with: issue: ${{ steps.search.outputs.issue }} transition: "To Do" diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml index e95af6cdcc..2af1e46532 100644 --- a/.github/workflows/merge.yml +++ b/.github/workflows/merge.yml @@ -20,7 +20,7 @@ jobs: name: test runs-on: ubuntu-latest steps: - - uses: benc-uk/workflow-dispatch@798e70c97009500150087d30d9f11c5444830385 # v1.2.2 + - uses: benc-uk/workflow-dispatch@v1.2.2 name: test with: workflow: test.yml diff --git a/.github/workflows/nightly-acceptance.yml b/.github/workflows/nightly-acceptance.yml index 6db7684bb8..18a8ba958d 100644 --- a/.github/workflows/nightly-acceptance.yml +++ b/.github/workflows/nightly-acceptance.yml @@ -16,7 +16,7 @@ jobs: name: cloud runs-on: ubuntu-latest steps: - - uses: benc-uk/workflow-dispatch@798e70c97009500150087d30d9f11c5444830385 # v1.2.2 + - uses: benc-uk/workflow-dispatch@v1.2.2 name: cloud with: workflow: cloud.yml diff --git a/.github/workflows/nightly-api-gateway-conformance.yml b/.github/workflows/nightly-api-gateway-conformance.yml deleted file mode 100644 index abeec34659..0000000000 --- a/.github/workflows/nightly-api-gateway-conformance.yml +++ /dev/null @@ -1,27 +0,0 @@ -# Dispatch to the consul-k8s-workflows with a nightly cron -name: nightly-api-gateway-conformance -on: - schedule: - # * is a special character in YAML so you have to quote this string - # Run nightly at 12AM UTC/8PM EST/5PM PST. - - cron: '0 0 * * *' - - -# these should be the only settings that you will ever need to change -env: - BRANCH: ${{ github.ref_name }} - CONTEXT: "nightly" - -jobs: - api-gateway-conformance: - name: api-gateway-conformance - runs-on: ubuntu-latest - steps: - - uses: benc-uk/workflow-dispatch@798e70c97009500150087d30d9f11c5444830385 # v1.2.2 - name: conformance - with: - workflow: api-gateway-conformance.yml - repo: hashicorp/consul-k8s-workflows - ref: main - token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} - inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}" }' diff --git a/.github/workflows/nightly-cleanup.yml b/.github/workflows/nightly-cleanup.yml deleted file mode 100644 index 83d6688ac5..0000000000 --- a/.github/workflows/nightly-cleanup.yml +++ /dev/null @@ -1,26 +0,0 @@ -# Dispatch to the consul-k8s-workflows with a nightly cron -name: nightly-cleanup -on: - schedule: - # * is a special character in YAML so you have to quote this string - # Run nightly at 12PM UTC/8AM EST/5AM PST - - cron: '0 12 * * *' - -# these should be the only settings that you will ever need to change -env: - BRANCH: ${{ github.ref_name }} - CONTEXT: "nightly" - -jobs: - cleanup: - name: cleanup - runs-on: ubuntu-latest - steps: - - uses: benc-uk/workflow-dispatch@798e70c97009500150087d30d9f11c5444830385 # v1.2.2 - name: cleanup - with: - workflow: cleanup.yml - repo: hashicorp/consul-k8s-workflows - ref: main - token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} - inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}" }' diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 2959554e21..65f924e6d0 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -14,7 +14,7 @@ jobs: name: test runs-on: ubuntu-latest steps: - - uses: benc-uk/workflow-dispatch@798e70c97009500150087d30d9f11c5444830385 # v1.2.2 + - uses: benc-uk/workflow-dispatch@v1.2.2 name: test with: workflow: test.yml diff --git a/.github/workflows/weekly-acceptance-0-49-x.yml b/.github/workflows/weekly-acceptance-0-49-x.yml index 5e1c17f3c7..691d0b6236 100644 --- a/.github/workflows/weekly-acceptance-0-49-x.yml +++ b/.github/workflows/weekly-acceptance-0-49-x.yml @@ -18,7 +18,7 @@ jobs: name: cloud runs-on: ubuntu-latest steps: - - uses: benc-uk/workflow-dispatch@798e70c97009500150087d30d9f11c5444830385 # v1.2.2 + - uses: benc-uk/workflow-dispatch@v1.2.2 name: cloud with: workflow: cloud.yml diff --git a/.github/workflows/weekly-acceptance-1-0-x.yml b/.github/workflows/weekly-acceptance-1-0-x.yml index 11dda52bed..663f779bb5 100644 --- a/.github/workflows/weekly-acceptance-1-0-x.yml +++ b/.github/workflows/weekly-acceptance-1-0-x.yml @@ -19,7 +19,7 @@ jobs: name: cloud runs-on: ubuntu-latest steps: - - uses: benc-uk/workflow-dispatch@798e70c97009500150087d30d9f11c5444830385 # v1.2.2 + - uses: benc-uk/workflow-dispatch@v1.2.2 name: cloud with: workflow: cloud.yml diff --git a/.github/workflows/weekly-acceptance-1-1-x.yml b/.github/workflows/weekly-acceptance-1-1-x.yml index 86153587b0..2fc21aba5b 100644 --- a/.github/workflows/weekly-acceptance-1-1-x.yml +++ b/.github/workflows/weekly-acceptance-1-1-x.yml @@ -19,7 +19,7 @@ jobs: name: cloud runs-on: ubuntu-latest steps: - - uses: benc-uk/workflow-dispatch@798e70c97009500150087d30d9f11c5444830385 # v1.2.2 + - uses: benc-uk/workflow-dispatch@v1.2.2 name: cloud with: workflow: cloud.yml diff --git a/.github/workflows/weekly-acceptance-1-2-x.yml b/.github/workflows/weekly-acceptance-1-2-x.yml deleted file mode 100644 index 353a086f16..0000000000 --- a/.github/workflows/weekly-acceptance-1-2-x.yml +++ /dev/null @@ -1,30 +0,0 @@ -# Dispatch to the consul-k8s-workflows with a weekly cron -# -# A separate file is needed for each release because the cron schedules are different for each release. -name: weekly-acceptance-1-2-x -on: - schedule: - # * is a special character in YAML so you have to quote this string - # Run weekly on Wednesday at 3AM UTC/11PM EST/8PM PST - # - cron: '0 3 * * 3' - - cron: '0 0 * * *' # Temporarily nightly until 1.2.0 GA - - -# these should be the only settings that you will ever need to change -env: - BRANCH: "release/1.2.x" - CONTEXT: "weekly" - -jobs: - cloud: - name: cloud - runs-on: ubuntu-latest - steps: - - uses: benc-uk/workflow-dispatch@798e70c97009500150087d30d9f11c5444830385 # v1.2.2 - name: cloud - with: - workflow: cloud.yml - repo: hashicorp/consul-k8s-workflows - ref: main - token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} - inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}" }' diff --git a/.go-version b/.go-version index 8909929f6e..e54f3135a7 100644 --- a/.go-version +++ b/.go-version @@ -1 +1 @@ -1.20.7 +1.19.12 diff --git a/.golangci.yml b/.golangci.yml index dcad005d10..95aa25b0ff 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - linters: # enables all defaults + the below, `golangci-lint linters` to see the list of active linters. enable: diff --git a/.release/ci.hcl b/.release/ci.hcl index 5c781045e7..ffc2fd60d5 100644 --- a/.release/ci.hcl +++ b/.release/ci.hcl @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - schema = "1" project "consul-k8s" { diff --git a/.release/release-metadata.hcl b/.release/release-metadata.hcl index 10741e9c36..c053fdac2f 100644 --- a/.release/release-metadata.hcl +++ b/.release/release-metadata.hcl @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - url_docker_registry_dockerhub = "https://hub.docker.com/r/hashicorp/consul-k8s-control-plane" url_license = "https://github.com/hashicorp/consul-k8s/blob/main/LICENSE" url_project_website = "https://www.consul.io/docs/k8s" diff --git a/.release/security-scan.hcl b/.release/security-scan.hcl index 692fea1578..42576d29b2 100644 --- a/.release/security-scan.hcl +++ b/.release/security-scan.hcl @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - container { dependencies = true alpine_secdb = true diff --git a/CHANGELOG.md b/CHANGELOG.md index d9a4d76bef..1cb973e91a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,103 +1,12 @@ -## 1.2.1 (Aug 10, 2023) -BREAKING CHANGES: - -* control-plane: All policies managed by consul-k8s will now be updated on upgrade. If you previously edited the policies after install, your changes will be overwritten. [[GH-2392](https://github.com/hashicorp/consul-k8s/issues/2392)] - -SECURITY: - -* Upgrade to use Go 1.20.6 and `x/net/http` 0.12.0. - This resolves [CVE-2023-29406](https://github.com/advisories/GHSA-f8f7-69v5-w4vx)(`net/http`). [[GH-2642](https://github.com/hashicorp/consul-k8s/issues/2642)] -* Upgrade to use Go 1.20.7 and `x/net` 0.13.0. - This resolves [CVE-2023-29409](https://nvd.nist.gov/vuln/detail/CVE-2023-29409)(`crypto/tls`) - and [CVE-2023-3978](https://nvd.nist.gov/vuln/detail/CVE-2023-3978)(`net/html`). [[GH-2710](https://github.com/hashicorp/consul-k8s/issues/2710)] - -FEATURES: - -* Add support for configuring graceful shutdown proxy lifecycle management settings. [[GH-2233](https://github.com/hashicorp/consul-k8s/issues/2233)] -* api-gateway: adds ability to map privileged ports on Gateway listeners to unprivileged ports so that containers do not require additional privileges [[GH-2707](https://github.com/hashicorp/consul-k8s/issues/2707)] -* api-gateway: support deploying to OpenShift 4.11 [[GH-2184](https://github.com/hashicorp/consul-k8s/issues/2184)] -* helm: Adds `acls.resources` field which can be configured to override the `resource` settings for the `server-acl-init` and `server-acl-init-cleanup` Jobs. [[GH-2416](https://github.com/hashicorp/consul-k8s/issues/2416)] -* sync-catalog: add ability to support weighted loadbalancing by service annotation `consul.hashicorp.com/service-weight: ` [[GH-2293](https://github.com/hashicorp/consul-k8s/issues/2293)] - -IMPROVEMENTS: - -* (Consul Enterprise) Add support to provide inputs via helm for audit log related configuration [[GH-2370](https://github.com/hashicorp/consul-k8s/issues/2370)] -* (api-gateway) make API gateway controller less verbose [[GH-2524](https://github.com/hashicorp/consul-k8s/issues/2524)] -* Add support to provide the logLevel flag via helm for multiple low level components. Introduces the following fields -1. `global.acls.logLevel` -2. `global.tls.logLevel` -3. `global.federation.logLevel` -4. `global.gossipEncryption.logLevel` -5. `server.logLevel` -6. `client.logLevel` -7. `meshGateway.logLevel` -8. `ingressGateways.logLevel` -9. `terminatingGateways.logLevel` -10. `telemetryCollector.logLevel` [[GH-2302](https://github.com/hashicorp/consul-k8s/issues/2302)] -* control-plane: increase timeout after login for ACL replication to 60 seconds [[GH-2656](https://github.com/hashicorp/consul-k8s/issues/2656)] -* helm: adds values for `securityContext` and `annotations` on TLS and ACL init/cleanup jobs. [[GH-2525](https://github.com/hashicorp/consul-k8s/issues/2525)] -* helm: set container securityContexts to match the `restricted` Pod Security Standards policy to support running Consul in a namespace with restricted PSA enforcement enabled [[GH-2572](https://github.com/hashicorp/consul-k8s/issues/2572)] -* helm: update `imageConsulDataplane` value to `hashicorp/consul-dataplane:1.2.0` [[GH-2476](https://github.com/hashicorp/consul-k8s/issues/2476)] -* helm: update `image` value to `hashicorp/consul:1.16.0` [[GH-2476](https://github.com/hashicorp/consul-k8s/issues/2476)] - -BUG FIXES: - -* api-gateway: Fix creation of invalid Kubernetes Service when multiple Gateway listeners have the same port. [[GH-2413](https://github.com/hashicorp/consul-k8s/issues/2413)] -* api-gateway: fix helm install when setting copyAnnotations or nodeSelector [[GH-2597](https://github.com/hashicorp/consul-k8s/issues/2597)] -* api-gateway: fixes bug where envoy will silently reject RSA keys less than 2048 bits in length when not in FIPS mode, and - will reject keys that are not 2048, 3072, or 4096 bits in length in FIPS mode. We now validate - and reject invalid certs earlier. [[GH-2478](https://github.com/hashicorp/consul-k8s/issues/2478)] -* api-gateway: set route condition appropriately when parent ref includes non-existent section name [[GH-2420](https://github.com/hashicorp/consul-k8s/issues/2420)] -* control-plane: Always update ACL policies upon upgrade. [[GH-2392](https://github.com/hashicorp/consul-k8s/issues/2392)] -* control-plane: fix bug in endpoints controller when deregistering services from consul when a node is deleted. [[GH-2571](https://github.com/hashicorp/consul-k8s/issues/2571)] -* helm: fix CONSUL_LOGIN_DATACENTER for consul client-daemonset. [[GH-2652](https://github.com/hashicorp/consul-k8s/issues/2652)] -* helm: fix ui ingress manifest formatting, and exclude `ingressClass` when not defined. [[GH-2687](https://github.com/hashicorp/consul-k8s/issues/2687)] -* transparent-proxy: Fix issue where connect-inject lacked sufficient `mesh:write` privileges in some deployments, - which prevented virtual IPs from persisting properly. [[GH-2520](https://github.com/hashicorp/consul-k8s/issues/2520)] - -## 1.1.4 (Aug 10, 2023) - -SECURITY: - -* Upgrade to use Go 1.20.6 and `x/net/http` 0.12.0. - This resolves [CVE-2023-29406](https://github.com/advisories/GHSA-f8f7-69v5-w4vx)(`net/http`). [[GH-2642](https://github.com/hashicorp/consul-k8s/issues/2642)] -* Upgrade to use Go 1.20.7 and `x/net` 0.13.0. - This resolves [CVE-2023-29409](https://nvd.nist.gov/vuln/detail/CVE-2023-29409)(`crypto/tls`) - and [CVE-2023-3978](https://nvd.nist.gov/vuln/detail/CVE-2023-3978)(`net/html`). [[GH-2710](https://github.com/hashicorp/consul-k8s/issues/2710)] - -IMPROVEMENTS: - -* Add support to provide the logLevel flag via helm for multiple low level components. Introduces the following fields -1. `global.acls.logLevel` -2. `global.tls.logLevel` -3. `global.federation.logLevel` -4. `global.gossipEncryption.logLevel` -5. `server.logLevel` -6. `client.logLevel` -7. `meshGateway.logLevel` -8. `ingressGateways.logLevel` -9. `terminatingGateways.logLevel` -10. `telemetryCollector.logLevel` [[GH-2302](https://github.com/hashicorp/consul-k8s/issues/2302)] -* control-plane: increase timeout after login for ACL replication to 60 seconds [[GH-2656](https://github.com/hashicorp/consul-k8s/issues/2656)] -* helm: adds values for `securityContext` and `annotations` on TLS and ACL init/cleanup jobs. [[GH-2525](https://github.com/hashicorp/consul-k8s/issues/2525)] -* helm: do not set container securityContexts by default on OpenShift < 4.11 [[GH-2678](https://github.com/hashicorp/consul-k8s/issues/2678)] -* helm: set container securityContexts to match the `restricted` Pod Security Standards policy to support running Consul in a namespace with restricted PSA enforcement enabled [[GH-2572](https://github.com/hashicorp/consul-k8s/issues/2572)] - -BUG FIXES: - -* control-plane: fix bug in endpoints controller when deregistering services from consul when a node is deleted. [[GH-2571](https://github.com/hashicorp/consul-k8s/issues/2571)] -* helm: fix CONSUL_LOGIN_DATACENTER for consul client-daemonset. [[GH-2652](https://github.com/hashicorp/consul-k8s/issues/2652)] -* helm: fix ui ingress manifest formatting, and exclude `ingressClass` when not defined. [[GH-2687](https://github.com/hashicorp/consul-k8s/issues/2687)] - ## 1.0.9 (Aug 10, 2023) SECURITY: * Upgrade to use Go 1.19.11 and `x/net/http` 0.12.0. - This resolves [CVE-2023-29406](https://github.com/advisories/GHSA-f8f7-69v5-w4vx)(`net/http`). [[GH-2650](https://github.com/hashicorp/consul-k8s/issues/2650)] +This resolves [CVE-2023-29406](https://github.com/advisories/GHSA-f8f7-69v5-w4vx)(`net/http`). [[GH-2650](https://github.com/hashicorp/consul-k8s/issues/2650)] * Upgrade to use Go 1.19.12 and `x/net` 0.13.0. - This resolves [CVE-2023-29409](https://nvd.nist.gov/vuln/detail/CVE-2023-29409)(`crypto/tls`) - and [CVE-2023-3978](https://nvd.nist.gov/vuln/detail/CVE-2023-3978)(`net/html`). [[GH-2717](https://github.com/hashicorp/consul-k8s/issues/2717)] +This resolves [CVE-2023-29409](https://nvd.nist.gov/vuln/detail/CVE-2023-29409)(`crypto/tls`) +and [CVE-2023-3978](https://nvd.nist.gov/vuln/detail/CVE-2023-3978)(`net/html`). [[GH-2717](https://github.com/hashicorp/consul-k8s/issues/2717)] IMPROVEMENTS: @@ -122,86 +31,6 @@ BUG FIXES: * helm: fix CONSUL_LOGIN_DATACENTER for consul client-daemonset. [[GH-2652](https://github.com/hashicorp/consul-k8s/issues/2652)] * helm: fix ui ingress manifest formatting, and exclude `ingressClass` when not defined. [[GH-2687](https://github.com/hashicorp/consul-k8s/issues/2687)] -## 0.49.8 (July 12, 2023) - -IMPROVEMENTS: - -* helm: Add `connectInject.prepareDataplanesUpgrade` setting for help upgrading to dataplanes. This setting is required if upgrading from non-dataplanes to dataplanes when ACLs are enabled. See https://developer.hashicorp.com/consul/docs/k8s/upgrade#upgrading-to-consul-dataplane for more information. [[GH-2514](https://github.com/hashicorp/consul-k8s/issues/2514)] - -## 1.2.0 (June 28, 2023) - -FEATURES: - -* Add support for configuring Consul server-side rate limiting [[GH-2166](https://github.com/hashicorp/consul-k8s/issues/2166)] -* api-gateway: Add API Gateway for Consul on Kubernetes leveraging Consul native API Gateway configuration. [[GH-2152](https://github.com/hashicorp/consul-k8s/issues/2152)] -* crd: Add `mutualTLSMode` to the ProxyDefaults and ServiceDefaults CRDs and `allowEnablingPermissiveMutualTLS` to the Mesh CRD to support configuring permissive mutual TLS. [[GH-2100](https://github.com/hashicorp/consul-k8s/issues/2100)] -* helm: Add `JWTProvider` CRD for configuring the `jwt-provider` config entry. [[GH-2209](https://github.com/hashicorp/consul-k8s/issues/2209)] -* helm: Update the ServiceIntentions CRD to support `JWT` fields. [[GH-2213](https://github.com/hashicorp/consul-k8s/issues/2213)] - -IMPROVEMENTS: - -* cli: update minimum go version for project to 1.20. [[GH-2102](https://github.com/hashicorp/consul-k8s/issues/2102)] -* control-plane: add FIPS support [[GH-2165](https://github.com/hashicorp/consul-k8s/issues/2165)] -* control-plane: server ACL Init always appends both, the secrets from the serviceAccount's secretRefs and the one created by the Helm chart, to support Openshift secret handling. [[GH-1770](https://github.com/hashicorp/consul-k8s/issues/1770)] -* control-plane: set agent localities on Consul servers to the server node's `topology.kubernetes.io/region` label. [[GH-2093](https://github.com/hashicorp/consul-k8s/issues/2093)] -* control-plane: update alpine to 3.17 in the Docker image. [[GH-1934](https://github.com/hashicorp/consul-k8s/issues/1934)] -* control-plane: update minimum go version for project to 1.20. [[GH-2102](https://github.com/hashicorp/consul-k8s/issues/2102)] -* helm: Kubernetes v1.27 is now supported. Minimum tested version of Kubernetes is now v1.24. [[GH-2304](https://github.com/hashicorp/consul-k8s/issues/2304)] -* helm: Update the default amount of memory used by the connect-inject controller so that its less likely to get OOM killed. [[GH-2249](https://github.com/hashicorp/consul-k8s/issues/2249)] -* helm: add failover policy field to service resolver and proxy default CRDs [[GH-2030](https://github.com/hashicorp/consul-k8s/issues/2030)] -* helm: add samenessGroup CRD [[GH-2048](https://github.com/hashicorp/consul-k8s/issues/2048)] -* helm: add samenessGroup field to exported services CRD [[GH-2075](https://github.com/hashicorp/consul-k8s/issues/2075)] -* helm: add samenessGroup field to service resolver CRD [[GH-2086](https://github.com/hashicorp/consul-k8s/issues/2086)] -* helm: add samenessGroup field to source intention CRD [[GH-2097](https://github.com/hashicorp/consul-k8s/issues/2097)] -* helm: update `imageConsulDataplane` value to `hashicorp/consul-dataplane:1.1.0`. [[GH-1953](https://github.com/hashicorp/consul-k8s/issues/1953)] - -SECURITY: - -* Update [Go-Discover](https://github.com/hashicorp/go-discover) in the container has been updated to address [CVE-2020-14040](https://github.com/advisories/GHSA-5rcv-m4m3-hfh7) [[GH-2390](https://github.com/hashicorp/consul-k8s/issues/2390)] -* Bump Dockerfile base image to `alpine:3.18`. Resolves [CVE-2023-2650](https://github.com/advisories/GHSA-gqxg-9vfr-p9cg) vulnerability in openssl@3.0.8-r4 [[GH-2284](https://github.com/hashicorp/consul-k8s/issues/2284)] -* Fix Prometheus CVEs by bumping controller-runtime. [[GH-2183](https://github.com/hashicorp/consul-k8s/issues/2183)] -* Upgrade to use Go 1.20.4. - This resolves vulnerabilities [CVE-2023-24537](https://github.com/advisories/GHSA-9f7g-gqwh-jpf5)(`go/scanner`), - [CVE-2023-24538](https://github.com/advisories/GHSA-v4m2-x4rp-hv22)(`html/template`), - [CVE-2023-24534](https://github.com/advisories/GHSA-8v5j-pwr7-w5f8)(`net/textproto`) and - [CVE-2023-24536](https://github.com/advisories/GHSA-9f7g-gqwh-jpf5)(`mime/multipart`). - Also, `golang.org/x/net` has been updated to v0.7.0 to resolve CVEs [CVE-2022-41721 - ](https://github.com/advisories/GHSA-fxg5-wq6x-vr4w - ), [CVE-2022-27664](https://github.com/advisories/GHSA-69cg-p879-7622) and [CVE-2022-41723 - ](https://github.com/advisories/GHSA-vvpx-j8f3-3w6h - .) [[GH-2102](https://github.com/hashicorp/consul-k8s/issues/2102)] - -BUG FIXES: - -* control-plane: Fix casing of the Enforce Consecutive 5xx field on Service Defaults and acceptance test fixtures. [[GH-2266](https://github.com/hashicorp/consul-k8s/issues/2266)] -* control-plane: fix issue where consul-connect-injector acl token was unintentionally being deleted and not recreated when a container was restarted due to a livenessProbe failure. [[GH-1914](https://github.com/hashicorp/consul-k8s/issues/1914)] - -## 1.1.3 (June 28, 2023) -BREAKING CHANGES: - -* control-plane: All policies managed by consul-k8s will now be updated on upgrade. If you previously edited the policies after install, your changes will be overwritten. [[GH-2392](https://github.com/hashicorp/consul-k8s/issues/2392)] - -SECURITY: - -* Bump Dockerfile base image to `alpine:3.18`. Resolves [CVE-2023-2650](https://github.com/advisories/GHSA-gqxg-9vfr-p9cg) vulnerability in openssl@3.0.8-r4 [[GH-2284](https://github.com/hashicorp/consul-k8s/issues/2284)] -* Update [Go-Discover](https://github.com/hashicorp/go-discover) in the container has been updated to address [CVE-2020-14040](https://github.com/advisories/GHSA-5rcv-m4m3-hfh7) [[GH-2390](https://github.com/hashicorp/consul-k8s/issues/2390)] - -FEATURES: - -* Add support for configuring graceful shutdown proxy lifecycle management settings. [[GH-2233](https://github.com/hashicorp/consul-k8s/issues/2233)] -* helm: Adds `acls.resources` field which can be configured to override the `resource` settings for the `server-acl-init` and `server-acl-init-cleanup` Jobs. [[GH-2416](https://github.com/hashicorp/consul-k8s/issues/2416)] -* sync-catalog: add ability to support weighted loadbalancing by service annotation `consul.hashicorp.com/service-weight: ` [[GH-2293](https://github.com/hashicorp/consul-k8s/issues/2293)] - -IMPROVEMENTS: - -* (Consul Enterprise) Add support to provide inputs via helm for audit log related configuration [[GH-2369](https://github.com/hashicorp/consul-k8s/issues/2369)] -* helm: Update the default amount of memory used by the connect-inject controller so that its less likely to get OOM killed. [[GH-2249](https://github.com/hashicorp/consul-k8s/issues/2249)] - -BUG FIXES: - -* control-plane: Always update ACL policies upon upgrade. [[GH-2392](https://github.com/hashicorp/consul-k8s/issues/2392)] -* control-plane: Fix casing of the Enforce Consecutive 5xx field on Service Defaults and acceptance test fixtures. [[GH-2266](https://github.com/hashicorp/consul-k8s/issues/2266)] - ## 1.0.8 (June 28, 2023) BREAKING CHANGES: @@ -234,71 +63,6 @@ BUG FIXES: * control-plane: fix issue with multiport pods crashlooping due to dataplane port conflicts by ensuring dns redirection is disabled for non-tproxy pods [[GH-2176](https://github.com/hashicorp/consul-k8s/issues/2176)] * crd: fix bug on service intentions CRD causing some updates to be ignored. [[GH-2194](https://github.com/hashicorp/consul-k8s/issues/2194)] - -## 0.49.7 (June 28, 2023) -BREAKING CHANGES: - -* control-plane: All policies managed by consul-k8s will now be updated on upgrade. If you previously edited the policies after install, your changes will be overwritten. [[GH-2392](https://github.com/hashicorp/consul-k8s/issues/2392)] - -SECURITY: - -* Bump Dockerfile base image for RedHat UBI `consul-k8s-control-plane` image to `ubi-minimal:9.2`. [[GH-2204](https://github.com/hashicorp/consul-k8s/issues/2204)] -* Bump Dockerfile base image to `alpine:3.18`. Resolves [CVE-2023-2650](https://github.com/advisories/GHSA-gqxg-9vfr-p9cg) vulnerability in openssl@3.0.8-r4 [[GH-2284](https://github.com/hashicorp/consul-k8s/issues/2284)] - -FEATURES: - -* helm: Adds `acls.resources` field which can be configured to override the `resource` settings for the `server-acl-init` and `server-acl-init-cleanup` Jobs. [[GH-2416](https://github.com/hashicorp/consul-k8s/issues/2416)] - -IMPROVEMENTS: - -* (Consul Enterprise) Add support to provide inputs via helm for audit log related configuration [[GH-2265](https://github.com/hashicorp/consul-k8s/issues/2265)] -* helm: Update the default amount of memory used by the connect-inject controller so that its less likely to get OOM killed. [[GH-2249](https://github.com/hashicorp/consul-k8s/issues/2249)] - -BUG FIXES: - -* control-plane: Always update ACL policies upon upgrade. [[GH-2392](https://github.com/hashicorp/consul-k8s/issues/2392)] -* crd: fix bug on service intentions CRD causing some updates to be ignored. [[GH-2194](https://github.com/hashicorp/consul-k8s/issues/2194)] - -## 1.1.2 (June 5, 2023) - -SECURITY: - -* Bump Dockerfile base image for RedHat UBI `consul-k8s-control-plane` image to `ubi-minimal:9.2`. [[GH-2204](https://github.com/hashicorp/consul-k8s/issues/2204)] -* Bump `controller-runtime` to address CVEs in dependencies. [[GH-2226](https://github.com/hashicorp/consul-k8s/issues/2226)] -* Upgrade to use Go 1.20.4. -This resolves vulnerabilities [CVE-2023-24537](https://github.com/advisories/GHSA-9f7g-gqwh-jpf5)(`go/scanner`), -[CVE-2023-24538](https://github.com/advisories/GHSA-v4m2-x4rp-hv22)(`html/template`), -[CVE-2023-24534](https://github.com/advisories/GHSA-8v5j-pwr7-w5f8)(`net/textproto`) and -[CVE-2023-24536](https://github.com/advisories/GHSA-9f7g-gqwh-jpf5)(`mime/multipart`). -Also, `golang.org/x/net` has been updated to v0.7.0 to resolve CVEs [CVE-2022-41721 -](https://github.com/advisories/GHSA-fxg5-wq6x-vr4w -), [CVE-2022-27664](https://github.com/advisories/GHSA-69cg-p879-7622) and [CVE-2022-41723 -](https://github.com/advisories/GHSA-vvpx-j8f3-3w6h -.) [[GH-2104](https://github.com/hashicorp/consul-k8s/issues/2104)] - -FEATURES: - -* Add support for consul-telemetry-collector to forward envoy metrics to an otelhttp compatible receiver or HCP [[GH-2134](https://github.com/hashicorp/consul-k8s/issues/2134)] -* consul-telemetry-collector: Configure envoy proxy config during registration when consul-telemetry-collector is enabled. [[GH-2143](https://github.com/hashicorp/consul-k8s/issues/2143)] -* sync-catalog: add ability to sync hostname from a Kubernetes Ingress resource to the Consul Catalog during service registration. [[GH-2098](https://github.com/hashicorp/consul-k8s/issues/2098)] - -IMPROVEMENTS: - -* cli: Add `consul-k8s config read` command that returns the helm configuration in yaml format. [[GH-2078](https://github.com/hashicorp/consul-k8s/issues/2078)] -* cli: add consul-telemetry-gateway allow-all intention for -demo [[GH-2262](https://github.com/hashicorp/consul-k8s/issues/2262)] -* cli: update cloud preset to enable telemetry collector [[GH-2205](https://github.com/hashicorp/consul-k8s/issues/2205)] -* consul-telemetry-collector: add acceptance tests for consul telemetry collector component [[GH-2195](https://github.com/hashicorp/consul-k8s/issues/2195)] - -BUG FIXES: - -* crd: fix bug on service intentions CRD causing some updates to be ignored. [[GH-2194](https://github.com/hashicorp/consul-k8s/issues/2083)] -* api-gateway: fix issue where the API Gateway controller is unable to start up successfully when Vault is configured as the secrets backend [[GH-2083](https://github.com/hashicorp/consul-k8s/issues/2083)] -* control-plane: add support for idleTimeout in the Service Router config [[GH-2156](https://github.com/hashicorp/consul-k8s/issues/2156)] -* control-plane: fix issue with json tags of service defaults fields EnforcingConsecutive5xx, MaxEjectionPercent and BaseEjectionTime. [[GH-2160](https://github.com/hashicorp/consul-k8s/issues/2160)] -* control-plane: fix issue with multiport pods crashlooping due to dataplane port conflicts by ensuring dns redirection is disabled for non-tproxy pods [[GH-2176](https://github.com/hashicorp/consul-k8s/issues/2176)] -* helm: add missing `$HOST_IP` environment variable to to mesh gateway deployments. [[GH-1808](https://github.com/hashicorp/consul-k8s/issues/1808)] -* sync-catalog: fix issue where the sync-catalog ACL token were set with an incorrect ENV VAR. [[GH-2068](https://github.com/hashicorp/consul-k8s/issues/2068)] - ## 1.0.7 (May 17, 2023) SECURITY: @@ -330,37 +94,6 @@ BUG FIXES: * helm: add missing `$HOST_IP` environment variable to to mesh gateway deployments. [[GH-1808](https://github.com/hashicorp/consul-k8s/issues/1808)] * sync-catalog: fix issue where the sync-catalog ACL token were set with an incorrect ENV VAR. [[GH-2068](https://github.com/hashicorp/consul-k8s/issues/2068)] -## 0.49.6 (May 17, 2023) - -SECURITY: - -* Upgrade to use Go 1.19.9. -This resolves vulnerabilities [CVE-2023-24537](https://github.com/advisories/GHSA-9f7g-gqwh-jpf5)(`go/scanner`), -[CVE-2023-24538](https://github.com/advisories/GHSA-v4m2-x4rp-hv22)(`html/template`), -[CVE-2023-24534](https://github.com/advisories/GHSA-8v5j-pwr7-w5f8)(`net/textproto`) and -[CVE-2023-24536](https://github.com/advisories/GHSA-9f7g-gqwh-jpf5)(`mime/multipart`). -Also, `golang.org/x/net` has been updated to v0.7.0 to resolve CVEs [CVE-2022-41721 -](https://github.com/advisories/GHSA-fxg5-wq6x-vr4w -), [CVE-2022-27664](https://github.com/advisories/GHSA-69cg-p879-7622) and [CVE-2022-41723 -](https://github.com/advisories/GHSA-vvpx-j8f3-3w6h -.) [[GH-2110](https://github.com/hashicorp/consul-k8s/issues/2110)] - -IMPROVEMENTS: - -* helm: Set default `limits.cpu` resource setting to `null` for `consul-connect-inject-init` container to speed up registration times when onboarding services onto the mesh during the init container lifecycle. [[GH-2008](https://github.com/hashicorp/consul-k8s/issues/2008)] - -## 1.1.1 (March 31, 2023) - -IMPROVEMENTS: - -* helm: Set default `limits.cpu` resource setting to `null` for `consul-connect-inject-init` container to speed up registration times when onboarding services onto the mesh during the init container lifecycle. [[GH-2008](https://github.com/hashicorp/consul-k8s/issues/2008)] -* helm: When the `global.acls.bootstrapToken` field is set and the content of the secret is empty, the bootstrap ACL token is written to that secret after bootstrapping ACLs. This applies to both the Vault and Consul secrets backends. [[GH-1920](https://github.com/hashicorp/consul-k8s/issues/1920)] - -BUG FIXES: - -* api-gateway: fix ACL issue where when adminPartitions and ACLs are enabled, API Gateway Controller is unable to create a new namespace in Consul [[GH-2029](https://github.com/hashicorp/consul-k8s/issues/2029)] -* api-gateway: fix issue where specifying an external server SNI name while using client nodes resulted in a TLS verification error. [[GH-2013](https://github.com/hashicorp/consul-k8s/issues/2013)] - ## 1.0.6 (March 20, 2023) IMPROVEMENTS: @@ -383,24 +116,7 @@ IMPROVEMENTS: * control-plane: update alpine to 3.17 in the Docker image. [[GH-1934](https://github.com/hashicorp/consul-k8s/issues/1934)] * helm: update `imageConsulDataplane` value to `hashicorp/consul-dataplane:1.1.0`. [[GH-1953](https://github.com/hashicorp/consul-k8s/issues/1953)] -## 0.49.5 (March 9, 2023) - -SECURITY: - -* upgrade to use Go 1.19.6. This resolves vulnerabilities CVE-2022-41724 in crypto/tls and CVE-2022-41723 in net/http. [[GH-1975](https://github.com/hashicorp/consul-k8s/issues/1975)] - -IMPROVEMENTS: - -* cli: update minimum go version for project to 1.19. [[GH-1975](https://github.com/hashicorp/consul-k8s/issues/1975)] -* control-plane: server ACL Init always appends both, the secrets from the serviceAccount's secretRefs and the one created by the Helm chart, to support Openshift secret handling. [[GH-1770](https://github.com/hashicorp/consul-k8s/issues/1770)] -* control-plane: update alpine to 3.17 in the Docker image. [[GH-1934](https://github.com/hashicorp/consul-k8s/issues/1934)] -* control-plane: update minimum go version for project to 1.19. [[GH-1975](https://github.com/hashicorp/consul-k8s/issues/1975)] - -BUG FIXES: - -* control-plane: fix issue where consul-connect-injector acl token was unintentionally being deleted and not recreated when a container was restarted due to a livenessProbe failure. [[GH-1914](https://github.com/hashicorp/consul-k8s/issues/1914)] - -## 1.1.0 (February 27, 2023) +## 1.0.4 (February 7, 2023) BREAKING CHANGES: * Helm: @@ -416,36 +132,10 @@ BREAKING CHANGES: values: ["kube-system","local-path-storage"] ``` [[GH-1869](https://github.com/hashicorp/consul-k8s/pull/1869)] - + IMPROVEMENTS: -* Helm: - * CNI: Add `connectInject.cni.namespace` stanza which allows the CNI plugin resources to be deployed in a namespace other than the namespace that Consul is installed. [[GH-1756](https://github.com/hashicorp/consul-k8s/pull/1756)] - * Kubernetes v1.26 is now supported. Minimum tested version of Kubernetes is now v1.23. [[GH-1852](https://github.com/hashicorp/consul-k8s/pull/1852)] - * Add a `global.extraLabels` stanza to allow setting global Kubernetes labels for all components deployed by the `consul-k8s` Helm chart. [[GH-1778](https://github.com/hashicorp/consul-k8s/pull/1778)] - * Add the `accessLogs` field to the `ProxyDefaults` CRD. [[GH-1816](https://github.com/hashicorp/consul-k8s/pull/1816)] - * Add the `envoyExtensions` field to the `ProxyDefaults` and `ServiceDefaults` CRD. [[GH-1823]](https://github.com/hashicorp/consul-k8s/pull/1823) - * Add the `balanceInboundConnections` field to the `ServiceDefaults` CRD. [[GH-1823]](https://github.com/hashicorp/consul-k8s/pull/1823) - * Add the `upstreamConfig.overrides[].peer` field to the `ServiceDefaults` CRD. [[GH-1853]](https://github.com/hashicorp/consul-k8s/pull/1853) -* Control-Plane - * Update minimum go version for project to 1.20 [[GH-1908](https://github.com/hashicorp/consul-k8s/pull/1908)] - * Add support for the annotation `consul.hashicorp.com/use-proxy-health-check`. When this annotation is used by a service, it configures a readiness endpoint on Consul Dataplane and queries it instead of the proxy's inbound port which forwards requests to the application. [[GH-1824](https://github.com/hashicorp/consul-k8s/pull/1824)], [[GH-1841](https://github.com/hashicorp/consul-k8s/pull/1841)] - * Add health check for synced services based on the status of the Kubernetes readiness probe on synced pod. [[GH-1821](https://github.com/hashicorp/consul-k8s/pull/1821)] - * Remove extraneous `gnupg` dependency from `consul-k8s-control-plane` since it is no longer needed for validating binary artifacts prior to release. [[GH-1882](https://github.com/hashicorp/consul-k8s/pull/1882)] - * Server ACL Init always appends both, the secrets from the serviceAccount's secretRefs and the one created by the Helm chart, to support Openshift secret handling. [[GH-1770](https://github.com/hashicorp/consul-k8s/pull/1770)] - * Update alpine to 3.17 in the Docker image. [[GH-1934](https://github.com/hashicorp/consul-k8s/pull/1934)] -* CLI: - * Update minimum go version for project to 1.20 [[GH-1908](https://github.com/hashicorp/consul-k8s/pull/1908)] - * Add `consul-k8s proxy log podname` command for displaying and modifying Envoy log levels for a given Pod. [GH-1844](https://github.com/hashicorp/consul-k8s/pull/1844), [GH-1849](https://github.com/hashicorp/consul-k8s/pull/1849), [GH-1864](https://github.com/hashicorp/consul-k8s/pull/1864) - -BUG FIXES: -* Control Plane - * Don't incorrectly diff intention config entries when upgrading from Consul pre-1.12 to 1.12+ [[GH-1804](https://github.com/hashicorp/consul-k8s/pull/1804)] - * Add discover binary to control-plane image [[GH-1749](https://github.com/hashicorp/consul-k8s/pull/1749)] -* Helm: - * Don't pass in a CA file to the API Gateway controller when `externalServers.useSystemRoots` is `true`. [[GH-1743](https://github.com/hashicorp/consul-k8s/pull/1743)] - * Use the correct autogenerated cert for the API Gateway Controller when connecting to servers versus clients. [[GH-1753](https://github.com/hashicorp/consul-k8s/pull/1753)] -* Security: - * Upgrade to use Go 1.20.1 This resolves vulnerabilities [CVE-2022-41724](https://go.dev/issue/58001) in `crypto/tls` and [CVE-2022-41723](https://go.dev/issue/57855) in `net/http`. [[GH-1908](https://github.com/hashicorp/consul-k8s/pull/1908)] + * Control Plane + * Remove extraneous `gnupg` dependency from `consul-k8s-control-plane` since it is no longer needed for validating binary artifacts prior to release. [[GH-1882](https://github.com/hashicorp/consul-k8s/pull/1882)] ## 1.0.3 (January 30, 2023) @@ -461,42 +151,19 @@ BUG FIXES: * Control Plane * Don't incorrectly diff intention config entries when upgrading from Consul pre-1.12 to 1.12+ [[GH-1804](https://github.com/hashicorp/consul-k8s/pull/1804)] -## 0.49.3 (January 30, 2023) - -IMPROVEMENTS: -* Helm: - * Add a `global.extraLabels` stanza to allow setting global Kubernetes labels for all components deployed by the `consul-k8s` Helm chart. [[GH-1778](https://github.com/hashicorp/consul-k8s/pull/1778)] -* Control-Plane - * Add support for the annotation `consul.hashicorp.com/use-proxy-health-check`. When this annotation is used by a service, it configures a readiness endpoint on Consul Dataplane and queries it instead of the proxy's inbound port which forwards requests to the application. [[GH-1824](https://github.com/hashicorp/consul-k8s/pull/1824)], [[GH-1843](https://github.com/hashicorp/consul-k8s/pull/1843)] - * Add health check for synced services based on the status of the Kubernetes readiness probe on synced pod. [[GH-1821](https://github.com/hashicorp/consul-k8s/pull/1821)] - -BUG FIXES: -* Control Plane - * Don't incorrectly diff intention config entries when upgrading from Consul pre-1.12 to 1.12+ [[GH-1804](https://github.com/hashicorp/consul-k8s/pull/1804)] - ## 1.0.2 (December 1, 2022) IMPROVEMENTS: * Helm: * CNI: Add `connectInject.cni.namespace` stanza which allows the CNI plugin resources to be deployed in a namespace other than the namespace that Consul is installed. [[GH-1756](https://github.com/hashicorp/consul-k8s/pull/1756)] +* Control Plane: + * Server ACL Init always appends both, the secrets from the serviceAccount's secretRefs and the one created by the Helm chart, to support Openshift secret handling. [[GH-1770](https://github.com/hashicorp/consul-k8s/pull/1770)] BUG FIXES: * Helm: * Use the correct autogenerated cert for the API Gateway Controller when connecting to servers versus clients. [[GH-1753](https://github.com/hashicorp/consul-k8s/pull/1753)] * Don't mount the CA cert when `externalServers.useSystemRoots` is `true`. [[GH-1753](https://github.com/hashicorp/consul-k8s/pull/1753)] -## 0.49.2 (December 1, 2022) - -IMPROVEMENTS: -* Control Plane - * Bump Dockerfile base image for RedHat UBI `consul-k8s-control-plane` image to `ubi-minimal:9.1`. [[GH-1725](https://github.com/hashicorp/consul-k8s/pull/1725)] -* Helm - * Add fields `localConnectTimeoutMs` and `localRequestTimeoutMs` to the `ServiceDefaults` CRD. [[GH-1647](https://github.com/hashicorp/consul-k8s/pull/1647)] - -BUG FIXES: -* Helm: - * Disable PodSecurityPolicies templating for `gossip-encryption-autogenerate` and `partition-init` when `global.enablePodSecurityPolicies` is `false`. [[GH-1693](https://github.com/hashicorp/consul-k8s/pull/1693)] - ## 1.0.1 (November 21, 2022) BUG FIXES: @@ -599,6 +266,7 @@ IMPROVEMENTS: * API Gateway: Allow controller to read MeshServices for use as a route backend. [[GH-1574](https://github.com/hashicorp/consul-k8s/pull/1574)] * API Gateway: Add support for using dynamic server discovery strings when running without agents. [[GH-1732](https://github.com/hashicorp/consul-k8s/pull/1732)] + BUG FIXES: * CLI * Allow optional environment variables for use in the cloud preset to the CLI for cluster bootstrapping. [[GH-1608](https://github.com/hashicorp/consul-k8s/pull/1608)] @@ -607,24 +275,7 @@ BUG FIXES: * Peering * Add `peering:read` permissions to mesh gateway token to fix peering connections through the mesh gateways. [[GH-1685](https://github.com/hashicorp/consul-k8s/pull/1685)] * Helm: - * Disable PodSecurityPolicies in all templates when `global.enablePodSecurityPolicies` is `false`. [[GH-1693](https://github.com/hashicorp/consul-k8s/pull/1693)] - -## 0.49.1 (November 14, 2022) -BREAKING CHANGES: -* Peering: - * Rename `PeerName` to `Peer` in ExportedServices CRD. [[GH-1596](https://github.com/hashicorp/consul-k8s/pull/1596)] - -FEATURES: -* Ingress Gateway - * Add support for MaxConnections, MaxConcurrentRequests, and MaxPendingRequests to Ingress Gateway CRD. [[GH-1691](https://github.com/hashicorp/consul-k8s/pull/1691)] - -IMPROVEMENTS: -* Helm: - * Add `tolerations` and `nodeSelector` to Server ACL init jobs and `nodeSelector` to Webhook cert manager. [[GH-1581](https://github.com/hashicorp/consul-k8s/pull/1581)] - * API Gateway: Allow controller to read MeshServices for use as a route backend. [[GH-1574](https://github.com/hashicorp/consul-k8s/pull/1574)] - * API Gateway: Add `tolerations` to `apiGateway.managedGatewayClass` and `apiGateway.controller` [[GH-1650](https://github.com/hashicorp/consul-k8s/pull/1650)] - * API Gateway: Create PodSecurityPolicy for controller when `global.enablePodSecurityPolicies=true`. [[GH-1656](https://github.com/hashicorp/consul-k8s/pull/1656)] - * API Gateway: Create PodSecurityPolicy and allow controller to bind it to ServiceAccounts that it creates for Gateway Deployments when `global.enablePodSecurityPolicies=true`. [[GH-1672](https://github.com/hashicorp/consul-k8s/pull/1672)] + * Disable PodSecurityPolicies templating for `gossip-encryption-autogenerate` and `partition-init` when `global.enablePodSecurityPolicies` is `false`. [[GH-1693](https://github.com/hashicorp/consul-k8s/pull/1693)] ## 0.49.0 (September 29, 2022) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4eca76a1dd..4fb322799b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,11 +22,10 @@ 1. [Running the tests](#running-the-tests) 1. [Writing Unit tests](#writing-unit-tests) 1. [Writing Acceptance tests](#writing-acceptance-tests) -1. [Using the Acceptance Test Framework to Debug](#using-acceptance-test-framework-to-debug) 1. [Helm Reference Docs](#helm-reference-docs) -1. [Managing External CRD Dependencies](#managing-external-crd-dependencies) 1. [Adding a Changelog Entry](#adding-a-changelog-entry) + ## Contributing 101 ### Building and running `consul-k8s-control-plane` @@ -168,7 +167,7 @@ rebase the branch on main, fixing any conflicts along the way before the code ca ```bash operator-sdk create api --group consul --version v1alpha1 --kind IngressGateway --controller --namespaced=true --make=false --resource=true ``` -1. Re-order the generated ingressgateway_types.go file, so it looks like: +1. Re-order the file so it looks like: ```go func init() { SchemeBuilder.Register(&IngressGateway{}, &IngressGatewayList{}) @@ -214,7 +213,6 @@ rebase the branch on main, fixing any conflicts along the way before the code ca ```go // ServiceRouter is the Schema for the servicerouters API // +kubebuilder:printcolumn:name="Synced",type="string",JSONPath=".status.conditions[?(@.type==\"Synced\")].status",description="The sync status of the resource with Consul" - // +kubebuilder:printcolumn:name="Last Synced",type="date",JSONPath=".status.lastSyncedTime",description="The last successful synced time of the resource with Consul" // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="The age of the resource" type ServiceRouter struct { ``` @@ -233,7 +231,7 @@ rebase the branch on main, fixing any conflicts along the way before the code ca ``` 1. Go to the Consul `api` package for the config entry, e.g. https://github.com/hashicorp/consul/blob/main/api/config_entry_gateways.go 1. Copy the top-level fields over into the `Spec` struct except for - `Kind`, `Name`, `Namespace`, `Partition`, `Meta`, `CreateIndex` and `ModifyIndex`. In this + `Kind`, `Name`, `Namespace`, `Meta`, `CreateIndex` and `ModifyIndex`. In this example, the top-level fields remaining are `TLS` and `Listeners`: ```go @@ -322,6 +320,8 @@ rebase the branch on main, fixing any conflicts along the way before the code ca ### Controller 1. Delete the file `control-plane/controllers/suite_test.go`. We don't write suite tests, just unit tests. +1. Move `control-plane/controllers/ingressgateway_controller.go` to `control-plane/controller` directory. +1. Delete the `control-plane/controllers` directory. 1. Rename `Reconciler` to `Controller`, e.g. `IngressGatewayReconciler` => `IngressGatewayController` 1. Use the existing controller files as a guide and make this file match. 1. Add your controller as a case in the tests in `configentry_controller_test.go`: @@ -376,7 +376,7 @@ rebase the branch on main, fixing any conflicts along the way before the code ca ### Generating YAML 1. Run `make ctrl-manifests` to generate the CRD and webhook YAML. -1. Uncomment your CRD in `control-plane/config/crd/kustomization` under `patches:` +1. Uncomment your CRD in `control-plane/config/crd/kustomization` under `patchesStrategicMerge:` 1. Update the sample, e.g. `control-plane/config/samples/consul_v1alpha1_ingressgateway.yaml` to a valid resource that can be used for testing: ```yaml @@ -395,13 +395,13 @@ rebase the branch on main, fixing any conflicts along the way before the code ca ``` ### Updating Helm chart -1. Update `charts/consul/templates/connect-inject-mutatingwebhookconfiguration` with the webhook for this resource +1. Update `charts/consul/templates/controller-mutatingwebhookconfiguration` with the webhook for this resource using the updated `control-plane/config/webhook/manifests.v1beta1.yaml` and replacing `clientConfig.service.name/namespace` with the templated strings shown below to match the other webhooks.: ```yaml - clientConfig: service: - name: {{ template "consul.fullname" . }}-connect-injector + name: {{ template "consul.fullname" . }}-controller-webhook namespace: {{ .Release.Namespace }} path: /mutate-v1alpha1-ingressgateway failurePolicy: Fail @@ -421,31 +421,24 @@ rebase the branch on main, fixing any conflicts along the way before the code ca - ingressgateways sideEffects: None ``` -1. Update `charts/consul/templates/connect-inject-clusterrole.yaml` to allow the controller to +1. Update `charts/consul/templates/controller-clusterrole.yaml` to allow the controller to manage your resource type. ### Testing A New CRD -1. Build a Docker image for consul-k8s via `make control-plane-dev-docker` and push to a docker repository: - ``` - docker tag consul-k8s-control-plane-dev /consul-k8s-control-plane-dev: - docker push /consul-k8s-control-plane-dev: - ``` +1. Build a Docker image for consul-k8s via `make dev-docker` and tagging your image appropriately. Remember to CD into the `control-plane` directory! 1. Install using the updated Helm repository, with a values like: ```yaml global: - imageK8S: lkysow/consul-k8s-control-plane-dev:nov26 + imageK8S: ghcr.io/lkysow/consul-k8s-dev:nov26 name: consul server: replicas: 1 bootstrapExpect: 1 - ui: - enabled: true - connectInject: + controller: enabled: true ``` -1. Create a sample CRD -1. Run `kubectl apply -f ` to apply your sample CRD. -1. Check its synced status (for example CRD called ingressgateway): +1. `kubectl apply` your sample CRD. +1. Check its synced status: ```bash kubectl get ingressgateway NAME SYNCED AGE @@ -941,198 +934,6 @@ Here are some things to consider before adding a test: --- -## Using Acceptance Test Framework to Debug -### Acceptance Tests - -The [consul-k8s](https://github.com/hashicorp/consul-k8s) repository has an extensive list of [acceptance](https://github.com/hashicorp/consul-k8s/tree/main/acceptance/tests) -tests that are used by CI to run per-PR and nightly acceptance tests. -It is built on its own framework that uses Helm and the consul-k8s CLI to deploy consul (and other tools) in various -configurations that provide test coverage for most features that exist and provides coverage for more advanced deployments -than are typically covered in guides. -Importantly, it is **automated**, so you are able to rapidly deploy known working -configurations in known working environments. -It can be very helpful for bootstrapping complex environments such as when using Vault as a CA for Consul or for federating test clusters. - -The tests are organized like this : -```shell -demo $ tree -L 1 -d acceptance/tests -acceptance/tests -├── basic -├── cli -├── config-entries -├── connect -├── consul-dns -├── example -├── fixtures -├── ingress-gateway -├── metrics -├── partitions -├── peering -├── snapshot-agent -├── sync -├── terminating-gateway -├── vault -└── wan-federation -``` - -### Basic Running of Tests -Any given test can be run either through GoLand or another IDE, or via command line using `go test -run`. - -To run all of the connect tests from command line: -```shell -$ cd acceptance/tests -$ go test ./connect/... -v -p 1 -timeout 2h -failfast -use-kind -no-cleanup-on-failure -kubecontext=kind-dc1 -secondary-kubecontext=kind-dc2 -enable-enterprise -enable-multi-cluster -debug-directory=/tmp/debug -consul-k8s-image=kyleschochenmaier/consul-k8s-acls -``` - -When running from command line a few things are important: -* Some tests use Enterprise features, in which case you need: - * Set environment variables `CONSUL_ENT_LICENSE` and possibly `VAULT_LICENSE`. - * Use `-enable-enterprise` on command line when running the test. -* Multi-cluster tests require `-enable-multi-cluster -kubecontext=kind-dc1 -secondary-kubecontext=kind-dc2` -* Using `.//...` is required as part of the command-line to pick up necessary environmental config. - -### Using the framework to debug in an environment -=> NOTE: It is helpful to tune the docker desktop resource settings so that docker has at least 4GB memory, plenty of cpu cores and 2GB of swap. - -* If using Kind, `-use-kind` should be added, and be sure you cluster is up and running: -```shell -$ kind create cluster --name=dc1 && kind create cluster --name=dc2 -``` -* Pick a test which replicates the environment you are wanting to work with. - Ex: pick a test from `partitions/` or `vault/` or `connect/`. -* If you need the environment to persist, add a `time.Sleep(1*time.Hour)` to the end of the test in the test file. -* Use the following flags if you need to use or test out a specific consul/k8s image: - `-consul-k8s-image=` && `-consul-image=` -* You can set custom helm flags by modifying the test file directly in the respective directory. - -Finally, run the test like shown above: -```shell -$ cd acceptance/tests -$ go test -run Vault_WANFederationViaGateways ./vault/... -p 1 -timeout 2h -failfast -use-kind -no-cleanup-on-failure -kubecontext=kind-dc1 -secondary-kubecontext=kind-dc2 -enable-multi-cluster -debug-directory=/tmp/debug -``` -You can interact with the running kubernetes clusters now using `kubectl [COMMAND] --context=` - -* `kind delete clusters --all` is helpful for cleanup! - -### Example Debugging session using the acceptance test framework to bootstrap and debug a Vault backed federated Consul installation: -This test utilizes the `consul-k8s` acceptance test framework, with a custom consul-k8s branch which: -* Modifies the acceptance test to use custom consul+consul-k8s images and sleeps at the end of the test to allow analysis. -* Modifies the helm chart to pass in `connect_ca.intermediate_cert_ttl` and `connect_ca.leaf_cert_ttl` in the `server-configmap` - -1. First clone the consul-k8s repo and then check out the branch locally: `git checkout origin/consul-vault-provider-wanfed-acceptance`. -2. Start the kind clusters: `kind create cluster --name=dc1 && kind create cluster --name=dc2` -3. run the `TestVault_WANFederationViaGateways` acceptance test in `acceptance/tests/vault/vault_wan_fed_test.go` - I use goland, but this command should get you most of the way: -```shell -$ cd acceptance/tests -$ go test -run Vault_WANFederationViaGateways ./vault/... -p 1 -timeout 2h -failfast -use-kind -no-cleanup-on-failure -kubecontext=kind-dc1 -secondary-kubecontext=kind-dc2 -enable-multi-cluster -debug-directory=/tmp/debug -``` -NOTE: This specific acceptance test is considered FLAKY with Kind, if things don't come up it's best to run against GKE/AKS/etc, in which case you just modify the `kubecontext` command parameters to point to your clusters. It is worth noting that you will need to setup any necessary networking for non-Kind clusters manually. - -NOTE: This test requires a VAULT_LICENSE set as an environment variable in the shell where you run `go test` - -4. Wait 10-20 minutes to allow the first intermediate ca renewal, this test is particularly resource intensive so it can take time for everything to come online on a laptop, use `kubectl get pods` to validate that `static-server` and `static-client` have been deployed and are online. - -You can validate the ICA rotation by doing: -```shell -# Fetch the vault root token: -$ kubectl get secrets -root-token -o json //----> b64 decode the `data.token` field. -$ kubectl exec -it -- sh -$ export VAULT_TOKEN= -$ export VAULT_ADDR=https://-vault:8200 - -# Fetch the consul bootstrap token -$ vault kv get consul/secret/bootstrap - -# Examine the vault issuers, there should be 2 by now if ICA renewal has occured: -# NOTE: for a federated setup the issuers url for dc2 is `vault list dc2/connect_inter/issuers`! -$ vault list dc1/connect_inter/issuers - -Keys ----- -29bdffbd-87ec-cfe0-fd05-b78f99eba243 -344eea3c-f085-943a-c3ff-66721ef408f4 - -# Now login to the consul-server -$ kubectl exec -it -- sh -$ export CONSUL_HTTP_TOKEN= -$ export CONSUL_HTTP_ADDR=https://localhost:8501 -$ export CONSUL_HTTP_SSL_VERIFY=false - -# Read the `connect/ca/roots` endpoint: -# It should change + rotate with the expiration of the ICA (defined by `intermediate_cert_ttl` which is `15m` in the branch for this gist. - -$ curl -k --header "X-Consul-Token: 1428da53-5e88-db1a-6ad5-e50212b011da" https://127.0.0.1:8501/v1/agent/connect/ca/roots | jq - . - % Total % Received % Xferd Average Speed Time Time Time Current - Dload Upload Total Spent Left Speed -100 3113 100 3113 0 0 6222 0 --:--:-- --:--:-- --:--:-- 7705 -{ - "ActiveRootID": "36:be:19:0e:56:d1:c2:1a:d8:54:22:97:88:3c:91:17:1d:d2:d3:e0", - "TrustDomain": "34a76791-b9b2-b93e-b0e4-1989ed11a28e.consul", - "Roots": [ - { - "ID": "36:be:19:0e:56:d1:c2:1a:d8:54:22:97:88:3c:91:17:1d:d2:d3:e0", - "Name": "Vault CA Primary Cert", - "SerialNumber": 15998414315735550000, - "SigningKeyID": "fe:b9:d6:0b:c6:ce:2c:25:4f:d8:59:cb:11:ea:a5:42:5f:8e:41:4b", - "ExternalTrustDomain": "34a76791-b9b2-b93e-b0e4-1989ed11a28e", - "NotBefore": "2022-11-16T20:16:15Z", - "NotAfter": "2032-11-13T20:16:45Z", - "RootCert": "-----BEGIN CERTIFICATE-----\nMIICLDCCAdKgAwIBAgIUKQ9BPHF9mtC7yFPC3gXJDpLxCHIwCgYIKoZIzj0EAwIw\nLzEtMCsGA1UEAxMkcHJpLTEwOTJudTEudmF1bHQuY2EuMzRhNzY3OTEuY29uc3Vs\nMB4XDTIyMTExNjIwMTYxNVoXDTMyMTExMzIwMTY0NVowLzEtMCsGA1UEAxMkcHJp\nLTEwOTJudTEudmF1bHQuY2EuMzRhNzY3OTEuY29uc3VsMFkwEwYHKoZIzj0CAQYI\nKoZIzj0DAQcDQgAETnpGixC1kW8ep2JcGjRR2jbdESvjlEm9nSIWVAcilemUGFwi\nJ0YW0XUmJeEzRyfwLXnOw6voPzXRf1zXKjdTD6OByzCByDAOBgNVHQ8BAf8EBAMC\nAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUtb6EjDxyI+myIjDc+7KbiN8u\n8XowHwYDVR0jBBgwFoAUtb6EjDxyI+myIjDc+7KbiN8u8XowZQYDVR0RBF4wXIIk\ncHJpLTEwOTJudTEudmF1bHQuY2EuMzRhNzY3OTEuY29uc3VshjRzcGlmZmU6Ly8z\nNGE3Njc5MS1iOWIyLWI5M2UtYjBlNC0xOTg5ZWQxMWEyOGUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCIHBezFSQAK5Nolf0rs3ErvlDcA8Z9esldh6gHupuGsNkAiEA\n9qL+P9PJAW4CrbTL0iF2yZUyJC2nwSSa2K0nYG8bXWQ=\n-----END CERTIFICATE-----\n", - "IntermediateCerts": [ - "-----BEGIN CERTIFICATE-----\nMIICLzCCAdSgAwIBAgIUbILCP3ODM4ScNBOm0jw59Fxju0swCgYIKoZIzj0EAwIw\nLzEtMCsGA1UEAxMkcHJpLTEwOTJudTEudmF1bHQuY2EuMzRhNzY3OTEuY29uc3Vs\nMB4XDTIyMTExNjIwMzIxNloXDTIyMTExNjIwNDc0NlowMDEuMCwGA1UEAxMlcHJp\nLTE4MThxNWlnLnZhdWx0LmNhLjM0YTc2NzkxLmNvbnN1bDBZMBMGByqGSM49AgEG\nCCqGSM49AwEHA0IABI30ikgrwTjbPaGgfNYkushvrEUUpxLzxMMEBlE82ilog1RW\nqwuEU29Qsa+N4SrfOf37xNv/Ey8SXPs5l2HmXJWjgcwwgckwDgYDVR0PAQH/BAQD\nAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFCZpC/BTdaggL2kj6Dfyk3+a\nNqBvMB8GA1UdIwQYMBaAFLW+hIw8ciPpsiIw3Puym4jfLvF6MGYGA1UdEQRfMF2C\nJXByaS0xODE4cTVpZy52YXVsdC5jYS4zNGE3Njc5MS5jb25zdWyGNHNwaWZmZTov\nLzM0YTc2NzkxLWI5YjItYjkzZS1iMGU0LTE5ODllZDExYTI4ZS5jb25zdWwwCgYI\nKoZIzj0EAwIDSQAwRgIhAJ8RHgR5qkyW2q866vGYJy+7BJ4zUXs3OJ76QLmxxU3K\nAiEA70S7wBEm1ZduTAk1ZfZPJEUGxvAXAcgy7EWeO/6MJ5o=\n-----END CERTIFICATE-----\n", - "-----BEGIN CERTIFICATE-----\nMIICLTCCAdKgAwIBAgIUU3qwESuhh4PgW3/tnHDn3qnBMrAwCgYIKoZIzj0EAwIw\nLzEtMCsGA1UEAxMkcHJpLTEwOTJudTEudmF1bHQuY2EuMzRhNzY3OTEuY29uc3Vs\nMB4XDTIyMTExNjIwNDAxNloXDTIyMTExNjIwNTU0NlowLzEtMCsGA1UEAxMkcHJp\nLTFkY2hkbGkudmF1bHQuY2EuMzRhNzY3OTEuY29uc3VsMFkwEwYHKoZIzj0CAQYI\nKoZIzj0DAQcDQgAEpj0BWPkcH82su9XGOo9rN5Zr5+Jyp68LiHy+qlIgH3L+OAir\nYgmXmJfuNwI8S2BB8cu0Gk3w5cTF7O0p/qAghaOByzCByDAOBgNVHQ8BAf8EBAMC\nAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU/rnWC8bOLCVP2FnLEeqlQl+O\nQUswHwYDVR0jBBgwFoAUtb6EjDxyI+myIjDc+7KbiN8u8XowZQYDVR0RBF4wXIIk\ncHJpLTFkY2hkbGkudmF1bHQuY2EuMzRhNzY3OTEuY29uc3VshjRzcGlmZmU6Ly8z\nNGE3Njc5MS1iOWIyLWI5M2UtYjBlNC0xOTg5ZWQxMWEyOGUuY29uc3VsMAoGCCqG\nSM49BAMCA0kAMEYCIQCtq4LiZzkiIKUES9MrzUEflg7wcwQf7Km+8RcOGQbz9QIh\nANWHWt1fe8Hl1wQ55qxsV5lSfOpGAox5WHpgnsBC7cwU\n-----END CERTIFICATE-----\n" - ], - "Active": true, - "PrivateKeyType": "ec", - "PrivateKeyBits": 256, - "CreateIndex": 11, - "ModifyIndex": 797 - } - ] -} - -# You can x509 decode the ICA certs to verify they have been updated and have correct expiry: -$ openssl x509 -in cert.crt -text -noout -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 53:7a:b0:11:2b:a1:87:83:e0:5b:7f:ed:9c:70:e7:de:a9:c1:32:b0 - Signature Algorithm: ecdsa-with-SHA256 - Issuer: CN=pri-1092nu1.vault.ca.34a76791.consul - Validity - Not Before: Nov 16 20:40:16 2022 GMT - Not After : Nov 16 20:55:46 2022 GMT - Subject: CN=pri-1dchdli.vault.ca.34a76791.consul - Subject Public Key Info: - Public Key Algorithm: id-ecPublicKey - Public-Key: (256 bit) - pub: - 04:a6:3d:01:58:f9:1c:1f:cd:ac:bb:d5:c6:3a:8f: - 6b:37:96:6b:e7:e2:72:a7:af:0b:88:7c:be:aa:52: - 20:1f:72:fe:38:08:ab:62:09:97:98:97:ee:37:02: - 3c:4b:60:41:f1:cb:b4:1a:4d:f0:e5:c4:c5:ec:ed: - 29:fe:a0:20:85 - ASN1 OID: prime256v1 - NIST CURVE: P-256 - X509v3 extensions: - X509v3 Key Usage: critical - Certificate Sign, CRL Sign - X509v3 Basic Constraints: critical - CA:TRUE - X509v3 Subject Key Identifier: - FE:B9:D6:0B:C6:CE:2C:25:4F:D8:59:CB:11:EA:A5:42:5F:8E:41:4B - X509v3 Authority Key Identifier: - keyid:B5:BE:84:8C:3C:72:23:E9:B2:22:30:DC:FB:B2:9B:88:DF:2E:F1:7A - - X509v3 Subject Alternative Name: - DNS:pri-1dchdli.vault.ca.34a76791.consul, URI:spiffe://34a76791-b9b2-b93e-b0e4-1989ed11a28e.consul - -``` - ---- - ## Helm Reference Docs The Helm reference docs (https://www.consul.io/docs/k8s/helm) are automatically @@ -1209,26 +1010,6 @@ So that the documentation can look like: - `ports` ((#v-ingressgateways-defaults-service-ports)) (`array: [{port: 8080, port: 8443}]`) - Port docs ``` -## Managing External CRD Dependencies - -Some of the features of Consul on Kubernetes make use of CustomResourceDefinitions (CRDs) that we don't directly -manage. One such example is the Gateway API CRDs which we use to configure API Gateways, but are managed by SIG -Networking. - -To pull external CRDs into our Helm chart and make sure they get installed, we generate their configuration using -[Kustomize](https://kustomize.io/) which can pull in Kubernetes config from external sources. We split these -generated CRDs into individual files and store them in the `charts/consul/templates` directory. - -If you need to update the external CRDs we depend on, or add to them, you can do this by editing the -[control-plane/config/crd/external/kustomization.yaml](/control-plane/config/crd/external/kustomization.yaml) file. -Once modified, running - -```bash -make generate-external-crds -``` - -will update the CRDs in the `/templates` directory. - ## Adding a Changelog Entry Any change that a Consul-K8s user might need to know about should have a changelog entry. diff --git a/Makefile b/Makefile index 866a37fbfc..44a0a6a7f2 100644 --- a/Makefile +++ b/Makefile @@ -14,10 +14,6 @@ gen-helm-docs: ## Generate Helm reference docs from values.yaml and update Consu copy-crds-to-chart: ## Copy generated CRD YAML into charts/consul. Usage: make copy-crds-to-chart @cd hack/copy-crds-to-chart; go run ./... -generate-external-crds: ## Generate CRDs for externally defined CRDs and copy them to charts/consul. Usage: make generate-external-crds - @cd ./control-plane/config/crd/external; \ - kustomize build | yq --split-exp '.metadata.name + ".yaml"' --no-doc - bats-tests: ## Run Helm chart bats tests. bats --jobs 4 charts/consul/test/unit @@ -53,17 +49,6 @@ control-plane-dev-docker-multi-arch: check-remote-dev-image-env ## Build consul- --push \ -f $(CURDIR)/control-plane/Dockerfile $(CURDIR)/control-plane -control-plane-fips-dev-docker: ## Build consul-k8s-control-plane FIPS dev Docker image. - @$(SHELL) $(CURDIR)/control-plane/build-support/scripts/build-local.sh -o linux -a $(GOARCH) --fips - @docker build -t '$(DEV_IMAGE)' \ - --target=dev \ - --build-arg 'TARGETARCH=$(GOARCH)' \ - --build-arg 'GIT_COMMIT=$(GIT_COMMIT)' \ - --build-arg 'GIT_DIRTY=$(GIT_DIRTY)' \ - --build-arg 'GIT_DESCRIBE=$(GIT_DESCRIBE)' \ - --push \ - -f $(CURDIR)/control-plane/Dockerfile $(CURDIR)/control-plane - control-plane-test: ## Run go test for the control plane. cd control-plane; go test ./... @@ -85,7 +70,7 @@ cni-plugin-lint: cd control-plane/cni; golangci-lint run -c ../../.golangci.yml ctrl-generate: get-controller-gen ## Run CRD code generation. - cd control-plane; $(CONTROLLER_GEN) object paths="./..." + cd control-plane; $(CONTROLLER_GEN) object:headerFile="build-support/controller/boilerplate.go.txt" paths="./..." # Perform a terraform fmt check but don't change anything terraform-fmt-check: @@ -107,10 +92,6 @@ cli-dev: @echo "==> Installing consul-k8s CLI tool for ${GOOS}/${GOARCH}" @cd cli; go build -o ./bin/consul-k8s; cp ./bin/consul-k8s ${GOPATH}/bin/ -cli-fips-dev: - @echo "==> Installing consul-k8s CLI tool for ${GOOS}/${GOARCH}" - @cd cli; CGO_ENABLED=1 GOEXPERIMENT=boringcrypto go build -o ./bin/consul-k8s -tags "fips"; cp ./bin/consul-k8s ${GOPATH}/bin/ - cli-lint: ## Run linter in the control-plane directory. cd cli; golangci-lint run -c ../.golangci.yml @@ -130,30 +111,22 @@ kind-cni-calico: kubectl create -f $(CURDIR)/acceptance/framework/environment/cni-kind/custom-resources.yaml @sleep 20 -kind-delete: +# Helper target for doing local cni acceptance testing +kind-cni: kind delete cluster --name dc1 kind delete cluster --name dc2 - kind delete cluster --name dc3 - kind delete cluster --name dc4 - - -# Helper target for doing local cni acceptance testing -kind-cni: kind-delete kind create cluster --config=$(CURDIR)/acceptance/framework/environment/cni-kind/kind.config --name dc1 --image $(KIND_NODE_IMAGE) make kind-cni-calico kind create cluster --config=$(CURDIR)/acceptance/framework/environment/cni-kind/kind.config --name dc2 --image $(KIND_NODE_IMAGE) make kind-cni-calico - kind create cluster --config=$(CURDIR)/acceptance/framework/environment/cni-kind/kind.config --name dc3 --image $(KIND_NODE_IMAGE) - make kind-cni-calico - kind create cluster --config=$(CURDIR)/acceptance/framework/environment/cni-kind/kind.config --name dc4 --image $(KIND_NODE_IMAGE) - make kind-cni-calico # Helper target for doing local acceptance testing -kind: kind-delete +kind: + kind delete cluster --name dc1 + kind delete cluster --name dc2 kind create cluster --name dc1 --image $(KIND_NODE_IMAGE) kind create cluster --name dc2 --image $(KIND_NODE_IMAGE) - kind create cluster --name dc3 --image $(KIND_NODE_IMAGE) - kind create cluster --name dc4 --image $(KIND_NODE_IMAGE) + # Helper target for loading local dev images (run with `DEV_IMAGE=...` to load non-k8s images) kind-load: @@ -173,8 +146,6 @@ lint: cni-plugin-lint ## Run linter in the control-plane, cli, and acceptance di ctrl-manifests: get-controller-gen ## Generate CRD manifests. cd control-plane; $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases make copy-crds-to-chart - make generate-external-crds - make add-copyright-header get-controller-gen: ## Download controller-gen program needed for operator SDK. ifeq (, $(shell which controller-gen)) @@ -191,13 +162,6 @@ else CONTROLLER_GEN=$(shell which controller-gen) endif -add-copyright-header: ## Add copyright header to all files in the project -ifeq (, $(shell which copywrite)) - @echo "Installing copywrite" - @go install github.com/hashicorp/copywrite@latest -endif - @copywrite headers --spdx "MPL-2.0" - # ===========> CI Targets ci.aws-acceptance-test-cleanup: ## Deletes AWS resources left behind after failed acceptance tests. @@ -277,7 +241,7 @@ endif # ===========> Makefile config .DEFAULT_GOAL := help -.PHONY: gen-helm-docs copy-crds-to-chart generate-external-crds bats-tests help ci.aws-acceptance-test-cleanup version cli-dev prepare-dev prepare-release +.PHONY: gen-helm-docs copy-crds-to-chart bats-tests help ci.aws-acceptance-test-cleanup version cli-dev prepare-dev prepare-release SHELL = bash GOOS?=$(shell go env GOOS) GOARCH?=$(shell go env GOARCH) diff --git a/README.md b/README.md index d43a12b455..1d3a3733ab 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ by contacting us at [security@hashicorp.com](mailto:security@hashicorp.com). The following pre-requisites must be met before installing Consul on Kubernetes. - * **Kubernetes 1.24.x - 1.27.x** - This represents the earliest versions of Kubernetes tested. + * **Kubernetes 1.23.x - 1.26.x** - This represents the earliest versions of Kubernetes tested. It is possible that this chart works with earlier versions, but it is untested. * Helm install diff --git a/acceptance/ci-inputs/aks_acceptance_test_packages.yaml b/acceptance/ci-inputs/aks_acceptance_test_packages.yaml index c1f1093200..cef04a3205 100644 --- a/acceptance/ci-inputs/aks_acceptance_test_packages.yaml +++ b/acceptance/ci-inputs/aks_acceptance_test_packages.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - - {runner: 0, test-packages: "connect peering snapshot-agent wan-federation"} - {runner: 1, test-packages: "consul-dns example partitions metrics sync"} - {runner: 2, test-packages: "basic cli config-entries api-gateway ingress-gateway terminating-gateway vault"} \ No newline at end of file diff --git a/acceptance/ci-inputs/eks_acceptance_test_packages.yaml b/acceptance/ci-inputs/eks_acceptance_test_packages.yaml index c1f1093200..cef04a3205 100644 --- a/acceptance/ci-inputs/eks_acceptance_test_packages.yaml +++ b/acceptance/ci-inputs/eks_acceptance_test_packages.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - - {runner: 0, test-packages: "connect peering snapshot-agent wan-federation"} - {runner: 1, test-packages: "consul-dns example partitions metrics sync"} - {runner: 2, test-packages: "basic cli config-entries api-gateway ingress-gateway terminating-gateway vault"} \ No newline at end of file diff --git a/acceptance/ci-inputs/gke_acceptance_test_packages.yaml b/acceptance/ci-inputs/gke_acceptance_test_packages.yaml index c1f1093200..cef04a3205 100644 --- a/acceptance/ci-inputs/gke_acceptance_test_packages.yaml +++ b/acceptance/ci-inputs/gke_acceptance_test_packages.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - - {runner: 0, test-packages: "connect peering snapshot-agent wan-federation"} - {runner: 1, test-packages: "consul-dns example partitions metrics sync"} - {runner: 2, test-packages: "basic cli config-entries api-gateway ingress-gateway terminating-gateway vault"} \ No newline at end of file diff --git a/acceptance/ci-inputs/kind-inputs.yaml b/acceptance/ci-inputs/kind-inputs.yaml index ba21d2cdaf..70dc2f0974 100644 --- a/acceptance/ci-inputs/kind-inputs.yaml +++ b/acceptance/ci-inputs/kind-inputs.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - kindVersion: v0.19.0 -kindNodeImage: kindest/node:v1.27.1 +kindNodeImage: kindest/node:v1.25.9 kubectlVersion: v1.27.1 diff --git a/acceptance/ci-inputs/kind_acceptance_test_packages.yaml b/acceptance/ci-inputs/kind_acceptance_test_packages.yaml index e0e126bbda..74991abd76 100644 --- a/acceptance/ci-inputs/kind_acceptance_test_packages.yaml +++ b/acceptance/ci-inputs/kind_acceptance_test_packages.yaml @@ -1,10 +1,6 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - - {runner: 0, test-packages: "partitions"} - {runner: 1, test-packages: "peering"} -- {runner: 2, test-packages: "sameness"} -- {runner: 3, test-packages: "connect snapshot-agent wan-federation"} -- {runner: 4, test-packages: "cli vault metrics"} -- {runner: 5, test-packages: "api-gateway ingress-gateway sync example consul-dns"} -- {runner: 6, test-packages: "config-entries terminating-gateway basic"} +- {runner: 2, test-packages: "connect snapshot-agent wan-federation"} +- {runner: 3, test-packages: "cli vault metrics"} +- {runner: 4, test-packages: "api-gateway ingress-gateway sync example consul-dns"} +- {runner: 5, test-packages: "config-entries terminating-gateway basic"} \ No newline at end of file diff --git a/acceptance/framework/cli/cli.go b/acceptance/framework/cli/cli.go index 11a158269f..5297382d94 100644 --- a/acceptance/framework/cli/cli.go +++ b/acceptance/framework/cli/cli.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package cli import ( diff --git a/acceptance/framework/config/config.go b/acceptance/framework/config/config.go index a638732c37..cba72db125 100644 --- a/acceptance/framework/config/config.go +++ b/acceptance/framework/config/config.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package config import ( @@ -88,15 +85,8 @@ type TestConfig struct { ConsulVersion *version.Version ConsulDataplaneVersion *version.Version EnvoyImage string - ConsulCollectorImage string - - HCPResourceID string - - VaultHelmChartVersion string - VaultServerVersion string NoCleanupOnFailure bool - NoCleanup bool DebugDirectory string UseAKS bool diff --git a/acceptance/framework/config/config_test.go b/acceptance/framework/config/config_test.go index 4d432da3b0..124bfe65f5 100644 --- a/acceptance/framework/config/config_test.go +++ b/acceptance/framework/config/config_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package config import ( diff --git a/acceptance/framework/connhelper/connect_helper.go b/acceptance/framework/connhelper/connect_helper.go index 314c0d853a..dc0bf9d402 100644 --- a/acceptance/framework/connhelper/connect_helper.go +++ b/acceptance/framework/connhelper/connect_helper.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package connhelper import ( @@ -133,22 +130,22 @@ func (c *ConnectHelper) DeployClientAndServer(t *testing.T) { // TODO: A base fixture is the wrong place for these files k8s.KubectlApply(t, opts, "../fixtures/bases/openshift/") - helpers.Cleanup(t, c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, func() { + helpers.Cleanup(t, c.Cfg.NoCleanupOnFailure, func() { k8s.KubectlDelete(t, opts, "../fixtures/bases/openshift/") }) - k8s.DeployKustomize(t, opts, c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, c.Cfg.DebugDirectory, "../fixtures/cases/static-server-openshift") + k8s.DeployKustomize(t, opts, c.Cfg.NoCleanupOnFailure, c.Cfg.DebugDirectory, "../fixtures/cases/static-server-openshift") if c.Cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, opts, c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, c.Cfg.DebugDirectory, "../fixtures/cases/static-client-openshift-tproxy") + k8s.DeployKustomize(t, opts, c.Cfg.NoCleanupOnFailure, c.Cfg.DebugDirectory, "../fixtures/cases/static-client-openshift-tproxy") } else { - k8s.DeployKustomize(t, opts, c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, c.Cfg.DebugDirectory, "../fixtures/cases/static-client-openshift-inject") + k8s.DeployKustomize(t, opts, c.Cfg.NoCleanupOnFailure, c.Cfg.DebugDirectory, "../fixtures/cases/static-client-openshift-inject") } } else { - k8s.DeployKustomize(t, c.Ctx.KubectlOptions(t), c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, c.Cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, c.Ctx.KubectlOptions(t), c.Cfg.NoCleanupOnFailure, c.Cfg.DebugDirectory, "../fixtures/cases/static-server-inject") if c.Cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, c.Ctx.KubectlOptions(t), c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, c.Cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, c.Ctx.KubectlOptions(t), c.Cfg.NoCleanupOnFailure, c.Cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { - k8s.DeployKustomize(t, c.Ctx.KubectlOptions(t), c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, c.Cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + k8s.DeployKustomize(t, c.Ctx.KubectlOptions(t), c.Cfg.NoCleanupOnFailure, c.Cfg.DebugDirectory, "../fixtures/cases/static-client-inject") } } // Check that both static-server and static-client have been injected and @@ -185,7 +182,7 @@ func (c *ConnectHelper) SetupAppNamespace(t *testing.T) { return } require.NoError(t, err) - helpers.Cleanup(t, c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, func() { + helpers.Cleanup(t, c.Cfg.NoCleanupOnFailure, func() { k8s.RunKubectl(t, opts, "delete", "ns", opts.Namespace) }) @@ -199,20 +196,6 @@ func (c *ConnectHelper) SetupAppNamespace(t *testing.T) { } -// CreateResolverRedirect creates a resolver that redirects to a static-server, a corresponding k8s service, -// and intentions. This helper is primarly used to ensure that the virtual-ips are persisted to consul properly. -func (c *ConnectHelper) CreateResolverRedirect(t *testing.T) { - logger.Log(t, "creating resolver redirect") - opts := c.KubectlOptsForApp(t) - c.SetupAppNamespace(t) - kustomizeDir := "../fixtures/cases/resolver-redirect-virtualip" - k8s.KubectlApplyK(t, opts, kustomizeDir) - - helpers.Cleanup(t, c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, func() { - k8s.KubectlDeleteK(t, opts, kustomizeDir) - }) -} - // TestConnectionFailureWithoutIntention ensures the connection to the static // server fails when no intentions are configured. func (c *ConnectHelper) TestConnectionFailureWithoutIntention(t *testing.T) { diff --git a/acceptance/framework/consul/cli_cluster.go b/acceptance/framework/consul/cli_cluster.go index a45bcc665c..b90e3134bd 100644 --- a/acceptance/framework/consul/cli_cluster.go +++ b/acceptance/framework/consul/cli_cluster.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package consul import ( @@ -45,7 +42,6 @@ type CLICluster struct { kubeConfig string kubeContext string noCleanupOnFailure bool - noCleanup bool debugDirectory string logger terratestLogger.TestLogger cli cli.CLI @@ -110,7 +106,6 @@ func NewCLICluster( kubeConfig: cfg.GetPrimaryKubeEnv().KubeConfig, kubeContext: cfg.GetPrimaryKubeEnv().KubeContext, noCleanupOnFailure: cfg.NoCleanupOnFailure, - noCleanup: cfg.NoCleanup, debugDirectory: cfg.DebugDirectory, logger: logger, cli: *cli, @@ -124,7 +119,7 @@ func (c *CLICluster) Create(t *testing.T) { // Make sure we delete the cluster if we receive an interrupt signal and // register cleanup so that we delete the cluster when test finishes. - helpers.Cleanup(t, c.noCleanupOnFailure, c.noCleanup, func() { + helpers.Cleanup(t, c.noCleanupOnFailure, func() { c.Destroy(t) }) @@ -203,14 +198,9 @@ func (c *CLICluster) Destroy(t *testing.T) { require.NoError(t, err) } -func (c *CLICluster) SetupConsulClient(t *testing.T, secure bool, release ...string) (*api.Client, string) { +func (c *CLICluster) SetupConsulClient(t *testing.T, secure bool) (*api.Client, string) { t.Helper() - releaseName := c.releaseName - if len(release) > 0 { - releaseName = release[0] - } - namespace := c.kubectlOptions.Namespace config := api.DefaultConfig() localPort := terratestk8s.GetAvailablePort(t) @@ -230,13 +220,13 @@ func (c *CLICluster) SetupConsulClient(t *testing.T, secure bool, release ...str // In secondary servers, we don't create a bootstrap token since ACLs are only bootstrapped in the primary. // Instead, we provide a replication token that serves the role of the bootstrap token. - aclSecretName := fmt.Sprintf("%s-consul-bootstrap-acl-token", releaseName) + aclSecretName := fmt.Sprintf("%s-consul-bootstrap-acl-token", c.releaseName) if c.releaseName == CLIReleaseName { aclSecretName = "consul-bootstrap-acl-token" } aclSecret, err := c.kubernetesClient.CoreV1().Secrets(namespace).Get(context.Background(), aclSecretName, metav1.GetOptions{}) if err != nil && errors.IsNotFound(err) { - federationSecret := fmt.Sprintf("%s-consul-federation", releaseName) + federationSecret := fmt.Sprintf("%s-consul-federation", c.releaseName) if c.releaseName == CLIReleaseName { federationSecret = "consul-federation" } @@ -250,8 +240,8 @@ func (c *CLICluster) SetupConsulClient(t *testing.T, secure bool, release ...str } } - serverPod := fmt.Sprintf("%s-consul-server-0", releaseName) - if releaseName == CLIReleaseName { + serverPod := fmt.Sprintf("%s-consul-server-0", c.releaseName) + if c.releaseName == CLIReleaseName { serverPod = "consul-server-0" } tunnel := terratestk8s.NewTunnelWithLogger( diff --git a/acceptance/framework/consul/cluster.go b/acceptance/framework/consul/cluster.go index 1b9a543245..41dbec86f8 100644 --- a/acceptance/framework/consul/cluster.go +++ b/acceptance/framework/consul/cluster.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package consul import ( @@ -12,7 +9,7 @@ import ( // Cluster represents a consul cluster object. type Cluster interface { // SetupConsulClient returns a new Consul client. - SetupConsulClient(t *testing.T, secure bool, release ...string) (*api.Client, string) + SetupConsulClient(t *testing.T, secure bool) (*api.Client, string) // Create creates a new Consul Cluster. Create(t *testing.T) diff --git a/acceptance/framework/consul/helm_cluster.go b/acceptance/framework/consul/helm_cluster.go index 81787ebfc3..3f4d31173d 100644 --- a/acceptance/framework/consul/helm_cluster.go +++ b/acceptance/framework/consul/helm_cluster.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package consul import ( @@ -18,7 +15,6 @@ import ( "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/logger" "github.com/hashicorp/consul-k8s/acceptance/framework/portforward" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "github.com/hashicorp/consul/api" "github.com/hashicorp/consul/sdk/testutil/retry" "github.com/stretchr/testify/require" @@ -27,11 +23,7 @@ import ( rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/selection" "k8s.io/client-go/kubernetes" - "sigs.k8s.io/controller-runtime/pkg/client" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) // HelmCluster implements Cluster and uses Helm @@ -49,10 +41,8 @@ type HelmCluster struct { ctx environment.TestContext helmOptions *helm.Options releaseName string - runtimeClient client.Client kubernetesClient kubernetes.Interface noCleanupOnFailure bool - noCleanup bool debugDirectory string logger terratestLogger.TestLogger } @@ -105,10 +95,8 @@ func NewHelmCluster( ctx: ctx, helmOptions: opts, releaseName: releaseName, - runtimeClient: ctx.ControllerRuntimeClient(t), kubernetesClient: ctx.KubernetesClient(t), noCleanupOnFailure: cfg.NoCleanupOnFailure, - noCleanup: cfg.NoCleanup, debugDirectory: cfg.DebugDirectory, logger: logger, } @@ -119,7 +107,7 @@ func (h *HelmCluster) Create(t *testing.T) { // Make sure we delete the cluster if we receive an interrupt signal and // register cleanup so that we delete the cluster when test finishes. - helpers.Cleanup(t, h.noCleanupOnFailure, h.noCleanup, func() { + helpers.Cleanup(t, h.noCleanupOnFailure, func() { h.Destroy(t) }) @@ -155,47 +143,14 @@ func (h *HelmCluster) Destroy(t *testing.T) { "--wait": nil, } - // Clean up any stuck gateway resources, note that we swallow all errors from - // here down since the terratest helm installation may actually already be - // deleted at this point, in which case these operations will fail on non-existent - // CRD cleanups. - requirement, err := labels.NewRequirement("release", selection.Equals, []string{h.releaseName}) - require.NoError(t, err) - - // Forcibly delete all gateway classes and remove their finalizers. - _ = h.runtimeClient.DeleteAllOf(context.Background(), &gwv1beta1.GatewayClass{}, client.HasLabels{"release=" + h.releaseName}) - - var gatewayClassList gwv1beta1.GatewayClassList - if h.runtimeClient.List(context.Background(), &gatewayClassList, &client.ListOptions{ - LabelSelector: labels.NewSelector().Add(*requirement), - }) == nil { - for _, item := range gatewayClassList.Items { - item.SetFinalizers([]string{}) - _ = h.runtimeClient.Update(context.Background(), &item) - } - } - - // Forcibly delete all gateway class configs and remove their finalizers. - _ = h.runtimeClient.DeleteAllOf(context.Background(), &v1alpha1.GatewayClassConfig{}, client.HasLabels{"release=" + h.releaseName}) - - var gatewayClassConfigList v1alpha1.GatewayClassConfigList - if h.runtimeClient.List(context.Background(), &gatewayClassConfigList, &client.ListOptions{ - LabelSelector: labels.NewSelector().Add(*requirement), - }) == nil { - for _, item := range gatewayClassConfigList.Items { - item.SetFinalizers([]string{}) - _ = h.runtimeClient.Update(context.Background(), &item) - } - } - - retry.RunWith(&retry.Counter{Wait: 2 * time.Second, Count: 30}, t, func(r *retry.R) { + retry.RunWith(&retry.Counter{Wait: 1 * time.Second, Count: 15}, t, func(r *retry.R) { err := helm.DeleteE(t, h.helmOptions, h.releaseName, false) require.NoError(r, err) }) // Retry because sometimes certain resources (like PVC) take time to delete // in cloud providers. - retry.RunWith(&retry.Counter{Wait: 2 * time.Second, Count: 600}, t, func(r *retry.R) { + retry.RunWith(&retry.Counter{Wait: 1 * time.Second, Count: 600}, t, func(r *retry.R) { // Force delete any pods that have h.releaseName in their name because sometimes // graceful termination takes a long time and since this is an uninstall // we don't care that they're stopped gracefully. @@ -348,23 +303,14 @@ func (h *HelmCluster) Upgrade(t *testing.T, helmValues map[string]string) { k8s.WaitForAllPodsToBeReady(t, h.kubernetesClient, h.helmOptions.KubectlOptions.Namespace, fmt.Sprintf("release=%s", h.releaseName)) } -func (h *HelmCluster) CreatePortForwardTunnel(t *testing.T, remotePort int, release ...string) string { - releaseName := h.releaseName - if len(release) > 0 { - releaseName = release[0] - } - serverPod := fmt.Sprintf("%s-consul-server-0", releaseName) +func (h *HelmCluster) CreatePortForwardTunnel(t *testing.T, remotePort int) string { + serverPod := fmt.Sprintf("%s-consul-server-0", h.releaseName) return portforward.CreateTunnelToResourcePort(t, serverPod, remotePort, h.helmOptions.KubectlOptions, h.logger) } -func (h *HelmCluster) SetupConsulClient(t *testing.T, secure bool, release ...string) (client *api.Client, configAddress string) { +func (h *HelmCluster) SetupConsulClient(t *testing.T, secure bool) (client *api.Client, configAddress string) { t.Helper() - releaseName := h.releaseName - if len(release) > 0 { - releaseName = release[0] - } - namespace := h.helmOptions.KubectlOptions.Namespace config := api.DefaultConfig() remotePort := 8500 // use non-secure by default @@ -381,15 +327,15 @@ func (h *HelmCluster) SetupConsulClient(t *testing.T, secure bool, release ...st if h.ACLToken != "" { config.Token = h.ACLToken } else { - retry.RunWith(&retry.Counter{Wait: 2 * time.Second, Count: 600}, t, func(r *retry.R) { + retry.RunWith(&retry.Counter{Wait: 1 * time.Second, Count: 600}, t, func(r *retry.R) { // Get the ACL token. First, attempt to read it from the bootstrap token (this will be true in primary Consul servers). // If the bootstrap token doesn't exist, it means we are running against a secondary cluster // and will try to read the replication token from the federation secret. // In secondary servers, we don't create a bootstrap token since ACLs are only bootstrapped in the primary. // Instead, we provide a replication token that serves the role of the bootstrap token. - aclSecret, err := h.kubernetesClient.CoreV1().Secrets(namespace).Get(context.Background(), releaseName+"-consul-bootstrap-acl-token", metav1.GetOptions{}) + aclSecret, err := h.kubernetesClient.CoreV1().Secrets(namespace).Get(context.Background(), h.releaseName+"-consul-bootstrap-acl-token", metav1.GetOptions{}) if err != nil && errors.IsNotFound(err) { - federationSecret := fmt.Sprintf("%s-consul-federation", releaseName) + federationSecret := fmt.Sprintf("%s-consul-federation", h.releaseName) aclSecret, err = h.kubernetesClient.CoreV1().Secrets(namespace).Get(context.Background(), federationSecret, metav1.GetOptions{}) require.NoError(r, err) config.Token = string(aclSecret.Data["replicationToken"]) @@ -403,7 +349,7 @@ func (h *HelmCluster) SetupConsulClient(t *testing.T, secure bool, release ...st } } - config.Address = h.CreatePortForwardTunnel(t, remotePort, release...) + config.Address = h.CreatePortForwardTunnel(t, remotePort) consulClient, err := api.NewClient(config) require.NoError(t, err) @@ -510,7 +456,7 @@ func configurePodSecurityPolicies(t *testing.T, client kubernetes.Interface, cfg } } - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { _ = client.PolicyV1beta1().PodSecurityPolicies().Delete(context.Background(), pspName, metav1.DeleteOptions{}) _ = client.RbacV1().ClusterRoles().Delete(context.Background(), pspName, metav1.DeleteOptions{}) _ = client.RbacV1().RoleBindings(namespace).Delete(context.Background(), pspName, metav1.DeleteOptions{}) @@ -561,7 +507,7 @@ func configureSCCs(t *testing.T, client kubernetes.Interface, cfg *config.TestCo } } - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { _ = client.RbacV1().RoleBindings(namespace).Delete(context.Background(), anyuidRoleBinding, metav1.DeleteOptions{}) _ = client.RbacV1().RoleBindings(namespace).Delete(context.Background(), privilegedRoleBinding, metav1.DeleteOptions{}) }) @@ -585,7 +531,7 @@ func defaultValues() map[string]string { func CreateK8sSecret(t *testing.T, client kubernetes.Interface, cfg *config.TestConfig, namespace, secretName, secretKey, secret string) { - retry.RunWith(&retry.Counter{Wait: 2 * time.Second, Count: 15}, t, func(r *retry.R) { + retry.RunWith(&retry.Counter{Wait: 1 * time.Second, Count: 15}, t, func(r *retry.R) { _, err := client.CoreV1().Secrets(namespace).Get(context.Background(), secretName, metav1.GetOptions{}) if errors.IsNotFound(err) { _, err := client.CoreV1().Secrets(namespace).Create(context.Background(), &corev1.Secret{ @@ -603,7 +549,7 @@ func CreateK8sSecret(t *testing.T, client kubernetes.Interface, cfg *config.Test } }) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { _ = client.CoreV1().Secrets(namespace).Delete(context.Background(), secretName, metav1.DeleteOptions{}) }) } diff --git a/acceptance/framework/consul/helm_cluster_test.go b/acceptance/framework/consul/helm_cluster_test.go index 011ca23e0f..1f7b8b7d58 100644 --- a/acceptance/framework/consul/helm_cluster_test.go +++ b/acceptance/framework/consul/helm_cluster_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package consul import ( @@ -12,8 +9,6 @@ import ( "github.com/stretchr/testify/require" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/fake" - "sigs.k8s.io/controller-runtime/pkg/client" - runtimefake "sigs.k8s.io/controller-runtime/pkg/client/fake" ) // Test that if TestConfig has values that need to be provided @@ -87,8 +82,5 @@ func (c *ctx) KubectlOptionsForNamespace(ns string) *k8s.KubectlOptions { func (c *ctx) KubernetesClient(_ *testing.T) kubernetes.Interface { return fake.NewSimpleClientset() } -func (c *ctx) ControllerRuntimeClient(_ *testing.T) client.Client { - return runtimefake.NewClientBuilder().Build() -} var _ environment.TestContext = (*ctx)(nil) diff --git a/acceptance/framework/environment/cni-kind/custom-resources.yaml b/acceptance/framework/environment/cni-kind/custom-resources.yaml index 9a87195041..1d2e08da56 100644 --- a/acceptance/framework/environment/cni-kind/custom-resources.yaml +++ b/acceptance/framework/environment/cni-kind/custom-resources.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - # This section includes base Calico installation configuration. # For more information, see: https://projectcalico.docs.tigera.io/master/reference/installation/api#operator.tigera.io/v1.Installation apiVersion: operator.tigera.io/v1 diff --git a/acceptance/framework/environment/cni-kind/tigera-operator.yaml b/acceptance/framework/environment/cni-kind/tigera-operator.yaml index 8114b6f596..f13e65ceb0 100644 --- a/acceptance/framework/environment/cni-kind/tigera-operator.yaml +++ b/acceptance/framework/environment/cni-kind/tigera-operator.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: v1 kind: Namespace metadata: diff --git a/acceptance/framework/environment/environment.go b/acceptance/framework/environment/environment.go index 7014a3c05f..96245cfde3 100644 --- a/acceptance/framework/environment/environment.go +++ b/acceptance/framework/environment/environment.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package environment import ( @@ -9,15 +6,9 @@ import ( "github.com/gruntwork-io/terratest/modules/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/config" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/controller-runtime/pkg/client" - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) const ( @@ -38,7 +29,6 @@ type TestContext interface { // TODO: I don't love this. KubectlOptionsForNamespace(ns string) *k8s.KubectlOptions KubernetesClient(t *testing.T) kubernetes.Interface - ControllerRuntimeClient(t *testing.T) client.Client } type KubernetesEnvironment struct { @@ -83,9 +73,7 @@ type kubernetesContext struct { kubeContextName string namespace string - client kubernetes.Interface - runtimeClient client.Client - + client kubernetes.Interface options *k8s.KubectlOptions } @@ -172,31 +160,6 @@ func (k kubernetesContext) KubernetesClient(t *testing.T) kubernetes.Interface { return k.client } -func (k kubernetesContext) ControllerRuntimeClient(t *testing.T) client.Client { - if k.runtimeClient != nil { - return k.runtimeClient - } - - options := k.KubectlOptions(t) - configPath, err := options.GetConfigPath(t) - require.NoError(t, err) - config, err := k8s.LoadApiClientConfigE(configPath, options.ContextName) - require.NoError(t, err) - - s := runtime.NewScheme() - require.NoError(t, clientgoscheme.AddToScheme(s)) - require.NoError(t, gwv1alpha2.Install(s)) - require.NoError(t, gwv1beta1.Install(s)) - require.NoError(t, v1alpha1.AddToScheme(s)) - - client, err := client.New(config, client.Options{Scheme: s}) - require.NoError(t, err) - - k.runtimeClient = client - - return k.runtimeClient -} - func NewContext(namespace, pathToKubeConfig, kubeContextName string) *kubernetesContext { return &kubernetesContext{ namespace: namespace, diff --git a/acceptance/framework/flags/flags.go b/acceptance/framework/flags/flags.go index ce8e1ea726..3e9b733047 100644 --- a/acceptance/framework/flags/flags.go +++ b/acceptance/framework/flags/flags.go @@ -39,14 +39,8 @@ type TestFlags struct { flagConsulVersion string flagConsulDataplaneVersion string flagEnvoyImage string - flagConsulCollectorImage string - flagVaultHelmChartVersion string - flagVaultServerVersion string - - flagHCPResourceID string flagNoCleanupOnFailure bool - flagNoCleanup bool flagDebugDirectory string @@ -87,16 +81,12 @@ func (t *TestFlags) init() { flag.StringVar(&t.flagConsulDataplaneVersion, "consul-dataplane-version", "", "The consul-dataplane version used for all tests.") flag.StringVar(&t.flagHelmChartVersion, "helm-chart-version", config.HelmChartPath, "The helm chart used for all tests.") flag.StringVar(&t.flagEnvoyImage, "envoy-image", "", "The Envoy image to use for all tests.") - flag.StringVar(&t.flagConsulCollectorImage, "consul-collector-image", "", "The consul collector image to use for all tests.") - flag.StringVar(&t.flagVaultServerVersion, "vault-server-version", "", "The vault serverversion used for all tests.") - flag.StringVar(&t.flagVaultHelmChartVersion, "vault-helm-chart-version", "", "The Vault helm chart used for all tests.") flag.Var(&t.flagKubeconfigs, "kubeconfigs", "The list of paths to a kubeconfig files. If this is blank, "+ "the default kubeconfig path (~/.kube/config) will be used.") flag.Var(&t.flagKubecontexts, "kube-contexts", "The list of names of the Kubernetes contexts to use. If this is blank, "+ "the context set as the current context will be used by default.") flag.Var(&t.flagKubeNamespaces, "kube-namespaces", "The list of Kubernetes namespaces to use for tests.") - flag.StringVar(&t.flagHCPResourceID, "hcp-resource-id", "", "The hcp resource id to use for all tests.") flag.BoolVar(&t.flagEnableMultiCluster, "enable-multi-cluster", false, "If true, the tests that require multiple Kubernetes clusters will be run. "+ @@ -133,9 +123,6 @@ func (t *TestFlags) init() { "If true, the tests will not cleanup Kubernetes resources they create when they finish running."+ "Note this flag must be run with -failfast flag, otherwise subsequent tests will fail.") - flag.BoolVar(&t.flagNoCleanup, "no-cleanup", false, - "If true, the tests will not cleanup Kubernetes resources for Vault test") - flag.StringVar(&t.flagDebugDirectory, "debug-directory", "", "The directory where to write debug information about failed test runs, "+ "such as logs and pod definitions. If not provided, a temporary directory will be created by the tests.") @@ -198,7 +185,6 @@ func (t *TestFlags) TestConfigFromFlags() *config.TestConfig { kubeEnvs := config.NewKubeTestConfigList(t.flagKubeconfigs, t.flagKubecontexts, t.flagKubeNamespaces) c := &config.TestConfig{ - EnableEnterprise: t.flagEnableEnterprise, EnterpriseLicense: t.flagEnterpriseLicense, @@ -223,14 +209,8 @@ func (t *TestFlags) TestConfigFromFlags() *config.TestConfig { ConsulVersion: consulVersion, ConsulDataplaneVersion: consulDataplaneVersion, EnvoyImage: t.flagEnvoyImage, - ConsulCollectorImage: t.flagConsulCollectorImage, - VaultHelmChartVersion: t.flagVaultHelmChartVersion, - VaultServerVersion: t.flagVaultServerVersion, - - HCPResourceID: t.flagHCPResourceID, NoCleanupOnFailure: t.flagNoCleanupOnFailure, - NoCleanup: t.flagNoCleanup, DebugDirectory: tempDir, UseAKS: t.flagUseAKS, UseEKS: t.flagUseEKS, diff --git a/acceptance/framework/flags/flags_test.go b/acceptance/framework/flags/flags_test.go index 1e2bf0a039..05e9ab19c0 100644 --- a/acceptance/framework/flags/flags_test.go +++ b/acceptance/framework/flags/flags_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flags import ( diff --git a/acceptance/framework/helpers/helpers.go b/acceptance/framework/helpers/helpers.go index f8b1d2f15d..5f99a682ae 100644 --- a/acceptance/framework/helpers/helpers.go +++ b/acceptance/framework/helpers/helpers.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package helpers import ( @@ -38,7 +35,7 @@ func CheckForPriorInstallations(t *testing.T, client kubernetes.Interface, optio // Check if there's an existing cluster and fail if there is one. // We may need to retry since this is the first command run once the Kube // cluster is created and sometimes the API server returns errors. - retry.RunWith(&retry.Counter{Wait: 2 * time.Second, Count: 15}, t, func(r *retry.R) { + retry.RunWith(&retry.Counter{Wait: 1 * time.Second, Count: 15}, t, func(r *retry.R) { var err error // NOTE: It's okay to pass in `t` to RunHelmCommandAndGetOutputE despite being in a retry // because we're using RunHelmCommandAndGetOutputE (not RunHelmCommandAndGetOutput) so the `t` won't @@ -58,7 +55,7 @@ func CheckForPriorInstallations(t *testing.T, client kubernetes.Interface, optio // Wait for all pods in the "default" namespace to exit. A previous // release may not be listed by Helm but its pods may still be terminating. - retry.RunWith(&retry.Counter{Wait: 2 * time.Second, Count: 60}, t, func(r *retry.R) { + retry.RunWith(&retry.Counter{Wait: 1 * time.Second, Count: 60}, t, func(r *retry.R) { pods, err := client.CoreV1().Pods(options.KubectlOptions.Namespace).List(context.Background(), metav1.ListOptions{LabelSelector: labelSelector}) require.NoError(r, err) if len(pods.Items) > 0 { @@ -87,7 +84,7 @@ func SetupInterruptHandler(cleanup func()) { // Cleanup will both register a cleanup function with t // and SetupInterruptHandler to make sure resources get cleaned up // if an interrupt signal is caught. -func Cleanup(t *testing.T, noCleanupOnFailure bool, noCleanup bool, cleanup func()) { +func Cleanup(t *testing.T, noCleanupOnFailure bool, cleanup func()) { t.Helper() // Always clean up when an interrupt signal is caught. @@ -97,7 +94,7 @@ func Cleanup(t *testing.T, noCleanupOnFailure bool, noCleanup bool, cleanup func // We need to wrap the cleanup function because t that is passed in to this function // might not have the information on whether the test has failed yet. wrappedCleanupFunc := func() { - if !((noCleanupOnFailure && t.Failed()) || noCleanup) { + if !(noCleanupOnFailure && t.Failed()) { logger.Logf(t, "cleaning up resources for %s", t.Name()) cleanup() } else { diff --git a/acceptance/framework/helpers/helpers_test.go b/acceptance/framework/helpers/helpers_test.go index 2a994d7f9f..c1b4a916e2 100644 --- a/acceptance/framework/helpers/helpers_test.go +++ b/acceptance/framework/helpers/helpers_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package helpers import ( diff --git a/acceptance/framework/k8s/debug.go b/acceptance/framework/k8s/debug.go index 773769fced..5bf588f959 100644 --- a/acceptance/framework/k8s/debug.go +++ b/acceptance/framework/k8s/debug.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package k8s import ( diff --git a/acceptance/framework/k8s/deploy.go b/acceptance/framework/k8s/deploy.go index 828586c8cf..869ebdd804 100644 --- a/acceptance/framework/k8s/deploy.go +++ b/acceptance/framework/k8s/deploy.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package k8s import ( @@ -21,7 +18,7 @@ import ( // Deploy creates a Kubernetes deployment by applying configuration stored at filepath, // sets up a cleanup function and waits for the deployment to become available. -func Deploy(t *testing.T, options *k8s.KubectlOptions, noCleanupOnFailure bool, noCleanup bool, debugDirectory string, filepath string) { +func Deploy(t *testing.T, options *k8s.KubectlOptions, noCleanupOnFailure bool, debugDirectory string, filepath string) { t.Helper() KubectlApply(t, options, filepath) @@ -33,7 +30,7 @@ func Deploy(t *testing.T, options *k8s.KubectlOptions, noCleanupOnFailure bool, err = yaml.NewYAMLOrJSONDecoder(file, 1024).Decode(&deployment) require.NoError(t, err) - helpers.Cleanup(t, noCleanupOnFailure, noCleanup, func() { + helpers.Cleanup(t, noCleanupOnFailure, func() { // Note: this delete command won't wait for pods to be fully terminated. // This shouldn't cause any test pollution because the underlying // objects are deployments, and so when other tests create these @@ -47,7 +44,7 @@ func Deploy(t *testing.T, options *k8s.KubectlOptions, noCleanupOnFailure bool, // DeployKustomize creates a Kubernetes deployment by applying the kustomize directory stored at kustomizeDir, // sets up a cleanup function and waits for the deployment to become available. -func DeployKustomize(t *testing.T, options *k8s.KubectlOptions, noCleanupOnFailure bool, noCleanup bool, debugDirectory string, kustomizeDir string) { +func DeployKustomize(t *testing.T, options *k8s.KubectlOptions, noCleanupOnFailure bool, debugDirectory string, kustomizeDir string) { t.Helper() KubectlApplyK(t, options, kustomizeDir) @@ -59,7 +56,7 @@ func DeployKustomize(t *testing.T, options *k8s.KubectlOptions, noCleanupOnFailu err = yaml.NewYAMLOrJSONDecoder(strings.NewReader(output), 1024).Decode(&deployment) require.NoError(t, err) - helpers.Cleanup(t, noCleanupOnFailure, noCleanup, func() { + helpers.Cleanup(t, noCleanupOnFailure, func() { // Note: this delete command won't wait for pods to be fully terminated. // This shouldn't cause any test pollution because the underlying // objects are deployments, and so when other tests create these @@ -153,15 +150,6 @@ func CheckStaticServerConnectionFailing(t *testing.T, options *k8s.KubectlOption }, "", curlArgs...) } -// CheckStaticServerHTTPConnectionFailing is just like CheckStaticServerConnectionFailing -// except with HTTP-based intentions. -func CheckStaticServerHTTPConnectionFailing(t *testing.T, options *k8s.KubectlOptions, sourceApp string, curlArgs ...string) { - t.Helper() - CheckStaticServerConnection(t, options, sourceApp, false, []string{ - "curl: (22) The requested URL returned error: 403", - }, "", curlArgs...) -} - // labelMapToString takes a label map[string]string // and returns the string-ified version of, e.g app=foo,env=dev. func labelMapToString(labelMap map[string]string) string { diff --git a/acceptance/framework/k8s/helpers.go b/acceptance/framework/k8s/helpers.go index a6b4d1ca7c..7c3205928f 100644 --- a/acceptance/framework/k8s/helpers.go +++ b/acceptance/framework/k8s/helpers.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package k8s import ( @@ -43,10 +40,10 @@ func WaitForAllPodsToBeReady(t *testing.T, client kubernetes.Interface, namespac logger.Logf(t, "Waiting for pods with label %q to be ready.", podLabelSelector) - // Wait up to 20m. + // Wait up to 11m. // On Azure, volume provisioning can sometimes take close to 5 min, // so we need to give a bit more time for pods to become healthy. - counter := &retry.Counter{Count: 20 * 60, Wait: 2 * time.Second} + counter := &retry.Counter{Count: 11 * 60, Wait: 1 * time.Second} retry.RunWith(counter, t, func(r *retry.R) { pods, err := client.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{LabelSelector: podLabelSelector}) require.NoError(r, err) @@ -113,7 +110,7 @@ func ServiceHost(t *testing.T, cfg *config.TestConfig, ctx environment.TestConte var host string // It can take some time for the load balancers to be ready and have an IP/Hostname. // Wait for 5 minutes before failing. - retry.RunWith(&retry.Counter{Wait: 2 * time.Second, Count: 600}, t, func(r *retry.R) { + retry.RunWith(&retry.Counter{Wait: 1 * time.Second, Count: 600}, t, func(r *retry.R) { svc, err := ctx.KubernetesClient(t).CoreV1().Services(ctx.KubectlOptions(t).Namespace).Get(context.Background(), serviceName, metav1.GetOptions{}) require.NoError(t, err) require.NotEmpty(r, svc.Status.LoadBalancer.Ingress) diff --git a/acceptance/framework/k8s/kubectl.go b/acceptance/framework/k8s/kubectl.go index 2e37b9bd0a..e766395f47 100644 --- a/acceptance/framework/k8s/kubectl.go +++ b/acceptance/framework/k8s/kubectl.go @@ -1,10 +1,6 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package k8s import ( - "fmt" "strings" "testing" "time" @@ -17,10 +13,6 @@ import ( "github.com/stretchr/testify/require" ) -const ( - kubectlTimeout = "--timeout=120s" -) - // kubeAPIConnectErrs are errors that sometimes occur when talking to the // Kubernetes API related to connection issues. var kubeAPIConnectErrs = []string{ @@ -61,7 +53,7 @@ func RunKubectlAndGetOutputWithLoggerE(t *testing.T, options *k8s.KubectlOptions } counter := &retry.Counter{ - Count: 10, + Count: 3, Wait: 1 * time.Second, } var output string @@ -102,7 +94,7 @@ func KubectlApplyK(t *testing.T, options *k8s.KubectlOptions, kustomizeDir strin // deletes it from the cluster by running 'kubectl delete -f'. // If there's an error deleting the file, fail the test. func KubectlDelete(t *testing.T, options *k8s.KubectlOptions, configPath string) { - _, err := RunKubectlAndGetOutputE(t, options, "delete", kubectlTimeout, "-f", configPath) + _, err := RunKubectlAndGetOutputE(t, options, "delete", "--timeout=60s", "-f", configPath) require.NoError(t, err) } @@ -112,21 +104,7 @@ func KubectlDelete(t *testing.T, options *k8s.KubectlOptions, configPath string) func KubectlDeleteK(t *testing.T, options *k8s.KubectlOptions, kustomizeDir string) { // Ignore not found errors because Kubernetes automatically cleans up the kube secrets that we deployed // referencing the ServiceAccount when it is deleted. - _, err := RunKubectlAndGetOutputE(t, options, "delete", kubectlTimeout, "--ignore-not-found", "-k", kustomizeDir) - require.NoError(t, err) -} - -// KubectlScale takes a deployment and scales it to the provided number of replicas. -func KubectlScale(t *testing.T, options *k8s.KubectlOptions, deployment string, replicas int) { - _, err := RunKubectlAndGetOutputE(t, options, "scale", kubectlTimeout, fmt.Sprintf("--replicas=%d", replicas), deployment) - require.NoError(t, err) -} - -// KubectlLabel takes an object and applies the given label to it. -// Example: `KubectlLabel(t, options, "node", nodeId, corev1.LabelTopologyRegion, "us-east-1")`. -func KubectlLabel(t *testing.T, options *k8s.KubectlOptions, objectType string, objectId string, key string, value string) { - // `kubectl label` doesn't support timeouts - _, err := RunKubectlAndGetOutputE(t, options, "label", objectType, objectId, "--overwrite", fmt.Sprintf("%s=%s", key, value)) + _, err := RunKubectlAndGetOutputE(t, options, "delete", "--timeout=60s", "--ignore-not-found", "-k", kustomizeDir) require.NoError(t, err) } diff --git a/acceptance/framework/logger/logger.go b/acceptance/framework/logger/logger.go index 26777c4e22..c186e77436 100644 --- a/acceptance/framework/logger/logger.go +++ b/acceptance/framework/logger/logger.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package logger import ( diff --git a/acceptance/framework/portforward/port_forward.go b/acceptance/framework/portforward/port_forward.go index 3242541cc5..30f4aed157 100644 --- a/acceptance/framework/portforward/port_forward.go +++ b/acceptance/framework/portforward/port_forward.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package portforward import ( @@ -27,7 +24,7 @@ func CreateTunnelToResourcePort(t *testing.T, resourceName string, remotePort in logger) // Retry creating the port forward since it can fail occasionally. - retry.RunWith(&retry.Counter{Wait: 5 * time.Second, Count: 60}, t, func(r *retry.R) { + retry.RunWith(&retry.Counter{Wait: 3 * time.Second, Count: 60}, t, func(r *retry.R) { // NOTE: It's okay to pass in `t` to ForwardPortE despite being in a retry // because we're using ForwardPortE (not ForwardPort) so the `t` won't // get used to fail the test, just for logging. diff --git a/acceptance/framework/suite/suite.go b/acceptance/framework/suite/suite.go index 5d93d2c23f..67dff4f587 100644 --- a/acceptance/framework/suite/suite.go +++ b/acceptance/framework/suite/suite.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package suite import ( diff --git a/acceptance/framework/vault/helpers.go b/acceptance/framework/vault/helpers.go index d086e10391..4726e246ae 100644 --- a/acceptance/framework/vault/helpers.go +++ b/acceptance/framework/vault/helpers.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package vault import ( @@ -170,20 +167,6 @@ func (config *KV2Secret) SaveSecretAndAddReadPolicy(t *testing.T, vaultClient *v path "%s" { capabilities = ["read"] }`, config.Path) - config.saveSecretAndAddPolicy(t, vaultClient, policy) -} - -// SaveSecretAndAddUpdatePolicy will create an update policy for the PolicyName -// on the KV2Secret and then will save the secret in the KV2 store. -func (config *KV2Secret) SaveSecretAndAddUpdatePolicy(t *testing.T, vaultClient *vapi.Client) { - policy := fmt.Sprintf(` - path "%s" { - capabilities = ["read", "update"] - }`, config.Path) - config.saveSecretAndAddPolicy(t, vaultClient, policy) -} - -func (config *KV2Secret) saveSecretAndAddPolicy(t *testing.T, vaultClient *vapi.Client, policy string) { // Create the Vault Policy for the secret. logger.Log(t, "Creating policy") err := vaultClient.Sys().PutPolicy(config.PolicyName, policy) diff --git a/acceptance/framework/vault/vault_cluster.go b/acceptance/framework/vault/vault_cluster.go index 8b82e41841..dd6cbe4a68 100644 --- a/acceptance/framework/vault/vault_cluster.go +++ b/acceptance/framework/vault/vault_cluster.go @@ -1,13 +1,8 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package vault import ( "context" "fmt" - "os" - "strings" "testing" "time" @@ -15,7 +10,6 @@ import ( terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" terratestLogger "github.com/gruntwork-io/terratest/modules/logger" "github.com/hashicorp/consul-k8s/acceptance/framework/config" - "github.com/hashicorp/consul-k8s/acceptance/framework/consul" "github.com/hashicorp/consul-k8s/acceptance/framework/environment" "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" @@ -47,7 +41,6 @@ type VaultCluster struct { kubernetesClient kubernetes.Interface noCleanupOnFailure bool - noCleanup bool debugDirectory string logger terratestLogger.TestLogger } @@ -58,45 +51,18 @@ func NewVaultCluster(t *testing.T, ctx environment.TestContext, cfg *config.Test logger := terratestLogger.New(logger.TestLogger{}) kopts := ctx.KubectlOptions(t) - ns := ctx.KubectlOptions(t).Namespace - - entstr := "-ent" values := defaultHelmValues(releaseName) if cfg.EnablePodSecurityPolicies { values["global.psp.enable"] = "true" } - vaultReleaseName := helpers.RandomName() - k8sClient := environment.KubernetesClientFromOptions(t, ctx.KubectlOptions(t)) - vaultLicenseSecretName := fmt.Sprintf("%s-enterprise-license", vaultReleaseName) - vaultLicenseSecretKey := "license" - - vaultEnterpriseLicense := os.Getenv("VAULT_LICENSE") - - if cfg.VaultServerVersion != "" { - - if strings.Contains(cfg.VaultServerVersion, entstr) { - - logger.Logf(t, "Creating secret for Vault license") - consul.CreateK8sSecret(t, k8sClient, cfg, ns, vaultLicenseSecretName, vaultLicenseSecretKey, vaultEnterpriseLicense) - - values["server.image.repository"] = "docker.mirror.hashicorp.services/hashicorp/vault-enterprise" - values["server.enterpriseLicense.secretName"] = vaultLicenseSecretName - values["server.enterpriseLicense.secretKey"] = vaultLicenseSecretKey - } - values["server.image.tag"] = cfg.VaultServerVersion - } - vaultHelmChartVersion := defaultVaultHelmChartVersion - if cfg.VaultHelmChartVersion != "" { - vaultHelmChartVersion = cfg.VaultHelmChartVersion - } helpers.MergeMaps(values, helmValues) vaultHelmOpts := &helm.Options{ SetValues: values, KubectlOptions: kopts, Logger: logger, - Version: vaultHelmChartVersion, + Version: defaultVaultHelmChartVersion, } helm.AddRepo(t, vaultHelmOpts, "hashicorp", "https://helm.releases.hashicorp.com") @@ -113,7 +79,6 @@ func NewVaultCluster(t *testing.T, ctx environment.TestContext, cfg *config.Test kubectlOptions: kopts, kubernetesClient: ctx.KubernetesClient(t), noCleanupOnFailure: cfg.NoCleanupOnFailure, - noCleanup: cfg.NoCleanup, debugDirectory: cfg.DebugDirectory, logger: logger, releaseName: releaseName, @@ -145,7 +110,7 @@ func (v *VaultCluster) SetupVaultClient(t *testing.T) *vapi.Client { v.logger) // Retry creating the port forward since it can fail occasionally. - retry.RunWith(&retry.Counter{Wait: 5 * time.Second, Count: 60}, t, func(r *retry.R) { + retry.RunWith(&retry.Counter{Wait: 1 * time.Second, Count: 60}, t, func(r *retry.R) { // NOTE: It's okay to pass in `t` to ForwardPortE despite being in a retry // because we're using ForwardPortE (not ForwardPort) so the `t` won't // get used to fail the test, just for logging. @@ -249,7 +214,7 @@ func (v *VaultCluster) Create(t *testing.T, ctx environment.TestContext, vaultNa // Make sure we delete the cluster if we receive an interrupt signal and // register cleanup so that we delete the cluster when test finishes. - helpers.Cleanup(t, v.noCleanupOnFailure, v.noCleanup, func() { + helpers.Cleanup(t, v.noCleanupOnFailure, func() { v.Destroy(t) }) @@ -371,7 +336,7 @@ func (v *VaultCluster) createTLSCerts(t *testing.T) { require.NoError(t, err) t.Cleanup(func() { - if !(v.noCleanupOnFailure || v.noCleanup) { + if !v.noCleanupOnFailure { // We're ignoring error here because secret deletion is best-effort. _ = v.kubernetesClient.CoreV1().Secrets(namespace).Delete(context.Background(), certSecretName(v.releaseName), metav1.DeleteOptions{}) _ = v.kubernetesClient.CoreV1().Secrets(namespace).Delete(context.Background(), CASecretName(v.releaseName), metav1.DeleteOptions{}) @@ -444,7 +409,7 @@ func (v *VaultCluster) initAndUnseal(t *testing.T) { rootTokenSecret := fmt.Sprintf("%s-vault-root-token", v.releaseName) v.logger.Logf(t, "saving Vault root token to %q Kubernetes secret", rootTokenSecret) - helpers.Cleanup(t, v.noCleanupOnFailure, v.noCleanup, func() { + helpers.Cleanup(t, v.noCleanupOnFailure, func() { _ = v.kubernetesClient.CoreV1().Secrets(namespace).Delete(context.Background(), rootTokenSecret, metav1.DeleteOptions{}) }) _, err := v.kubernetesClient.CoreV1().Secrets(namespace).Create(context.Background(), &corev1.Secret{ diff --git a/acceptance/go.mod b/acceptance/go.mod index 1dd344a5c8..501d7e0a7b 100644 --- a/acceptance/go.mod +++ b/acceptance/go.mod @@ -1,145 +1,104 @@ module github.com/hashicorp/consul-k8s/acceptance -go 1.20 +go 1.19 require ( github.com/gruntwork-io/terratest v0.31.2 - github.com/hashicorp/consul-k8s/control-plane v0.0.0-20230609143603-198c4433d892 - github.com/hashicorp/consul/api v1.22.0-rc1 - github.com/hashicorp/consul/sdk v0.14.0-rc1 + github.com/hashicorp/consul-k8s/control-plane v0.0.0-20221117191905-0b1cc2b631e3 + github.com/hashicorp/consul/api v1.17.0 + github.com/hashicorp/consul/sdk v0.13.0 github.com/hashicorp/go-uuid v1.0.3 github.com/hashicorp/go-version v1.6.0 - github.com/hashicorp/hcp-sdk-go v0.50.0 - github.com/hashicorp/serf v0.10.1 - github.com/hashicorp/vault/api v1.8.3 - github.com/stretchr/testify v1.8.3 + github.com/hashicorp/vault/api v1.2.0 + github.com/stretchr/testify v1.7.2 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.26.3 - k8s.io/apimachinery v0.26.3 - k8s.io/client-go v0.26.3 - k8s.io/utils v0.0.0-20230209194617-a36077c30491 - sigs.k8s.io/controller-runtime v0.14.6 - sigs.k8s.io/gateway-api v0.7.1 + k8s.io/api v0.22.2 + k8s.io/apimachinery v0.22.2 + k8s.io/client-go v0.22.2 ) require ( + cloud.google.com/go v0.81.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/armon/go-radix v1.0.0 // indirect - github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/aws/aws-sdk-go v1.44.262 // indirect - github.com/beorn7/perks v1.0.1 // indirect + github.com/aws/aws-sdk-go v1.30.27 // indirect github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect github.com/cenkalti/backoff/v3 v3.0.0 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/deckarep/golang-set v1.7.1 // indirect - github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect - github.com/evanphx/json-patch/v5 v5.6.0 // indirect - github.com/fatih/color v1.14.1 // indirect + github.com/fatih/color v1.13.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect - github.com/go-errors/errors v1.4.2 // indirect - github.com/go-logr/logr v1.2.3 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/analysis v0.21.4 // indirect - github.com/go-openapi/errors v0.20.3 // indirect - github.com/go-openapi/jsonpointer v0.19.6 // indirect - github.com/go-openapi/jsonreference v0.20.1 // indirect - github.com/go-openapi/loads v0.21.2 // indirect - github.com/go-openapi/runtime v0.25.0 // indirect - github.com/go-openapi/spec v0.20.8 // indirect - github.com/go-openapi/strfmt v0.21.3 // indirect - github.com/go-openapi/swag v0.22.3 // indirect - github.com/go-openapi/validate v0.22.1 // indirect - github.com/go-ozzo/ozzo-validation v3.6.0+incompatible // indirect + github.com/go-errors/errors v1.0.2-0.20180813162953-d98b870cc4e0 // indirect + github.com/go-logr/logr v0.4.0 // indirect github.com/go-sql-driver/mysql v1.5.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/golang/snappy v0.0.4 // indirect - github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/snappy v0.0.1 // indirect + github.com/google/go-cmp v0.5.7 // indirect github.com/google/gofuzz v1.1.0 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.1.2 // indirect + github.com/googleapis/gnostic v0.5.5 // indirect github.com/gruntwork-io/gruntwork-cli v0.7.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-bexpr v0.1.11 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-hclog v1.5.0 // indirect - github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-hclog v1.2.2 // indirect + github.com/hashicorp/go-immutable-radix v1.3.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-plugin v1.4.5 // indirect + github.com/hashicorp/go-plugin v1.0.1 // indirect github.com/hashicorp/go-retryablehttp v0.6.6 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 // indirect - github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect - github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect + github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1 // indirect + github.com/hashicorp/go-secure-stdlib/strutil v0.1.1 // indirect github.com/hashicorp/go-sockaddr v1.0.2 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/hashicorp/vault/sdk v0.7.0 // indirect + github.com/hashicorp/serf v0.10.1 // indirect + github.com/hashicorp/vault/sdk v0.2.1 // indirect github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect github.com/imdario/mergo v0.3.12 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/josharian/intern v1.0.0 // indirect + github.com/jmespath/go-jmespath v0.3.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect - github.com/miekg/dns v1.1.50 // indirect - github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.0 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/mitchellh/pointerstructure v1.2.1 // indirect + github.com/mitchellh/mapstructure v1.4.2 // indirect github.com/mitchellh/reflectwalk v1.0.0 // indirect github.com/moby/spdystream v0.2.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oklog/run v1.0.0 // indirect - github.com/oklog/ulid v1.3.1 // indirect - github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/onsi/ginkgo v1.16.4 // indirect + github.com/onsi/gomega v1.15.0 // indirect github.com/pierrec/lz4 v2.5.2+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pquerna/otp v1.2.0 // indirect - github.com/prometheus/client_golang v1.14.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/russross/blackfriday/v2 v2.0.1 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect - github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect + github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/urfave/cli v1.22.2 // indirect - go.mongodb.org/mongo-driver v1.11.0 // indirect - go.opentelemetry.io/otel v1.11.1 // indirect - go.opentelemetry.io/otel/trace v1.11.1 // indirect - go.uber.org/atomic v1.9.0 // indirect + go.uber.org/atomic v1.7.0 // indirect golang.org/x/crypto v0.11.0 // indirect - golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect - golang.org/x/mod v0.9.0 // indirect golang.org/x/net v0.13.0 // indirect - golang.org/x/oauth2 v0.6.0 // indirect + golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect golang.org/x/sys v0.10.0 // indirect golang.org/x/term v0.10.0 // indirect golang.org/x/text v0.11.0 // indirect - golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.7.0 // indirect - gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect + golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect - google.golang.org/grpc v1.49.0 // indirect + google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect + google.golang.org/grpc v1.48.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/square/go-jose.v2 v2.5.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/klog/v2 v2.100.1 // indirect - k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect + k8s.io/klog/v2 v2.9.0 // indirect + k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect + k8s.io/utils v0.0.0-20220812165043-ad590609e2e5 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect + sigs.k8s.io/yaml v1.2.0 // indirect ) diff --git a/acceptance/go.sum b/acceptance/go.sum index 7361efbbb6..ed575b5e09 100644 --- a/acceptance/go.sum +++ b/acceptance/go.sum @@ -1,3 +1,4 @@ +bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -14,6 +15,12 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -42,12 +49,14 @@ github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8 github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= github.com/Azure/go-autorest/autorest v0.11.0/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= github.com/Azure/go-autorest/autorest v0.11.5/go.mod h1:foo3aIXRQ90zFve3r0QiDsrjGDUwWhKl0ZOQy1CT14k= +github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.2/go.mod h1:/3SMAM86bP6wC9Ev35peQDUeqFZBMH07vvUOmg4z/fE= +github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= github.com/Azure/go-autorest/autorest/azure/auth v0.5.1/go.mod h1:ea90/jvmnAwDrSooLH4sRIehEPtG/EPUXavDh31MnA4= github.com/Azure/go-autorest/autorest/azure/cli v0.4.0/go.mod h1:JljT387FplPzBA31vUcvsetLKF3pec5bdAxjVU4kI2s= github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= @@ -64,6 +73,7 @@ github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQ github.com/Azure/go-autorest/autorest/validation v0.3.0/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -71,6 +81,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190822182118-27a4ced34534/go.mod h1:iroGtC8B3tQiqtds1l+mgk/BBOrxbqjH+eUfFQYRc14= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -80,28 +92,26 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs= +github.com/armon/go-metrics v0.3.3/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= -github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.16.26/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.44.262 h1:gyXpcJptWoNkK+DiAiaBltlreoWKQXjAIh6FRh60F+I= -github.com/aws/aws-sdk-go v1.44.262/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.30.27 h1:9gPjZWVDSoQrBO2AvqrWObS6KAZByfEJxQoCYo4ZfK0= +github.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= @@ -111,8 +121,6 @@ github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3 github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -120,13 +128,24 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= +github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20200709052629-daa8e1ccc0bc/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= +github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= @@ -144,11 +163,8 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/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/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= -github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= @@ -157,6 +173,7 @@ github.com/docker/cli v0.0.0-20200109221225-a4f60165b7a3/go.mod h1:JLrzqnKDaYBop github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v1.4.2-0.20200319182547-c7ad2b866182/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= @@ -172,133 +189,73 @@ github.com/elazarl/goproxy v0.0.0-20190911111923-ecfe977594f1/go.mod h1:Ro8st/El github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= -github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= 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.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= -github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= -github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= github.com/frankban/quicktest v1.13.0 h1:yNZif1OkDfNoDfb9zZa9aXIpejNR4F23Wely0c+Qdqk= +github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-errors/errors v1.0.2-0.20180813162953-d98b870cc4e0 h1:skJKxRtNmevLqnayafdLe2AsenqRupVmzZSqrvb5caU= github.com/go-errors/errors v1.0.2-0.20180813162953-d98b870cc4e0/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= -github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-ldap/ldap/v3 v3.1.3/go.mod h1:3rbOH3jRS2u6jg2rJnKAMLE/xQyCKIveG2Sa/Cohzb8= +github.com/go-ldap/ldap/v3 v3.1.10/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= -github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= -github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc= -github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo= -github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.20.3 h1:rz6kiC84sqNQoqrtulzaL/VERgkoCyB6WdEkc2ujzUc= -github.com/go-openapi/errors v0.20.3/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk= +github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= +github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= -github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= -github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= -github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g= -github.com/go-openapi/loads v0.21.2 h1:r2a/xFIYeZ4Qd2TnGpWDIQNcP80dIaZgf704za8enro= -github.com/go-openapi/loads v0.21.2/go.mod h1:Jq58Os6SSGz0rzh62ptiu8Z31I+OTHqmULx5e/gJbNw= -github.com/go-openapi/runtime v0.25.0 h1:7yQTCdRbWhX8vnIjdzU8S00tBYf7Sg71EBeorlPHvhc= -github.com/go-openapi/runtime v0.25.0/go.mod h1:Ux6fikcHXyyob6LNWxtE96hWwjBPYF0DXgVFuMTneOs= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= -github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= -github.com/go-openapi/spec v0.20.8 h1:ubHmXNY3FCIOinT8RNrrPfGc9t7I1qhPtdOGoG2AxRU= -github.com/go-openapi/spec v0.20.8/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= -github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= -github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= -github.com/go-openapi/strfmt v0.21.3 h1:xwhj5X6CjXEZZHMWy1zKJxvW9AfHC9pkyUjLvHtKG7o= -github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9Fqs9NU= -github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= -github.com/go-ozzo/ozzo-validation v3.6.0+incompatible h1:msy24VGS42fKO9K1vLz82/GeYW1cILu7Nuuj1N3BBkE= -github.com/go-ozzo/ozzo-validation v3.6.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= -github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= -github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= -github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= -github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= -github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= -github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= -github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= -github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= -github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= -github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= -github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= -github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= -github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= -github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= -github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= -github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= @@ -310,7 +267,7 @@ github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -318,6 +275,7 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -334,17 +292,15 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= -github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= -github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -353,11 +309,12 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/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.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/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 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-containerregistry v0.0.0-20200110202235-f4fb41bf00a3/go.mod h1:2wIuQute9+hhWqvL3vEI7YB0EKluF4WcPzI1eAliazk= github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -365,6 +322,7 @@ github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -372,19 +330,26 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= +github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= +github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -397,55 +362,62 @@ github.com/gruntwork-io/gruntwork-cli v0.7.0 h1:YgSAmfCj9c61H+zuvHwKfYUwlMhu5arn github.com/gruntwork-io/gruntwork-cli v0.7.0/go.mod h1:jp6Z7NcLF2avpY8v71fBx6hds9eOFPELSuD/VPv7w00= github.com/gruntwork-io/terratest v0.31.2 h1:xvYHA80MUq5kx670dM18HInewOrrQrAN+XbVVtytUHg= github.com/gruntwork-io/terratest v0.31.2/go.mod h1:EEgJie28gX/4AD71IFqgMj6e99KP5mi81hEtzmDjxTo= -github.com/hashicorp/consul-k8s/control-plane v0.0.0-20230609143603-198c4433d892 h1:4iI0ztWbVPTSDax+m1/XDs4jIRorxY4kSMyuM0fX+Dc= -github.com/hashicorp/consul-k8s/control-plane v0.0.0-20230609143603-198c4433d892/go.mod h1:iZ8BJGSnY52wnxJTo2VIfGX63CPjqiNzbuqdOtJCKnI= -github.com/hashicorp/consul/api v1.22.0-rc1 h1:ePmGqndeMgaI38KUbSA/CqTzeEAIogXyWnfNJzglo70= -github.com/hashicorp/consul/api v1.22.0-rc1/go.mod h1:wtduXtbAqSGtBdi3tyA5SSAYGAG51rBejV9SEUBciMY= -github.com/hashicorp/consul/sdk v0.14.0-rc1 h1:PuETOfN0uxl28i0Pq6rK7TBCrIl7psMbL0YTSje4KvM= -github.com/hashicorp/consul/sdk v0.14.0-rc1/go.mod h1:gHYeuDa0+0qRAD6Wwr6yznMBvBwHKoxSBoW5l73+saE= +github.com/hashicorp/consul-k8s/control-plane v0.0.0-20221117191905-0b1cc2b631e3 h1:4wROIZB8Y4cN/wPILChc2zQ/q00z1VyJitdgyLbITdU= +github.com/hashicorp/consul-k8s/control-plane v0.0.0-20221117191905-0b1cc2b631e3/go.mod h1:j9Db/whkzvNC+KP2GftY0HxxleLm9swxXjlu3tYaOAw= +github.com/hashicorp/consul/api v1.17.0 h1:aqytbw31uCPNn37ST+717IyGod+P1eTgSGu3yjRo4bs= +github.com/hashicorp/consul/api v1.17.0/go.mod h1:ZNwemOPAdgtV4cCx9fqxNmw+PI3vliW6gYin2WD+F2g= +github.com/hashicorp/consul/sdk v0.13.0 h1:lce3nFlpv8humJL8rNrrGHYSKc3q+Kxfeg3Ii1m6ZWU= +github.com/hashicorp/consul/sdk v0.13.0/go.mod h1:0hs/l5fOVhJy/VdcoaNqUSi2AUs95eF5WKtv+EYIQqE= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-bexpr v0.1.11 h1:6DqdA/KBjurGby9yTY0bmkathya0lfwF2SeuubCI7dY= -github.com/hashicorp/go-bexpr v0.1.11/go.mod h1:f03lAo0duBlDIUMGCuad8oLcgejw4m7U+N8T+6Kz1AE= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= -github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.2.2 h1:ihRI7YFwcZdiSD7SIenIhHfQH3OuDvWerAUBZbeQS3M= +github.com/hashicorp/go-hclog v1.2.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= -github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.0 h1:8exGP7ego3OmkfksihtSouGMZ+hQrhxx+FVELeXpVPE= +github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g= +github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.4.5 h1:oTE/oQR4eghggRg8VY7PAz3dr++VwDNBGCcOfIvHpBo= -github.com/hashicorp/go-plugin v1.4.5/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= +github.com/hashicorp/go-plugin v1.0.1 h1:4OtAfUGbnKC6yS48p0CtMX2oFYtzFZVv6rok3cRWgnE= +github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-retryablehttp v0.6.2/go.mod h1:gEx6HMUGxYYhJScX7W1Il64m6cc2C1mDaW3NQ9sY1FY= github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 h1:cCRo8gK7oq6A2L6LICkUZ+/a5rLiRXFMf1Qd4xSwxTc= -github.com/hashicorp/go-secure-stdlib/mlock v0.1.1/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1 h1:78ki3QBevHwYrVxnyVeaEz+7WtifHhauYF23es/0KlI= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.1 h1:nd0HIW15E6FG1MsnArYaHfuw9C2zgzM8LxkG5Ty/788= github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -455,41 +427,34 @@ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+l github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/hcp-sdk-go v0.50.0 h1:vOUpVf4MQF/gtoBukuoYKs/i6KinTSpP5jhKCvsZ2bc= -github.com/hashicorp/hcp-sdk-go v0.50.0/go.mod h1:hZqky4HEzsKwvLOt4QJlZUrjeQmb4UCZUhDP2HyQFfc= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= -github.com/hashicorp/vault/api v1.8.3 h1:cHQOLcMhBR+aVI0HzhPxO62w2+gJhIrKguQNONPzu6o= -github.com/hashicorp/vault/api v1.8.3/go.mod h1:4g/9lj9lmuJQMtT6CmVMHC5FW1yENaVv+Nv4ZfG8fAg= -github.com/hashicorp/vault/sdk v0.7.0 h1:2pQRO40R1etpKkia5fb4kjrdYMx3BHklPxl1pxpxDHg= -github.com/hashicorp/vault/sdk v0.7.0/go.mod h1:KyfArJkhooyba7gYCKSq8v66QdqJmnbAxtV/OX1+JTs= +github.com/hashicorp/vault/api v1.0.5-0.20200519221902-385fac77e20f/go.mod h1:euTFbi2YJgwcju3imEt919lhJKF68nN1cQPq3aA+kBE= +github.com/hashicorp/vault/api v1.2.0 h1:ysGFc6XRGbv05NsWPzuO5VTv68Lj8jtwATxRLFOpP9s= +github.com/hashicorp/vault/api v1.2.0/go.mod h1:dAjw0T5shMnrfH7Q/Mst+LrcTKvStZBVs1PICEDpUqY= +github.com/hashicorp/vault/sdk v0.1.14-0.20200519221530-14615acda45f/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10= +github.com/hashicorp/vault/sdk v0.2.1 h1:S4O6Iv/dyKlE9AUTXGa7VOvZmsCvg36toPKgV4f2P4M= +github.com/hashicorp/vault/sdk v0.2.1/go.mod h1:WfUiO1vYzfBkz1TmoE4ZGU7HD0T0Cl/rZwaxjBkgN4U= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= +github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -502,22 +467,17 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= -github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -528,58 +488,46 @@ github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= -github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM= -github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= -github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= -github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= -github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.14.0 h1:/x0XQ6h+3U3nAyk1yx+bHPURrKa9sVVvYbuqZ7pIAtI= github.com/mitchellh/go-testing-interface v1.14.0/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/pointerstructure v1.2.1 h1:ZhBBeX8tSlRpu/FFhXH4RC4OJzFlqsQhoHZAz4x7TIw= -github.com/mitchellh/pointerstructure v1.2.1/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= +github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo= +github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= @@ -592,99 +540,89 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo/v2 v2.6.0 h1:9t9b9vRUbFq3C4qKFCGkVuq/fIHji802N1nrtkh1mNc= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU= +github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= +github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/oracle/oci-go-sdk v7.1.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/pquerna/otp v1.2.0 h1:/A3+Jn+cagqayeR3iHs/L62m5ue7710D35zl1zJ1kok= github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= 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= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= -github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= @@ -693,22 +631,22 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= -github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -717,10 +655,8 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -728,67 +664,47 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vdemeester/k8s-pkg-credentialprovider v0.0.0-20200107171650-7c61ffa44238/go.mod h1:JwQJCMWpUDqjZrB5jpw0f5VbN7U95zxFy1ZDpoEarGo= github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= -github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= -github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= -github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= -go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= -go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= -go.mongodb.org/mongo-driver v1.11.0 h1:FZKhBSTydeuffHj9CBjXlR8vQLee1cQyTWYPA6/tqiE= -go.mongodb.org/mongo-driver v1.11.0/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opentelemetry.io/otel v1.11.1 h1:4WLLAmcfkmDk2ukNXJyq3/kiz/3UzCaYq6PskJsaou4= -go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE= -go.opentelemetry.io/otel/sdk v1.11.1 h1:F7KmQgoHljhUuJyA+9BiU+EkJfyX5nVVF4wyzWZpKxs= -go.opentelemetry.io/otel/trace v1.11.1 h1:ofxdnzsNrGBYXbP7t7zpUK281+go5rF7dvdIZXF8gdQ= -go.opentelemetry.io/otel/trace v1.11.1/go.mod h1:f/Q9G7vzk5u91PhbmKbg1Qn0rzH1LJ4vbPHFGkTPtOk= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -796,10 +712,10 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -814,8 +730,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o= -golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -828,6 +742,8 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -836,15 +752,15 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -871,22 +787,24 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= -golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY= golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -894,15 +812,18 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= -golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 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= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -910,8 +831,6 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -920,32 +839,35 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -958,37 +880,37 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -997,18 +919,19 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1019,16 +942,13 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1036,6 +956,7 @@ golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1065,17 +986,20 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 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= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gomodules.xyz/jsonpatch/v2 v2.3.0 h1:8NFhfS6gzxNqjLIYnZxg319wZ5Qjnx4m/CcX+Klzazc= -gomodules.xyz/jsonpatch/v2 v2.3.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= @@ -1096,6 +1020,11 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1134,12 +1063,24 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 h1:hrbNEivu7Zn1pxvHk6MBrq9iE22woVILTHqexqBxe6I= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= @@ -1151,11 +1092,16 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw= -google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= 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= @@ -1169,27 +1115,29 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= 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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= @@ -1203,12 +1151,12 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= 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-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1218,22 +1166,20 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI= k8s.io/api v0.19.3/go.mod h1:VF+5FT1B74Pw3KxMdKyinLo+zynBaMBiAfGMuldcNDs= -k8s.io/api v0.26.3 h1:emf74GIQMTik01Aum9dPP0gAypL8JTLl/lHa4V9RFSU= -k8s.io/api v0.26.3/go.mod h1:PXsqwPMXBSBcL1lJ9CYDKy7kIReUydukS5JiRlxC3qE= -k8s.io/apiextensions-apiserver v0.26.3 h1:5PGMm3oEzdB1W/FTMgGIDmm100vn7IaUP5er36dB+YE= +k8s.io/api v0.22.2 h1:M8ZzAD0V6725Fjg53fKeTJxGsJvRbk4TEm/fexHMtfw= +k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8= k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= k8s.io/apimachinery v0.19.3/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= -k8s.io/apimachinery v0.26.3 h1:dQx6PNETJ7nODU3XPtrwkfuubs6w7sX0M8n61zHIV/k= -k8s.io/apimachinery v0.26.3/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I= +k8s.io/apimachinery v0.22.2 h1:ejz6y/zNma8clPVfNDLnPbleBo6MpoFy/HBiBqCouVk= +k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= k8s.io/apiserver v0.17.0/go.mod h1:ABM+9x/prjINN6iiffRVNCBR2Wk7uY4z+EtEGZD48cg= k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k= k8s.io/client-go v0.19.3/go.mod h1:+eEMktZM+MG0KO+PTkci8xnbCZHvj9TqR6Q1XDUIJOM= -k8s.io/client-go v0.26.3 h1:k1UY+KXfkxV2ScEL3gilKcF7761xkYsSD6BC9szIu8s= -k8s.io/client-go v0.26.3/go.mod h1:ZPNu9lm8/dbRIPAgteN30RSXea6vrCpFvq+MateTUuQ= +k8s.io/client-go v0.22.2 h1:DaSQgs02aCC1QcwUdkKZWOeaVsQjYvWv8ZazcZ6JcHc= +k8s.io/client-go v0.22.2/go.mod h1:sAlhrkVDf50ZHx6z4K0S40wISNTarf1r800F+RlCF6U= k8s.io/cloud-provider v0.17.0/go.mod h1:Ze4c3w2C0bRsjkBUoHpFi+qWe3ob1wI2/7cUn+YQIDE= k8s.io/code-generator v0.0.0-20191121015212-c4c8f8345c7e/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= k8s.io/component-base v0.17.0/go.mod h1:rKuRAokNMY2nn2A6LP/MiwpoaMRHpfRnrPaUJJj1Yoc= -k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4= k8s.io/csi-translation-lib v0.17.0/go.mod h1:HEF7MEz7pOLJCnxabi45IPkhSsE/KmxPQksuCrHKWls= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= @@ -1243,17 +1189,18 @@ k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= -k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM= +k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= -k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= -k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= +k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM= +k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/legacy-cloud-providers v0.17.0/go.mod h1:DdzaepJ3RtRy+e5YhNtrCYwlgyK87j/5+Yfp0L9Syp8= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= -k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20220812165043-ad590609e2e5 h1:XmRqFcQlCy/lKRZ39j+RVpokYNroHPqV3mcBRfnhT5o= +k8s.io/utils v0.0.0-20220812165043-ad590609e2e5/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= @@ -1262,18 +1209,12 @@ modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/controller-runtime v0.14.6 h1:oxstGVvXGNnMvY7TAESYk+lzr6S3V5VFxQ6d92KcwQA= -sigs.k8s.io/controller-runtime v0.14.6/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= -sigs.k8s.io/gateway-api v0.7.1 h1:Tts2jeepVkPA5rVG/iO+S43s9n7Vp7jCDhZDQYtPigQ= -sigs.k8s.io/gateway-api v0.7.1/go.mod h1:Xv0+ZMxX0lu1nSSDIIPEfbVztgNZ+3cfiYrJsa2Ooso= -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/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18= sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno= +sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/acceptance/tests/api-gateway/api_gateway_external_servers_test.go b/acceptance/tests/api-gateway/api_gateway_external_servers_test.go deleted file mode 100644 index aa0934dc65..0000000000 --- a/acceptance/tests/api-gateway/api_gateway_external_servers_test.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package apigateway - -import ( - "context" - "fmt" - "testing" - - "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" - "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" - "github.com/hashicorp/consul-k8s/acceptance/framework/logger" - "github.com/hashicorp/consul/api" - "github.com/hashicorp/consul/sdk/testutil/retry" - "github.com/stretchr/testify/require" - "k8s.io/apimachinery/pkg/types" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -// TestAPIGateway_ExternalServers tests that connect works when using external servers. -// It sets up an external Consul server in the same cluster but a different Helm installation -// and then treats this server as external. -func TestAPIGateway_ExternalServers(t *testing.T) { - cfg := suite.Config() - ctx := suite.Environment().DefaultContext(t) - - serverHelmValues := map[string]string{ - "global.acls.manageSystemACLs": "true", - "global.tls.enabled": "true", - - // Don't install injector, controller and cni on this cluster so that it's not installed twice. - "connectInject.enabled": "false", - "connectInject.cni.enabled": "false", - } - serverReleaseName := helpers.RandomName() - consulServerCluster := consul.NewHelmCluster(t, serverHelmValues, ctx, cfg, serverReleaseName) - - consulServerCluster.Create(t) - - helmValues := map[string]string{ - "server.enabled": "false", - "global.acls.manageSystemACLs": "true", - "global.tls.enabled": "true", - "connectInject.enabled": "true", - "externalServers.enabled": "true", - "externalServers.hosts[0]": fmt.Sprintf("%s-consul-server", serverReleaseName), - "externalServers.httpsPort": "8501", - "global.tls.caCert.secretName": fmt.Sprintf("%s-consul-ca-cert", serverReleaseName), - "global.tls.caCert.secretKey": "tls.crt", - "global.acls.bootstrapToken.secretName": fmt.Sprintf("%s-consul-bootstrap-acl-token", serverReleaseName), - "global.acls.bootstrapToken.secretKey": "token", - } - - releaseName := helpers.RandomName() - consulCluster := consul.NewHelmCluster(t, helmValues, ctx, cfg, releaseName) - consulCluster.SkipCheckForPreviousInstallations = true - - consulCluster.Create(t) - - logger.Log(t, "creating static-server and static-client deployments") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") - - // Override the default proxy config settings for this test - consulClient, _ := consulCluster.SetupConsulClient(t, true, serverReleaseName) - logger.Log(t, "have consul client") - _, _, err := consulClient.ConfigEntries().Set(&api.ProxyConfigEntry{ - Kind: api.ProxyDefaults, - Name: api.ProxyConfigGlobal, - Config: map[string]interface{}{ - "protocol": "http", - }, - }, nil) - require.NoError(t, err) - logger.Log(t, "set consul config entry") - - logger.Log(t, "creating api-gateway resources") - out, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "apply", "-k", "../fixtures/bases/api-gateway") - require.NoError(t, err, out) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - // Ignore errors here because if the test ran as expected - // the custom resources will have been deleted. - k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-k", "../fixtures/bases/api-gateway") - }) - - logger.Log(t, "patching route to target server") - k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "httproute", "http-route", "-p", `{"spec":{"rules":[{"backendRefs":[{"name":"static-server","port":80}]}]}}`, "--type=merge") - - // Grab a kubernetes client so that we can verify binding - // behavior prior to issuing requests through the gateway. - k8sClient := ctx.ControllerRuntimeClient(t) - - // On startup, the controller can take upwards of 1m to perform - // leader election so we may need to wait a long time for - // the reconcile loop to run (hence a ~1m timeout here). - var gatewayAddress string - retryCheck(t, 60, func(r *retry.R) { - var gateway gwv1beta1.Gateway - err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway", Namespace: "default"}, &gateway) - require.NoError(r, err) - - // check that we have an address to use - require.Len(r, gateway.Status.Addresses, 1) - // now we know we have an address, set it so we can use it - gatewayAddress = gateway.Status.Addresses[0].Value - }) - - k8sOptions := ctx.KubectlOptions(t) - targetAddress := fmt.Sprintf("http://%s/", gatewayAddress) - - // check that intentions keep our connection from happening - k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, targetAddress) - - // Now we create the allow intention. - _, _, err = consulClient.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{ - Kind: api.ServiceIntentions, - Name: "static-server", - Sources: []*api.SourceIntention{ - { - Name: "gateway", - Action: api.IntentionActionAllow, - }, - }, - }, nil) - require.NoError(t, err) - - // Test that we can make a call to the api gateway - // via the static-client pod. It should route to the static-server pod. - logger.Log(t, "trying calls to api gateway") - k8s.CheckStaticServerConnectionSuccessful(t, k8sOptions, StaticClientName, targetAddress) -} diff --git a/acceptance/tests/api-gateway/api_gateway_gatewayclassconfig_test.go b/acceptance/tests/api-gateway/api_gateway_gatewayclassconfig_test.go deleted file mode 100644 index 89ba07a1e7..0000000000 --- a/acceptance/tests/api-gateway/api_gateway_gatewayclassconfig_test.go +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package apigateway - -import ( - "context" - "fmt" - "testing" - "time" - - "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" - "github.com/hashicorp/consul-k8s/acceptance/framework/logger" - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul/api" - "github.com/hashicorp/consul/sdk/testutil/retry" - "github.com/stretchr/testify/require" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/utils/pointer" - "sigs.k8s.io/controller-runtime/pkg/client" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -// GatewayClassConfig tests the creation of a gatewayclassconfig object and makes sure that its configuration -// is properly applied to any child gateway objects, namely that the number of gateway instances match the defined -// minInstances,maxInstances and defaultInstances parameters, and that changing the parent gateway does not affect -// the child gateways. -func TestAPIGateway_GatewayClassConfig(t *testing.T) { - var ( - defaultInstances = pointer.Int32(2) - maxInstances = pointer.Int32(3) - minInstances = pointer.Int32(1) - - namespace = "default" - gatewayClassName = "gateway-class" - ) - - ctx := suite.Environment().DefaultContext(t) - cfg := suite.Config() - helmValues := map[string]string{ - "global.logLevel": "trace", - "connectInject.enabled": "true", - } - releaseName := helpers.RandomName() - consulCluster := consul.NewHelmCluster(t, helmValues, ctx, cfg, releaseName) - consulCluster.Create(t) - - // Override the default proxy config settings for this test. - consulClient, _ := consulCluster.SetupConsulClient(t, false) - _, _, err := consulClient.ConfigEntries().Set(&api.ProxyConfigEntry{ - Kind: api.ProxyDefaults, - Name: api.ProxyConfigGlobal, - Config: map[string]interface{}{ - "protocol": "http", - }, - }, nil) - require.NoError(t, err) - - k8sClient := ctx.ControllerRuntimeClient(t) - - // create a GatewayClassConfig with configuration set - gatewayClassConfigName := "gateway-class-config" - gatewayClassConfig := &v1alpha1.GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: gatewayClassConfigName, - }, - Spec: v1alpha1.GatewayClassConfigSpec{ - DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: defaultInstances, - MaxInstances: maxInstances, - MinInstances: minInstances, - }, - }, - } - logger.Log(t, "creating gateway class config") - err = k8sClient.Create(context.Background(), gatewayClassConfig) - require.NoError(t, err) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - logger.Log(t, "deleting all gateway class configs") - k8sClient.DeleteAllOf(context.Background(), &v1alpha1.GatewayClassConfig{}) - }) - - gatewayParametersRef := &gwv1beta1.ParametersReference{ - Group: gwv1beta1.Group(v1alpha1.ConsulHashicorpGroup), - Kind: gwv1beta1.Kind(v1alpha1.GatewayClassConfigKind), - Name: gatewayClassConfigName, - } - - // Create gateway class referencing gateway-class-config. - logger.Log(t, "creating controlled gateway class") - createGatewayClass(t, k8sClient, gatewayClassName, gatewayClassControllerName, gatewayParametersRef) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - logger.Log(t, "deleting all gateway classes") - k8sClient.DeleteAllOf(context.Background(), &gwv1beta1.GatewayClass{}) - }) - - // Create a certificate to reference in listeners. - certificateInfo := generateCertificate(t, nil, "certificate.consul.local") - certificateName := "certificate" - certificate := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: certificateName, - Namespace: namespace, - Labels: map[string]string{ - "test-certificate": "true", - }, - }, - Type: corev1.SecretTypeTLS, - Data: map[string][]byte{ - corev1.TLSCertKey: certificateInfo.CertPEM, - corev1.TLSPrivateKeyKey: certificateInfo.PrivateKeyPEM, - }, - } - logger.Log(t, "creating certificate") - err = k8sClient.Create(context.Background(), certificate) - require.NoError(t, err) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8sClient.Delete(context.Background(), certificate) - }) - - // Create gateway referencing gateway class. - gatewayName := "gcctestgateway" + namespace - logger.Log(t, "creating controlled gateway") - gateway := createGateway(t, k8sClient, gatewayName, namespace, gatewayClassName, certificateName) - - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - logger.Log(t, "deleting all gateways") - k8sClient.DeleteAllOf(context.Background(), &gwv1beta1.Gateway{}, client.InNamespace(namespace)) - }) - - // Ensure it exists. - logger.Log(t, "checking that gateway is synchronized to Consul") - checkConsulExists(t, consulClient, api.APIGateway, gatewayName) - - // Scenario: Gateway deployment should match the default instances defined on the gateway class config - logger.Log(t, "checking that gateway instances match defined gateway class config") - checkNumberOfInstances(t, k8sClient, consulClient, gateway.Name, gateway.Namespace, defaultInstances, gateway) - - // Scenario: Updating the GatewayClassConfig should not affect gateways that have already been created - logger.Log(t, "updating gatewayclassconfig values") - err = k8sClient.Get(context.Background(), types.NamespacedName{Name: gatewayClassConfigName, Namespace: namespace}, gatewayClassConfig) - require.NoError(t, err) - gatewayClassConfig.Spec.DeploymentSpec.DefaultInstances = pointer.Int32(8) - gatewayClassConfig.Spec.DeploymentSpec.MinInstances = pointer.Int32(5) - err = k8sClient.Update(context.Background(), gatewayClassConfig) - require.NoError(t, err) - checkNumberOfInstances(t, k8sClient, consulClient, gateway.Name, gateway.Namespace, defaultInstances, gateway) - - // Scenario: gateways should be able to scale independently and not get overridden by the controller unless it's above the max - scale(t, k8sClient, gateway.Name, gateway.Namespace, pointer.Int32(*maxInstances+1)) - checkNumberOfInstances(t, k8sClient, consulClient, gateway.Name, gateway.Namespace, maxInstances, gateway) - scale(t, k8sClient, gateway.Name, gateway.Namespace, pointer.Int32(0)) - checkNumberOfInstances(t, k8sClient, consulClient, gateway.Name, gateway.Namespace, minInstances, gateway) - -} - -func scale(t *testing.T, client client.Client, name, namespace string, scaleTo *int32) { - t.Helper() - - var deployment appsv1.Deployment - err := client.Get(context.Background(), types.NamespacedName{Name: name, Namespace: namespace}, &deployment) - require.NoError(t, err) - - logger.Log(t, fmt.Sprintf("scaling gateway from %d to %d", *deployment.Spec.Replicas, *scaleTo)) - - deployment.Spec.Replicas = scaleTo - err = client.Update(context.Background(), &deployment) - require.NoError(t, err) - -} - -func checkNumberOfInstances(t *testing.T, k8client client.Client, consulClient *api.Client, name, namespace string, wantNumber *int32, gateway *gwv1beta1.Gateway) { - t.Helper() - - retryCheckWithWait(t, 30, 10*time.Second, func(r *retry.R) { - logger.Log(t, "checking that gateway instances match defined gateway class config") - logger.Log(t, fmt.Sprintf("want: %d", *wantNumber)) - - // Ensure the number of replicas has been set properly. - var deployment appsv1.Deployment - err := k8client.Get(context.Background(), types.NamespacedName{Name: name, Namespace: namespace}, &deployment) - require.NoError(r, err) - logger.Log(t, fmt.Sprintf("deployment replicas: %d", *deployment.Spec.Replicas)) - require.EqualValues(r, *wantNumber, *deployment.Spec.Replicas, "deployment replicas should match the number of instances defined on the gateway class config") - - // Ensure the number of gateway pods matches the replicas generated. - podList := corev1.PodList{} - labels := common.LabelsForGateway(gateway) - err = k8client.List(context.Background(), &podList, client.InNamespace(namespace), client.MatchingLabels(labels)) - require.NoError(r, err) - logger.Log(t, fmt.Sprintf("number of pods: %d", len(podList.Items))) - require.EqualValues(r, *wantNumber, len(podList.Items), "number of pods should match the number of instances defined on the gateway class config") - - // Ensure the number of services matches the replicas generated. - services, _, err := consulClient.Catalog().Service(name, "", nil) - seenServices := map[string]interface{}{} - require.NoError(r, err) - logger.Log(t, fmt.Sprintf("number of services: %d", len(services))) - //we need to double check that we aren't double counting services with the same ID - for _, s := range services { - seenServices[s.ServiceID] = true - logger.Log(t, fmt.Sprintf("service info: id: %s, name: %s, namespace: %s", s.ServiceID, s.ServiceName, s.Namespace)) - } - - logger.Log(t, fmt.Sprintf("number of services: %d", len(services))) - require.EqualValues(r, int(*wantNumber), len(seenServices), "number of services should match the number of instances defined on the gateway class config") - }) -} diff --git a/acceptance/tests/api-gateway/api_gateway_lifecycle_test.go b/acceptance/tests/api-gateway/api_gateway_lifecycle_test.go deleted file mode 100644 index f6f66ed995..0000000000 --- a/acceptance/tests/api-gateway/api_gateway_lifecycle_test.go +++ /dev/null @@ -1,444 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package apigateway - -import ( - "context" - "fmt" - "testing" - - "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" - "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" - "github.com/hashicorp/consul-k8s/acceptance/framework/logger" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul/api" - "github.com/hashicorp/consul/sdk/testutil/retry" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -func TestAPIGateway_Lifecycle(t *testing.T) { - ctx := suite.Environment().DefaultContext(t) - cfg := suite.Config() - helmValues := map[string]string{ - "global.logLevel": "trace", - "connectInject.enabled": "true", - } - - releaseName := helpers.RandomName() - consulCluster := consul.NewHelmCluster(t, helmValues, ctx, cfg, releaseName) - - consulCluster.Create(t) - - k8sClient := ctx.ControllerRuntimeClient(t) - consulClient, _ := consulCluster.SetupConsulClient(t, false) - - defaultNamespace := "default" - - // create a service to target - targetName := "static-server" - logger.Log(t, "creating target server") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") - - // create a basic GatewayClassConfig - gatewayClassConfigName := "controlled-gateway-class-config" - gatewayClassConfig := &v1alpha1.GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: gatewayClassConfigName, - }, - } - logger.Log(t, "creating gateway class config") - err := k8sClient.Create(context.Background(), gatewayClassConfig) - require.NoError(t, err) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - logger.Log(t, "deleting all gateway class configs") - k8sClient.DeleteAllOf(context.Background(), &v1alpha1.GatewayClassConfig{}) - }) - - gatewayParametersRef := &gwv1beta1.ParametersReference{ - Group: gwv1beta1.Group(v1alpha1.ConsulHashicorpGroup), - Kind: gwv1beta1.Kind(v1alpha1.GatewayClassConfigKind), - Name: gatewayClassConfigName, - } - - // create three gateway classes, two we control, one we don't - controlledGatewayClassOneName := "controlled-gateway-class-one" - logger.Log(t, "creating controlled gateway class one") - createGatewayClass(t, k8sClient, controlledGatewayClassOneName, gatewayClassControllerName, gatewayParametersRef) - - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - logger.Log(t, "deleting all gateway classes") - k8sClient.DeleteAllOf(context.Background(), &gwv1beta1.GatewayClass{}) - }) - - controlledGatewayClassTwoName := "controlled-gateway-class-two" - logger.Log(t, "creating controlled gateway class two") - createGatewayClass(t, k8sClient, controlledGatewayClassTwoName, gatewayClassControllerName, gatewayParametersRef) - - uncontrolledGatewayClassName := "uncontrolled-gateway-class" - logger.Log(t, "creating uncontrolled gateway class") - createGatewayClass(t, k8sClient, uncontrolledGatewayClassName, "example.com/some-controller", nil) - - // Create a certificate to reference in listeners - certificateInfo := generateCertificate(t, nil, "certificate.consul.local") - certificateName := "certificate" - certificate := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: certificateName, - Namespace: defaultNamespace, - Labels: map[string]string{ - "test-certificate": "true", - }, - }, - Type: corev1.SecretTypeTLS, - Data: map[string][]byte{ - corev1.TLSCertKey: certificateInfo.CertPEM, - corev1.TLSPrivateKeyKey: certificateInfo.PrivateKeyPEM, - }, - } - logger.Log(t, "creating certificate") - err = k8sClient.Create(context.Background(), certificate) - require.NoError(t, err) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8sClient.Delete(context.Background(), certificate) - }) - - // Create three gateways with a basic HTTPS listener to correspond to the three classes - controlledGatewayOneName := "controlled-gateway-one" - logger.Log(t, "creating controlled gateway one") - controlledGatewayOne := createGateway(t, k8sClient, controlledGatewayOneName, defaultNamespace, controlledGatewayClassOneName, certificateName) - - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - logger.Log(t, "deleting all gateways") - k8sClient.DeleteAllOf(context.Background(), &gwv1beta1.Gateway{}, client.InNamespace(defaultNamespace)) - }) - - controlledGatewayTwoName := "controlled-gateway-two" - logger.Log(t, "creating controlled gateway two") - controlledGatewayTwo := createGateway(t, k8sClient, controlledGatewayTwoName, defaultNamespace, controlledGatewayClassTwoName, certificateName) - - uncontrolledGatewayName := "uncontrolled-gateway" - logger.Log(t, "creating uncontrolled gateway") - _ = createGateway(t, k8sClient, uncontrolledGatewayName, defaultNamespace, uncontrolledGatewayClassName, certificateName) - - // create two http routes associated with the first controlled gateway - routeOneName := "route-one" - logger.Log(t, "creating route one") - routeOne := createRoute(t, k8sClient, routeOneName, defaultNamespace, controlledGatewayOneName, targetName) - - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - logger.Log(t, "deleting all http routes") - k8sClient.DeleteAllOf(context.Background(), &gwv1beta1.HTTPRoute{}, client.InNamespace(defaultNamespace)) - }) - - routeTwoName := "route-two" - logger.Log(t, "creating route two") - routeTwo := createRoute(t, k8sClient, routeTwoName, defaultNamespace, controlledGatewayTwoName, targetName) - - // Scenario: Swapping a route to another controlled gateway should clean up the old parent statuses and references on Consul resources - - // check that the route is bound properly and objects are reflected in Consul - logger.Log(t, "checking that http route one is bound to gateway one") - checkRouteBound(t, k8sClient, routeOneName, defaultNamespace, controlledGatewayOneName) - - logger.Log(t, "checking that http route one is synchronized to Consul") - checkConsulRouteParent(t, consulClient, routeOneName, controlledGatewayOneName) - - // update the route to point to the other controlled gateway - logger.Log(t, "updating route one to be bound to gateway two") - updateKubernetes(t, k8sClient, routeOne, func(r *gwv1beta1.HTTPRoute) { - r.Spec.ParentRefs[0].Name = gwv1beta1.ObjectName(controlledGatewayTwoName) - }) - - // check that the route is bound properly and objects are reflected in Consul - logger.Log(t, "checking that http route one is bound to gateway two") - checkRouteBound(t, k8sClient, routeOneName, defaultNamespace, controlledGatewayTwoName) - - logger.Log(t, "checking that http route one is synchronized to Consul") - checkConsulRouteParent(t, consulClient, routeOneName, controlledGatewayTwoName) - - // Scenario: Binding a route to a controlled gateway and then associating it with another gateway we don’t control should clean up Consul resources, route statuses, and finalizers - // check that the route is bound properly and objects are reflected in Consul - - // check that our second http route is bound properly - logger.Log(t, "checking that http route two is bound to gateway two") - checkRouteBound(t, k8sClient, routeTwoName, defaultNamespace, controlledGatewayTwoName) - - logger.Log(t, "checking that http route two is synchronized to Consul") - checkConsulRouteParent(t, consulClient, routeTwoName, controlledGatewayTwoName) - - // update the route to point to the uncontrolled gateway - logger.Log(t, "updating route two to be bound to an uncontrolled gateway") - updateKubernetes(t, k8sClient, routeTwo, func(r *gwv1beta1.HTTPRoute) { - r.Spec.ParentRefs[0].Name = gwv1beta1.ObjectName(uncontrolledGatewayName) - }) - - // check that the route is unbound and all Consul objects and Kubernetes statuses are cleaned up - logger.Log(t, "checking that http route two is cleaned up because we no longer control it") - checkEmptyRoute(t, k8sClient, routeTwoName, defaultNamespace) - - logger.Log(t, "checking that http route two is deleted from Consul") - checkConsulNotExists(t, consulClient, api.HTTPRoute, routeTwoName) - - // Scenario: Switching a controlled gateway’s protocol that causes a route to unbind should cause the route to drop the parent ref in Consul and result in proper statuses set in Kubernetes - - // swap the gateway's protocol and see the route unbind - logger.Log(t, "marking gateway two as using TCP") - updateKubernetes(t, k8sClient, controlledGatewayTwo, func(g *gwv1beta1.Gateway) { - g.Spec.Listeners[0].Protocol = gwv1beta1.TCPProtocolType - }) - - // check that the route is unbound and all Consul objects and Kubernetes statuses are cleaned up - logger.Log(t, "checking that http route one is not bound to gateway two") - retryCheck(t, 60, func(r *retry.R) { - var route gwv1beta1.HTTPRoute - err := k8sClient.Get(context.Background(), types.NamespacedName{Name: routeOneName, Namespace: defaultNamespace}, &route) - require.NoError(r, err) - - require.Len(r, route.Status.Parents, 1) - require.EqualValues(r, controlledGatewayTwoName, route.Status.Parents[0].ParentRef.Name) - checkStatusCondition(r, route.Status.Parents[0].Conditions, falseCondition("Accepted", "NotAllowedByListeners")) - }) - - logger.Log(t, "checking that route one is deleted from Consul") - checkConsulNotExists(t, consulClient, api.HTTPRoute, routeOneName) - - // Scenario: Deleting a gateway should result in routes only referencing it to get cleaned up from Consul and their statuses/finalizers cleared, but routes referencing another controlled gateway should still exist in Consul and only have their statuses cleaned up from referencing the gateway we previously controlled. Any referenced certificates should also get cleaned up. - - // delete gateway two - logger.Log(t, "deleting gateway two in Kubernetes") - err = k8sClient.Delete(context.Background(), controlledGatewayTwo) - require.NoError(t, err) - - // check that the gateway is deleted from Consul - logger.Log(t, "checking that gateway two is deleted from Consul") - checkConsulNotExists(t, consulClient, api.APIGateway, controlledGatewayTwoName) - - // check that the Kubernetes route is cleaned up and the entries deleted from Consul - logger.Log(t, "checking that http route one is cleaned up in Kubernetes") - checkEmptyRoute(t, k8sClient, routeOneName, defaultNamespace) - - // Scenario: Changing a gateway class name on a gateway to something we don’t control should have the same affect as deleting it with the addition of cleaning up our finalizer from the gateway. - - // reset route one to point to our first gateway and check that it's bound properly - logger.Log(t, "remarking route one as bound to gateway one") - updateKubernetes(t, k8sClient, routeOne, func(r *gwv1beta1.HTTPRoute) { - r.Spec.ParentRefs[0].Name = gwv1beta1.ObjectName(controlledGatewayOneName) - }) - - logger.Log(t, "checking that http route one is bound to gateway one") - checkRouteBound(t, k8sClient, routeOneName, defaultNamespace, controlledGatewayOneName) - - logger.Log(t, "checking that http route one is synchronized to Consul") - checkConsulRouteParent(t, consulClient, routeOneName, controlledGatewayOneName) - - // make the gateway uncontrolled by pointing to a non-existent gateway class - logger.Log(t, "marking gateway one as not controlled by our controller") - updateKubernetes(t, k8sClient, controlledGatewayOne, func(g *gwv1beta1.Gateway) { - g.Spec.GatewayClassName = "non-existent" - }) - - // check that the Kubernetes gateway is cleaned up - logger.Log(t, "checking that gateway one is cleaned up in Kubernetes") - retryCheck(t, 60, func(r *retry.R) { - var route gwv1beta1.Gateway - err := k8sClient.Get(context.Background(), types.NamespacedName{Name: controlledGatewayOneName, Namespace: defaultNamespace}, &route) - require.NoError(r, err) - - require.Len(r, route.Finalizers, 0) - }) - - // check that the gateway is deleted from Consul - logger.Log(t, "checking that gateway one is deleted from Consul") - checkConsulNotExists(t, consulClient, api.APIGateway, controlledGatewayOneName) - - // check that the Kubernetes route is cleaned up and the entries deleted from Consul - logger.Log(t, "checking that http route one is cleaned up in Kubernetes") - checkEmptyRoute(t, k8sClient, routeOneName, defaultNamespace) - - logger.Log(t, "checking that http route one is deleted from Consul") - checkConsulNotExists(t, consulClient, api.HTTPRoute, routeOneName) - - // Scenario: Deleting a certificate referenced by a gateway’s listener should make the listener invalid and drop it from Consul. - - // reset the gateway - logger.Log(t, "remarking gateway one as controlled by our controller") - updateKubernetes(t, k8sClient, controlledGatewayOne, func(g *gwv1beta1.Gateway) { - g.Spec.GatewayClassName = gwv1beta1.ObjectName(controlledGatewayClassOneName) - }) - - // make sure it exists - logger.Log(t, "checking that gateway one is synchronized to Consul") - checkConsulExists(t, consulClient, api.APIGateway, controlledGatewayOneName) - - // make sure our certificate exists - logger.Log(t, "checking that the certificate is synchronized to Consul") - checkConsulExists(t, consulClient, api.InlineCertificate, certificateName) - - // delete the certificate in Kubernetes - logger.Log(t, "deleting the certificate in Kubernetes") - err = k8sClient.Delete(context.Background(), certificate) - require.NoError(t, err) - - // make sure the certificate no longer exists in Consul - logger.Log(t, "checking that the certificate is deleted from Consul") - checkConsulNotExists(t, consulClient, api.InlineCertificate, certificateName) -} - -func checkConsulNotExists(t *testing.T, client *api.Client, kind, name string, namespace ...string) { - t.Helper() - - opts := &api.QueryOptions{} - if len(namespace) != 0 { - opts.Namespace = namespace[0] - } - - retryCheck(t, 60, func(r *retry.R) { - _, _, err := client.ConfigEntries().Get(kind, name, opts) - require.Error(r, err) - require.EqualError(r, err, fmt.Sprintf("Unexpected response code: 404 (Config entry not found for %q / %q)", kind, name)) - }) -} - -func checkConsulExists(t *testing.T, client *api.Client, kind, name string) { - t.Helper() - - retryCheck(t, 60, func(r *retry.R) { - _, _, err := client.ConfigEntries().Get(kind, name, nil) - require.NoError(r, err) - }) -} - -func checkConsulRouteParent(t *testing.T, client *api.Client, name, parent string) { - t.Helper() - - retryCheck(t, 60, func(r *retry.R) { - entry, _, err := client.ConfigEntries().Get(api.HTTPRoute, name, nil) - require.NoError(r, err) - route := entry.(*api.HTTPRouteConfigEntry) - - require.Len(r, route.Parents, 1) - require.Equal(r, parent, route.Parents[0].Name) - }) -} - -func checkEmptyRoute(t *testing.T, client client.Client, name, namespace string) { - t.Helper() - - retryCheck(t, 60, func(r *retry.R) { - var route gwv1beta1.HTTPRoute - err := client.Get(context.Background(), types.NamespacedName{Name: name, Namespace: namespace}, &route) - require.NoError(r, err) - - require.Len(r, route.Status.Parents, 0) - require.Len(r, route.Finalizers, 0) - }) -} - -func checkRouteBound(t *testing.T, client client.Client, name, namespace, parent string) { - t.Helper() - - retryCheck(t, 60, func(r *retry.R) { - var route gwv1beta1.HTTPRoute - err := client.Get(context.Background(), types.NamespacedName{Name: name, Namespace: namespace}, &route) - require.NoError(r, err) - - require.Len(r, route.Status.Parents, 1) - require.EqualValues(r, gatewayClassControllerName, route.Status.Parents[0].ControllerName) - require.EqualValues(r, parent, route.Status.Parents[0].ParentRef.Name) - checkStatusCondition(r, route.Status.Parents[0].Conditions, trueCondition("Accepted", "Accepted")) - checkStatusCondition(r, route.Status.Parents[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) - checkStatusCondition(r, route.Status.Parents[0].Conditions, trueCondition("Synced", "Synced")) - }) -} - -func updateKubernetes[T client.Object](t *testing.T, k8sClient client.Client, o T, fn func(o T)) { - t.Helper() - - err := k8sClient.Get(context.Background(), client.ObjectKeyFromObject(o), o) - require.NoError(t, err) - fn(o) - err = k8sClient.Update(context.Background(), o) - require.NoError(t, err) -} - -func createRoute(t *testing.T, client client.Client, name, namespace, parent, target string) *gwv1beta1.HTTPRoute { - t.Helper() - - route := &gwv1beta1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - {Name: gwv1beta1.ObjectName(parent)}, - }, - }, - Rules: []gwv1beta1.HTTPRouteRule{ - {BackendRefs: []gwv1beta1.HTTPBackendRef{ - {BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{Name: gwv1beta1.ObjectName(target)}, - }}, - }}, - }, - }, - } - - err := client.Create(context.Background(), route) - require.NoError(t, err) - return route -} - -func createGateway(t *testing.T, client client.Client, name, namespace, gatewayClass, certificate string) *gwv1beta1.Gateway { - t.Helper() - - gateway := &gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gwv1beta1.ObjectName(gatewayClass), - Listeners: []gwv1beta1.Listener{{ - Name: gwv1beta1.SectionName("listener"), - Protocol: gwv1beta1.HTTPSProtocolType, - Port: 8443, - TLS: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{{ - Name: gwv1beta1.ObjectName(certificate), - }}, - }, - }}, - }, - } - - err := client.Create(context.Background(), gateway) - require.NoError(t, err) - - return gateway -} - -func createGatewayClass(t *testing.T, client client.Client, name, controllerName string, parameters *gwv1beta1.ParametersReference) { - t.Helper() - - gatewayClass := &gwv1beta1.GatewayClass{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - }, - Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: gwv1beta1.GatewayController(controllerName), - ParametersRef: parameters, - }, - } - - err := client.Create(context.Background(), gatewayClass) - require.NoError(t, err) -} diff --git a/acceptance/tests/api-gateway/api_gateway_tenancy_test.go b/acceptance/tests/api-gateway/api_gateway_tenancy_test.go deleted file mode 100644 index f7b0ac6d79..0000000000 --- a/acceptance/tests/api-gateway/api_gateway_tenancy_test.go +++ /dev/null @@ -1,404 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package apigateway - -import ( - "context" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/base64" - "encoding/pem" - "fmt" - "math/big" - "path" - "strconv" - "testing" - "time" - - terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" - "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/hashicorp/consul-k8s/acceptance/framework/config" - "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/environment" - "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" - "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" - "github.com/hashicorp/consul-k8s/acceptance/framework/logger" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul/api" - "github.com/hashicorp/consul/sdk/testutil/retry" -) - -var ( - gatewayGroup = gwv1beta1.Group(gwv1beta1.GroupVersion.Group) - consulGroup = gwv1beta1.Group(v1alpha1.GroupVersion.Group) - gatewayKind = gwv1beta1.Kind("Gateway") - serviceKind = gwv1beta1.Kind("Service") - secretKind = gwv1beta1.Kind("Secret") - meshServiceKind = gwv1beta1.Kind("MeshService") - httpRouteKind = gwv1beta1.Kind("HTTPRoute") - tcpRouteKind = gwv1beta1.Kind("TCPRoute") -) - -func TestAPIGateway_Tenancy(t *testing.T) { - cases := []struct { - secure bool - namespaceMirroring bool - }{ - { - secure: false, - namespaceMirroring: false, - }, - { - secure: true, - namespaceMirroring: false, - }, - { - secure: false, - namespaceMirroring: true, - }, - { - secure: true, - namespaceMirroring: true, - }, - } - for _, c := range cases { - name := fmt.Sprintf("secure: %t, namespaces: %t", c.secure, c.namespaceMirroring) - t.Run(name, func(t *testing.T) { - cfg := suite.Config() - - if !cfg.EnableEnterprise && c.namespaceMirroring { - t.Skipf("skipping this test because -enable-enterprise is not set") - } - - ctx := suite.Environment().DefaultContext(t) - - helmValues := map[string]string{ - "global.enableConsulNamespaces": strconv.FormatBool(c.namespaceMirroring), - "global.acls.manageSystemACLs": strconv.FormatBool(c.secure), - "global.tls.enabled": strconv.FormatBool(c.secure), - "global.logLevel": "trace", - "connectInject.enabled": "true", - "connectInject.consulNamespaces.mirroringK8S": strconv.FormatBool(c.namespaceMirroring), - } - - releaseName := helpers.RandomName() - consulCluster := consul.NewHelmCluster(t, helmValues, ctx, cfg, releaseName) - - consulCluster.Create(t) - - serviceNamespace, serviceK8SOptions := createNamespace(t, ctx, cfg) - certificateNamespace, certificateK8SOptions := createNamespace(t, ctx, cfg) - gatewayNamespace, gatewayK8SOptions := createNamespace(t, ctx, cfg) - routeNamespace, routeK8SOptions := createNamespace(t, ctx, cfg) - - logger.Logf(t, "creating target server in %s namespace", serviceNamespace) - k8s.DeployKustomize(t, serviceK8SOptions, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") - - logger.Logf(t, "creating certificate resources in %s namespace", certificateNamespace) - applyFixture(t, cfg, certificateK8SOptions, "cases/api-gateways/certificate") - - logger.Logf(t, "creating gateway in %s namespace", gatewayNamespace) - applyFixture(t, cfg, gatewayK8SOptions, "cases/api-gateways/gateway") - - logger.Logf(t, "creating route resources in %s namespace", routeNamespace) - applyFixture(t, cfg, routeK8SOptions, "cases/api-gateways/httproute") - - // patch certificate with data - logger.Log(t, "patching certificate with generated data") - certificate := generateCertificate(t, nil, "gateway.test.local") - k8s.RunKubectl(t, certificateK8SOptions, "patch", "secret", "certificate", "-p", fmt.Sprintf(`{"data":{"tls.crt":"%s","tls.key":"%s"}}`, base64.StdEncoding.EncodeToString(certificate.CertPEM), base64.StdEncoding.EncodeToString(certificate.PrivateKeyPEM)), "--type=merge") - - // patch the resources to reference each other - logger.Log(t, "patching gateway to certificate") - k8s.RunKubectl(t, gatewayK8SOptions, "patch", "gateway", "gateway", "-p", fmt.Sprintf(`{"spec":{"listeners":[{"protocol":"HTTPS","port":8082,"name":"https","tls":{"certificateRefs":[{"name":"certificate","namespace":"%s"}]},"allowedRoutes":{"namespaces":{"from":"All"}}}]}}`, certificateNamespace), "--type=merge") - - logger.Log(t, "patching route to target server") - k8s.RunKubectl(t, routeK8SOptions, "patch", "httproute", "route", "-p", fmt.Sprintf(`{"spec":{"rules":[{"backendRefs":[{"name":"static-server","namespace":"%s","port":80}]}]}}`, serviceNamespace), "--type=merge") - - logger.Log(t, "patching route to gateway") - k8s.RunKubectl(t, routeK8SOptions, "patch", "httproute", "route", "-p", fmt.Sprintf(`{"spec":{"parentRefs":[{"name":"gateway","namespace":"%s"}]}}`, gatewayNamespace), "--type=merge") - - // Grab a kubernetes and consul client so that we can verify binding - // behavior prior to issuing requests through the gateway. - k8sClient := ctx.ControllerRuntimeClient(t) - consulClient, _ := consulCluster.SetupConsulClient(t, c.secure) - - retryCheck(t, 120, func(r *retry.R) { - var gateway gwv1beta1.Gateway - err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway", Namespace: gatewayNamespace}, &gateway) - require.NoError(r, err) - - // check our statuses - checkStatusCondition(r, gateway.Status.Conditions, trueCondition("Accepted", "Accepted")) - checkStatusCondition(r, gateway.Status.Conditions, falseCondition("Programmed", "Pending")) - // we expect a sync error here since dropping the listener means the gateway is now invalid - checkStatusCondition(r, gateway.Status.Conditions, falseCondition("Synced", "SyncError")) - - require.Len(r, gateway.Status.Listeners, 1) - require.EqualValues(r, 1, gateway.Status.Listeners[0].AttachedRoutes) - checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, trueCondition("Accepted", "Accepted")) - checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, falseCondition("Conflicted", "NoConflicts")) - checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, falseCondition("ResolvedRefs", "RefNotPermitted")) - }) - - // since the sync operation should fail above, check that we don't have the entry in Consul. - checkConsulNotExists(t, consulClient, api.APIGateway, "gateway", namespaceForConsul(c.namespaceMirroring, gatewayNamespace)) - - // route failure - retryCheck(t, 60, func(r *retry.R) { - var httproute gwv1beta1.HTTPRoute - err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "route", Namespace: routeNamespace}, &httproute) - require.NoError(r, err) - - require.Len(r, httproute.Status.Parents, 1) - require.EqualValues(r, gatewayClassControllerName, httproute.Status.Parents[0].ControllerName) - require.EqualValues(r, "gateway", httproute.Status.Parents[0].ParentRef.Name) - require.NotNil(r, httproute.Status.Parents[0].ParentRef.Namespace) - require.EqualValues(r, gatewayNamespace, *httproute.Status.Parents[0].ParentRef.Namespace) - checkStatusCondition(r, httproute.Status.Parents[0].Conditions, trueCondition("Accepted", "Accepted")) - checkStatusCondition(r, httproute.Status.Parents[0].Conditions, falseCondition("ResolvedRefs", "RefNotPermitted")) - }) - - // we only sync validly referenced certificates over, so check to make sure it is not created. - checkConsulNotExists(t, consulClient, api.InlineCertificate, "certificate", namespaceForConsul(c.namespaceMirroring, certificateNamespace)) - - // now create reference grants - createReferenceGrant(t, k8sClient, "gateway-certificate", gatewayNamespace, certificateNamespace) - createReferenceGrant(t, k8sClient, "route-service", routeNamespace, serviceNamespace) - - // gateway updated with references allowed - retryCheck(t, 60, func(r *retry.R) { - var gateway gwv1beta1.Gateway - err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway", Namespace: gatewayNamespace}, &gateway) - require.NoError(r, err) - - // check our statuses - checkStatusCondition(r, gateway.Status.Conditions, trueCondition("Accepted", "Accepted")) - checkStatusCondition(r, gateway.Status.Conditions, trueCondition("Programmed", "Programmed")) - checkStatusCondition(r, gateway.Status.Conditions, trueCondition("Synced", "Synced")) - require.Len(r, gateway.Status.Listeners, 1) - require.EqualValues(r, 1, gateway.Status.Listeners[0].AttachedRoutes) - checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, trueCondition("Accepted", "Accepted")) - checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, falseCondition("Conflicted", "NoConflicts")) - checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) - }) - - // check the Consul gateway is updated, with the listener. - retryCheck(t, 30, func(r *retry.R) { - entry, _, err := consulClient.ConfigEntries().Get(api.APIGateway, "gateway", &api.QueryOptions{ - Namespace: namespaceForConsul(c.namespaceMirroring, gatewayNamespace), - }) - require.NoError(r, err) - gateway := entry.(*api.APIGatewayConfigEntry) - - require.EqualValues(r, "gateway", gateway.Meta["k8s-name"]) - require.EqualValues(r, gatewayNamespace, gateway.Meta["k8s-namespace"]) - require.Len(r, gateway.Listeners, 1) - checkConsulStatusCondition(t, gateway.Status.Conditions, trueConsulCondition("Accepted", "Accepted")) - checkConsulStatusCondition(t, gateway.Status.Conditions, trueConsulCondition("ResolvedRefs", "ResolvedRefs")) - }) - - // route updated with gateway and services allowed - retryCheck(t, 30, func(r *retry.R) { - var httproute gwv1beta1.HTTPRoute - err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "route", Namespace: routeNamespace}, &httproute) - require.NoError(r, err) - - require.Len(r, httproute.Status.Parents, 1) - require.EqualValues(r, gatewayClassControllerName, httproute.Status.Parents[0].ControllerName) - require.EqualValues(r, "gateway", httproute.Status.Parents[0].ParentRef.Name) - require.NotNil(r, httproute.Status.Parents[0].ParentRef.Namespace) - require.EqualValues(r, gatewayNamespace, *httproute.Status.Parents[0].ParentRef.Namespace) - checkStatusCondition(r, httproute.Status.Parents[0].Conditions, trueCondition("Accepted", "Accepted")) - checkStatusCondition(r, httproute.Status.Parents[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) - }) - - // now check to make sure that the route is updated and valid - retryCheck(t, 30, func(r *retry.R) { - // since we're not bound, check to make sure that the route doesn't target the gateway in Consul. - entry, _, err := consulClient.ConfigEntries().Get(api.HTTPRoute, "route", &api.QueryOptions{ - Namespace: namespaceForConsul(c.namespaceMirroring, routeNamespace), - }) - require.NoError(r, err) - route := entry.(*api.HTTPRouteConfigEntry) - - require.EqualValues(r, "route", route.Meta["k8s-name"]) - require.EqualValues(r, routeNamespace, route.Meta["k8s-namespace"]) - require.Len(r, route.Parents, 1) - }) - - // and check to make sure that the certificate exists - retryCheck(t, 30, func(r *retry.R) { - entry, _, err := consulClient.ConfigEntries().Get(api.InlineCertificate, "certificate", &api.QueryOptions{ - Namespace: namespaceForConsul(c.namespaceMirroring, certificateNamespace), - }) - require.NoError(r, err) - certificate := entry.(*api.InlineCertificateConfigEntry) - - require.EqualValues(r, "certificate", certificate.Meta["k8s-name"]) - require.EqualValues(r, certificateNamespace, certificate.Meta["k8s-namespace"]) - }) - }) - } -} - -func applyFixture(t *testing.T, cfg *config.TestConfig, k8sOptions *terratestk8s.KubectlOptions, fixture string) { - t.Helper() - - out, err := k8s.RunKubectlAndGetOutputE(t, k8sOptions, "apply", "-k", path.Join("../fixtures", fixture)) - require.NoError(t, err, out) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.RunKubectlAndGetOutputE(t, k8sOptions, "delete", "-k", path.Join("../fixtures", fixture)) - }) -} - -func createNamespace(t *testing.T, ctx environment.TestContext, cfg *config.TestConfig) (string, *terratestk8s.KubectlOptions) { - t.Helper() - - namespace := helpers.RandomName() - - logger.Logf(t, "creating Kubernetes namespace %s", namespace) - k8s.RunKubectl(t, ctx.KubectlOptions(t), "create", "ns", namespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "ns", namespace) - }) - - return namespace, &terratestk8s.KubectlOptions{ - ContextName: ctx.KubectlOptions(t).ContextName, - ConfigPath: ctx.KubectlOptions(t).ConfigPath, - Namespace: namespace, - } -} - -type certificateInfo struct { - Cert *x509.Certificate - PrivateKey *rsa.PrivateKey - CertPEM []byte - PrivateKeyPEM []byte -} - -func generateCertificate(t *testing.T, ca *certificateInfo, commonName string) *certificateInfo { - t.Helper() - - bits := 2048 - privateKey, err := rsa.GenerateKey(rand.Reader, bits) - require.NoError(t, err) - - usage := x509.KeyUsageDigitalSignature - if ca == nil { - usage = x509.KeyUsageCertSign - } - - expiration := time.Now().AddDate(10, 0, 0) - cert := &x509.Certificate{ - SerialNumber: big.NewInt(1), - Subject: pkix.Name{ - Organization: []string{"Testing, INC."}, - Country: []string{"US"}, - Province: []string{""}, - Locality: []string{"San Francisco"}, - StreetAddress: []string{"Fake Street"}, - PostalCode: []string{"11111"}, - CommonName: commonName, - }, - IsCA: ca == nil, - NotBefore: time.Now().Add(-10 * time.Minute), - NotAfter: expiration, - SubjectKeyId: []byte{1, 2, 3, 4, 6}, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, - KeyUsage: usage, - BasicConstraintsValid: true, - } - caCert := cert - if ca != nil { - caCert = ca.Cert - } - caPrivateKey := privateKey - if ca != nil { - caPrivateKey = ca.PrivateKey - } - data, err := x509.CreateCertificate(rand.Reader, cert, caCert, &privateKey.PublicKey, caPrivateKey) - require.NoError(t, err) - - certBytes := pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: data, - }) - - privateKeyBytes := pem.EncodeToMemory(&pem.Block{ - Type: "RSA PRIVATE KEY", - Bytes: x509.MarshalPKCS1PrivateKey(privateKey), - }) - - return &certificateInfo{ - Cert: cert, - CertPEM: certBytes, - PrivateKey: privateKey, - PrivateKeyPEM: privateKeyBytes, - } -} - -func retryCheck(t *testing.T, count int, fn func(r *retry.R)) { - retryCheckWithWait(t, count, 2*time.Second, fn) -} - -func retryCheckWithWait(t *testing.T, count int, wait time.Duration, fn func(r *retry.R)) { - t.Helper() - - counter := &retry.Counter{Count: count, Wait: wait} - retry.RunWith(counter, t, fn) -} - -func createReferenceGrant(t *testing.T, client client.Client, name, from, to string) { - t.Helper() - - // we just create a reference grant for all combinations in the given namespaces - - require.NoError(t, client.Create(context.Background(), &gwv1beta1.ReferenceGrant{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: to, - }, - Spec: gwv1beta1.ReferenceGrantSpec{ - From: []gwv1beta1.ReferenceGrantFrom{{ - Group: gatewayGroup, - Kind: gatewayKind, - Namespace: gwv1beta1.Namespace(from), - }, { - Group: gatewayGroup, - Kind: httpRouteKind, - Namespace: gwv1beta1.Namespace(from), - }, { - Group: gatewayGroup, - Kind: tcpRouteKind, - Namespace: gwv1beta1.Namespace(from), - }}, - To: []gwv1beta1.ReferenceGrantTo{{ - Group: gatewayGroup, - Kind: gatewayKind, - }, { - Kind: serviceKind, - }, { - Group: consulGroup, - Kind: meshServiceKind, - }, { - Kind: secretKind, - }}, - }, - })) -} - -func namespaceForConsul(namespaceMirroringEnabled bool, namespace string) string { - if namespaceMirroringEnabled { - return namespace - } - return "" -} diff --git a/acceptance/tests/api-gateway/api_gateway_test.go b/acceptance/tests/api-gateway/api_gateway_test.go deleted file mode 100644 index df4a097b7e..0000000000 --- a/acceptance/tests/api-gateway/api_gateway_test.go +++ /dev/null @@ -1,588 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package apigateway - -import ( - "context" - "encoding/base64" - "fmt" - "strconv" - "testing" - "time" - - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - - "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" - "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" - "github.com/hashicorp/consul-k8s/acceptance/framework/logger" - "github.com/hashicorp/consul/api" - "github.com/hashicorp/consul/sdk/testutil/retry" - - "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -const ( - StaticClientName = "static-client" - gatewayClassControllerName = "consul.hashicorp.com/gateway-controller" - gatewayClassFinalizer = "gateway-exists-finalizer.consul.hashicorp.com" - gatewayFinalizer = "gateway-finalizer.consul.hashicorp.com" -) - -// Test that api gateway basic functionality works in a default installation and a secure installation. -func TestAPIGateway_Basic(t *testing.T) { - cases := []struct { - secure bool - }{ - { - secure: false, - }, - { - secure: true, - }, - } - for _, c := range cases { - name := fmt.Sprintf("secure: %t", c.secure) - t.Run(name, func(t *testing.T) { - ctx := suite.Environment().DefaultContext(t) - cfg := suite.Config() - helmValues := map[string]string{ - "connectInject.enabled": "true", - "global.acls.manageSystemACLs": strconv.FormatBool(c.secure), - "global.tls.enabled": strconv.FormatBool(c.secure), - "global.logLevel": "trace", - } - - releaseName := helpers.RandomName() - consulCluster := consul.NewHelmCluster(t, helmValues, ctx, cfg, releaseName) - - consulCluster.Create(t) - - // Override the default proxy config settings for this test - consulClient, _ := consulCluster.SetupConsulClient(t, c.secure) - _, _, err := consulClient.ConfigEntries().Set(&api.ProxyConfigEntry{ - Kind: api.ProxyDefaults, - Name: api.ProxyConfigGlobal, - Config: map[string]interface{}{ - "protocol": "http", - }, - }, nil) - require.NoError(t, err) - - logger.Log(t, "creating api-gateway resources") - out, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "apply", "-k", "../fixtures/bases/api-gateway") - require.NoError(t, err, out) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - // Ignore errors here because if the test ran as expected - // the custom resources will have been deleted. - k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-k", "../fixtures/bases/api-gateway") - }) - - // Create certificate secret, we do this separately since - // applying the secret will make an invalid certificate that breaks other tests - logger.Log(t, "creating certificate secret") - out, err = k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "apply", "-f", "../fixtures/bases/api-gateway/certificate.yaml") - require.NoError(t, err, out) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - // Ignore errors here because if the test ran as expected - // the custom resources will have been deleted. - k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-f", "../fixtures/bases/api-gateway/certificate.yaml") - }) - - // patch certificate with data - logger.Log(t, "patching certificate secret with generated data") - certificate := generateCertificate(t, nil, "gateway.test.local") - k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "secret", "certificate", "-p", fmt.Sprintf(`{"data":{"tls.crt":"%s","tls.key":"%s"}}`, base64.StdEncoding.EncodeToString(certificate.CertPEM), base64.StdEncoding.EncodeToString(certificate.PrivateKeyPEM)), "--type=merge") - - logger.Log(t, "creating target http server") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") - - // We use the static-client pod so that we can make calls to the api gateway - // via kubectl exec without needing a route into the cluster from the test machine. - logger.Log(t, "creating static-client pod") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-client") - - logger.Log(t, "patching route to target http server") - k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "httproute", "http-route", "-p", `{"spec":{"rules":[{"backendRefs":[{"name":"static-server","port":80}]}]}}`, "--type=merge") - - logger.Log(t, "creating target tcp server") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-server-tcp") - - logger.Log(t, "creating tcp-route") - k8s.RunKubectl(t, ctx.KubectlOptions(t), "apply", "-f", "../fixtures/cases/api-gateways/tcproute/route.yaml") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - // Ignore errors here because if the test ran as expected - // the custom resources will have been deleted. - k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-f", "../fixtures/cases/api-gateways/tcproute/route.yaml") - }) - - // Grab a kubernetes client so that we can verify binding - // behavior prior to issuing requests through the gateway. - k8sClient := ctx.ControllerRuntimeClient(t) - - // On startup, the controller can take upwards of 1m to perform - // leader election so we may need to wait a long time for - // the reconcile loop to run (hence the timeout here). - var gatewayAddress string - counter := &retry.Counter{Count: 120, Wait: 2 * time.Second} - retry.RunWith(counter, t, func(r *retry.R) { - var gateway gwv1beta1.Gateway - err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway", Namespace: "default"}, &gateway) - require.NoError(r, err) - - // check our finalizers - require.Len(r, gateway.Finalizers, 1) - require.EqualValues(r, gatewayFinalizer, gateway.Finalizers[0]) - - // check our statuses - checkStatusCondition(r, gateway.Status.Conditions, trueCondition("Accepted", "Accepted")) - checkStatusCondition(r, gateway.Status.Conditions, trueCondition("ConsulAccepted", "Accepted")) - require.Len(r, gateway.Status.Listeners, 3) - - require.EqualValues(r, 1, gateway.Status.Listeners[0].AttachedRoutes) - checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, trueCondition("Accepted", "Accepted")) - checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, falseCondition("Conflicted", "NoConflicts")) - checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) - require.EqualValues(r, 1, gateway.Status.Listeners[1].AttachedRoutes) - checkStatusCondition(r, gateway.Status.Listeners[1].Conditions, trueCondition("Accepted", "Accepted")) - checkStatusCondition(r, gateway.Status.Listeners[1].Conditions, falseCondition("Conflicted", "NoConflicts")) - checkStatusCondition(r, gateway.Status.Listeners[1].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) - require.EqualValues(r, 1, gateway.Status.Listeners[2].AttachedRoutes) - checkStatusCondition(r, gateway.Status.Listeners[2].Conditions, trueCondition("Accepted", "Accepted")) - checkStatusCondition(r, gateway.Status.Listeners[2].Conditions, falseCondition("Conflicted", "NoConflicts")) - checkStatusCondition(r, gateway.Status.Listeners[2].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) - - // check that we have an address to use - require.Len(r, gateway.Status.Addresses, 1) - // now we know we have an address, set it so we can use it - gatewayAddress = gateway.Status.Addresses[0].Value - }) - - // now that we've satisfied those assertions, we know reconciliation is done - // so we can run assertions on the routes and the other objects - - // gateway class checks - var gatewayClass gwv1beta1.GatewayClass - err = k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway-class"}, &gatewayClass) - require.NoError(t, err) - - // check our finalizers - require.Len(t, gatewayClass.Finalizers, 1) - require.EqualValues(t, gatewayClassFinalizer, gatewayClass.Finalizers[0]) - - // http route checks - var httproute gwv1beta1.HTTPRoute - err = k8sClient.Get(context.Background(), types.NamespacedName{Name: "http-route", Namespace: "default"}, &httproute) - require.NoError(t, err) - - // check our finalizers - require.Len(t, httproute.Finalizers, 1) - require.EqualValues(t, gatewayFinalizer, httproute.Finalizers[0]) - - // check parent status - require.Len(t, httproute.Status.Parents, 1) - require.EqualValues(t, gatewayClassControllerName, httproute.Status.Parents[0].ControllerName) - require.EqualValues(t, "gateway", httproute.Status.Parents[0].ParentRef.Name) - checkStatusCondition(t, httproute.Status.Parents[0].Conditions, trueCondition("Accepted", "Accepted")) - checkStatusCondition(t, httproute.Status.Parents[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) - checkStatusCondition(t, httproute.Status.Parents[0].Conditions, trueCondition("ConsulAccepted", "Accepted")) - - // tcp route checks - var tcpRoute gwv1alpha2.TCPRoute - err = k8sClient.Get(context.Background(), types.NamespacedName{Name: "tcp-route", Namespace: "default"}, &tcpRoute) - require.NoError(t, err) - - // check our finalizers - require.Len(t, tcpRoute.Finalizers, 1) - require.EqualValues(t, gatewayFinalizer, tcpRoute.Finalizers[0]) - - // check parent status - require.Len(t, tcpRoute.Status.Parents, 1) - require.EqualValues(t, gatewayClassControllerName, tcpRoute.Status.Parents[0].ControllerName) - require.EqualValues(t, "gateway", tcpRoute.Status.Parents[0].ParentRef.Name) - checkStatusCondition(t, tcpRoute.Status.Parents[0].Conditions, trueCondition("Accepted", "Accepted")) - checkStatusCondition(t, tcpRoute.Status.Parents[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) - checkStatusCondition(t, tcpRoute.Status.Parents[0].Conditions, trueCondition("ConsulAccepted", "Accepted")) - - // check that the Consul entries were created - var gateway *api.APIGatewayConfigEntry - var httpRoute *api.HTTPRouteConfigEntry - var route *api.TCPRouteConfigEntry - retry.RunWith(counter, t, func(r *retry.R) { - entry, _, err := consulClient.ConfigEntries().Get(api.APIGateway, "gateway", nil) - require.NoError(r, err) - gateway = entry.(*api.APIGatewayConfigEntry) - - entry, _, err = consulClient.ConfigEntries().Get(api.HTTPRoute, "http-route", nil) - require.NoError(r, err) - httpRoute = entry.(*api.HTTPRouteConfigEntry) - - entry, _, err = consulClient.ConfigEntries().Get(api.TCPRoute, "tcp-route", nil) - require.NoError(r, err) - route = entry.(*api.TCPRouteConfigEntry) - }) - - // now check the gateway status conditions - checkConsulStatusCondition(t, gateway.Status.Conditions, trueConsulCondition("Accepted", "Accepted")) - - // and the route status conditions - checkConsulStatusCondition(t, httpRoute.Status.Conditions, trueConsulCondition("Bound", "Bound")) - checkConsulStatusCondition(t, route.Status.Conditions, trueConsulCondition("Bound", "Bound")) - - // finally we check that we can actually route to the service via the gateway - k8sOptions := ctx.KubectlOptions(t) - targetHTTPAddress := fmt.Sprintf("http://%s", gatewayAddress) - targetHTTPSAddress := fmt.Sprintf("https://%s", gatewayAddress) - targetTCPAddress := fmt.Sprintf("http://%s:81", gatewayAddress) - - if c.secure { - // check that intentions keep our connection from happening - k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, targetHTTPAddress) - - k8s.CheckStaticServerConnectionFailing(t, k8sOptions, StaticClientName, targetTCPAddress) - - k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, "-k", targetHTTPSAddress) - - // Now we create the allow intention. - _, _, err = consulClient.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{ - Kind: api.ServiceIntentions, - Name: "static-server", - Sources: []*api.SourceIntention{ - { - Name: "gateway", - Action: api.IntentionActionAllow, - }, - }, - }, nil) - require.NoError(t, err) - - // Now we create the allow intention tcp. - _, _, err = consulClient.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{ - Kind: api.ServiceIntentions, - Name: "static-server-tcp", - Sources: []*api.SourceIntention{ - { - Name: "gateway", - Action: api.IntentionActionAllow, - }, - }, - }, nil) - require.NoError(t, err) - } - - // Test that we can make a call to the api gateway - // via the static-client pod. It should route to the static-server pod. - logger.Log(t, "trying calls to api gateway http") - k8s.CheckStaticServerConnectionSuccessful(t, k8sOptions, StaticClientName, targetHTTPAddress) - - logger.Log(t, "trying calls to api gateway tcp") - k8s.CheckStaticServerConnectionSuccessful(t, k8sOptions, StaticClientName, targetTCPAddress) - - logger.Log(t, "trying calls to api gateway https") - k8s.CheckStaticServerConnectionSuccessful(t, k8sOptions, StaticClientName, targetHTTPSAddress, "-k") - }) - } -} - -func TestAPIGateway_JWTAuth_Basic(t *testing.T) { - t.Skip("skipping this test until GW JWT auth is complete") - ctx := suite.Environment().DefaultContext(t) - cfg := suite.Config() - - if !cfg.EnableEnterprise { - t.Skipf("skipping this test because -enable-enterprise is not set") - } - - helmValues := map[string]string{ - "connectInject.enabled": "true", - "connectInject.consulNamespaces.mirroringK8S": "true", - "global.acls.manageSystemACLs": "true", // acls must be enabled for JWT auth to take place - "global.tls.enabled": "true", - "global.logLevel": "trace", - } - - releaseName := helpers.RandomName() - consulCluster := consul.NewHelmCluster(t, helmValues, ctx, cfg, releaseName) - - consulCluster.Create(t) - - // Override the default proxy config settings for this test - consulClient, _ := consulCluster.SetupConsulClient(t, true) - _, _, err := consulClient.ConfigEntries().Set(&api.ProxyConfigEntry{ - Kind: api.ProxyDefaults, - Name: api.ProxyConfigGlobal, - Config: map[string]interface{}{ - "protocol": "http", - }, - }, nil) - require.NoError(t, err) - - logger.Log(t, "creating api-gateway resources") - out, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "apply", "-k", "../fixtures/cases/api-gateways/jwt-auth") - require.NoError(t, err, out) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - // Ignore errors here because if the test ran as expected - // the custom resources will have been deleted. - k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-k", "../fixtures/cases/api-gateways/jwt-auth") - }) - - // Create certificate secret, we do this separately since - // applying the secret will make an invalid certificate that breaks other tests - logger.Log(t, "creating certificate secret") - out, err = k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "apply", "-f", "../fixtures/bases/api-gateway/certificate.yaml") - require.NoError(t, err, out) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - // Ignore errors here because if the test ran as expected - // the custom resources will have been deleted. - k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-f", "../fixtures/bases/api-gateway/certificate.yaml") - }) - - // patch certificate with data - logger.Log(t, "patching certificate secret with generated data") - certificate := generateCertificate(t, nil, "gateway.test.local") - k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "secret", "certificate", "-p", fmt.Sprintf(`{"data":{"tls.crt":"%s","tls.key":"%s"}}`, base64.StdEncoding.EncodeToString(certificate.CertPEM), base64.StdEncoding.EncodeToString(certificate.PrivateKeyPEM)), "--type=merge") - - // We use the static-client pod so that we can make calls to the api gateway - // via kubectl exec without needing a route into the cluster from the test machine. - logger.Log(t, "creating static-client pod") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-client") - - k8s.RunKubectl(t, ctx.KubectlOptions(t), "wait", "--for=condition=available", "--timeout=5m", fmt.Sprintf("deploy/%s", "static-server")) - // Grab a kubernetes client so that we can verify binding - // behavior prior to issuing requests through the gateway. - k8sClient := ctx.ControllerRuntimeClient(t) - - // On startup, the controller can take upwards of 1m to perform - // leader election so we may need to wait a long time for - // the reconcile loop to run (hence the 2m timeout here). - var ( - gatewayAddress string - gatewayClass gwv1beta1.GatewayClass - httpRoute gwv1beta1.HTTPRoute - httpRouteAuth gwv1beta1.HTTPRoute - ) - - counter := &retry.Counter{Count: 60, Wait: 2 * time.Second} - retry.RunWith(counter, t, func(r *retry.R) { - var gateway gwv1beta1.Gateway - err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway", Namespace: "default"}, &gateway) - require.NoError(r, err) - - // check our finalizers - require.Len(r, gateway.Finalizers, 1) - require.EqualValues(r, gatewayFinalizer, gateway.Finalizers[0]) - - // check our statuses - checkStatusCondition(r, gateway.Status.Conditions, trueCondition("Accepted", "Accepted")) - checkStatusCondition(r, gateway.Status.Conditions, trueCondition("ConsulAccepted", "Accepted")) - require.Len(r, gateway.Status.Listeners, 4) - - require.EqualValues(r, 1, gateway.Status.Listeners[0].AttachedRoutes) - checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, trueCondition("Accepted", "Accepted")) - checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, falseCondition("Conflicted", "NoConflicts")) - checkStatusCondition(r, gateway.Status.Listeners[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) - require.EqualValues(r, 1, gateway.Status.Listeners[1].AttachedRoutes) - checkStatusCondition(r, gateway.Status.Listeners[1].Conditions, trueCondition("Accepted", "Accepted")) - checkStatusCondition(r, gateway.Status.Listeners[1].Conditions, falseCondition("Conflicted", "NoConflicts")) - checkStatusCondition(r, gateway.Status.Listeners[1].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) - - // check that we have an address to use - require.Len(r, gateway.Status.Addresses, 1) - // now we know we have an address, set it so we can use it - gatewayAddress = gateway.Status.Addresses[0].Value - - // gateway class checks - err = k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway-class"}, &gatewayClass) - require.NoError(r, err) - - // check our finalizers - require.Len(r, gatewayClass.Finalizers, 1) - require.EqualValues(r, gatewayClassFinalizer, gatewayClass.Finalizers[0]) - - // http route checks - err = k8sClient.Get(context.Background(), types.NamespacedName{Name: "http-route", Namespace: "default"}, &httpRoute) - require.NoError(r, err) - - // http route checks - err = k8sClient.Get(context.Background(), types.NamespacedName{Name: "http-route-auth", Namespace: "default"}, &httpRouteAuth) - require.NoError(r, err) - - // check our finalizers - require.Len(r, httpRoute.Finalizers, 1) - require.EqualValues(r, gatewayFinalizer, httpRoute.Finalizers[0]) - - // check parent status - require.Len(r, httpRoute.Status.Parents, 1) - require.EqualValues(r, gatewayClassControllerName, httpRoute.Status.Parents[0].ControllerName) - require.EqualValues(r, "gateway", httpRoute.Status.Parents[0].ParentRef.Name) - checkStatusCondition(r, httpRoute.Status.Parents[0].Conditions, trueCondition("Accepted", "Accepted")) - checkStatusCondition(r, httpRoute.Status.Parents[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) - checkStatusCondition(r, httpRoute.Status.Parents[0].Conditions, trueCondition("ConsulAccepted", "Accepted")) - - // check our finalizers - require.Len(r, httpRouteAuth.Finalizers, 1) - require.EqualValues(r, gatewayFinalizer, httpRouteAuth.Finalizers[0]) - - // check parent status - require.Len(r, httpRouteAuth.Status.Parents, 1) - require.EqualValues(r, gatewayClassControllerName, httpRouteAuth.Status.Parents[0].ControllerName) - require.EqualValues(r, "gateway", httpRouteAuth.Status.Parents[0].ParentRef.Name) - checkStatusCondition(r, httpRouteAuth.Status.Parents[0].Conditions, trueCondition("Accepted", "Accepted")) - checkStatusCondition(r, httpRouteAuth.Status.Parents[0].Conditions, trueCondition("ResolvedRefs", "ResolvedRefs")) - checkStatusCondition(r, httpRouteAuth.Status.Parents[0].Conditions, trueCondition("ConsulAccepted", "Accepted")) - }) - - // check that the Consul entries were created - entry, _, err := consulClient.ConfigEntries().Get(api.APIGateway, "gateway", nil) - require.NoError(t, err) - gateway := entry.(*api.APIGatewayConfigEntry) - - entry, _, err = consulClient.ConfigEntries().Get(api.HTTPRoute, "http-route", nil) - require.NoError(t, err) - consulHTTPRoute := entry.(*api.HTTPRouteConfigEntry) - - entry, _, err = consulClient.ConfigEntries().Get(api.HTTPRoute, "http-route-auth", nil) - require.NoError(t, err) - consulHTTPRouteAuth := entry.(*api.HTTPRouteConfigEntry) - - // now check the gateway status conditions - checkConsulStatusCondition(t, gateway.Status.Conditions, trueConsulCondition("Accepted", "Accepted")) - - // and the route status conditions - checkConsulStatusCondition(t, consulHTTPRoute.Status.Conditions, trueConsulCondition("Bound", "Bound")) - checkConsulStatusCondition(t, consulHTTPRouteAuth.Status.Conditions, trueConsulCondition("Bound", "Bound")) - - // finally we check that we can actually route to the service(s) via the gateway - k8sOptions := ctx.KubectlOptions(t) - targetHTTPAddress := fmt.Sprintf("http://%s/v1", gatewayAddress) - targetHTTPAddressAdmin := fmt.Sprintf("http://%s:8080/admin", gatewayAddress) - targetHTTPAddressPet := fmt.Sprintf("http://%s:8080/pet", gatewayAddress) - // valid JWT token with role of "doctor" - doctorToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJQUzI1NiIsImtpZCI6IkMtRTFuQ2p3Z0JDLVB1R00yTzQ2N0ZSRGhLeDhBa1ZjdElTQWJvM3JpZXcifQ.eyJpc3MiOiJsb2NhbCIsInJvbGUiOiJkb2N0b3IifQ.FfgpzjMf8Evh6K-fJ1cLXklfIXOm-vojVbWlPPbGVFtzxZ9hxMxoyAY_G8i36SfGrpUlp-RJ6ohMvprMrEgyRgbenu7u5kkm5iGHW-zpMus4izXRxPELBcpWOGF105HIssT2NYRstXieNR8EVzvGfLdvR0GW8ttEERgseqGvuAfdb4-aNYsysGwUUHbsZjazA6H1rZmWqHdCLOJ2ZwFsIdckO9CadnkyTILpcPUmLYyUVJdtlLGOySb0GG8c_dPML_IR5jSXCSUZt6S2JBNBNBdqukrlqpA-fIaaWft0dbWVMhv8DqPC8znult8dKvLZ1qXeU0itsqqJUyE16ihJjw" - // valid JWT token with role of "pet" - petToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJQUzI1NiIsImtpZCI6IkMtRTFuQ2p3Z0JDLVB1R00yTzQ2N0ZSRGhLeDhBa1ZjdElTQWJvM3JpZXcifQ.eyJpc3MiOiJsb2NhbCIsInJvbGUiOiJwZXQifQ.l94rJayGGTMB426HwEw5ipSjaIHjm-UWDHiBAlB_Slmi814AxAfl_0AdRwSz67UDnkoygKbvPpR5xUB03JCXNshLZuKLegWsBeQg_OJYvZGmFagl5NglBFvH7Jbta4e1eQoAxZI6Xyy1jHbu7jFBjQPVnK8EaRvWoW8Pe8a8rp_5xhub0pomhvRF6Pm5kAS4cMnxvqpVc5Oo5nO7ws_SmoNnbt2Ok14k23Zx5E2EWmGStOfbgFsdbhVbepB2DMzqv1j8jvBbwa_OxCwc_7pEOthOOxRV6L3ZjgbRSB4GumlXAOCBYXD1cRLgrMSrWB1GkefAKu8PV0Ho1px6sI9Evg" - - // check that intentions keep our connection from happening - k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, targetHTTPAddress) - k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, targetHTTPAddressAdmin) - k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, "-H", doctorToken, targetHTTPAddressAdmin) - k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, "-H", petToken, targetHTTPAddressAdmin) - - k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, targetHTTPAddressPet) - k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, "-H", doctorToken, targetHTTPAddressPet) - k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, "-H", petToken, targetHTTPAddressPet) - - // Now we create the allow intention. - _, _, err = consulClient.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{ - Kind: api.ServiceIntentions, - Name: "static-server", - Sources: []*api.SourceIntention{ - { - Name: "gateway", - Action: api.IntentionActionAllow, - }, - }, - }, nil) - require.NoError(t, err) - - _, _, err = consulClient.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{ - Kind: api.ServiceIntentions, - Name: "static-server-protected", - Sources: []*api.SourceIntention{ - { - Name: "gateway", - Action: api.IntentionActionAllow, - }, - }, - }, nil) - require.NoError(t, err) - - // Test that we can make a call to the api gateway - logger.Log(t, "trying calls to api gateway http") - k8s.CheckStaticServerConnectionSuccessful(t, k8sOptions, StaticClientName, targetHTTPAddress) - - // ensure that overrides -> route extension -> default by making a request to the admin route with a JWT that has an issuer of "local" and a "role" of "doctor" - // we can see that: - // * the "iss" verification in the gateway override takes precedence over the "iss" verification in the route filter - // * the "role" verification in the route extension takes precedence over the "role" verification in the gateway default - // should fail because we're missing JWT - logger.Log(t, "trying calls to api gateway /admin should fail without JWT token") - k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, targetHTTPAddressAdmin) - - // should fail because we use the token with the wrong role and correct issuer - logger.Log(t, "trying calls to api gateway /admin should fail with wrong role") - k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, "-H", petToken, targetHTTPAddressAdmin) - - // will succeed because we use the token with the correct role and the correct issuer - logger.Log(t, "trying calls to api gateway /admin should succeed with JWT token with correct role") - k8s.CheckStaticServerConnectionSuccessful(t, k8sOptions, StaticClientName, "-H", doctorToken, targetHTTPAddressAdmin) - - // ensure that overrides -> route extension -> default by making a request to the admin route with a JWT that has an issuer of "local" and a "role" of "pet" - // the route does not define - // we can see that: - // * the "iss" verification in the gateway override takes precedence over the "iss" verification in the route filter - // * the "role" verification in the route extension takes precedence over the "role" verification in the gateway default - // should fail because we're missing JWT - logger.Log(t, "trying calls to api gateway /pet should fail without JWT token") - k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, targetHTTPAddressPet) - - // should fail because we use the token with the wrong role and correct issuer - logger.Log(t, "trying calls to api gateway /pet should fail with wrong role") - k8s.CheckStaticServerHTTPConnectionFailing(t, k8sOptions, StaticClientName, "-H", doctorToken, targetHTTPAddressPet) - - // will succeed because we use the token with the correct role and the correct issuer - logger.Log(t, "trying calls to api gateway /pet should succeed with JWT token with correct role") - k8s.CheckStaticServerConnectionSuccessful(t, k8sOptions, StaticClientName, "-H", petToken, targetHTTPAddressPet) -} - -func checkStatusCondition(t require.TestingT, conditions []metav1.Condition, toCheck metav1.Condition) { - for _, c := range conditions { - if c.Type == toCheck.Type { - require.EqualValues(t, toCheck.Reason, c.Reason) - require.EqualValues(t, toCheck.Status, c.Status) - return - } - } - - t.Errorf("expected condition not found: %s", toCheck.Type) -} - -func trueCondition(conditionType, reason string) metav1.Condition { - return metav1.Condition{ - Type: conditionType, - Reason: reason, - Status: metav1.ConditionTrue, - } -} - -func falseCondition(conditionType, reason string) metav1.Condition { - return metav1.Condition{ - Type: conditionType, - Reason: reason, - Status: metav1.ConditionFalse, - } -} - -func checkConsulStatusCondition(t require.TestingT, conditions []api.Condition, toCheck api.Condition) { - for _, c := range conditions { - if c.Type == toCheck.Type { - require.EqualValues(t, toCheck.Reason, c.Reason) - require.EqualValues(t, toCheck.Status, c.Status) - return - } - } - - t.Errorf("expected condition not found: %s", toCheck.Type) -} - -func trueConsulCondition(conditionType, reason string) api.Condition { - return api.Condition{ - Type: conditionType, - Reason: reason, - Status: "True", - } -} diff --git a/acceptance/tests/api-gateway/example_test.go b/acceptance/tests/api-gateway/example_test.go new file mode 100644 index 0000000000..b324ac31fe --- /dev/null +++ b/acceptance/tests/api-gateway/example_test.go @@ -0,0 +1,64 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// Rename package to your test package. +package example + +import ( + "context" + "testing" + + "github.com/hashicorp/consul-k8s/acceptance/framework/consul" + "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" + "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestExample(t *testing.T) { + // Get test configuration. + cfg := suite.Config() + + // Get the default context. + ctx := suite.Environment().DefaultContext(t) + + // Create Helm values for the Helm install. + helmValues := map[string]string{ + "exampleFeature.enabled": "true", + } + + // Generate a random name for this test. + releaseName := helpers.RandomName() + + // Create a new Consul cluster object. + consulCluster := consul.NewHelmCluster(t, helmValues, ctx, cfg, releaseName) + + // Create the Consul cluster with Helm. + consulCluster.Create(t) + + // Make test assertions. + + // To run kubectl commands, you need to get KubectlOptions from the test context. + // There are a number of kubectl commands available in the helpers/kubectl.go file. + // For example, to call 'kubectl apply' from the test write the following: + k8s.KubectlApply(t, ctx.KubectlOptions(t), "path/to/config") + + // Clean up any Kubernetes resources you have created + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.KubectlDelete(t, ctx.KubectlOptions(t), "path/to/config") + }) + + // Similarly, you can obtain Kubernetes client from your test context. + // You can use it to, for example, read all services in a namespace: + k8sClient := ctx.KubernetesClient(t) + services, err := k8sClient.CoreV1().Services(ctx.KubectlOptions(t).Namespace).List(context.Background(), metav1.ListOptions{}) + require.NoError(t, err) + require.NotNil(t, services.Items) + + // To make Consul API calls, you can get the Consul client from the consulCluster object, + // indicating whether the client needs to be secure or not (i.e. whether TLS and ACLs are enabled on the Consul cluster): + consulClient, _ := consulCluster.SetupConsulClient(t, true) + consulServices, _, err := consulClient.Catalog().Services(nil) + require.NoError(t, err) + require.NotNil(t, consulServices) +} diff --git a/acceptance/tests/api-gateway/main_test.go b/acceptance/tests/api-gateway/main_test.go index f408845b3e..f92fff8a59 100644 --- a/acceptance/tests/api-gateway/main_test.go +++ b/acceptance/tests/api-gateway/main_test.go @@ -1,10 +1,10 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package apigateway +// Rename package to your test package. +package example import ( - "os" "testing" testsuite "github.com/hashicorp/consul-k8s/acceptance/framework/suite" @@ -13,6 +13,25 @@ import ( var suite testsuite.Suite func TestMain(m *testing.M) { - suite = testsuite.NewSuite(m) - os.Exit(suite.Run()) + // First, uncomment the line below to create a new suite so that all flags are parsed. + /* + suite = framework.NewSuite(m) + */ + + // If the test suite needs to run only when certain test flags are passed, + // you need to handle that in the TestMain function. + // Uncomment and modify example code below if that is the case. + /* + if suite.Config().EnableExampleFeature { + os.Exit(suite.Run()) + } else { + fmt.Println("Skipping example feature tests because -enable-example-feature is not set") + os.Exit(0) + } + */ + + // If the test suite should run in every case, uncomment the line below. + /* + os.Exit(suite.Run()) + */ } diff --git a/acceptance/tests/basic/basic_test.go b/acceptance/tests/basic/basic_test.go index 4727b53495..91047711a1 100644 --- a/acceptance/tests/basic/basic_test.go +++ b/acceptance/tests/basic/basic_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package basic import ( diff --git a/acceptance/tests/basic/main_test.go b/acceptance/tests/basic/main_test.go index 0e400ef870..fa858ee4ae 100644 --- a/acceptance/tests/basic/main_test.go +++ b/acceptance/tests/basic/main_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package basic import ( diff --git a/acceptance/tests/cli/cli_install_test.go b/acceptance/tests/cli/cli_install_test.go index bb497f913f..1af26d62c7 100644 --- a/acceptance/tests/cli/cli_install_test.go +++ b/acceptance/tests/cli/cli_install_test.go @@ -1,10 +1,6 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package cli import ( - "context" "fmt" "strings" "testing" @@ -16,7 +12,6 @@ import ( "github.com/hashicorp/consul-k8s/acceptance/framework/logger" "github.com/hashicorp/consul/sdk/testutil/retry" "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) const ipv4RegEx = "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)" @@ -26,11 +21,9 @@ const ipv4RegEx = "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9] func TestInstall(t *testing.T) { cases := map[string]struct { secure bool - tproxy bool }{ - "not-secure": {secure: false, tproxy: false}, - "secure": {secure: true, tproxy: false}, - "not-secure-tproxy": {secure: false, tproxy: true}, + "not-secure": {secure: false}, + "secure": {secure: true}, } for name, c := range cases { @@ -39,7 +32,6 @@ func TestInstall(t *testing.T) { require.NoError(t, err) cfg := suite.Config() - cfg.EnableTransparentProxy = c.tproxy ctx := suite.Environment().DefaultContext(t) connHelper := connhelper.ConnectHelper{ @@ -91,39 +83,6 @@ func TestInstall(t *testing.T) { } }) - // Troubleshoot: Get the client pod so we can portForward to it and get the 'troubleshoot upstreams' output - clientPod, err := connHelper.Ctx.KubernetesClient(t).CoreV1().Pods(connHelper.Ctx.KubectlOptions(t).Namespace).List(context.Background(), metav1.ListOptions{ - LabelSelector: "app=static-client", - }) - require.NoError(t, err) - - clientPodName := clientPod.Items[0].Name - upstreamsOut, err := cli.Run(t, ctx.KubectlOptions(t), "troubleshoot", "upstreams", "-pod", clientPodName) - logger.Log(t, string(upstreamsOut)) - require.NoError(t, err) - - if c.tproxy { - // If tproxy is enabled we are looking for the upstream ip which is the ClusterIP of the Kubernetes Service - serverService, err := connHelper.Ctx.KubernetesClient(t).CoreV1().Services(connHelper.Ctx.KubectlOptions(t).Namespace).List(context.Background(), metav1.ListOptions{ - FieldSelector: "metadata.name=static-server", - }) - require.NoError(t, err) - serverIP := serverService.Items[0].Spec.ClusterIP - - proxyOut, err := cli.Run(t, ctx.KubectlOptions(t), "troubleshoot", "proxy", "-pod", clientPodName, "-upstream-ip", serverIP) - require.NoError(t, err) - require.Regexp(t, "Upstream resources are valid", string(proxyOut)) - logger.Log(t, string(proxyOut)) - } else { - // With tproxy disabled and explicit upstreams we need the envoy-id of the server - require.Regexp(t, "static-server", string(upstreamsOut)) - - proxyOut, err := cli.Run(t, ctx.KubectlOptions(t), "troubleshoot", "proxy", "-pod", clientPodName, "-upstream-envoy-id", "static-server") - require.NoError(t, err) - require.Regexp(t, "Upstream resources are valid", string(proxyOut)) - logger.Log(t, string(proxyOut)) - } - connHelper.TestConnectionSuccess(t) connHelper.TestConnectionFailureWhenUnhealthy(t) }) diff --git a/acceptance/tests/cli/cli_upgrade_test.go b/acceptance/tests/cli/cli_upgrade_test.go index 8c8970d88a..6fcf82f738 100644 --- a/acceptance/tests/cli/cli_upgrade_test.go +++ b/acceptance/tests/cli/cli_upgrade_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package cli import ( diff --git a/acceptance/tests/cli/main_test.go b/acceptance/tests/cli/main_test.go index 2e66d89543..85cef25abe 100644 --- a/acceptance/tests/cli/main_test.go +++ b/acceptance/tests/cli/main_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package cli import ( diff --git a/acceptance/tests/cloud/basic_test.go b/acceptance/tests/cloud/basic_test.go deleted file mode 100644 index e17169700a..0000000000 --- a/acceptance/tests/cloud/basic_test.go +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package cloud - -import ( - "crypto/tls" - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "strings" - "testing" - "time" - - terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" - "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/environment" - "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" - "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" - "github.com/hashicorp/consul-k8s/acceptance/framework/logger" - "github.com/hashicorp/serf/testutil/retry" - "github.com/stretchr/testify/require" -) - -type TokenResponse struct { - Token string `json:"token"` -} - -var ( - resourceSecretName = "resource-sec-name" - resourceSecretKey = "resource-sec-key" - resourceSecretKeyValue = "organization/11eb1a35-aac0-f7c7-8fe1-0242ac110008/project/11eb1a35-ab64-d576-8fe1-0242ac110008/hashicorp.consul.global-network-manager.cluster/TEST" - - clientIDSecretName = "clientid-sec-name" - clientIDSecretKey = "clientid-sec-key" - clientIDSecretKeyValue = "clientid" - - clientSecretName = "client-sec-name" - clientSecretKey = "client-sec-key" - clientSecretKeyValue = "client-secret" - - apiHostSecretName = "apihost-sec-name" - apiHostSecretKey = "apihost-sec-key" - apiHostSecretKeyValue = "fake-server:443" - - authUrlSecretName = "authurl-sec-name" - authUrlSecretKey = "authurl-sec-key" - authUrlSecretKeyValue = "https://fake-server:443" - - scadaAddressSecretName = "scadaaddress-sec-name" - scadaAddressSecretKey = "scadaaddress-sec-key" - scadaAddressSecretKeyValue = "fake-server:443" -) - -// The fake-server has a requestToken endpoint to retrieve the token. -func requestToken(endpoint string) (string, error) { - tr := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - } - - client := &http.Client{Transport: tr} - url := fmt.Sprintf("https://%s/token", endpoint) - req, err := http.NewRequest("GET", url, nil) - if err != nil { - fmt.Println("Error creating request:", err) - return "", errors.New("error creating request") - } - - // Perform the request - resp, err := client.Do(req) - if err != nil { - fmt.Println("Error sending request:", err) - return "", errors.New("error making request") - } - defer resp.Body.Close() - - // Read the response body - body, err := io.ReadAll(resp.Body) - if err != nil { - fmt.Println("Error reading response:", err) - return "", errors.New("error reading body") - } - - var tokenResponse TokenResponse - err = json.Unmarshal(body, &tokenResponse) - if err != nil { - fmt.Println("Error parsing response:", err) - return "", errors.New("error parsing body") - } - - return tokenResponse.Token, nil - -} - -func TestBasicCloud(t *testing.T) { - ctx := suite.Environment().DefaultContext(t) - - kubectlOptions := ctx.KubectlOptions(t) - ns := kubectlOptions.Namespace - k8sClient := environment.KubernetesClientFromOptions(t, kubectlOptions) - - cfg := suite.Config() - - if cfg.HCPResourceID != "" { - resourceSecretKeyValue = cfg.HCPResourceID - } - consul.CreateK8sSecret(t, k8sClient, cfg, ns, resourceSecretName, resourceSecretKey, resourceSecretKeyValue) - consul.CreateK8sSecret(t, k8sClient, cfg, ns, clientIDSecretName, clientIDSecretKey, clientIDSecretKeyValue) - consul.CreateK8sSecret(t, k8sClient, cfg, ns, clientSecretName, clientSecretKey, clientSecretKeyValue) - consul.CreateK8sSecret(t, k8sClient, cfg, ns, apiHostSecretName, apiHostSecretKey, apiHostSecretKeyValue) - consul.CreateK8sSecret(t, k8sClient, cfg, ns, authUrlSecretName, authUrlSecretKey, authUrlSecretKeyValue) - consul.CreateK8sSecret(t, k8sClient, cfg, ns, scadaAddressSecretName, scadaAddressSecretKey, scadaAddressSecretKeyValue) - - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/cloud/hcp-mock") - podName, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "get", "pod", "-l", "app=fake-server", "-o", `jsonpath="{.items[0].metadata.name}"`) - podName = strings.ReplaceAll(podName, "\"", "") - if err != nil { - logger.Log(t, "error finding pod name") - return - } - logger.Log(t, "fake-server pod name:"+podName) - localPort := terratestk8s.GetAvailablePort(t) - tunnel := terratestk8s.NewTunnelWithLogger( - ctx.KubectlOptions(t), - terratestk8s.ResourceTypePod, - podName, - localPort, - 443, - logger.TestLogger{}) - defer tunnel.Close() - // Retry creating the port forward since it can fail occasionally. - retry.RunWith(&retry.Counter{Wait: 5 * time.Second, Count: 60}, t, func(r *retry.R) { - // NOTE: It's okay to pass in `t` to ForwardPortE despite being in a retry - // because we're using ForwardPortE (not ForwardPort) so the `t` won't - // get used to fail the test, just for logging. - require.NoError(r, tunnel.ForwardPortE(t)) - }) - - logger.Log(t, "fake-server addr:"+tunnel.Endpoint()) - consulToken, err := requestToken(tunnel.Endpoint()) - if err != nil { - logger.Log(t, "error finding consul token") - return - } - - logger.Log(t, "consul test token :"+consulToken) - - releaseName := helpers.RandomName() - - helmValues := map[string]string{ - "global.imagePullPolicy": "IfNotPresent", - "global.cloud.enabled": "true", - "global.cloud.resourceId.secretName": resourceSecretName, - "global.cloud.resourceId.secretKey": resourceSecretKey, - - "global.cloud.clientId.secretName": clientIDSecretName, - "global.cloud.clientId.secretKey": clientIDSecretKey, - - "global.cloud.clientSecret.secretName": clientSecretName, - "global.cloud.clientSecret.secretKey": clientSecretKey, - - "global.cloud.apiHost.secretName": apiHostSecretName, - "global.cloud.apiHost.secretKey": apiHostSecretKey, - - "global.cloud.authUrl.secretName": authUrlSecretName, - "global.cloud.authUrl.secretKey": authUrlSecretKey, - - "global.cloud.scadaAddress.secretName": scadaAddressSecretName, - "global.cloud.scadaAddress.secretKey": scadaAddressSecretKey, - "connectInject.default": "true", - - // TODO: Follow up with this bug - "global.acls.manageSystemACLs": "false", - "global.gossipEncryption.autoGenerate": "false", - "global.tls.enabled": "true", - "global.tls.enableAutoEncrypt": "true", - // TODO: Take this out - - "telemetryCollector.enabled": "true", - "telemetryCollector.image": cfg.ConsulCollectorImage, - "telemetryCollector.cloud.clientId.secretName": clientIDSecretName, - "telemetryCollector.cloud.clientId.secretKey": clientIDSecretKey, - - "telemetryCollector.cloud.clientSecret.secretName": clientSecretName, - "telemetryCollector.cloud.clientSecret.secretKey": clientSecretKey, - // Either we set the global.trustedCAs (make sure it's idented exactly) or we - // set TLS to insecure - - "telemetryCollector.extraEnvironmentVars.HCP_API_TLS": "insecure", - "telemetryCollector.extraEnvironmentVars.HCP_AUTH_TLS": "insecure", - "telemetryCollector.extraEnvironmentVars.HCP_SCADA_TLS": "insecure", - "telemetryCollector.extraEnvironmentVars.OTLP_EXPORTER_TLS": "insecure", - - "server.extraEnvironmentVars.HCP_API_TLS": "insecure", - "server.extraEnvironmentVars.HCP_AUTH_TLS": "insecure", - "server.extraEnvironmentVars.HCP_SCADA_TLS": "insecure", - - // This is pregenerated CA used for testing. It can be replaced at any time and isn't - // meant for anything other than testing - // "global.trustedCAs[0]": `-----BEGIN CERTIFICATE----- - // MIICrjCCAZYCCQD5LxMcnMY8rDANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQDDA5m - // YWtlLXNlcnZlci1jYTAeFw0yMzA1MTkxMjIwMzhaFw0zMzA1MTYxMjIwMzhaMBkx - // FzAVBgNVBAMMDmZha2Utc2VydmVyLWNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A - // MIIBCgKCAQEAwhbiII7sMultedFzQVhVZz5Ti+9lWrpZb8y0ZR6NaNvoxDPX151t - // Adh5NegSeH/+351iDBGZHhmKECtBuk8FJgk88O7y8A7Yg+/lyeZd0SJTEeiYUe7d - // sSaBTYSmixyn6s15Y5MVp9gM7t2YXrocRkFxDtdhLMWf0zwzJEwDouFMMiFZw5II - // yDbI6UfwKyB8C8ln10+TcczbheaOMQ1jGn35YWAG/LEdutU6DO2Y/GZYQ41nyLF1 - // klqh34USQPVQSQW7R7GiDxyhh1fGaDF6RAzH4RerzQSNvvTHmBXIGurB/Hnu1n3p - // CwWeatWMU5POy1es73S/EPM0NpWD5RabSwIDAQABMA0GCSqGSIb3DQEBCwUAA4IB - // AQBayoTltSW55PvKVp9cmqGOBMlkIMKPd6Ny4bCb/3UF+3bzQmIblh3O3kEt7WoY - // fA9vp+6cSRGVqgBfR2bi40RrerLNA79yywIZjfBMteNuRoul5VeD+mLyFCo4197r - // Atl2TEx2kl2V8rjCsEBcTqKqetVOMLYEZ2tbCeUt1A/K7OzaJfHgelEYcsVt68Q9 - // /BLoo2UXfOpRrcsx7u7s5HPVbG3bx+1MvGJZ2C3i0B6agnkGDzEpoM4KZGxEefB9 - // DOHIJfie9d9BQD52nZh3SGHz0b3vfJ430XrQmaNZ26fuIEyIYrpvyAhBXckj2iTD - // 1TXpqr/1D7EUbddktyhXTK9e - // -----END CERTIFICATE-----`, - } - if cfg.ConsulImage != "" { - helmValues["global.image"] = cfg.ConsulImage - } - if cfg.ConsulCollectorImage != "" { - helmValues["telemetryCollector.image"] = cfg.ConsulCollectorImage - } - - consulCluster := consul.NewHelmCluster(t, helmValues, suite.Environment().DefaultContext(t), suite.Config(), releaseName) - consulCluster.Create(t) - - logger.Log(t, "creating static-server deployment") - - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-server") - t.Log("Finished deployment. Validating expected conditions now") - // Give some time for collector send metrics - time.Sleep(5 * time.Second) - err = validate(tunnel.Endpoint()) - logger.Log(t, fmt.Sprintf("result: %v", err)) - require.NoError(t, err) - -} - -func validate(endpoint string) error { - tr := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - } - - client := &http.Client{Transport: tr} - url := fmt.Sprintf("https://%s/validation", endpoint) - req, err := http.NewRequest("GET", url, nil) - if err != nil { - fmt.Println("Error creating request:", err) - return errors.New("error creating validation request") - } - - // Perform the request - resp, err := client.Do(req) - if err != nil { - fmt.Println("Error sending request:", err) - return errors.New("error making validation request") - } - if resp.StatusCode == http.StatusExpectationFailed { - // Read the response body - body, err := io.ReadAll(resp.Body) - if err != nil { - fmt.Println("Error reading response:", err) - return errors.New("error reading body") - } - var message errMsg - err = json.Unmarshal(body, &message) - if err != nil { - fmt.Println("Error parsing response:", err) - return errors.New("error parsing body") - } - - return fmt.Errorf("Failed validation: %s", message) - } else if resp.StatusCode != http.StatusOK { - return errors.New("unexpected status code response from failure") - } - - return nil - -} - -type errMsg struct { - Error string `json:"error"` -} diff --git a/acceptance/tests/cloud/main_test.go b/acceptance/tests/cloud/main_test.go deleted file mode 100644 index 85d1867933..0000000000 --- a/acceptance/tests/cloud/main_test.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package cloud - -import ( - "os" - "testing" - - testsuite "github.com/hashicorp/consul-k8s/acceptance/framework/suite" -) - -var suite testsuite.Suite - -func TestMain(m *testing.M) { - suite = testsuite.NewSuite(m) - os.Exit(suite.Run()) -} diff --git a/acceptance/tests/cloud/remote_dev_test.go b/acceptance/tests/cloud/remote_dev_test.go deleted file mode 100644 index 457dc4f269..0000000000 --- a/acceptance/tests/cloud/remote_dev_test.go +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package cloud - -import ( - "crypto/tls" - "encoding/json" - "os" - "testing" - "time" - - "github.com/stretchr/testify/require" - - "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/environment" - "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" - "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" - "github.com/hashicorp/consul-k8s/acceptance/framework/logger" - - hcpgnm "github.com/hashicorp/hcp-sdk-go/clients/cloud-global-network-manager-service/preview/2022-02-15/client/global_network_manager_service" - "github.com/hashicorp/hcp-sdk-go/clients/cloud-global-network-manager-service/preview/2022-02-15/models" - hcpcfg "github.com/hashicorp/hcp-sdk-go/config" - "github.com/hashicorp/hcp-sdk-go/httpclient" - "github.com/hashicorp/hcp-sdk-go/resource" -) - -type DevTokenResponse struct { - Token string `json:"token"` -} - -type hcp struct { - ResourceID string - ClientID string - ClientSecret string - AuthURL string - APIHostname string - ScadaAddress string -} - -func TestRemoteDevCloud(t *testing.T) { - _, rIDok := os.LookupEnv("HCP_RESOURCE_ID") - _, cIDok := os.LookupEnv("HCP_CLIENT_ID") - _, cSECok := os.LookupEnv("HCP_CLIENT_SECRET") - - if !rIDok || !cIDok || !cSECok { - t.Log("Must set HCP_RESOURCE_ID, HCP_CLIENT_ID and HCP_CLIENT_SECRET") - t.FailNow() - } - - apiHost := os.Getenv("HCP_AUTH_URL") - if apiHost == "" { - apiHost = "https://api.hcp.dev" - } - authURL := os.Getenv("HCP_API_HOST") - if authURL == "" { - authURL = "https://auth.idp.hcp.dev" - } - scadaAddr := os.Getenv("HCP_SCADA_ADDRESS") - if scadaAddr == "" { - scadaAddr = "scada.internal.hcp.dev:7224" - } - - ctx := suite.Environment().DefaultContext(t) - - kubectlOptions := ctx.KubectlOptions(t) - ns := kubectlOptions.Namespace - k8sClient := environment.KubernetesClientFromOptions(t, kubectlOptions) - - var ( - resourceSecretName = "resource-sec-name" - resourceSecretKey = "resource-sec-key" - resourceSecretKeyValue = os.Getenv("HCP_RESOURCE_ID") - - clientIDSecretName = "clientid-sec-name" - clientIDSecretKey = "clientid-sec-key" - clientIDSecretKeyValue = os.Getenv("HCP_CLIENT_ID") - - clientSecretName = "client-sec-name" - clientSecretKey = "client-sec-key" - clientSecretKeyValue = os.Getenv("HCP_CLIENT_SECRET") - - apiHostSecretName = "apihost-sec-name" - apiHostSecretKey = "apihost-sec-key" - apiHostSecretKeyValue = apiHost - - authUrlSecretName = "authurl-sec-name" - authUrlSecretKey = "authurl-sec-key" - authUrlSecretKeyValue = authURL - - scadaAddressSecretName = "scadaaddress-sec-name" - scadaAddressSecretKey = "scadaaddress-sec-key" - scadaAddressSecretKeyValue = scadaAddr - - bootstrapTokenSecretName = "bootstrap-token" - bootstrapTokenSecretKey = "token" - ) - - hcpCfg := hcp{ - ResourceID: resourceSecretKeyValue, - ClientID: clientIDSecretKeyValue, - ClientSecret: clientSecretKeyValue, - AuthURL: authUrlSecretKeyValue, - APIHostname: apiHostSecretKeyValue, - ScadaAddress: scadaAddressSecretKeyValue, - } - - aclToken := hcpCfg.fetchAgentBootstrapConfig(t) - - cfg := suite.Config() - consul.CreateK8sSecret(t, k8sClient, cfg, ns, resourceSecretName, resourceSecretKey, resourceSecretKeyValue) - consul.CreateK8sSecret(t, k8sClient, cfg, ns, clientIDSecretName, clientIDSecretKey, clientIDSecretKeyValue) - consul.CreateK8sSecret(t, k8sClient, cfg, ns, clientSecretName, clientSecretKey, clientSecretKeyValue) - consul.CreateK8sSecret(t, k8sClient, cfg, ns, apiHostSecretName, apiHostSecretKey, apiHostSecretKeyValue) - consul.CreateK8sSecret(t, k8sClient, cfg, ns, authUrlSecretName, authUrlSecretKey, authUrlSecretKeyValue) - consul.CreateK8sSecret(t, k8sClient, cfg, ns, scadaAddressSecretName, scadaAddressSecretKey, scadaAddressSecretKeyValue) - consul.CreateK8sSecret(t, k8sClient, cfg, ns, bootstrapTokenSecretName, bootstrapTokenSecretKey, aclToken) - - releaseName := helpers.RandomName() - - helmValues := map[string]string{ - "global.imagePullPolicy": "IfNotPresent", - "global.cloud.enabled": "true", - "global.cloud.resourceId.secretName": resourceSecretName, - "global.cloud.resourceId.secretKey": resourceSecretKey, - - "global.cloud.clientId.secretName": clientIDSecretName, - "global.cloud.clientId.secretKey": clientIDSecretKey, - - "global.cloud.clientSecret.secretName": clientSecretName, - "global.cloud.clientSecret.secretKey": clientSecretKey, - - "global.cloud.apiHost.secretName": apiHostSecretName, - "global.cloud.apiHost.secretKey": apiHostSecretKey, - - "global.cloud.authUrl.secretName": authUrlSecretName, - "global.cloud.authUrl.secretKey": authUrlSecretKey, - - "global.cloud.scadaAddress.secretName": scadaAddressSecretName, - "global.cloud.scadaAddress.secretKey": scadaAddressSecretKey, - "connectInject.default": "true", - - "global.acls.manageSystemACLs": "true", - "global.acls.bootstrapToken.secretName": bootstrapTokenSecretName, - "global.acls.bootstrapToken.secretKey": bootstrapTokenSecretKey, - - "global.gossipEncryption.autoGenerate": "false", - "global.tls.enabled": "true", - "global.tls.enableAutoEncrypt": "true", - - "telemetryCollector.enabled": "true", - "telemetryCollector.cloud.clientId.secretName": clientIDSecretName, - "telemetryCollector.cloud.clientId.secretKey": clientIDSecretKey, - - "telemetryCollector.cloud.clientSecret.secretName": clientSecretName, - "telemetryCollector.cloud.clientSecret.secretKey": clientSecretKey, - // Either we set the global.trustedCAs (make sure it's idented exactly) or we - // set TLS to insecure - - "telemetryCollector.extraEnvironmentVars.HCP_API_ADDRESS": apiHostSecretKeyValue, - } - - if cfg.ConsulImage != "" { - helmValues["global.image"] = cfg.ConsulImage - } - if cfg.ConsulCollectorImage != "" { - helmValues["telemetryCollector.image"] = cfg.ConsulCollectorImage - } - - consulCluster := consul.NewHelmCluster(t, helmValues, suite.Environment().DefaultContext(t), cfg, releaseName) - consulCluster.Create(t) - - logger.Log(t, "setting acl permissions for collector and services") - aclDir := "../fixtures/bases/cloud/service-intentions" - k8s.KubectlApplyK(t, ctx.KubectlOptions(t), aclDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.KubectlDeleteK(t, ctx.KubectlOptions(t), aclDir) - }) - - logger.Log(t, "creating static-server deployment") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-server") - time.Sleep(1 * time.Hour) - - // TODO: add in test assertions here - -} - -// fetchAgentBootstrapConfig use the resource-id, client-id, and client-secret -// to call to the agent bootstrap config endpoint and parse the response into a -// CloudBootstrapConfig struct. -func (c *hcp) fetchAgentBootstrapConfig(t *testing.T) string { - cfg, err := c.HCPConfig() - require.NoError(t, err) - logger.Log(t, "Fetching Consul cluster configuration from HCP") - httpClientCfg := httpclient.Config{ - HCPConfig: cfg, - } - clientRuntime, err := httpclient.New(httpClientCfg) - require.NoError(t, err) - - hcpgnmClient := hcpgnm.New(clientRuntime, nil) - clusterResource, err := resource.FromString(c.ResourceID) - require.NoError(t, err) - - params := hcpgnm.NewAgentBootstrapConfigParams(). - WithID(clusterResource.ID). - WithLocationOrganizationID(clusterResource.Organization). - WithLocationProjectID(clusterResource.Project) - - resp, err := hcpgnmClient.AgentBootstrapConfig(params, nil) - require.NoError(t, err) - - bootstrapConfig := resp.GetPayload() - logger.Log(t, "HCP configuration successfully fetched.") - - return c.parseBootstrapConfigResponse(t, bootstrapConfig) -} - -// ConsulConfig represents 'cluster.consul_config' in the response -// fetched from the agent bootstrap config endpoint in HCP. -type ConsulConfig struct { - ACL ACL `json:"acl"` -} - -// ACL represents 'cluster.consul_config.acl' in the response -// fetched from the agent bootstrap config endpoint in HCP. -type ACL struct { - Tokens Tokens `json:"tokens"` -} - -// Tokens represents 'cluster.consul_config.acl.tokens' in the -// response fetched from the agent bootstrap config endpoint in HCP. -type Tokens struct { - Agent string `json:"agent"` - InitialManagement string `json:"initial_management"` -} - -// parseBootstrapConfigResponse unmarshals the boostrap parseBootstrapConfigResponse -// and also sets the HCPConfig values to return CloudBootstrapConfig struct. -func (c *hcp) parseBootstrapConfigResponse(t *testing.T, bootstrapRepsonse *models.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse) string { - - var consulConfig ConsulConfig - err := json.Unmarshal([]byte(bootstrapRepsonse.Bootstrap.ConsulConfig), &consulConfig) - require.NoError(t, err) - - return consulConfig.ACL.Tokens.InitialManagement -} - -func (c *hcp) HCPConfig(opts ...hcpcfg.HCPConfigOption) (hcpcfg.HCPConfig, error) { - if c.ClientID != "" && c.ClientSecret != "" { - opts = append(opts, hcpcfg.WithClientCredentials(c.ClientID, c.ClientSecret)) - } - if c.AuthURL != "" { - opts = append(opts, hcpcfg.WithAuth(c.AuthURL, &tls.Config{})) - } - if c.APIHostname != "" { - opts = append(opts, hcpcfg.WithAPI(c.APIHostname, &tls.Config{})) - } - if c.ScadaAddress != "" { - opts = append(opts, hcpcfg.WithSCADA(c.ScadaAddress, &tls.Config{})) - } - opts = append(opts, hcpcfg.FromEnv(), hcpcfg.WithoutBrowserLogin()) - return hcpcfg.NewHCPConfig(opts...) -} diff --git a/acceptance/tests/config-entries/config_entries_namespaces_test.go b/acceptance/tests/config-entries/config_entries_namespaces_test.go index a013c99e09..a2580069a8 100644 --- a/acceptance/tests/config-entries/config_entries_namespaces_test.go +++ b/acceptance/tests/config-entries/config_entries_namespaces_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package config_entries import ( @@ -102,7 +99,7 @@ func TestControllerNamespaces(t *testing.T) { if err != nil && !strings.Contains(out, "(AlreadyExists)") { require.NoError(t, err) } - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "ns", KubeNS) }) @@ -136,7 +133,7 @@ func TestControllerNamespaces(t *testing.T) { // On startup, the controller can take upwards of 1m to perform // leader election so we may need to wait a long time for // the reconcile loop to run (hence the 1m timeout here). - counter := &retry.Counter{Count: 60, Wait: 2 * time.Second} + counter := &retry.Counter{Count: 60, Wait: 1 * time.Second} retry.RunWith(counter, t, func(r *retry.R) { // service-defaults entry, _, err := consulClient.ConfigEntries().Get(api.ServiceDefaults, "defaults", queryOpts) @@ -159,6 +156,13 @@ func TestControllerNamespaces(t *testing.T) { require.True(r, ok, "could not cast to ProxyConfigEntry") require.Equal(r, api.MeshGatewayModeLocal, proxyDefaultEntry.MeshGateway.Mode) + // exported-services + entry, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", defaultOpts) + require.NoError(r, err) + exportedServicesEntry, ok := entry.(*api.ExportedServicesConfigEntry) + require.True(r, ok, "could not cast to ExportedServicesConfigEntry") + require.Equal(r, "frontend", exportedServicesEntry.Services[0].Name) + // mesh entry, _, err = consulClient.ConfigEntries().Get(api.MeshConfig, "mesh", defaultOpts) require.NoError(r, err) @@ -209,68 +213,6 @@ func TestControllerNamespaces(t *testing.T) { require.Equal(r, "certFile", terminatingGatewayEntry.Services[0].CertFile) require.Equal(r, "keyFile", terminatingGatewayEntry.Services[0].KeyFile) require.Equal(r, "sni", terminatingGatewayEntry.Services[0].SNI) - - // jwt-provider - entry, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "jwt-provider", defaultOpts) - require.NoError(r, err) - jwtProviderConfigEntry, ok := entry.(*api.JWTProviderConfigEntry) - require.True(r, ok, "could not cast to JWTProviderConfigEntry") - require.Equal(r, "jwks.txt", jwtProviderConfigEntry.JSONWebKeySet.Local.Filename) - require.Equal(r, "test-issuer", jwtProviderConfigEntry.Issuer) - require.ElementsMatch(r, []string{"aud1", "aud2"}, jwtProviderConfigEntry.Audiences) - require.Equal(r, "x-jwt-header", jwtProviderConfigEntry.Locations[0].Header.Name) - require.Equal(r, "x-query-param", jwtProviderConfigEntry.Locations[1].QueryParam.Name) - require.Equal(r, "session-id", jwtProviderConfigEntry.Locations[2].Cookie.Name) - require.Equal(r, "x-forwarded-jwt", jwtProviderConfigEntry.Forwarding.HeaderName) - require.True(r, jwtProviderConfigEntry.Forwarding.PadForwardPayloadHeader) - require.Equal(r, 45, jwtProviderConfigEntry.ClockSkewSeconds) - require.Equal(r, 15, jwtProviderConfigEntry.CacheConfig.Size) - - // exported-services - entry, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", defaultOpts) - require.NoError(r, err) - exportedServicesConfigEntry, ok := entry.(*api.ExportedServicesConfigEntry) - require.True(r, ok, "could not cast to ExportedServicesConfigEntry") - require.Equal(r, "frontend", exportedServicesConfigEntry.Services[0].Name) - require.Equal(r, "frontend", exportedServicesConfigEntry.Services[0].Namespace) - require.Equal(r, "partitionName", exportedServicesConfigEntry.Services[0].Consumers[0].Partition) - require.Equal(r, "peerName", exportedServicesConfigEntry.Services[0].Consumers[1].Peer) - require.Equal(r, "groupName", exportedServicesConfigEntry.Services[0].Consumers[2].SamenessGroup) - - // control-plane-request-limit - entry, _, err = consulClient.ConfigEntries().Get(api.RateLimitIPConfig, "controlplanerequestlimit", defaultOpts) - require.NoError(r, err) - rateLimitIPConfigEntry, ok := entry.(*api.RateLimitIPConfigEntry) - require.True(r, ok, "could not cast to RateLimitIPConfigEntry") - require.Equal(r, "permissive", rateLimitIPConfigEntry.Mode) - require.Equal(r, 100.0, rateLimitIPConfigEntry.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.WriteRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.ACL.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.ACL.WriteRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Catalog.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Catalog.WriteRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.ConfigEntry.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.ConfigEntry.WriteRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.ConnectCA.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.ConnectCA.WriteRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Coordinate.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Coordinate.WriteRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.DiscoveryChain.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.DiscoveryChain.WriteRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Health.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Health.WriteRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Intention.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Intention.WriteRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.KV.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.KV.WriteRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Tenancy.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Tenancy.WriteRate) - //require.Equal(r, 100.0, rateLimitIPConfigEntry.PreparedQuery.ReadRate) - //require.Equal(r, 100.0, rateLimitIPConfigEntry.PreparedQuery.WriteRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Session.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Session.WriteRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Txn.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Txn.WriteRate) }) } @@ -288,6 +230,10 @@ func TestControllerNamespaces(t *testing.T) { patchMeshGatewayMode := "remote" k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "-n", KubeNS, "proxydefaults", "global", "-p", fmt.Sprintf(`{"spec":{"meshGateway":{"mode": "%s"}}}`, patchMeshGatewayMode), "--type=merge") + logger.Log(t, "patching partition-exports custom resource") + patchServiceName := "backend" + k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "-n", KubeNS, "exportedservices", "default", "-p", fmt.Sprintf(`{"spec":{"services":[{"name": "%s", "namespace": "front", "consumers":[{"partition": "foo"}]}]}}`, patchServiceName), "--type=merge") + logger.Log(t, "patching mesh custom resource") k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "-n", KubeNS, "mesh", "mesh", "-p", fmt.Sprintf(`{"spec":{"transparentProxy":{"meshDestinationsOnly": %t}}}`, false), "--type=merge") @@ -309,18 +255,7 @@ func TestControllerNamespaces(t *testing.T) { patchSNI := "patch-sni" k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "-n", KubeNS, "terminatinggateway", "terminating-gateway", "-p", fmt.Sprintf(`{"spec": {"services": [{"name":"name","caFile":"caFile","certFile":"certFile","keyFile":"keyFile","sni":"%s"}]}}`, patchSNI), "--type=merge") - logger.Log(t, "patching jwt-provider custom resource") - patchIssuer := "other-issuer" - k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "-n", KubeNS, "jwtprovider", "jwt-provider", "-p", fmt.Sprintf(`{"spec": {"issuer": "%s"}}`, patchIssuer), "--type=merge") - - logger.Log(t, "patching exported-services custom resource") - patchPartition := "destination" - k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "-n", KubeNS, "exportedservices", "default", "-p", fmt.Sprintf(`{"spec": {"services": [{"name": "frontend", "namespace": "frontend", "consumers": [{"partition": "%s"}, {"peer": "peerName"}, {"samenessGroup": "groupName"}]}]}}`, patchPartition), "--type=merge") - - logger.Log(t, "patching control-plane-request-limit custom resource") - k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "-n", KubeNS, "controlplanerequestlimit", "controlplanerequestlimit", "-p", `{"spec": {"mode": "disabled"}}`, "--type=merge") - - counter := &retry.Counter{Count: 20, Wait: 2 * time.Second} + counter := &retry.Counter{Count: 10, Wait: 500 * time.Millisecond} retry.RunWith(counter, t, func(r *retry.R) { // service-defaults entry, _, err := consulClient.ConfigEntries().Get(api.ServiceDefaults, "defaults", queryOpts) @@ -343,6 +278,13 @@ func TestControllerNamespaces(t *testing.T) { require.True(r, ok, "could not cast to ProxyConfigEntry") require.Equal(r, api.MeshGatewayModeRemote, proxyDefaultsEntry.MeshGateway.Mode) + // partition-exports + entry, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", defaultOpts) + require.NoError(r, err) + exportedServicesEntry, ok := entry.(*api.ExportedServicesConfigEntry) + require.True(r, ok, "could not cast to ExportedServicesConfigEntry") + require.Equal(r, "backend", exportedServicesEntry.Services[0].Name) + // mesh entry, _, err = consulClient.ConfigEntries().Get(api.MeshConfig, "mesh", defaultOpts) require.NoError(r, err) @@ -386,27 +328,6 @@ func TestControllerNamespaces(t *testing.T) { terminatingGatewayEntry, ok := entry.(*api.TerminatingGatewayConfigEntry) require.True(r, ok, "could not cast to TerminatingGatewayConfigEntry") require.Equal(r, patchSNI, terminatingGatewayEntry.Services[0].SNI) - - // jwt-Provider - entry, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "jwt-provider", defaultOpts) - require.NoError(r, err) - jwtProviderConfigEntry, ok := entry.(*api.JWTProviderConfigEntry) - require.True(r, ok, "could not cast to JWTProviderConfigEntry") - require.Equal(r, patchIssuer, jwtProviderConfigEntry.Issuer) - - // exported-services - entry, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", defaultOpts) - require.NoError(r, err) - exportedServicesConfigEntry, ok := entry.(*api.ExportedServicesConfigEntry) - require.True(r, ok, "could not cast to ExportedServicesConfigEntry") - require.Equal(r, patchPartition, exportedServicesConfigEntry.Services[0].Consumers[0].Partition) - - // control-plane-request-limit - entry, _, err = consulClient.ConfigEntries().Get(api.RateLimitIPConfig, "controlplanerequestlimit", defaultOpts) - require.NoError(r, err) - rateLimitIPConfigEntry, ok := entry.(*api.RateLimitIPConfigEntry) - require.True(r, ok, "could not cast to RateLimitIPConfigEntry") - require.Equal(r, rateLimitIPConfigEntry.Mode, "disabled") }) } @@ -421,6 +342,9 @@ func TestControllerNamespaces(t *testing.T) { logger.Log(t, "deleting proxy-defaults custom resource") k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "-n", KubeNS, "proxydefaults", "global") + logger.Log(t, "deleting partition-exports custom resource") + k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "-n", KubeNS, "exportedservices", "default") + logger.Log(t, "deleting mesh custom resource") k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "-n", KubeNS, "mesh", "mesh") @@ -439,16 +363,7 @@ func TestControllerNamespaces(t *testing.T) { logger.Log(t, "deleting terminating-gateway custom resource") k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "-n", KubeNS, "terminatinggateway", "terminating-gateway") - logger.Log(t, "deleting jwt-provider custom resource") - k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "-n", KubeNS, "jwtprovider", "jwt-provider") - - logger.Log(t, "deleting exported-services custom resource") - k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "-n", KubeNS, "exportedservices", "default") - - logger.Log(t, "deleting control-plane-request-limit custom resource") - k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "-n", KubeNS, "controlplanerequestlimit", "controlplanerequestlimit") - - counter := &retry.Counter{Count: 20, Wait: 2 * time.Second} + counter := &retry.Counter{Count: 10, Wait: 500 * time.Millisecond} retry.RunWith(counter, t, func(r *retry.R) { // service-defaults _, _, err := consulClient.ConfigEntries().Get(api.ServiceDefaults, "defaults", queryOpts) @@ -465,6 +380,11 @@ func TestControllerNamespaces(t *testing.T) { require.Error(r, err) require.Contains(r, err.Error(), "404 (Config entry not found") + // partition-exports + _, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", defaultOpts) + require.Error(r, err) + require.Contains(r, err.Error(), "404 (Config entry not found") + // mesh _, _, err = consulClient.ConfigEntries().Get(api.MeshConfig, "mesh", defaultOpts) require.Error(r, err) @@ -494,21 +414,6 @@ func TestControllerNamespaces(t *testing.T) { _, _, err = consulClient.ConfigEntries().Get(api.IngressGateway, "terminating-gateway", queryOpts) require.Error(r, err) require.Contains(r, err.Error(), "404 (Config entry not found") - - // jwt-provider - _, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "jwt-provider", defaultOpts) - require.Error(r, err) - require.Contains(r, err.Error(), "404 (Config entry not found") - - // exported-services - _, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", defaultOpts) - require.Error(r, err) - require.Contains(r, err.Error(), "404 (Config entry not found") - - // control-plane-request-limit - _, _, err = consulClient.ConfigEntries().Get(api.RateLimitIPConfig, "controlplanerequestlimit", defaultOpts) - require.Error(r, err) - require.Contains(r, err.Error(), "404 (Config entry not found") }) } }) diff --git a/acceptance/tests/config-entries/config_entries_test.go b/acceptance/tests/config-entries/config_entries_test.go index a36e9baaf5..035edf6a8f 100644 --- a/acceptance/tests/config-entries/config_entries_test.go +++ b/acceptance/tests/config-entries/config_entries_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package config_entries import ( @@ -86,7 +83,7 @@ func TestController(t *testing.T) { // endpoint fails initially. out, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "apply", "-k", "../fixtures/bases/crds-oss") require.NoError(r, err, out) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { // Ignore errors here because if the test ran as expected // the custom resources will have been deleted. k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-k", "../fixtures/bases/crds-oss") @@ -95,8 +92,8 @@ func TestController(t *testing.T) { // On startup, the controller can take upwards of 1m to perform // leader election so we may need to wait a long time for - // the reconcile loop to run (hence the 2m timeout here). - counter := &retry.Counter{Count: 60, Wait: 2 * time.Second} + // the reconcile loop to run (hence the 1m timeout here). + counter := &retry.Counter{Count: 60, Wait: 1 * time.Second} retry.RunWith(counter, t, func(r *retry.R) { // service-defaults entry, _, err := consulClient.ConfigEntries().Get(api.ServiceDefaults, "defaults", nil) @@ -178,67 +175,6 @@ func TestController(t *testing.T) { require.Equal(r, "certFile", terminatingGatewayEntry.Services[0].CertFile) require.Equal(r, "keyFile", terminatingGatewayEntry.Services[0].KeyFile) require.Equal(r, "sni", terminatingGatewayEntry.Services[0].SNI) - - // jwt-provider - entry, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "jwt-provider", nil) - require.NoError(r, err) - jwtProviderConfigEntry, ok := entry.(*api.JWTProviderConfigEntry) - require.True(r, ok, "could not cast to JWTProviderConfigEntry") - require.Equal(r, "jwks.txt", jwtProviderConfigEntry.JSONWebKeySet.Local.Filename) - require.Equal(r, "test-issuer", jwtProviderConfigEntry.Issuer) - require.ElementsMatch(r, []string{"aud1", "aud2"}, jwtProviderConfigEntry.Audiences) - require.Equal(r, "x-jwt-header", jwtProviderConfigEntry.Locations[0].Header.Name) - require.Equal(r, "x-query-param", jwtProviderConfigEntry.Locations[1].QueryParam.Name) - require.Equal(r, "session-id", jwtProviderConfigEntry.Locations[2].Cookie.Name) - require.Equal(r, "x-forwarded-jwt", jwtProviderConfigEntry.Forwarding.HeaderName) - require.True(r, jwtProviderConfigEntry.Forwarding.PadForwardPayloadHeader) - require.Equal(r, 45, jwtProviderConfigEntry.ClockSkewSeconds) - require.Equal(r, 15, jwtProviderConfigEntry.CacheConfig.Size) - - // exported-services - entry, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", nil) - require.NoError(r, err) - exportedServicesConfigEntry, ok := entry.(*api.ExportedServicesConfigEntry) - require.True(r, ok, "could not cast to ExportedServicesConfigEntry") - require.Equal(r, "frontend", exportedServicesConfigEntry.Services[0].Name) - require.Equal(r, "peerName", exportedServicesConfigEntry.Services[0].Consumers[0].Peer) - require.Equal(r, "groupName", exportedServicesConfigEntry.Services[0].Consumers[1].SamenessGroup) - - // control-plane-request-limit - entry, _, err = consulClient.ConfigEntries().Get(api.RateLimitIPConfig, "controlplanerequestlimit", nil) - require.NoError(r, err) - rateLimitIPConfigEntry, ok := entry.(*api.RateLimitIPConfigEntry) - require.True(r, ok, "could not cast to RateLimitIPConfigEntry") - require.Equal(r, "permissive", rateLimitIPConfigEntry.Mode) - require.Equal(r, 100.0, rateLimitIPConfigEntry.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.WriteRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.ACL.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.ACL.WriteRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Catalog.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Catalog.WriteRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.ConfigEntry.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.ConfigEntry.WriteRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.ConnectCA.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.ConnectCA.WriteRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Coordinate.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Coordinate.WriteRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.DiscoveryChain.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.DiscoveryChain.WriteRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Health.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Health.WriteRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Intention.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Intention.WriteRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.KV.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.KV.WriteRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Tenancy.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Tenancy.WriteRate) - //require.Equal(r, 100.0, rateLimitIPConfigEntry.PreparedQuery.ReadRate) - //require.Equal(r, 100.0, rateLimitIPConfigEntry.PreparedQuery.WriteRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Session.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Session.WriteRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Txn.ReadRate) - require.Equal(r, 100.0, rateLimitIPConfigEntry.Txn.WriteRate) - }) } @@ -277,17 +213,6 @@ func TestController(t *testing.T) { patchSNI := "patch-sni" k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "terminatinggateway", "terminating-gateway", "-p", fmt.Sprintf(`{"spec": {"services": [{"name":"name","caFile":"caFile","certFile":"certFile","keyFile":"keyFile","sni":"%s"}]}}`, patchSNI), "--type=merge") - logger.Log(t, "patching JWTProvider custom resource") - patchIssuer := "other-issuer" - k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "jwtprovider", "jwt-provider", "-p", fmt.Sprintf(`{"spec": {"issuer": "%s"}}`, patchIssuer), "--type=merge") - - logger.Log(t, "patching ExportedServices custom resource") - patchPeer := "destination" - k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "exportedservices", "default", "-p", fmt.Sprintf(`{"spec": {"services": [{"name": "frontend", "consumers": [{"peer": "%s"}, {"samenessGroup": "groupName"}]}]}}`, patchPeer), "--type=merge") - - logger.Log(t, "patching control-plane-request-limit custom resource") - k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "controlplanerequestlimit", "controlplanerequestlimit", "-p", `{"spec": {"mode": "disabled"}}`, "--type=merge") - counter := &retry.Counter{Count: 10, Wait: 500 * time.Millisecond} retry.RunWith(counter, t, func(r *retry.R) { // service-defaults @@ -355,27 +280,6 @@ func TestController(t *testing.T) { terminatingGatewayEntry, ok := entry.(*api.TerminatingGatewayConfigEntry) require.True(r, ok, "could not cast to TerminatingGatewayConfigEntry") require.Equal(r, patchSNI, terminatingGatewayEntry.Services[0].SNI) - - // jwt-provider - entry, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "jwt-provider", nil) - require.NoError(r, err) - jwtProviderConfigEntry, ok := entry.(*api.JWTProviderConfigEntry) - require.True(r, ok, "could not cast to JWTProviderConfigEntry") - require.Equal(r, patchIssuer, jwtProviderConfigEntry.Issuer) - - // exported-services - entry, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", nil) - require.NoError(r, err) - exportedServicesConfigEntry, ok := entry.(*api.ExportedServicesConfigEntry) - require.True(r, ok, "could not cast to ExportedServicesConfigEntry") - require.Equal(r, patchPeer, exportedServicesConfigEntry.Services[0].Consumers[0].Peer) - - // control-plane-request-limit - entry, _, err = consulClient.ConfigEntries().Get(api.RateLimitIPConfig, "controlplanerequestlimit", nil) - require.NoError(r, err) - rateLimitIPConfigEntry, ok := entry.(*api.RateLimitIPConfigEntry) - require.True(r, ok, "could not cast to RateLimitIPConfigEntry") - require.Equal(r, rateLimitIPConfigEntry.Mode, "disabled") }) } @@ -408,15 +312,6 @@ func TestController(t *testing.T) { logger.Log(t, "deleting terminating-gateway custom resource") k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "terminatinggateway", "terminating-gateway") - logger.Log(t, "deleting jwt-provider custom resource") - k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "jwtprovider", "jwt-provider") - - logger.Log(t, "deleting exported-services custom resource") - k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "exportedservices", "default") - - logger.Log(t, "deleting control-plane-request-limit custom resource") - k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "controlplanerequestlimit", "controlplanerequestlimit") - counter := &retry.Counter{Count: 10, Wait: 500 * time.Millisecond} retry.RunWith(counter, t, func(r *retry.R) { // service-defaults @@ -460,22 +355,7 @@ func TestController(t *testing.T) { require.Contains(r, err.Error(), "404 (Config entry not found") // terminating-gateway - _, _, err = consulClient.ConfigEntries().Get(api.TerminatingGateway, "terminating-gateway", nil) - require.Error(r, err) - require.Contains(r, err.Error(), "404 (Config entry not found") - - // jwt-provider - _, _, err = consulClient.ConfigEntries().Get(api.JWTProvider, "jwt-provider", nil) - require.Error(r, err) - require.Contains(r, err.Error(), "404 (Config entry not found") - - // exported-services - _, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", nil) - require.Error(r, err) - require.Contains(r, err.Error(), "404 (Config entry not found") - - // control-plane-request-limit - _, _, err = consulClient.ConfigEntries().Get(api.RateLimitIPConfig, "controlplanerequestlimit", nil) + _, _, err = consulClient.ConfigEntries().Get(api.IngressGateway, "terminating-gateway", nil) require.Error(r, err) require.Contains(r, err.Error(), "404 (Config entry not found") }) diff --git a/acceptance/tests/config-entries/main_test.go b/acceptance/tests/config-entries/main_test.go index 805045dcef..64034f7663 100644 --- a/acceptance/tests/config-entries/main_test.go +++ b/acceptance/tests/config-entries/main_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package config_entries import ( diff --git a/acceptance/tests/connect/connect_external_servers_test.go b/acceptance/tests/connect/connect_external_servers_test.go index 46f9e14573..56d7b16bc0 100644 --- a/acceptance/tests/connect/connect_external_servers_test.go +++ b/acceptance/tests/connect/connect_external_servers_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package connect import ( @@ -75,11 +72,11 @@ func TestConnectInject_ExternalServers(t *testing.T) { consulCluster.Create(t) logger.Log(t, "creating static-server and static-client deployments") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") if cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") } // Check that both static-server and static-client have been injected and now have 2 containers. diff --git a/acceptance/tests/connect/connect_inject_namespaces_test.go b/acceptance/tests/connect/connect_inject_namespaces_test.go index 7b7785a44f..dbfc4725e4 100644 --- a/acceptance/tests/connect/connect_inject_namespaces_test.go +++ b/acceptance/tests/connect/connect_inject_namespaces_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package connect import ( @@ -101,12 +98,12 @@ func TestConnectInjectNamespaces(t *testing.T) { logger.Logf(t, "creating namespaces %s and %s", staticServerNamespace, StaticClientNamespace) k8s.RunKubectl(t, ctx.KubectlOptions(t), "create", "ns", staticServerNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "ns", staticServerNamespace) }) k8s.RunKubectl(t, ctx.KubectlOptions(t), "create", "ns", StaticClientNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { // Note: this deletion will take longer in cases when the static-client deployment // hasn't yet fully terminated. k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "ns", StaticClientNamespace) @@ -147,11 +144,11 @@ func TestConnectInjectNamespaces(t *testing.T) { } logger.Log(t, "creating static-server and static-client deployments") - k8s.DeployKustomize(t, staticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, staticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") if cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { - k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") + k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") } // Check that both static-server and static-client have been injected and now have 2 containers. @@ -305,7 +302,7 @@ func TestConnectInjectNamespaces_CleanupController(t *testing.T) { logger.Logf(t, "creating namespace %s", StaticClientNamespace) k8s.RunKubectl(t, ctx.KubectlOptions(t), "create", "ns", StaticClientNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "ns", StaticClientNamespace) }) @@ -315,7 +312,7 @@ func TestConnectInjectNamespaces_CleanupController(t *testing.T) { ConfigPath: ctx.KubectlOptions(t).ConfigPath, Namespace: StaticClientNamespace, } - k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") + k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") logger.Log(t, "waiting for static-client to be registered with Consul") consulClient, _ := consulCluster.SetupConsulClient(t, c.secure) diff --git a/acceptance/tests/connect/connect_inject_test.go b/acceptance/tests/connect/connect_inject_test.go index ff8b5bf2df..2909f8e5d0 100644 --- a/acceptance/tests/connect/connect_inject_test.go +++ b/acceptance/tests/connect/connect_inject_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package connect import ( @@ -61,35 +58,6 @@ func TestConnectInject(t *testing.T) { } } -// TestConnectInject_VirtualIPFailover ensures that KubeDNS entries are saved to the virtual IP address table in Consul. -func TestConnectInject_VirtualIPFailover(t *testing.T) { - cfg := suite.Config() - if !cfg.EnableTransparentProxy { - // This can only be tested in transparent proxy mode. - t.SkipNow() - } - ctx := suite.Environment().DefaultContext(t) - - releaseName := helpers.RandomName() - connHelper := connhelper.ConnectHelper{ - ClusterKind: consul.Helm, - Secure: true, - ReleaseName: releaseName, - Ctx: ctx, - UseAppNamespace: cfg.EnableRestrictedPSAEnforcement, - Cfg: cfg, - } - - connHelper.Setup(t) - - connHelper.Install(t) - connHelper.CreateResolverRedirect(t) - connHelper.DeployClientAndServer(t) - - opts := connHelper.KubectlOptsForApp(t) - k8s.CheckStaticServerConnectionSuccessful(t, opts, "static-client", "http://resolver-redirect") -} - // Test the endpoints controller cleans up force-killed pods. func TestConnectInject_CleanupKilledPods(t *testing.T) { for _, secure := range []bool{false, true} { @@ -113,7 +81,7 @@ func TestConnectInject_CleanupKilledPods(t *testing.T) { consulCluster.Create(t) logger.Log(t, "creating static-client deployment") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") logger.Log(t, "waiting for static-client to be registered with Consul") consulClient, _ := consulCluster.SetupConsulClient(t, secure) @@ -220,8 +188,8 @@ func TestConnectInject_MultiportServices(t *testing.T) { } logger.Log(t, "creating multiport static-server and static-client deployments") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/multiport-app") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-inject-multiport") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/multiport-app") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject-multiport") // Check that static-client has been injected and now has 2 containers. podList, err := ctx.KubernetesClient(t).CoreV1().Pods(ctx.KubectlOptions(t).Namespace).List(context.Background(), metav1.ListOptions{ @@ -280,7 +248,7 @@ func TestConnectInject_MultiportServices(t *testing.T) { // pod to static-server. // Deploy static-server. - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") // For outbound connections from the multi port pod, only intentions from the first service in the multiport // pod need to be created, since all upstream connections are made through the first service's envoy proxy. diff --git a/acceptance/tests/connect/connect_proxy_lifecycle_test.go b/acceptance/tests/connect/connect_proxy_lifecycle_test.go index 39dd0b4ae7..ae70a0fdeb 100644 --- a/acceptance/tests/connect/connect_proxy_lifecycle_test.go +++ b/acceptance/tests/connect/connect_proxy_lifecycle_test.go @@ -11,10 +11,10 @@ import ( "testing" "time" + "github.com/gruntwork-io/terratest/modules/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/connhelper" "github.com/hashicorp/consul-k8s/acceptance/framework/consul" "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" - "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/logger" "github.com/hashicorp/consul/sdk/testutil/retry" "github.com/stretchr/testify/require" @@ -33,6 +33,7 @@ const ( // Test the endpoints controller cleans up force-killed pods. func TestConnectInject_ProxyLifecycleShutdown(t *testing.T) { + t.Skipf("skiping this test, will be re-added in a future commit") cfg := suite.Config() cfg.SkipWhenOpenshiftAndCNI(t) diff --git a/acceptance/tests/connect/main_test.go b/acceptance/tests/connect/main_test.go index e44c75e519..a2b5925bed 100644 --- a/acceptance/tests/connect/main_test.go +++ b/acceptance/tests/connect/main_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package connect import ( diff --git a/acceptance/tests/connect/permissive_mtls_test.go b/acceptance/tests/connect/permissive_mtls_test.go deleted file mode 100644 index 929d56acfd..0000000000 --- a/acceptance/tests/connect/permissive_mtls_test.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package connect - -import ( - "context" - "testing" - - "github.com/hashicorp/consul-k8s/acceptance/framework/config" - "github.com/hashicorp/consul-k8s/acceptance/framework/connhelper" - "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" - "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" - "github.com/hashicorp/consul-k8s/acceptance/framework/logger" - "github.com/hashicorp/consul/sdk/testutil/retry" - "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func TestConnectInject_PermissiveMTLS(t *testing.T) { - cfg := suite.Config() - if !cfg.EnableTransparentProxy { - t.Skipf("skipping this because -enable-transparent-proxy is not set") - } - cfg.SkipWhenOpenshiftAndCNI(t) - - ctx := suite.Environment().DefaultContext(t) - - releaseName := helpers.RandomName() - connHelper := connhelper.ConnectHelper{ - ClusterKind: consul.Helm, - Secure: true, - ReleaseName: releaseName, - Ctx: ctx, - Cfg: cfg, - } - connHelper.Setup(t) - connHelper.Install(t) - - deployNonMeshClient(t, connHelper) - deployStaticServer(t, cfg, connHelper) - - kubectlOpts := connHelper.Ctx.KubectlOptions(t) - logger.Logf(t, "Check that incoming non-mTLS connection fails in MutualTLSMode = strict") - k8s.CheckStaticServerConnectionFailing(t, kubectlOpts, "static-client", "http://static-server") - - logger.Log(t, "Set allowEnablingPermissiveMutualTLS = true") - writeCrd(t, connHelper, "../fixtures/cases/permissive-mtls/mesh-config-permissive-allowed.yaml") - - logger.Log(t, "Set mutualTLSMode = permissive for static-server") - writeCrd(t, connHelper, "../fixtures/cases/permissive-mtls/service-defaults-static-server-permissive.yaml") - - logger.Log(t, "Check that incoming mTLS connection is successful in MutualTLSMode = permissive") - k8s.CheckStaticServerConnectionSuccessful(t, kubectlOpts, "static-client", "http://static-server") -} - -func deployNonMeshClient(t *testing.T, ch connhelper.ConnectHelper) { - t.Helper() - - logger.Log(t, "Creating static-client deployment with connect-inject=false") - k8s.DeployKustomize(t, ch.Ctx.KubectlOptions(t), ch.Cfg.NoCleanupOnFailure, ch.Cfg.NoCleanup, ch.Cfg.DebugDirectory, "../fixtures/bases/static-client") - requirePodContainers(t, ch, "app=static-client", 1) -} - -func deployStaticServer(t *testing.T, cfg *config.TestConfig, ch connhelper.ConnectHelper) { - t.Helper() - - logger.Log(t, "Creating static-server deployment with connect-inject=true") - k8s.DeployKustomize(t, ch.Ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") - requirePodContainers(t, ch, "app=static-server", 2) -} - -func writeCrd(t *testing.T, ch connhelper.ConnectHelper, path string) { - t.Helper() - - t.Cleanup(func() { - _, _ = k8s.RunKubectlAndGetOutputE(t, ch.Ctx.KubectlOptions(t), "delete", "-f", path) - }) - - _, err := k8s.RunKubectlAndGetOutputE(t, ch.Ctx.KubectlOptions(t), "apply", "-f", path) - require.NoError(t, err) -} - -func requirePodContainers(t *testing.T, ch connhelper.ConnectHelper, selector string, nContainers int) { - t.Helper() - - opts := ch.Ctx.KubectlOptions(t) - client := ch.Ctx.KubernetesClient(t) - retry.Run(t, func(r *retry.R) { - podList, err := client.CoreV1(). - Pods(opts.Namespace). - List(context.Background(), metav1.ListOptions{LabelSelector: selector}) - require.NoError(r, err) - require.Len(r, podList.Items, 1) - require.Len(r, podList.Items[0].Spec.Containers, nContainers) - }) -} diff --git a/acceptance/tests/consul-dns/consul_dns_test.go b/acceptance/tests/consul-dns/consul_dns_test.go index 84741175af..47cfb4af07 100644 --- a/acceptance/tests/consul-dns/consul_dns_test.go +++ b/acceptance/tests/consul-dns/consul_dns_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package consuldns import ( @@ -62,10 +59,10 @@ func TestConsulDNS(t *testing.T) { dnsPodName := fmt.Sprintf("%s-dns-pod", releaseName) dnsTestPodArgs := []string{ - "run", "-it", dnsPodName, "--restart", "Never", "--image", "anubhavmishra/tiny-tools", "--", "dig", fmt.Sprintf("@%s-consul-dns", releaseName), "consul.service.consul", + "run", "-i", dnsPodName, "--restart", "Never", "--image", "anubhavmishra/tiny-tools", "--", "dig", fmt.Sprintf("@%s-consul-dns", releaseName), "consul.service.consul", } - helpers.Cleanup(t, suite.Config().NoCleanupOnFailure, suite.Config().NoCleanup, func() { + helpers.Cleanup(t, suite.Config().NoCleanupOnFailure, func() { // Note: this delete command won't wait for pods to be fully terminated. // This shouldn't cause any test pollution because the underlying // objects are deployments, and so when other tests create these diff --git a/acceptance/tests/consul-dns/main_test.go b/acceptance/tests/consul-dns/main_test.go index 1a17337d0a..848f30ad8f 100644 --- a/acceptance/tests/consul-dns/main_test.go +++ b/acceptance/tests/consul-dns/main_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package consuldns import ( diff --git a/acceptance/tests/example/example_test.go b/acceptance/tests/example/example_test.go index 07b04d6097..9c6457f906 100644 --- a/acceptance/tests/example/example_test.go +++ b/acceptance/tests/example/example_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - // Rename package to your test package. package example @@ -44,7 +41,7 @@ func TestExample(t *testing.T) { k8s.KubectlApply(t, ctx.KubectlOptions(t), "path/to/config") // Clean up any Kubernetes resources you have created - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.KubectlDelete(t, ctx.KubectlOptions(t), "path/to/config") }) diff --git a/acceptance/tests/example/main_test.go b/acceptance/tests/example/main_test.go index f92fff8a59..323f421d32 100644 --- a/acceptance/tests/example/main_test.go +++ b/acceptance/tests/example/main_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - // Rename package to your test package. package example diff --git a/acceptance/tests/fixtures/bases/api-gateway/apigateway.yaml b/acceptance/tests/fixtures/bases/api-gateway/apigateway.yaml deleted file mode 100644 index 2a355e1b2f..0000000000 --- a/acceptance/tests/fixtures/bases/api-gateway/apigateway.yaml +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: Gateway -metadata: - name: gateway -spec: - gatewayClassName: gateway-class - listeners: - - protocol: HTTP - port: 80 - name: http - allowedRoutes: - namespaces: - from: "All" - - protocol: TCP - port: 81 - name: tcp - allowedRoutes: - namespaces: - from: "All" - - protocol: HTTPS - port: 443 - name: https - tls: - certificateRefs: - - name: "certificate" - allowedRoutes: - namespaces: - from: "All" diff --git a/acceptance/tests/fixtures/bases/api-gateway/certificate.yaml b/acceptance/tests/fixtures/bases/api-gateway/certificate.yaml deleted file mode 100644 index d35dc559e2..0000000000 --- a/acceptance/tests/fixtures/bases/api-gateway/certificate.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: v1 -kind: Secret -metadata: - name: certificate -type: kubernetes.io/tls -data: - tls.crt: "" - tls.key: "" \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/api-gateway/gatewayclass.yaml b/acceptance/tests/fixtures/bases/api-gateway/gatewayclass.yaml deleted file mode 100644 index 9ff985fd49..0000000000 --- a/acceptance/tests/fixtures/bases/api-gateway/gatewayclass.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: GatewayClass -metadata: - name: gateway-class -spec: - controllerName: "consul.hashicorp.com/gateway-controller" - parametersRef: - group: consul.hashicorp.com - kind: GatewayClassConfig - name: gateway-class-config diff --git a/acceptance/tests/fixtures/bases/api-gateway/gatewayclassconfig.yaml b/acceptance/tests/fixtures/bases/api-gateway/gatewayclassconfig.yaml deleted file mode 100644 index b8dfae7aa5..0000000000 --- a/acceptance/tests/fixtures/bases/api-gateway/gatewayclassconfig.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: GatewayClassConfig -metadata: - name: gateway-class-config \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/api-gateway/httproute.yaml b/acceptance/tests/fixtures/bases/api-gateway/httproute.yaml deleted file mode 100644 index d59c4e067e..0000000000 --- a/acceptance/tests/fixtures/bases/api-gateway/httproute.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: http-route -spec: - parentRefs: - - name: gateway \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/api-gateway/kustomization.yaml b/acceptance/tests/fixtures/bases/api-gateway/kustomization.yaml deleted file mode 100644 index e2125414d9..0000000000 --- a/acceptance/tests/fixtures/bases/api-gateway/kustomization.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - gatewayclassconfig.yaml - - gatewayclass.yaml - - apigateway.yaml - - httproute.yaml - - meshservice.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/api-gateway/meshservice.yaml b/acceptance/tests/fixtures/bases/api-gateway/meshservice.yaml deleted file mode 100644 index 4c32452bc3..0000000000 --- a/acceptance/tests/fixtures/bases/api-gateway/meshservice.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: MeshService -metadata: - name: mesh-service -spec: - name: static-server \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/cloud/hcp-mock/deployment.yaml b/acceptance/tests/fixtures/bases/cloud/hcp-mock/deployment.yaml deleted file mode 100644 index 7278557cdb..0000000000 --- a/acceptance/tests/fixtures/bases/cloud/hcp-mock/deployment.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apps/v1 -kind: Deployment -metadata: - name: fake-server -spec: - replicas: 1 - selector: - matchLabels: - app: fake-server - template: - metadata: - name: fake-server - labels: - app: fake-server - spec: - containers: - - name: fake-server - # TODO: move this to a hashicorp mirror - image: docker.io/chaapppie/fakeserver:latest - ports: - - containerPort: 443 - name: https - - containerPort: 8080 - name: http - serviceAccountName: fake-server - terminationGracePeriodSeconds: 0 # so deletion is quick diff --git a/acceptance/tests/fixtures/bases/cloud/hcp-mock/kustomization.yaml b/acceptance/tests/fixtures/bases/cloud/hcp-mock/kustomization.yaml deleted file mode 100644 index dc9c951ab2..0000000000 --- a/acceptance/tests/fixtures/bases/cloud/hcp-mock/kustomization.yaml +++ /dev/null @@ -1,10 +0,0 @@ - - -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - deployment.yaml - - service.yaml - - serviceaccount.yaml - diff --git a/acceptance/tests/fixtures/bases/cloud/hcp-mock/service.yaml b/acceptance/tests/fixtures/bases/cloud/hcp-mock/service.yaml deleted file mode 100644 index 0cc6f1b9ce..0000000000 --- a/acceptance/tests/fixtures/bases/cloud/hcp-mock/service.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: v1 -kind: Service -metadata: - name: fake-server -spec: - selector: - app: fake-server - ports: - - name: https - port: 443 - targetPort: 443 - - name: http - port: 8080 - targetPort: 8080 diff --git a/acceptance/tests/fixtures/bases/cloud/hcp-mock/serviceaccount.yaml b/acceptance/tests/fixtures/bases/cloud/hcp-mock/serviceaccount.yaml deleted file mode 100644 index f52d9640cd..0000000000 --- a/acceptance/tests/fixtures/bases/cloud/hcp-mock/serviceaccount.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: v1 -kind: ServiceAccount -metadata: - name: fake-server diff --git a/acceptance/tests/fixtures/bases/cloud/service-intentions/acl.yaml b/acceptance/tests/fixtures/bases/cloud/service-intentions/acl.yaml deleted file mode 100644 index fb3f77f496..0000000000 --- a/acceptance/tests/fixtures/bases/cloud/service-intentions/acl.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceIntentions -metadata: - name: consul-telemetry-collector -spec: - destination: - name: 'consul-telemetry-collector' - sources: - - name: '*' - action: allow - - - \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/cloud/service-intentions/kustomization.yaml b/acceptance/tests/fixtures/bases/cloud/service-intentions/kustomization.yaml deleted file mode 100644 index 9c19bf4ca3..0000000000 --- a/acceptance/tests/fixtures/bases/cloud/service-intentions/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - acl.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/crds-oss/controlplanerequestlimit.yaml b/acceptance/tests/fixtures/bases/crds-oss/controlplanerequestlimit.yaml deleted file mode 100644 index 5e8e32dbb5..0000000000 --- a/acceptance/tests/fixtures/bases/crds-oss/controlplanerequestlimit.yaml +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ControlPlaneRequestLimit -metadata: - name: controlplanerequestlimit -spec: - mode: "permissive" - readRate: 100.0 - writeRate: 100.0 - acl: - readRate: 100.0 - writeRate: 100.0 - catalog: - readRate: 100.0 - writeRate: 100.0 - configEntry: - readRate: 100.0 - writeRate: 100.0 - connectCA: - readRate: 100.0 - writeRate: 100.0 - coordinate: - readRate: 100.0 - writeRate: 100.0 - discoveryChain: - readRate: 100.0 - writeRate: 100.0 - health: - readRate: 100.0 - writeRate: 100.0 - intention: - readRate: 100.0 - writeRate: 100.0 - kv: - readRate: 100.0 - writeRate: 100.0 - tenancy: - readRate: 100.0 - writeRate: 100.0 -# preparedQuery: -# readRate: 100.0 -# writeRate: 100.0 - session: - readRate: 100.0 - writeRate: 100.0 - txn: - readRate: 100.0 - writeRate: 100.0 diff --git a/acceptance/tests/fixtures/bases/crds-oss/exportedservices.yaml b/acceptance/tests/fixtures/bases/crds-oss/exportedservices.yaml deleted file mode 100644 index 51d69ae709..0000000000 --- a/acceptance/tests/fixtures/bases/crds-oss/exportedservices.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ExportedServices -metadata: - name: default -spec: - services: - - name: frontend - consumers: - - peer: peerName - - samenessGroup: groupName \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/crds-oss/ingressgateway.yaml b/acceptance/tests/fixtures/bases/crds-oss/ingressgateway.yaml index 79b5b194bc..d613dc217c 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/ingressgateway.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/ingressgateway.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: IngressGateway metadata: diff --git a/acceptance/tests/fixtures/bases/crds-oss/jwtprovider.yaml b/acceptance/tests/fixtures/bases/crds-oss/jwtprovider.yaml deleted file mode 100644 index d35e532bf2..0000000000 --- a/acceptance/tests/fixtures/bases/crds-oss/jwtprovider.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: JWTProvider -metadata: - name: jwt-provider -spec: - jsonWebKeySet: - local: - filename: "jwks.txt" - issuer: "test-issuer" - audiences: - - "aud1" - - "aud2" - locations: - - header: - name: "x-jwt-header" - valuePrefix: "bearer" - forward: true - - queryParam: - name: "x-query-param" - - cookie: - name: "session-id" - forwarding: - headerName: "x-forwarded-jwt" - padForwardPayloadHeader: true - clockSkewSeconds: 45 - cacheConfig: - size: 15 \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/crds-oss/kustomization.yaml b/acceptance/tests/fixtures/bases/crds-oss/kustomization.yaml index 77afbc9522..040b64f155 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/kustomization.yaml @@ -1,16 +1,10 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - resources: -- ingressgateway.yaml -- mesh.yaml -- proxydefaults.yaml -- servicedefaults.yaml -- serviceintentions.yaml -- serviceresolver.yaml -- servicerouter.yaml -- servicesplitter.yaml -- terminatinggateway.yaml -- jwtprovider.yaml -- exportedservices.yaml -- controlplanerequestlimit.yaml \ No newline at end of file + - ingressgateway.yaml + - mesh.yaml + - proxydefaults.yaml + - servicedefaults.yaml + - serviceintentions.yaml + - serviceresolver.yaml + - servicerouter.yaml + - servicesplitter.yaml + - terminatinggateway.yaml diff --git a/acceptance/tests/fixtures/bases/crds-oss/mesh.yaml b/acceptance/tests/fixtures/bases/crds-oss/mesh.yaml index 9af8106b27..85474c1db6 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/mesh.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/mesh.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: Mesh metadata: diff --git a/acceptance/tests/fixtures/bases/crds-oss/proxydefaults.yaml b/acceptance/tests/fixtures/bases/crds-oss/proxydefaults.yaml index af4d7c30c2..040da247f8 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/proxydefaults.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/proxydefaults.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: ProxyDefaults metadata: @@ -22,14 +19,3 @@ spec: - path: /health listenerPort: 22000 localPathPort: 8080 - envoyExtensions: - - name: builtin/aws/lambda - required: false - arguments: - payloadPassthrough: false - arn: arn:aws:lambda:us-west-2:111111111111:function:lambda-1234 - - name: builtin/aws/lambda - required: false - arguments: - payloadPassthrough: false - arn: arn:aws:lambda:us-east-1:111111111111:function:lambda-1234 diff --git a/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml b/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml index cd9c35fa39..825c0484d8 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceDefaults metadata: @@ -22,23 +19,9 @@ spec: interval: 1s maxFailures: 10 enforcingConsecutive5xx: 60 - maxEjectionPercent: 100 - baseEjectionTime: 20s - name: "bar" limits: maxConnections: 5 passiveHealthCheck: interval: 10s - maxFailures: 2 - balanceInboundConnections: "exact_balance" - envoyExtensions: - - name: builtin/aws/lambda - required: false - arguments: - payloadPassthrough: false - arn: arn:aws:lambda:us-west-2:111111111111:function:lambda-1234 - - name: builtin/aws/lambda - required: false - arguments: - payloadPassthrough: false - arn: arn:aws:lambda:us-east-1:111111111111:function:lambda-1234 \ No newline at end of file + maxFailures: 2 \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/crds-oss/serviceexports.yaml b/acceptance/tests/fixtures/bases/crds-oss/serviceexports.yaml new file mode 100644 index 0000000000..8ae095cd8e --- /dev/null +++ b/acceptance/tests/fixtures/bases/crds-oss/serviceexports.yaml @@ -0,0 +1,10 @@ +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ServiceExports +metadata: + name: exports +spec: + services: + - name: frontend + namespace: frontend + consumers: + - partition: other \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/crds-oss/serviceintentions.yaml b/acceptance/tests/fixtures/bases/crds-oss/serviceintentions.yaml index fe3408cbd5..6b86312a78 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/serviceintentions.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/serviceintentions.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceDefaults metadata: diff --git a/acceptance/tests/fixtures/bases/crds-oss/serviceresolver.yaml b/acceptance/tests/fixtures/bases/crds-oss/serviceresolver.yaml index fc236966d6..05dee04184 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/serviceresolver.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/serviceresolver.yaml @@ -1,10 +1,7 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceResolver metadata: name: resolver spec: redirect: - service: bar \ No newline at end of file + service: bar diff --git a/acceptance/tests/fixtures/bases/crds-oss/servicerouter.yaml b/acceptance/tests/fixtures/bases/crds-oss/servicerouter.yaml index 0f04cd4cd4..8526f1202d 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/servicerouter.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/servicerouter.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceDefaults metadata: diff --git a/acceptance/tests/fixtures/bases/crds-oss/servicesplitter.yaml b/acceptance/tests/fixtures/bases/crds-oss/servicesplitter.yaml index 2eb9c3fccc..f0e8d74fe4 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/servicesplitter.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/servicesplitter.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceDefaults metadata: diff --git a/acceptance/tests/fixtures/bases/crds-oss/terminatinggateway.yaml b/acceptance/tests/fixtures/bases/crds-oss/terminatinggateway.yaml index 23daa6da20..77333b2011 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/terminatinggateway.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/terminatinggateway.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: TerminatingGateway metadata: diff --git a/acceptance/tests/fixtures/bases/exportedservices-default/exportedservices-default.yaml b/acceptance/tests/fixtures/bases/exportedservices-default/exportedservices-default.yaml index 0e523bdd7e..a260602109 100644 --- a/acceptance/tests/fixtures/bases/exportedservices-default/exportedservices-default.yaml +++ b/acceptance/tests/fixtures/bases/exportedservices-default/exportedservices-default.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: ExportedServices metadata: diff --git a/acceptance/tests/fixtures/bases/exportedservices-default/kustomization.yaml b/acceptance/tests/fixtures/bases/exportedservices-default/kustomization.yaml index 59527a69fb..e540a4def1 100644 --- a/acceptance/tests/fixtures/bases/exportedservices-default/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/exportedservices-default/kustomization.yaml @@ -1,5 +1,2 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - resources: - exportedservices-default.yaml diff --git a/acceptance/tests/fixtures/bases/exportedservices-secondary/exportedservices-secondary.yaml b/acceptance/tests/fixtures/bases/exportedservices-secondary/exportedservices-secondary.yaml index 69151577f9..a514ed50d9 100644 --- a/acceptance/tests/fixtures/bases/exportedservices-secondary/exportedservices-secondary.yaml +++ b/acceptance/tests/fixtures/bases/exportedservices-secondary/exportedservices-secondary.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: ExportedServices metadata: diff --git a/acceptance/tests/fixtures/bases/exportedservices-secondary/kustomization.yaml b/acceptance/tests/fixtures/bases/exportedservices-secondary/kustomization.yaml index e1781d47c1..10af8e20c5 100644 --- a/acceptance/tests/fixtures/bases/exportedservices-secondary/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/exportedservices-secondary/kustomization.yaml @@ -1,5 +1,2 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - resources: - exportedservices-secondary.yaml diff --git a/acceptance/tests/fixtures/bases/intention/intention.yaml b/acceptance/tests/fixtures/bases/intention/intention.yaml index 973866be03..c7bf26dac2 100644 --- a/acceptance/tests/fixtures/bases/intention/intention.yaml +++ b/acceptance/tests/fixtures/bases/intention/intention.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceIntentions metadata: diff --git a/acceptance/tests/fixtures/bases/intention/kustomization.yaml b/acceptance/tests/fixtures/bases/intention/kustomization.yaml index 572e7727a4..8d15c05511 100644 --- a/acceptance/tests/fixtures/bases/intention/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/intention/kustomization.yaml @@ -1,5 +1,2 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - resources: - intention.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/mesh-gateway/kustomization.yaml b/acceptance/tests/fixtures/bases/mesh-gateway/kustomization.yaml index c271e6af8b..6a913f2c44 100644 --- a/acceptance/tests/fixtures/bases/mesh-gateway/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/mesh-gateway/kustomization.yaml @@ -1,5 +1,2 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - resources: - proxydefaults.yaml diff --git a/acceptance/tests/fixtures/bases/mesh-gateway/proxydefaults.yaml b/acceptance/tests/fixtures/bases/mesh-gateway/proxydefaults.yaml index 1560f6c640..2d28036fe5 100644 --- a/acceptance/tests/fixtures/bases/mesh-gateway/proxydefaults.yaml +++ b/acceptance/tests/fixtures/bases/mesh-gateway/proxydefaults.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: ProxyDefaults metadata: diff --git a/acceptance/tests/fixtures/bases/mesh-peering/kustomization.yaml b/acceptance/tests/fixtures/bases/mesh-peering/kustomization.yaml index e964c5ab05..b48237763e 100644 --- a/acceptance/tests/fixtures/bases/mesh-peering/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/mesh-peering/kustomization.yaml @@ -1,5 +1,2 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - resources: - meshpeering.yaml diff --git a/acceptance/tests/fixtures/bases/mesh-peering/meshpeering.yaml b/acceptance/tests/fixtures/bases/mesh-peering/meshpeering.yaml index 2fb6a04bb6..de84382d3e 100644 --- a/acceptance/tests/fixtures/bases/mesh-peering/meshpeering.yaml +++ b/acceptance/tests/fixtures/bases/mesh-peering/meshpeering.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: Mesh metadata: diff --git a/acceptance/tests/fixtures/bases/multiport-app/anyuid-scc-rolebinding.yaml b/acceptance/tests/fixtures/bases/multiport-app/anyuid-scc-rolebinding.yaml index 5c2e0dcfa2..f80bd41d81 100644 --- a/acceptance/tests/fixtures/bases/multiport-app/anyuid-scc-rolebinding.yaml +++ b/acceptance/tests/fixtures/bases/multiport-app/anyuid-scc-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/acceptance/tests/fixtures/bases/multiport-app/deployment.yaml b/acceptance/tests/fixtures/bases/multiport-app/deployment.yaml index f345b27c5e..a99d415d80 100644 --- a/acceptance/tests/fixtures/bases/multiport-app/deployment.yaml +++ b/acceptance/tests/fixtures/bases/multiport-app/deployment.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/bases/multiport-app/kustomization.yaml b/acceptance/tests/fixtures/bases/multiport-app/kustomization.yaml index fb792d63a7..ead8b3afe5 100644 --- a/acceptance/tests/fixtures/bases/multiport-app/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/multiport-app/kustomization.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - resources: - deployment.yaml - service.yaml diff --git a/acceptance/tests/fixtures/bases/multiport-app/privileged-scc-rolebinding.yaml b/acceptance/tests/fixtures/bases/multiport-app/privileged-scc-rolebinding.yaml index d0910816ed..f909785b36 100644 --- a/acceptance/tests/fixtures/bases/multiport-app/privileged-scc-rolebinding.yaml +++ b/acceptance/tests/fixtures/bases/multiport-app/privileged-scc-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/acceptance/tests/fixtures/bases/multiport-app/psp-rolebinding.yaml b/acceptance/tests/fixtures/bases/multiport-app/psp-rolebinding.yaml index f321858164..fce63f0076 100644 --- a/acceptance/tests/fixtures/bases/multiport-app/psp-rolebinding.yaml +++ b/acceptance/tests/fixtures/bases/multiport-app/psp-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/acceptance/tests/fixtures/bases/multiport-app/secret.yaml b/acceptance/tests/fixtures/bases/multiport-app/secret.yaml index bc8d931f1b..cb3fa957e2 100644 --- a/acceptance/tests/fixtures/bases/multiport-app/secret.yaml +++ b/acceptance/tests/fixtures/bases/multiport-app/secret.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: v1 kind: Secret metadata: diff --git a/acceptance/tests/fixtures/bases/multiport-app/service.yaml b/acceptance/tests/fixtures/bases/multiport-app/service.yaml index 8684c75f21..d18da258a3 100644 --- a/acceptance/tests/fixtures/bases/multiport-app/service.yaml +++ b/acceptance/tests/fixtures/bases/multiport-app/service.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: v1 kind: Service metadata: diff --git a/acceptance/tests/fixtures/bases/multiport-app/serviceaccount.yaml b/acceptance/tests/fixtures/bases/multiport-app/serviceaccount.yaml index 87b33476f4..2293c2e173 100644 --- a/acceptance/tests/fixtures/bases/multiport-app/serviceaccount.yaml +++ b/acceptance/tests/fixtures/bases/multiport-app/serviceaccount.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: v1 kind: ServiceAccount metadata: diff --git a/acceptance/tests/fixtures/bases/openshift/network-attachment.yaml b/acceptance/tests/fixtures/bases/openshift/network-attachment.yaml index c2f36c5e1a..4b3f7948ee 100644 --- a/acceptance/tests/fixtures/bases/openshift/network-attachment.yaml +++ b/acceptance/tests/fixtures/bases/openshift/network-attachment.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: "k8s.cni.cncf.io/v1" kind: NetworkAttachmentDefinition metadata: diff --git a/acceptance/tests/fixtures/bases/peering/peering-acceptor.yaml b/acceptance/tests/fixtures/bases/peering/peering-acceptor.yaml index 1b3ea3956b..3eff952833 100644 --- a/acceptance/tests/fixtures/bases/peering/peering-acceptor.yaml +++ b/acceptance/tests/fixtures/bases/peering/peering-acceptor.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: PeeringAcceptor metadata: diff --git a/acceptance/tests/fixtures/bases/peering/peering-dialer.yaml b/acceptance/tests/fixtures/bases/peering/peering-dialer.yaml index 9bf9f03819..ec125d7bb6 100644 --- a/acceptance/tests/fixtures/bases/peering/peering-dialer.yaml +++ b/acceptance/tests/fixtures/bases/peering/peering-dialer.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: PeeringDialer metadata: diff --git a/acceptance/tests/fixtures/bases/resolver-redirect/intention.yaml b/acceptance/tests/fixtures/bases/resolver-redirect/intention.yaml deleted file mode 100644 index faff0cd251..0000000000 --- a/acceptance/tests/fixtures/bases/resolver-redirect/intention.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceIntentions -metadata: - name: client-to-server -spec: - destination: - name: static-server - sources: - - name: static-client - action: allow ---- -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceIntentions -metadata: - name: client-to-redirect -spec: - destination: - name: resolver-redirect - sources: - - name: static-client - action: allow \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/resolver-redirect/kustomization.yaml b/acceptance/tests/fixtures/bases/resolver-redirect/kustomization.yaml deleted file mode 100644 index 323957ad53..0000000000 --- a/acceptance/tests/fixtures/bases/resolver-redirect/kustomization.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - intention.yaml - - service.yaml - - serviceaccount.yaml - - resolver.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/resolver-redirect/resolver.yaml b/acceptance/tests/fixtures/bases/resolver-redirect/resolver.yaml deleted file mode 100644 index 9adbcc9fb4..0000000000 --- a/acceptance/tests/fixtures/bases/resolver-redirect/resolver.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceResolver -metadata: - name: resolver-redirect -spec: - redirect: - service: static-server \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/resolver-redirect/service.yaml b/acceptance/tests/fixtures/bases/resolver-redirect/service.yaml deleted file mode 100644 index e63ae97cca..0000000000 --- a/acceptance/tests/fixtures/bases/resolver-redirect/service.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: v1 -kind: Service -metadata: - name: resolver-redirect -spec: - selector: - # Nothing needs to be selected. We only utilize this service so that KubeDNS has a ClusterIP to resolve. - app: idonotexist - ports: - - name: http - port: 80 - targetPort: 8080 diff --git a/acceptance/tests/fixtures/bases/resolver-redirect/serviceaccount.yaml b/acceptance/tests/fixtures/bases/resolver-redirect/serviceaccount.yaml deleted file mode 100644 index c74ecd667b..0000000000 --- a/acceptance/tests/fixtures/bases/resolver-redirect/serviceaccount.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: v1 -kind: ServiceAccount -metadata: - name: resolver-redirect diff --git a/acceptance/tests/fixtures/bases/sameness/cluster-01-a-default-ns/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/cluster-01-a-default-ns/kustomization.yaml deleted file mode 100644 index 3f9d23c28a..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/cluster-01-a-default-ns/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - sameness.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/cluster-01-a-default-ns/sameness.yaml b/acceptance/tests/fixtures/bases/sameness/cluster-01-a-default-ns/sameness.yaml deleted file mode 100644 index 9c43bb505f..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/cluster-01-a-default-ns/sameness.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: SamenessGroup -metadata: - name: group-01 -spec: - defaultForFailover: true - members: - - partition: default - - partition: ap1 - - peer: cluster-02-a - - peer: cluster-03-a \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/cluster-01-b-default-ns/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/cluster-01-b-default-ns/kustomization.yaml deleted file mode 100644 index 3f9d23c28a..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/cluster-01-b-default-ns/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - sameness.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/cluster-01-b-default-ns/sameness.yaml b/acceptance/tests/fixtures/bases/sameness/cluster-01-b-default-ns/sameness.yaml deleted file mode 100644 index bf83338243..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/cluster-01-b-default-ns/sameness.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: SamenessGroup -metadata: - name: group-01 -spec: - defaultForFailover: true - members: - - partition: ap1 - - partition: default - - peer: cluster-02-a - - peer: cluster-03-a \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/cluster-02-a-default-ns/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/cluster-02-a-default-ns/kustomization.yaml deleted file mode 100644 index 3f9d23c28a..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/cluster-02-a-default-ns/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - sameness.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/cluster-02-a-default-ns/sameness.yaml b/acceptance/tests/fixtures/bases/sameness/cluster-02-a-default-ns/sameness.yaml deleted file mode 100644 index 2ed466585b..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/cluster-02-a-default-ns/sameness.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: SamenessGroup -metadata: - name: group-01 -spec: - defaultForFailover: true - members: - - partition: default - - peer: cluster-01-a - - peer: cluster-01-b - - peer: cluster-03-a \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/cluster-03-a-default-ns/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/cluster-03-a-default-ns/kustomization.yaml deleted file mode 100644 index 3f9d23c28a..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/cluster-03-a-default-ns/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - sameness.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/cluster-03-a-default-ns/sameness.yaml b/acceptance/tests/fixtures/bases/sameness/cluster-03-a-default-ns/sameness.yaml deleted file mode 100644 index 83a3c1e71a..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/cluster-03-a-default-ns/sameness.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: SamenessGroup -metadata: - name: group-01 -spec: - defaultForFailover: true - members: - - partition: default - - peer: cluster-01-a - - peer: cluster-01-b - - peer: cluster-02-a \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/exportedservices-ap1/exportedservices-ap1.yaml b/acceptance/tests/fixtures/bases/sameness/exportedservices-ap1/exportedservices-ap1.yaml deleted file mode 100644 index 3dc494dd43..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/exportedservices-ap1/exportedservices-ap1.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ExportedServices -metadata: - name: ap1 -spec: - services: [] diff --git a/acceptance/tests/fixtures/bases/sameness/exportedservices-ap1/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/exportedservices-ap1/kustomization.yaml deleted file mode 100644 index 1793fa6db7..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/exportedservices-ap1/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - exportedservices-ap1.yaml diff --git a/acceptance/tests/fixtures/bases/sameness/override-ns/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/override-ns/kustomization.yaml deleted file mode 100644 index 0646179949..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/override-ns/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - service-defaults.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/override-ns/service-defaults.yaml b/acceptance/tests/fixtures/bases/sameness/override-ns/service-defaults.yaml deleted file mode 100644 index 87f6a71f32..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/override-ns/service-defaults.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceDefaults -metadata: - name: static-server -spec: - protocol: http \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-a-dialer/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-a-dialer/kustomization.yaml deleted file mode 100644 index cf214eac6c..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-a-dialer/kustomization.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - peering-dialer-cluster-02-a.yaml - - peering-dialer-cluster-03-a.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-a-dialer/peering-dialer-cluster-02-a.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-a-dialer/peering-dialer-cluster-02-a.yaml deleted file mode 100644 index d4c51553f3..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-a-dialer/peering-dialer-cluster-02-a.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: PeeringDialer -metadata: - name: cluster-02-a -spec: - peer: - secret: - name: "cluster-02-a-cluster-01-a-peering-token" - key: "data" - backend: "kubernetes" diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-a-dialer/peering-dialer-cluster-03-a.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-a-dialer/peering-dialer-cluster-03-a.yaml deleted file mode 100644 index e6f9f9a6c9..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-a-dialer/peering-dialer-cluster-03-a.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: PeeringDialer -metadata: - name: cluster-03-a -spec: - peer: - secret: - name: "cluster-03-a-cluster-01-a-peering-token" - key: "data" - backend: "kubernetes" diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-b-dialer/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-b-dialer/kustomization.yaml deleted file mode 100644 index cf214eac6c..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-b-dialer/kustomization.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - peering-dialer-cluster-02-a.yaml - - peering-dialer-cluster-03-a.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-b-dialer/peering-dialer-cluster-02-a.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-b-dialer/peering-dialer-cluster-02-a.yaml deleted file mode 100644 index 8f0f7064df..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-b-dialer/peering-dialer-cluster-02-a.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: PeeringDialer -metadata: - name: cluster-02-a -spec: - peer: - secret: - name: "cluster-02-a-cluster-01-b-peering-token" - key: "data" - backend: "kubernetes" diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-b-dialer/peering-dialer-cluster-03-a.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-b-dialer/peering-dialer-cluster-03-a.yaml deleted file mode 100644 index 27cdd27ff8..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/peering/cluster-01-b-dialer/peering-dialer-cluster-03-a.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: PeeringDialer -metadata: - name: cluster-03-a -spec: - peer: - secret: - name: "cluster-03-a-cluster-01-b-peering-token" - key: "data" - backend: "kubernetes" diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-acceptor/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-acceptor/kustomization.yaml deleted file mode 100644 index 4c485ee633..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-acceptor/kustomization.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - peering-acceptor-cluster-01-a.yaml - - peering-acceptor-cluster-01-b.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-acceptor/peering-acceptor-cluster-01-a.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-acceptor/peering-acceptor-cluster-01-a.yaml deleted file mode 100644 index b20b61328f..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-acceptor/peering-acceptor-cluster-01-a.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: PeeringAcceptor -metadata: - name: cluster-01-a -spec: - peer: - secret: - name: "cluster-02-a-cluster-01-a-peering-token" - key: "data" - backend: "kubernetes" \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-acceptor/peering-acceptor-cluster-01-b.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-acceptor/peering-acceptor-cluster-01-b.yaml deleted file mode 100644 index c2d5c21b37..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-acceptor/peering-acceptor-cluster-01-b.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: PeeringAcceptor -metadata: - name: cluster-01-b -spec: - peer: - secret: - name: "cluster-02-a-cluster-01-b-peering-token" - key: "data" - backend: "kubernetes" \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-dialer/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-dialer/kustomization.yaml deleted file mode 100644 index c90eab30cc..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-dialer/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - peering-dialer-cluster-03-a.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-dialer/peering-dialer-cluster-03-a.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-dialer/peering-dialer-cluster-03-a.yaml deleted file mode 100644 index 80518a04c2..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/peering/cluster-02-a-dialer/peering-dialer-cluster-03-a.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: PeeringDialer -metadata: - name: cluster-03-a -spec: - peer: - secret: - name: "cluster-03-a-cluster-02-a-peering-token" - key: "data" - backend: "kubernetes" diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/kustomization.yaml deleted file mode 100644 index 543a846805..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/kustomization.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - peering-acceptor-cluster-01-a.yaml - - peering-acceptor-cluster-01-b.yaml - - peering-acceptor-cluster-02-a.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/peering-acceptor-cluster-01-a.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/peering-acceptor-cluster-01-a.yaml deleted file mode 100644 index 06c87e15a6..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/peering-acceptor-cluster-01-a.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: PeeringAcceptor -metadata: - name: cluster-01-a -spec: - peer: - secret: - name: "cluster-03-a-cluster-01-a-peering-token" - key: "data" - backend: "kubernetes" \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/peering-acceptor-cluster-01-b.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/peering-acceptor-cluster-01-b.yaml deleted file mode 100644 index 0a835ecef5..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/peering-acceptor-cluster-01-b.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: PeeringAcceptor -metadata: - name: cluster-01-b -spec: - peer: - secret: - name: "cluster-03-a-cluster-01-b-peering-token" - key: "data" - backend: "kubernetes" \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/peering-acceptor-cluster-02-a.yaml b/acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/peering-acceptor-cluster-02-a.yaml deleted file mode 100644 index e60ea8b083..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/peering/cluster-03-a-acceptor/peering-acceptor-cluster-02-a.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: PeeringAcceptor -metadata: - name: cluster-02-a -spec: - peer: - secret: - name: "cluster-03-a-cluster-02-a-peering-token" - key: "data" - backend: "kubernetes" \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/peering/mesh/kustomization.yaml b/acceptance/tests/fixtures/bases/sameness/peering/mesh/kustomization.yaml deleted file mode 100644 index 926e91236d..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/peering/mesh/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - mesh.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/sameness/peering/mesh/mesh.yaml b/acceptance/tests/fixtures/bases/sameness/peering/mesh/mesh.yaml deleted file mode 100644 index 2fb6a04bb6..0000000000 --- a/acceptance/tests/fixtures/bases/sameness/peering/mesh/mesh.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: Mesh -metadata: - name: mesh -spec: - peering: - peerThroughMeshGateways: true diff --git a/acceptance/tests/fixtures/bases/static-client/anyuid-scc-rolebinding.yaml b/acceptance/tests/fixtures/bases/static-client/anyuid-scc-rolebinding.yaml index b80bc5c562..be44c13e0a 100644 --- a/acceptance/tests/fixtures/bases/static-client/anyuid-scc-rolebinding.yaml +++ b/acceptance/tests/fixtures/bases/static-client/anyuid-scc-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/acceptance/tests/fixtures/bases/static-client/deployment.yaml b/acceptance/tests/fixtures/bases/static-client/deployment.yaml index ff29f1b03a..66bb771f6f 100644 --- a/acceptance/tests/fixtures/bases/static-client/deployment.yaml +++ b/acceptance/tests/fixtures/bases/static-client/deployment.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/bases/static-client/kustomization.yaml b/acceptance/tests/fixtures/bases/static-client/kustomization.yaml index 9aa0009dc4..b9d8e11f73 100644 --- a/acceptance/tests/fixtures/bases/static-client/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/static-client/kustomization.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - resources: - deployment.yaml - service.yaml diff --git a/acceptance/tests/fixtures/bases/static-client/privileged-scc-rolebinding.yaml b/acceptance/tests/fixtures/bases/static-client/privileged-scc-rolebinding.yaml index 36f6652e65..b8e097c3d8 100644 --- a/acceptance/tests/fixtures/bases/static-client/privileged-scc-rolebinding.yaml +++ b/acceptance/tests/fixtures/bases/static-client/privileged-scc-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/acceptance/tests/fixtures/bases/static-client/psp-rolebinding.yaml b/acceptance/tests/fixtures/bases/static-client/psp-rolebinding.yaml index e516e57f7c..a377c8038d 100644 --- a/acceptance/tests/fixtures/bases/static-client/psp-rolebinding.yaml +++ b/acceptance/tests/fixtures/bases/static-client/psp-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/acceptance/tests/fixtures/bases/static-client/service.yaml b/acceptance/tests/fixtures/bases/static-client/service.yaml index abd6ee4faf..16fbee0463 100644 --- a/acceptance/tests/fixtures/bases/static-client/service.yaml +++ b/acceptance/tests/fixtures/bases/static-client/service.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: v1 kind: Service metadata: diff --git a/acceptance/tests/fixtures/bases/static-client/serviceaccount.yaml b/acceptance/tests/fixtures/bases/static-client/serviceaccount.yaml index 23578e5ead..b9bf136862 100644 --- a/acceptance/tests/fixtures/bases/static-client/serviceaccount.yaml +++ b/acceptance/tests/fixtures/bases/static-client/serviceaccount.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: v1 kind: ServiceAccount metadata: diff --git a/acceptance/tests/fixtures/bases/static-metrics-app/deployment.yaml b/acceptance/tests/fixtures/bases/static-metrics-app/deployment.yaml index 21852487e3..a3020ddb47 100644 --- a/acceptance/tests/fixtures/bases/static-metrics-app/deployment.yaml +++ b/acceptance/tests/fixtures/bases/static-metrics-app/deployment.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/bases/static-metrics-app/kustomization.yaml b/acceptance/tests/fixtures/bases/static-metrics-app/kustomization.yaml index ccc066e0a8..6d1374a18e 100644 --- a/acceptance/tests/fixtures/bases/static-metrics-app/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/static-metrics-app/kustomization.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - resources: - deployment.yaml - service.yaml diff --git a/acceptance/tests/fixtures/bases/static-metrics-app/service.yaml b/acceptance/tests/fixtures/bases/static-metrics-app/service.yaml index 2e553f7e83..a37db26875 100644 --- a/acceptance/tests/fixtures/bases/static-metrics-app/service.yaml +++ b/acceptance/tests/fixtures/bases/static-metrics-app/service.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: v1 kind: Service metadata: diff --git a/acceptance/tests/fixtures/bases/static-server-https/anyuid-scc-rolebinding.yaml b/acceptance/tests/fixtures/bases/static-server-https/anyuid-scc-rolebinding.yaml index 2be7cf13db..d224a082da 100644 --- a/acceptance/tests/fixtures/bases/static-server-https/anyuid-scc-rolebinding.yaml +++ b/acceptance/tests/fixtures/bases/static-server-https/anyuid-scc-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/acceptance/tests/fixtures/bases/static-server-https/configmap.yaml b/acceptance/tests/fixtures/bases/static-server-https/configmap.yaml index 5a037ecdc6..dcb975978e 100644 --- a/acceptance/tests/fixtures/bases/static-server-https/configmap.yaml +++ b/acceptance/tests/fixtures/bases/static-server-https/configmap.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: v1 kind: ConfigMap metadata: diff --git a/acceptance/tests/fixtures/bases/static-server-https/deployment.yaml b/acceptance/tests/fixtures/bases/static-server-https/deployment.yaml index c95bdb4289..3071162c15 100644 --- a/acceptance/tests/fixtures/bases/static-server-https/deployment.yaml +++ b/acceptance/tests/fixtures/bases/static-server-https/deployment.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/bases/static-server-https/kustomization.yaml b/acceptance/tests/fixtures/bases/static-server-https/kustomization.yaml index da166af201..78508b7938 100644 --- a/acceptance/tests/fixtures/bases/static-server-https/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/static-server-https/kustomization.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - resources: - deployment.yaml - configmap.yaml diff --git a/acceptance/tests/fixtures/bases/static-server-https/privileged-scc-rolebinding.yaml b/acceptance/tests/fixtures/bases/static-server-https/privileged-scc-rolebinding.yaml index bed477ed26..1b9fe13789 100644 --- a/acceptance/tests/fixtures/bases/static-server-https/privileged-scc-rolebinding.yaml +++ b/acceptance/tests/fixtures/bases/static-server-https/privileged-scc-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/acceptance/tests/fixtures/bases/static-server-https/psp-rolebinding.yaml b/acceptance/tests/fixtures/bases/static-server-https/psp-rolebinding.yaml index 3c6cfad8f1..acccd2fcec 100644 --- a/acceptance/tests/fixtures/bases/static-server-https/psp-rolebinding.yaml +++ b/acceptance/tests/fixtures/bases/static-server-https/psp-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/acceptance/tests/fixtures/bases/static-server-https/service.yaml b/acceptance/tests/fixtures/bases/static-server-https/service.yaml index 7f4f930c0a..168c90d3d0 100644 --- a/acceptance/tests/fixtures/bases/static-server-https/service.yaml +++ b/acceptance/tests/fixtures/bases/static-server-https/service.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: v1 kind: Service metadata: diff --git a/acceptance/tests/fixtures/bases/static-server-https/serviceaccount.yaml b/acceptance/tests/fixtures/bases/static-server-https/serviceaccount.yaml index ced9002d6b..f4267a573e 100644 --- a/acceptance/tests/fixtures/bases/static-server-https/serviceaccount.yaml +++ b/acceptance/tests/fixtures/bases/static-server-https/serviceaccount.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: v1 kind: ServiceAccount metadata: diff --git a/acceptance/tests/fixtures/bases/static-server-tcp/anyuid-scc-rolebinding.yaml b/acceptance/tests/fixtures/bases/static-server-tcp/anyuid-scc-rolebinding.yaml deleted file mode 100644 index eb86dc8bae..0000000000 --- a/acceptance/tests/fixtures/bases/static-server-tcp/anyuid-scc-rolebinding.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: static-server-tcp-openshift-anyuid -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: system:openshift:scc:anyuid -subjects: - - kind: ServiceAccount - name: static-server-tcp \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/static-server-tcp/deployment.yaml b/acceptance/tests/fixtures/bases/static-server-tcp/deployment.yaml deleted file mode 100644 index 9aa5177e9e..0000000000 --- a/acceptance/tests/fixtures/bases/static-server-tcp/deployment.yaml +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: static-server-tcp - name: static-server-tcp -spec: - replicas: 1 - selector: - matchLabels: - app: static-server-tcp - template: - metadata: - annotations: - "consul.hashicorp.com/connect-inject": "true" - labels: - app: static-server-tcp - spec: - containers: - - name: static-server - image: docker.mirror.hashicorp.services/kschoche/http-echo:latest - args: - - -text="hello world" - - -listen=:8080 - ports: - - containerPort: 8080 - name: http - livenessProbe: - httpGet: - port: 8080 - initialDelaySeconds: 1 - failureThreshold: 1 - periodSeconds: 1 - startupProbe: - httpGet: - port: 8080 - initialDelaySeconds: 1 - failureThreshold: 30 - periodSeconds: 1 - readinessProbe: - exec: - command: ['sh', '-c', 'test ! -f /tmp/unhealthy'] - initialDelaySeconds: 1 - failureThreshold: 1 - periodSeconds: 1 - serviceAccountName: static-server-tcp diff --git a/acceptance/tests/fixtures/bases/static-server-tcp/kustomization.yaml b/acceptance/tests/fixtures/bases/static-server-tcp/kustomization.yaml deleted file mode 100644 index 2180aa94e1..0000000000 --- a/acceptance/tests/fixtures/bases/static-server-tcp/kustomization.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - deployment.yaml - - service.yaml - - serviceaccount.yaml - - servicedefaults.yaml - - psp-rolebinding.yaml - - anyuid-scc-rolebinding.yaml - - privileged-scc-rolebinding.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/static-server-tcp/privileged-scc-rolebinding.yaml b/acceptance/tests/fixtures/bases/static-server-tcp/privileged-scc-rolebinding.yaml deleted file mode 100644 index ac28006765..0000000000 --- a/acceptance/tests/fixtures/bases/static-server-tcp/privileged-scc-rolebinding.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: static-server-tcp-openshift-privileged -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: system:openshift:scc:privileged -subjects: - - kind: ServiceAccount - name: static-server-tcp \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/static-server-tcp/psp-rolebinding.yaml b/acceptance/tests/fixtures/bases/static-server-tcp/psp-rolebinding.yaml deleted file mode 100644 index f4f008dbea..0000000000 --- a/acceptance/tests/fixtures/bases/static-server-tcp/psp-rolebinding.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: static-server-tcp -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: test-psp -subjects: - - kind: ServiceAccount - name: static-server-tcp \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/static-server-tcp/service.yaml b/acceptance/tests/fixtures/bases/static-server-tcp/service.yaml deleted file mode 100644 index 6ceccf940a..0000000000 --- a/acceptance/tests/fixtures/bases/static-server-tcp/service.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: v1 -kind: Service -metadata: - name: static-server-tcp - labels: - app: static-server-tcp -spec: - ports: - - name: http - port: 8080 - selector: - app: static-server-tcp diff --git a/acceptance/tests/fixtures/bases/static-server-tcp/serviceaccount.yaml b/acceptance/tests/fixtures/bases/static-server-tcp/serviceaccount.yaml deleted file mode 100644 index af2247af8e..0000000000 --- a/acceptance/tests/fixtures/bases/static-server-tcp/serviceaccount.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: v1 -kind: ServiceAccount -metadata: - name: static-server-tcp diff --git a/acceptance/tests/fixtures/bases/static-server-tcp/servicedefaults.yaml b/acceptance/tests/fixtures/bases/static-server-tcp/servicedefaults.yaml deleted file mode 100644 index f89765cf6d..0000000000 --- a/acceptance/tests/fixtures/bases/static-server-tcp/servicedefaults.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceDefaults -metadata: - name: static-server-tcp - namespace: default -spec: - protocol: tcp \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/static-server/anyuid-scc-rolebinding.yaml b/acceptance/tests/fixtures/bases/static-server/anyuid-scc-rolebinding.yaml index 2be7cf13db..d224a082da 100644 --- a/acceptance/tests/fixtures/bases/static-server/anyuid-scc-rolebinding.yaml +++ b/acceptance/tests/fixtures/bases/static-server/anyuid-scc-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/acceptance/tests/fixtures/bases/static-server/deployment.yaml b/acceptance/tests/fixtures/bases/static-server/deployment.yaml index 9f5776c9ca..1c724b0d37 100644 --- a/acceptance/tests/fixtures/bases/static-server/deployment.yaml +++ b/acceptance/tests/fixtures/bases/static-server/deployment.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: apps/v1 kind: Deployment metadata: @@ -18,9 +15,7 @@ spec: spec: containers: - name: static-server - # Using alpine vs latest as there is a build issue with M1s. Also other tests in multiport-app reference - # alpine so standardizing this. - image: docker.mirror.hashicorp.services/hashicorp/http-echo:alpine + image: docker.mirror.hashicorp.services/hashicorp/http-echo:latest args: - -text="hello world" - -listen=:8080 diff --git a/acceptance/tests/fixtures/bases/static-server/kustomization.yaml b/acceptance/tests/fixtures/bases/static-server/kustomization.yaml index 9aa0009dc4..b9d8e11f73 100644 --- a/acceptance/tests/fixtures/bases/static-server/kustomization.yaml +++ b/acceptance/tests/fixtures/bases/static-server/kustomization.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - resources: - deployment.yaml - service.yaml diff --git a/acceptance/tests/fixtures/bases/static-server/privileged-scc-rolebinding.yaml b/acceptance/tests/fixtures/bases/static-server/privileged-scc-rolebinding.yaml index bed477ed26..1b9fe13789 100644 --- a/acceptance/tests/fixtures/bases/static-server/privileged-scc-rolebinding.yaml +++ b/acceptance/tests/fixtures/bases/static-server/privileged-scc-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/acceptance/tests/fixtures/bases/static-server/psp-rolebinding.yaml b/acceptance/tests/fixtures/bases/static-server/psp-rolebinding.yaml index 3c6cfad8f1..acccd2fcec 100644 --- a/acceptance/tests/fixtures/bases/static-server/psp-rolebinding.yaml +++ b/acceptance/tests/fixtures/bases/static-server/psp-rolebinding.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/acceptance/tests/fixtures/bases/static-server/service.yaml b/acceptance/tests/fixtures/bases/static-server/service.yaml index 6d8d54acc2..4776c07239 100644 --- a/acceptance/tests/fixtures/bases/static-server/service.yaml +++ b/acceptance/tests/fixtures/bases/static-server/service.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: v1 kind: Service metadata: diff --git a/acceptance/tests/fixtures/bases/static-server/serviceaccount.yaml b/acceptance/tests/fixtures/bases/static-server/serviceaccount.yaml index ced9002d6b..f4267a573e 100644 --- a/acceptance/tests/fixtures/bases/static-server/serviceaccount.yaml +++ b/acceptance/tests/fixtures/bases/static-server/serviceaccount.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: v1 kind: ServiceAccount metadata: diff --git a/acceptance/tests/fixtures/cases/api-gateways/certificate/certificate.yaml b/acceptance/tests/fixtures/cases/api-gateways/certificate/certificate.yaml deleted file mode 100644 index d35dc559e2..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/certificate/certificate.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: v1 -kind: Secret -metadata: - name: certificate -type: kubernetes.io/tls -data: - tls.crt: "" - tls.key: "" \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/api-gateways/certificate/kustomization.yaml b/acceptance/tests/fixtures/cases/api-gateways/certificate/kustomization.yaml deleted file mode 100644 index 42b7526335..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/certificate/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - certificate.yaml diff --git a/acceptance/tests/fixtures/cases/api-gateways/dc1-to-dc2-resolver/kustomization.yaml b/acceptance/tests/fixtures/cases/api-gateways/dc1-to-dc2-resolver/kustomization.yaml deleted file mode 100644 index cdbcd688c0..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/dc1-to-dc2-resolver/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - serviceresolver.yaml diff --git a/acceptance/tests/fixtures/cases/api-gateways/dc1-to-dc2-resolver/serviceresolver.yaml b/acceptance/tests/fixtures/cases/api-gateways/dc1-to-dc2-resolver/serviceresolver.yaml deleted file mode 100644 index ca009754b4..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/dc1-to-dc2-resolver/serviceresolver.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceResolver -metadata: - name: static-server -spec: - redirect: - service: static-server - datacenter: dc2 \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/api-gateways/dc2-to-dc1-resolver/kustomization.yaml b/acceptance/tests/fixtures/cases/api-gateways/dc2-to-dc1-resolver/kustomization.yaml deleted file mode 100644 index cdbcd688c0..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/dc2-to-dc1-resolver/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - serviceresolver.yaml diff --git a/acceptance/tests/fixtures/cases/api-gateways/dc2-to-dc1-resolver/serviceresolver.yaml b/acceptance/tests/fixtures/cases/api-gateways/dc2-to-dc1-resolver/serviceresolver.yaml deleted file mode 100644 index af8cdb72ed..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/dc2-to-dc1-resolver/serviceresolver.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceResolver -metadata: - name: static-server -spec: - redirect: - service: static-server - datacenter: dc1 \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/api-gateways/gateway/gateway.yaml b/acceptance/tests/fixtures/cases/api-gateways/gateway/gateway.yaml deleted file mode 100644 index 7f0428b039..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/gateway/gateway.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: Gateway -metadata: - name: gateway -spec: - gatewayClassName: consul - listeners: - - protocol: HTTPS - port: 8080 - name: https - tls: - certificateRefs: - - name: "certificate" - namespace: "default" - allowedRoutes: - namespaces: - from: "All" diff --git a/acceptance/tests/fixtures/cases/api-gateways/gateway/kustomization.yaml b/acceptance/tests/fixtures/cases/api-gateways/gateway/kustomization.yaml deleted file mode 100644 index 8efac31693..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/gateway/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - gateway.yaml diff --git a/acceptance/tests/fixtures/cases/api-gateways/httproute/kustomization.yaml b/acceptance/tests/fixtures/cases/api-gateways/httproute/kustomization.yaml deleted file mode 100644 index 7a6713835c..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/httproute/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - route.yaml diff --git a/acceptance/tests/fixtures/cases/api-gateways/httproute/route.yaml b/acceptance/tests/fixtures/cases/api-gateways/httproute/route.yaml deleted file mode 100644 index 9f7f66b433..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/httproute/route.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: route -spec: {} \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/api-gateway.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/api-gateway.yaml deleted file mode 100644 index bef7e96f12..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/api-gateway.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: Gateway -metadata: - name: gateway -spec: - gatewayClassName: gateway-class - listeners: - - protocol: HTTP - port: 8080 - name: http-auth - allowedRoutes: - namespaces: - from: "All" - - protocol: HTTP - port: 80 - name: http - allowedRoutes: - namespaces: - from: "All" - - protocol: TCP - port: 81 - name: tcp - allowedRoutes: - namespaces: - from: "All" - - protocol: HTTPS - port: 443 - name: https - tls: - certificateRefs: - - name: "certificate" - allowedRoutes: - namespaces: - from: "All" diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/gateway-policy.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/gateway-policy.yaml deleted file mode 100644 index 8e47d75062..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/gateway-policy.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ConsulGatewayPolicy -metadata: - name: my-policy -spec: - targetRef: - name: gateway - kind: Gateway - group: gateway.networking.kuberenetes.io - sectionName: http - override: - Providers: - - Provider: "local" - VerifyClaims: - - Path: - - "iss" - Value: "local" - default: - Providers: - - Provider: "local" - VerifyClaims: - - Path: - - "iss" - Value: "local" diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute-auth.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute-auth.yaml deleted file mode 100644 index 4963277c55..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute-auth.yaml +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: http-route-auth -spec: - parentRefs: - - name: gateway - sectionName: http-auth - rules: - - matches: - - path: - type: PathPrefix - value: "/admin" - backendRefs: - - name: static-server - port: 80 - filters: - - type: ExtensionRef - extensionRef: - group: consul.hashicorp.com - kind: HTTPRouteAuthFilter - name: route-jwt-auth-filter - - matches: - - path: - type: PathPrefix - value: "/pet" - backendRefs: - - name: static-server - port: 80 diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute.yaml deleted file mode 100644 index 52e206a91e..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/httproute.yaml +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: http-route -spec: - parentRefs: - - name: gateway - sectionName: http - rules: - - matches: - - path: - type: PathPrefix - value: "/v1" - backendRefs: - - name: static-server - port: 80 diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-provider.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-provider.yaml deleted file mode 100644 index 37eb034d3c..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-provider.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: consul.hashicorp.com/v1alpha1 -kind: JWTProvider -metadata: - name: local -spec: - issuer: local - jsonWebKeySet: - local: - jwks: "ewogICAgImtleXMiOiBbCiAgICAgICAgewogICAgICAgICAgICAicCI6ICI5TTlWSVhJR0hpR3FlTnhseEJ2V0xFV09oUFh3dXhXZUpod01uM3dGdG9STEtfZmF6VWxjWEc1cUViLTdpMXo3VmlPUWVZRnh6WUZYTS1pbVU3OVFRa1dTVUVSazR2dHZuc2R5UnpUSnVPc3A0ZUhuWFVMSHJPOU51NkJ5bC1VeVprMzFvSnFGeGllM0pHQXlRLUM2OVF2NVFkVjFZV0hfVDkyTzk4d1hYZGMiLAogICAgICAgICAgICAia3R5IjogIlJTQSIsCiAgICAgICAgICAgICJxIjogInFIVnZBb3h0ckgxUTVza25veXNMMkhvbC1ubnU3ZlM3Mjg4clRGdE9jeG9Jb29nWXBKVTljemxwcjctSlo2bjc0TUViVHBBMHRkSUR5TEtQQ0xIN3JKTFRrZzBDZVZNQWpmY01zdkRUcWdFOHNBWE42bzd2ZjYya2hwcExYOHVCU3JxSHkyV1JhZXJsbDROU09hcmRGSkQ2MWhHSVF2cEpXRk4xazFTV3pWcyIsCiAgICAgICAgICAgICJkIjogIlp3elJsVklRZkg5ekZ6d1hOZ2hEMHhkZVctalBCbmRkWnJNZ0wwQ2JjeXZZYlg2X1c0ajlhM1dmYWpobmI2bTFILW9CWjRMczVmNXNRVTB2ZFJ2ZG1laFItUG43aWNRcUdURFNKUTYtdWVtNm15UVRWaEo2UmZiM0lINVJ2VDJTOXUzcVFDZWFadWN3aXFoZ1RCbFhnOWFfV0pwVHJYNFhPQ3JCR1ZsTng3Z2JETVJOamNEN0FnRkZ3S2p2TEZVdDRLTkZmdEJqaFF0TDFLQ2VwblNmamtvRm1RUTVlX3RSS2ozX2U1V3pNSkJkekpQejNkR2YxZEk3OF9wYmJFbmFMcWhqNWg0WUx2UU5JUUhVcURYSGx4ZDc1Qlh3aFJReE1nUDRfd1EwTFk2cVRKNGFDa2Q0RDJBTUtqMzJqeVFiVTRKTE9jQjFNMnZBRWFyc2NTU3l0USIsCiAgICAgICAgICAgICJlIjogIkFRQUIiLAogICAgICAgICAgICAidXNlIjogInNpZyIsCiAgICAgICAgICAgICJraWQiOiAiQy1FMW5DandnQkMtUHVHTTJPNDY3RlJEaEt4OEFrVmN0SVNBYm8zcmlldyIsCiAgICAgICAgICAgICJxaSI6ICJ0N2VOQjhQV21xVHdKREZLQlZKZExrZnJJT2drMFJ4MnREODBGNHB5cjhmNzRuNGlVWXFmWG1haVZtbGx2c2FlT3JlNHlIczQ4UE45NVZsZlVvS3Z6ZEJFaDNZTDFINGZTOGlYYXNzNGJiVnVuWHR4U0hMZFFPYUNZYUplSmhBbGMyUWQ4elR0NFFQWk9yRWVWLVJTYU0tN095ekkwUWtSSF9tcmk1YmRrOXMiLAogICAgICAgICAgICAiZHAiOiAiYnBLckQtVXhrRENDal81MFZLU0NFeE1Ec1Zob2VBZm1tNjMxb1o5aDhUTkZ4TUU1YVptbUJ2VzBJUG9wMm1PUF9qTW9FVWxfUG1RYUlBOEgtVEdqTFp2QTMxSlZBeFN3TU5aQzdwaVFPRjYzVnhneTZUTzlmb1hENVdndC1oLUNxU1N6T2V3eFdmUWNTMmpMcTA3NUFxOTYwTnA2SHhjbE8weUdRN1JDSlpjIiwKICAgICAgICAgICAgImFsZyI6ICJQUzI1NiIsCiAgICAgICAgICAgICJkcSI6ICJpdVZveGwwckFKSEM1c2JzbTZpZWQ3c2ZIVXIwS2Rja0hiVFBLb0lPU1BFcU5YaXBlT3BrWkdEdU55NWlDTXNyRnNHaDFrRW9kTkhZdE40ay1USm5KSDliV296SGdXbGloNnN2R1V0Zi1raFMxWC16ckxaMTJudzlyNDRBbjllWG54bjFaVXMxZm5OakltM3dtZ083algyTWxIeVlNVUZVd0RMd09xNEFPUWsiLAogICAgICAgICAgICAibiI6ICJvUmhjeUREdmp3NFZ4SHRRNTZhRDlNSmRTaWhWSk1nTHd1b2FCQVhhc0RjVDNEWVZjcENlVGxDMVBPdzdPNW1Ec2ZSWVFtcGpoendyRDVZWU8yeDE4REl4czdyNTNJdFMxRy1ybnQxQ1diVE9fUzFJT01DR2xxYzh5VWJnLUhSUkRETXQyb2V3TjJoRGtxYlBKVFJNbXpjRkpNMHRpTm1RZVVMcWViZEVYaWVUblJMT1BkMWg2ZmJycVNLS01mSXlIbGZ1WXFQc1VWSEdkMVBESGljZ3NMazFtZDhtYTNIS1hWM0hJdzZrdUV6R0hQb1gxNHo4YWF6RFFZWndUR3ZxVGlPLUdRUlVDZUJueVo4bVhyWnRmSjNqVk83UUhXcEx3MlM1VDVwVTRwcE0xQXppWTFxUDVfY3ZpOTNZT2Zrb09PalRTX3V3RENZWGFxWjB5bTJHYlEiCiAgICAgICAgfQogICAgXQp9Cg==" diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-route-filter.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-route-filter.yaml deleted file mode 100644 index e0a3128bed..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/jwt-route-filter.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: consul.hashicorp.com/v1alpha1 -kind: HTTPRouteAuthFilter -metadata: - name: example-route-jwt-filter -spec: - type: JWT - JWTProviders: - - Provider: "local" - VerifyClaims: - - Path: - - "role" - Value: "doctor" diff --git a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/kustomization.yaml b/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/kustomization.yaml deleted file mode 100644 index 9730a1a4ac..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/jwt-auth/kustomization.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: -- ../../../bases/api-gateway -- ../../static-server-inject -- ./httproute.yaml -- ./jwt-provider.yaml - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -patches: -- patch: httproute-no-auth.yaml -- path: api-gateway.yaml diff --git a/acceptance/tests/fixtures/cases/api-gateways/mesh/kustomization.yaml b/acceptance/tests/fixtures/cases/api-gateways/mesh/kustomization.yaml deleted file mode 100644 index c271e6af8b..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/mesh/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - proxydefaults.yaml diff --git a/acceptance/tests/fixtures/cases/api-gateways/mesh/proxydefaults.yaml b/acceptance/tests/fixtures/cases/api-gateways/mesh/proxydefaults.yaml deleted file mode 100644 index ccc0905e32..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/mesh/proxydefaults.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ProxyDefaults -metadata: - name: global -spec: - config: - protocol: http - meshGateway: - mode: local \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/api-gateways/peer-resolver/kustomization.yaml b/acceptance/tests/fixtures/cases/api-gateways/peer-resolver/kustomization.yaml deleted file mode 100644 index cdbcd688c0..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/peer-resolver/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - serviceresolver.yaml diff --git a/acceptance/tests/fixtures/cases/api-gateways/peer-resolver/serviceresolver.yaml b/acceptance/tests/fixtures/cases/api-gateways/peer-resolver/serviceresolver.yaml deleted file mode 100644 index 20874fe1f9..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/peer-resolver/serviceresolver.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceResolver -metadata: - name: static-server -spec: - redirect: - peer: server - namespace: ns1 - service: static-server \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/api-gateways/resolver/kustomization.yaml b/acceptance/tests/fixtures/cases/api-gateways/resolver/kustomization.yaml deleted file mode 100644 index cdbcd688c0..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/resolver/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - serviceresolver.yaml diff --git a/acceptance/tests/fixtures/cases/api-gateways/resolver/serviceresolver.yaml b/acceptance/tests/fixtures/cases/api-gateways/resolver/serviceresolver.yaml deleted file mode 100644 index 18960a37db..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/resolver/serviceresolver.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceResolver -metadata: - name: static-server -spec: - redirect: - partition: default - namespace: ns1 - service: static-server \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/api-gateways/tcproute/route.yaml b/acceptance/tests/fixtures/cases/api-gateways/tcproute/route.yaml deleted file mode 100644 index 37602c65af..0000000000 --- a/acceptance/tests/fixtures/cases/api-gateways/tcproute/route.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: TCPRoute -metadata: - name: tcp-route -spec: - parentRefs: - - name: gateway - rules: - - backendRefs: - - kind: Service - name: static-server-tcp \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/crd-partitions/default-partition-default/kustomization.yaml b/acceptance/tests/fixtures/cases/crd-partitions/default-partition-default/kustomization.yaml index f3d0bca3ce..499fdc5bc1 100644 --- a/acceptance/tests/fixtures/cases/crd-partitions/default-partition-default/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crd-partitions/default-partition-default/kustomization.yaml @@ -1,9 +1,5 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization resources: -- ../../../bases/exportedservices-default -patches: -- path: patch.yaml + - ../../../bases/exportedservices-default + +patchesStrategicMerge: +- patch.yaml diff --git a/acceptance/tests/fixtures/cases/crd-partitions/default-partition-default/patch.yaml b/acceptance/tests/fixtures/cases/crd-partitions/default-partition-default/patch.yaml index a06855e317..c98ecb6f48 100644 --- a/acceptance/tests/fixtures/cases/crd-partitions/default-partition-default/patch.yaml +++ b/acceptance/tests/fixtures/cases/crd-partitions/default-partition-default/patch.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: ExportedServices metadata: diff --git a/acceptance/tests/fixtures/cases/crd-partitions/default-partition-ns1/kustomization.yaml b/acceptance/tests/fixtures/cases/crd-partitions/default-partition-ns1/kustomization.yaml index f3d0bca3ce..499fdc5bc1 100644 --- a/acceptance/tests/fixtures/cases/crd-partitions/default-partition-ns1/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crd-partitions/default-partition-ns1/kustomization.yaml @@ -1,9 +1,5 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization resources: -- ../../../bases/exportedservices-default -patches: -- path: patch.yaml + - ../../../bases/exportedservices-default + +patchesStrategicMerge: +- patch.yaml diff --git a/acceptance/tests/fixtures/cases/crd-partitions/default-partition-ns1/patch.yaml b/acceptance/tests/fixtures/cases/crd-partitions/default-partition-ns1/patch.yaml index 3e86df911d..f826174aec 100644 --- a/acceptance/tests/fixtures/cases/crd-partitions/default-partition-ns1/patch.yaml +++ b/acceptance/tests/fixtures/cases/crd-partitions/default-partition-ns1/patch.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: ExportedServices metadata: diff --git a/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-default/kustomization.yaml b/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-default/kustomization.yaml index 77c6bd3fae..5a9c8412aa 100644 --- a/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-default/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-default/kustomization.yaml @@ -1,9 +1,5 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization resources: -- ../../../bases/exportedservices-secondary -patches: -- path: patch.yaml + - ../../../bases/exportedservices-secondary + +patchesStrategicMerge: +- patch.yaml diff --git a/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-default/patch.yaml b/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-default/patch.yaml index 141a080903..d2fc1ab914 100644 --- a/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-default/patch.yaml +++ b/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-default/patch.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: ExportedServices metadata: diff --git a/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-ns1/kustomization.yaml b/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-ns1/kustomization.yaml index 77c6bd3fae..5a9c8412aa 100644 --- a/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-ns1/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-ns1/kustomization.yaml @@ -1,9 +1,5 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization resources: -- ../../../bases/exportedservices-secondary -patches: -- path: patch.yaml + - ../../../bases/exportedservices-secondary + +patchesStrategicMerge: +- patch.yaml diff --git a/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-ns1/patch.yaml b/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-ns1/patch.yaml index 3b8c2a4068..4165f2d21a 100644 --- a/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-ns1/patch.yaml +++ b/acceptance/tests/fixtures/cases/crd-partitions/secondary-partition-ns1/patch.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: ExportedServices metadata: diff --git a/acceptance/tests/fixtures/cases/crd-peers/default-namespace/kustomization.yaml b/acceptance/tests/fixtures/cases/crd-peers/default-namespace/kustomization.yaml index f3d0bca3ce..499fdc5bc1 100644 --- a/acceptance/tests/fixtures/cases/crd-peers/default-namespace/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crd-peers/default-namespace/kustomization.yaml @@ -1,9 +1,5 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization resources: -- ../../../bases/exportedservices-default -patches: -- path: patch.yaml + - ../../../bases/exportedservices-default + +patchesStrategicMerge: +- patch.yaml diff --git a/acceptance/tests/fixtures/cases/crd-peers/default-namespace/patch.yaml b/acceptance/tests/fixtures/cases/crd-peers/default-namespace/patch.yaml index 0031c7f601..eed6bee4cc 100644 --- a/acceptance/tests/fixtures/cases/crd-peers/default-namespace/patch.yaml +++ b/acceptance/tests/fixtures/cases/crd-peers/default-namespace/patch.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: ExportedServices metadata: diff --git a/acceptance/tests/fixtures/cases/crd-peers/default/kustomization.yaml b/acceptance/tests/fixtures/cases/crd-peers/default/kustomization.yaml index f3d0bca3ce..499fdc5bc1 100644 --- a/acceptance/tests/fixtures/cases/crd-peers/default/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crd-peers/default/kustomization.yaml @@ -1,9 +1,5 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization resources: -- ../../../bases/exportedservices-default -patches: -- path: patch.yaml + - ../../../bases/exportedservices-default + +patchesStrategicMerge: +- patch.yaml diff --git a/acceptance/tests/fixtures/cases/crd-peers/default/patch.yaml b/acceptance/tests/fixtures/cases/crd-peers/default/patch.yaml index e632512ff1..b0dcd89ebb 100644 --- a/acceptance/tests/fixtures/cases/crd-peers/default/patch.yaml +++ b/acceptance/tests/fixtures/cases/crd-peers/default/patch.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: ExportedServices metadata: diff --git a/acceptance/tests/fixtures/cases/crd-peers/non-default-namespace/kustomization.yaml b/acceptance/tests/fixtures/cases/crd-peers/non-default-namespace/kustomization.yaml index f3d0bca3ce..499fdc5bc1 100644 --- a/acceptance/tests/fixtures/cases/crd-peers/non-default-namespace/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crd-peers/non-default-namespace/kustomization.yaml @@ -1,9 +1,5 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization resources: -- ../../../bases/exportedservices-default -patches: -- path: patch.yaml + - ../../../bases/exportedservices-default + +patchesStrategicMerge: +- patch.yaml diff --git a/acceptance/tests/fixtures/cases/crd-peers/non-default-namespace/patch.yaml b/acceptance/tests/fixtures/cases/crd-peers/non-default-namespace/patch.yaml index 300fbcf3d6..4162a0f27b 100644 --- a/acceptance/tests/fixtures/cases/crd-peers/non-default-namespace/patch.yaml +++ b/acceptance/tests/fixtures/cases/crd-peers/non-default-namespace/patch.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: ExportedServices metadata: diff --git a/acceptance/tests/fixtures/cases/crds-ent/exportedservices.yaml b/acceptance/tests/fixtures/cases/crds-ent/exportedservices.yaml index dd39ab3626..4703d23493 100644 --- a/acceptance/tests/fixtures/cases/crds-ent/exportedservices.yaml +++ b/acceptance/tests/fixtures/cases/crds-ent/exportedservices.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: ExportedServices metadata: @@ -10,6 +7,4 @@ spec: - name: frontend namespace: frontend consumers: - - partition: partitionName - - peer: peerName - - samenessGroup: groupName \ No newline at end of file + - partition: other \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/crds-ent/kustomization.yaml b/acceptance/tests/fixtures/cases/crds-ent/kustomization.yaml index 3fd5556ebd..cdc3e60cb1 100644 --- a/acceptance/tests/fixtures/cases/crds-ent/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crds-ent/kustomization.yaml @@ -1,9 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization resources: -- ../../bases/crds-oss -patches: -- path: exportedservices.yaml + - ../../bases/crds-oss + - exportedservices.yaml diff --git a/acceptance/tests/fixtures/cases/permissive-mtls/mesh-config-permissive-allowed.yaml b/acceptance/tests/fixtures/cases/permissive-mtls/mesh-config-permissive-allowed.yaml deleted file mode 100644 index c336a621e7..0000000000 --- a/acceptance/tests/fixtures/cases/permissive-mtls/mesh-config-permissive-allowed.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: Mesh -metadata: - name: mesh -spec: - allowEnablingPermissiveMutualTLS: true diff --git a/acceptance/tests/fixtures/cases/permissive-mtls/service-defaults-static-server-permissive.yaml b/acceptance/tests/fixtures/cases/permissive-mtls/service-defaults-static-server-permissive.yaml deleted file mode 100644 index 4559570544..0000000000 --- a/acceptance/tests/fixtures/cases/permissive-mtls/service-defaults-static-server-permissive.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceDefaults -metadata: - name: static-server - namespace: default -spec: - mutualTLSMode: "permissive" diff --git a/acceptance/tests/fixtures/cases/permissive-mtls/service-defaults-static-server-strict.yaml b/acceptance/tests/fixtures/cases/permissive-mtls/service-defaults-static-server-strict.yaml deleted file mode 100644 index cf84c73407..0000000000 --- a/acceptance/tests/fixtures/cases/permissive-mtls/service-defaults-static-server-strict.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceDefaults -metadata: - name: static-server - namespace: default -spec: - mutualTLSMode: "strict" diff --git a/acceptance/tests/fixtures/cases/resolver-redirect-virtualip/kustomization.yaml b/acceptance/tests/fixtures/cases/resolver-redirect-virtualip/kustomization.yaml deleted file mode 100644 index 09790e05c6..0000000000 --- a/acceptance/tests/fixtures/cases/resolver-redirect-virtualip/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -resources: - - ../../bases/resolver-redirect \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/sameness/cluster-01-a-acceptor/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/cluster-01-a-acceptor/kustomization.yaml deleted file mode 100644 index 08c7c9b818..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/cluster-01-a-acceptor/kustomization.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -resources: -- ../../../bases/sameness/peering/acceptor -patches: -- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/cluster-01-a-acceptor/patch.yaml b/acceptance/tests/fixtures/cases/sameness/cluster-01-a-acceptor/patch.yaml deleted file mode 100644 index 2746eeef2e..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/cluster-01-a-acceptor/patch.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: PeeringAcceptor -metadata: - name: acceptor -spec: - peer: - secret: - name: "cluster-01-a-peering-token" - key: "data" - backend: "kubernetes" \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/sameness/cluster-01-b-acceptor/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/cluster-01-b-acceptor/kustomization.yaml deleted file mode 100644 index 08c7c9b818..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/cluster-01-b-acceptor/kustomization.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -resources: -- ../../../bases/sameness/peering/acceptor -patches: -- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/cluster-01-b-acceptor/patch.yaml b/acceptance/tests/fixtures/cases/sameness/cluster-01-b-acceptor/patch.yaml deleted file mode 100644 index 9ca48dad0c..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/cluster-01-b-acceptor/patch.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: PeeringAcceptor -metadata: - name: acceptor -spec: - peer: - secret: - name: "cluster-01-b-peering-token" - key: "data" - backend: "kubernetes" \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/sameness/cluster-02-a-acceptor/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/cluster-02-a-acceptor/kustomization.yaml deleted file mode 100644 index 08c7c9b818..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/cluster-02-a-acceptor/kustomization.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -resources: -- ../../../bases/sameness/peering/acceptor -patches: -- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/cluster-02-a-acceptor/patch.yaml b/acceptance/tests/fixtures/cases/sameness/cluster-02-a-acceptor/patch.yaml deleted file mode 100644 index 4343992f8f..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/cluster-02-a-acceptor/patch.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: PeeringAcceptor -metadata: - name: acceptor -spec: - peer: - secret: - name: "cluster-02-a-peering-token" - key: "data" - backend: "kubernetes" \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/sameness/cluster-03-a-acceptor/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/cluster-03-a-acceptor/kustomization.yaml deleted file mode 100644 index 08c7c9b818..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/cluster-03-a-acceptor/kustomization.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -resources: -- ../../../bases/sameness/peering/acceptor -patches: -- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/cluster-03-a-acceptor/patch.yaml b/acceptance/tests/fixtures/cases/sameness/cluster-03-a-acceptor/patch.yaml deleted file mode 100644 index 1cd49b79d7..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/cluster-03-a-acceptor/patch.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: PeeringAcceptor -metadata: - name: acceptor -spec: - peer: - secret: - name: "cluster-03-a-peering-token" - key: "data" - backend: "kubernetes" \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/sameness/exported-services/ap1-partition/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/exported-services/ap1-partition/kustomization.yaml deleted file mode 100644 index d25bed6eee..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/exported-services/ap1-partition/kustomization.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -resources: -- ../../../../bases/sameness/exportedservices-ap1 -patches: -- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/exported-services/ap1-partition/patch.yaml b/acceptance/tests/fixtures/cases/sameness/exported-services/ap1-partition/patch.yaml deleted file mode 100644 index 22fa816fed..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/exported-services/ap1-partition/patch.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ExportedServices -metadata: - name: ap1 -spec: - services: - - name: static-server - namespace: ns2 - consumers: - - samenessGroup: group-01 - - name: mesh-gateway - consumers: - - samenessGroup: group-01 diff --git a/acceptance/tests/fixtures/cases/sameness/exported-services/default-partition/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/exported-services/default-partition/kustomization.yaml deleted file mode 100644 index 7f4ab4ba7c..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/exported-services/default-partition/kustomization.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -resources: -- ../../../../bases/exportedservices-default -patches: -- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/exported-services/default-partition/patch.yaml b/acceptance/tests/fixtures/cases/sameness/exported-services/default-partition/patch.yaml deleted file mode 100644 index 4dbacf99e1..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/exported-services/default-partition/patch.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ExportedServices -metadata: - name: default -spec: - services: - - name: static-server - namespace: ns2 - consumers: - - samenessGroup: group-01 - - name: mesh-gateway - consumers: - - samenessGroup: group-01 diff --git a/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition-tproxy/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition-tproxy/kustomization.yaml deleted file mode 100644 index 096edd19ed..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition-tproxy/kustomization.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -resources: -- ../../../../bases/static-client -patches: -- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition-tproxy/patch.yaml b/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition-tproxy/patch.yaml deleted file mode 100644 index 68f3c8dd91..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition-tproxy/patch.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apps/v1 -kind: Deployment -metadata: - name: static-client -spec: - template: - metadata: - annotations: - 'consul.hashicorp.com/connect-inject': 'true' - spec: - containers: - - name: static-client - image: anubhavmishra/tiny-tools:latest - # Just spin & wait forever, we'll use `kubectl exec` to demo - command: ['/bin/sh', '-c', '--'] - args: ['while true; do sleep 30; done;'] - # If ACLs are enabled, the serviceAccountName must match the Consul service name. - serviceAccountName: static-client diff --git a/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition/kustomization.yaml deleted file mode 100644 index 096edd19ed..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition/kustomization.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -resources: -- ../../../../bases/static-client -patches: -- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition/patch.yaml b/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition/patch.yaml deleted file mode 100644 index c1a14c6070..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/static-client/ap1-partition/patch.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apps/v1 -kind: Deployment -metadata: - name: static-client -spec: - template: - metadata: - annotations: - 'consul.hashicorp.com/connect-inject': 'true' - 'consul.hashicorp.com/connect-service-upstreams': 'static-server.ns2.ap1:8080' - spec: - containers: - - name: static-client - image: anubhavmishra/tiny-tools:latest - # Just spin & wait forever, we'll use `kubectl exec` to demo - command: ['/bin/sh', '-c', '--'] - args: ['while true; do sleep 30; done;'] - # If ACLs are enabled, the serviceAccountName must match the Consul service name. - serviceAccountName: static-client diff --git a/acceptance/tests/fixtures/cases/sameness/static-client/default-partition-tproxy/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-client/default-partition-tproxy/kustomization.yaml deleted file mode 100644 index 096edd19ed..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/static-client/default-partition-tproxy/kustomization.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -resources: -- ../../../../bases/static-client -patches: -- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-client/default-partition-tproxy/patch.yaml b/acceptance/tests/fixtures/cases/sameness/static-client/default-partition-tproxy/patch.yaml deleted file mode 100644 index e53ef7b509..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/static-client/default-partition-tproxy/patch.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apps/v1 -kind: Deployment -metadata: - name: static-client -spec: - template: - metadata: - annotations: - 'consul.hashicorp.com/connect-inject': 'true' - spec: - containers: - - name: static-client - image: anubhavmishra/tiny-tools:latest - # Just spin & wait forever, we'll use `kubectl exec` to demo - command: ['/bin/sh', '-c', '--'] - args: ['while true; do sleep 30; done;'] - # If ACLs are enabled, the serviceAccountName must match the Consul service name. - serviceAccountName: static-client \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/sameness/static-client/default-partition/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-client/default-partition/kustomization.yaml deleted file mode 100644 index 096edd19ed..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/static-client/default-partition/kustomization.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -resources: -- ../../../../bases/static-client -patches: -- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-client/default-partition/patch.yaml b/acceptance/tests/fixtures/cases/sameness/static-client/default-partition/patch.yaml deleted file mode 100644 index 1775e9abb1..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/static-client/default-partition/patch.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apps/v1 -kind: Deployment -metadata: - name: static-client -spec: - template: - metadata: - annotations: - 'consul.hashicorp.com/connect-inject': 'true' - 'consul.hashicorp.com/connect-service-upstreams': 'static-server.ns2.default:8080' - spec: - containers: - - name: static-client - image: anubhavmishra/tiny-tools:latest - # Just spin & wait forever, we'll use `kubectl exec` to demo - command: ['/bin/sh', '-c', '--'] - args: ['while true; do sleep 30; done;'] - # If ACLs are enabled, the serviceAccountName must match the Consul service name. - serviceAccountName: static-client \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/sameness/static-server/dc1-default/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-server/dc1-default/kustomization.yaml deleted file mode 100644 index e03603d26d..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/static-server/dc1-default/kustomization.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -resources: -- ../../../../bases/static-server -patches: -- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-server/dc1-default/patch.yaml b/acceptance/tests/fixtures/cases/sameness/static-server/dc1-default/patch.yaml deleted file mode 100644 index ca27b7ba42..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/static-server/dc1-default/patch.yaml +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apps/v1 -kind: Deployment -metadata: - name: static-server -spec: - template: - metadata: - annotations: - "consul.hashicorp.com/connect-inject": "true" - spec: - containers: - - name: static-server - image: docker.mirror.hashicorp.services/hashicorp/http-echo:alpine - args: - - -text="cluster-01-a" - - -listen=:8080 - ports: - - containerPort: 8080 - name: http - serviceAccountName: static-server diff --git a/acceptance/tests/fixtures/cases/sameness/static-server/dc1-partition/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-server/dc1-partition/kustomization.yaml deleted file mode 100644 index e03603d26d..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/static-server/dc1-partition/kustomization.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -resources: -- ../../../../bases/static-server -patches: -- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-server/dc1-partition/patch.yaml b/acceptance/tests/fixtures/cases/sameness/static-server/dc1-partition/patch.yaml deleted file mode 100644 index 044115d1d1..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/static-server/dc1-partition/patch.yaml +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apps/v1 -kind: Deployment -metadata: - name: static-server -spec: - template: - metadata: - annotations: - "consul.hashicorp.com/connect-inject": "true" - spec: - containers: - - name: static-server - image: docker.mirror.hashicorp.services/hashicorp/http-echo:alpine - args: - - -text="cluster-01-b" - - -listen=:8080 - ports: - - containerPort: 8080 - name: http - serviceAccountName: static-server diff --git a/acceptance/tests/fixtures/cases/sameness/static-server/dc2/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-server/dc2/kustomization.yaml deleted file mode 100644 index e03603d26d..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/static-server/dc2/kustomization.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -resources: -- ../../../../bases/static-server -patches: -- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-server/dc2/patch.yaml b/acceptance/tests/fixtures/cases/sameness/static-server/dc2/patch.yaml deleted file mode 100644 index 07ac3b9aa9..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/static-server/dc2/patch.yaml +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apps/v1 -kind: Deployment -metadata: - name: static-server -spec: - template: - metadata: - annotations: - "consul.hashicorp.com/connect-inject": "true" - spec: - containers: - - name: static-server - image: docker.mirror.hashicorp.services/hashicorp/http-echo:alpine - args: - - -text="cluster-02-a" - - -listen=:8080 - ports: - - containerPort: 8080 - name: http - serviceAccountName: static-server diff --git a/acceptance/tests/fixtures/cases/sameness/static-server/dc3/kustomization.yaml b/acceptance/tests/fixtures/cases/sameness/static-server/dc3/kustomization.yaml deleted file mode 100644 index e03603d26d..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/static-server/dc3/kustomization.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -resources: -- ../../../../bases/static-server -patches: -- path: patch.yaml diff --git a/acceptance/tests/fixtures/cases/sameness/static-server/dc3/patch.yaml b/acceptance/tests/fixtures/cases/sameness/static-server/dc3/patch.yaml deleted file mode 100644 index 135e7b14fb..0000000000 --- a/acceptance/tests/fixtures/cases/sameness/static-server/dc3/patch.yaml +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apps/v1 -kind: Deployment -metadata: - name: static-server -spec: - template: - metadata: - annotations: - "consul.hashicorp.com/connect-inject": "true" - spec: - containers: - - name: static-server - image: docker.mirror.hashicorp.services/hashicorp/http-echo:alpine - args: - - -text="cluster-03-a" - - -listen=:8080 - ports: - - containerPort: 8080 - name: http - serviceAccountName: static-server diff --git a/acceptance/tests/fixtures/cases/static-client-inject-multiport/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-inject-multiport/kustomization.yaml index 564d02a68f..9834f91903 100644 --- a/acceptance/tests/fixtures/cases/static-client-inject-multiport/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-inject-multiport/kustomization.yaml @@ -1,9 +1,5 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization resources: -- ../../bases/static-client -patches: -- path: patch.yaml + - ../../bases/static-client + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/static-client-inject-multiport/patch.yaml b/acceptance/tests/fixtures/cases/static-client-inject-multiport/patch.yaml index c8b0ae8412..c38ce8e448 100644 --- a/acceptance/tests/fixtures/cases/static-client-inject-multiport/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-client-inject-multiport/patch.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-client-inject/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-inject/kustomization.yaml index 564d02a68f..9834f91903 100644 --- a/acceptance/tests/fixtures/cases/static-client-inject/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-inject/kustomization.yaml @@ -1,9 +1,5 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization resources: -- ../../bases/static-client -patches: -- path: patch.yaml + - ../../bases/static-client + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/static-client-inject/patch.yaml b/acceptance/tests/fixtures/cases/static-client-inject/patch.yaml index aea6e8c778..2f8b32ec0a 100644 --- a/acceptance/tests/fixtures/cases/static-client-inject/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-client-inject/patch.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-client-multi-dc/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-multi-dc/kustomization.yaml index 564d02a68f..9834f91903 100644 --- a/acceptance/tests/fixtures/cases/static-client-multi-dc/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-multi-dc/kustomization.yaml @@ -1,9 +1,5 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization resources: -- ../../bases/static-client -patches: -- path: patch.yaml + - ../../bases/static-client + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/static-client-multi-dc/patch.yaml b/acceptance/tests/fixtures/cases/static-client-multi-dc/patch.yaml index 17e635b1d5..7b9b095073 100644 --- a/acceptance/tests/fixtures/cases/static-client-multi-dc/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-client-multi-dc/patch.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-client-namespaces/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-namespaces/kustomization.yaml index 564d02a68f..4c5d895a98 100644 --- a/acceptance/tests/fixtures/cases/static-client-namespaces/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-namespaces/kustomization.yaml @@ -1,9 +1,5 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization resources: -- ../../bases/static-client -patches: -- path: patch.yaml + - ../../bases/static-client + +patchesStrategicMerge: + - patch.yaml diff --git a/acceptance/tests/fixtures/cases/static-client-namespaces/patch.yaml b/acceptance/tests/fixtures/cases/static-client-namespaces/patch.yaml index 2ebdbf6d9e..b41a27df3f 100644 --- a/acceptance/tests/fixtures/cases/static-client-namespaces/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-client-namespaces/patch.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-client-openshift-inject/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-openshift-inject/kustomization.yaml index 564d02a68f..4d4a53b87f 100644 --- a/acceptance/tests/fixtures/cases/static-client-openshift-inject/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-openshift-inject/kustomization.yaml @@ -1,9 +1,8 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization resources: -- ../../bases/static-client -patches: -- path: patch.yaml + - ../../bases/static-client + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/static-client-openshift-tproxy/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-openshift-tproxy/kustomization.yaml index 564d02a68f..4d4a53b87f 100644 --- a/acceptance/tests/fixtures/cases/static-client-openshift-tproxy/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-openshift-tproxy/kustomization.yaml @@ -1,9 +1,8 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization resources: -- ../../bases/static-client -patches: -- path: patch.yaml + - ../../bases/static-client + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-default-partition/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-default-partition/kustomization.yaml index 0ae44380dd..7191edfb80 100644 --- a/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-default-partition/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-default-partition/kustomization.yaml @@ -1,9 +1,5 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization resources: -- ../../../bases/static-client -patches: -- path: patch.yaml + - ../../../bases/static-client + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-default-partition/patch.yaml b/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-default-partition/patch.yaml index 4044d78bdf..43507364f8 100644 --- a/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-default-partition/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-default-partition/patch.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-partition/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-partition/kustomization.yaml index 0ae44380dd..7191edfb80 100644 --- a/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-partition/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-partition/kustomization.yaml @@ -1,9 +1,5 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization resources: -- ../../../bases/static-client -patches: -- path: patch.yaml + - ../../../bases/static-client + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-partition/patch.yaml b/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-partition/patch.yaml index f6a3c50f74..42857c3d2b 100644 --- a/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-partition/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-client-partitions/default-ns-partition/patch.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-client-partitions/ns-default-partition/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-partitions/ns-default-partition/kustomization.yaml index 0ae44380dd..7191edfb80 100644 --- a/acceptance/tests/fixtures/cases/static-client-partitions/ns-default-partition/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-partitions/ns-default-partition/kustomization.yaml @@ -1,9 +1,5 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization resources: -- ../../../bases/static-client -patches: -- path: patch.yaml + - ../../../bases/static-client + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/static-client-partitions/ns-default-partition/patch.yaml b/acceptance/tests/fixtures/cases/static-client-partitions/ns-default-partition/patch.yaml index 720252e833..2fa8ec7e27 100644 --- a/acceptance/tests/fixtures/cases/static-client-partitions/ns-default-partition/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-client-partitions/ns-default-partition/patch.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-client-partitions/ns-partition/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-partitions/ns-partition/kustomization.yaml index 0ae44380dd..7191edfb80 100644 --- a/acceptance/tests/fixtures/cases/static-client-partitions/ns-partition/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-partitions/ns-partition/kustomization.yaml @@ -1,9 +1,5 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization resources: -- ../../../bases/static-client -patches: -- path: patch.yaml + - ../../../bases/static-client + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/static-client-partitions/ns-partition/patch.yaml b/acceptance/tests/fixtures/cases/static-client-partitions/ns-partition/patch.yaml index df04ceccda..f0ceb634bd 100644 --- a/acceptance/tests/fixtures/cases/static-client-partitions/ns-partition/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-client-partitions/ns-partition/patch.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-client-peers/default-namespace/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-peers/default-namespace/kustomization.yaml index 0ae44380dd..7191edfb80 100644 --- a/acceptance/tests/fixtures/cases/static-client-peers/default-namespace/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-peers/default-namespace/kustomization.yaml @@ -1,9 +1,5 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization resources: -- ../../../bases/static-client -patches: -- path: patch.yaml + - ../../../bases/static-client + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/static-client-peers/default-namespace/patch.yaml b/acceptance/tests/fixtures/cases/static-client-peers/default-namespace/patch.yaml index 075cb6ecd4..02ea8993ec 100644 --- a/acceptance/tests/fixtures/cases/static-client-peers/default-namespace/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-client-peers/default-namespace/patch.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-client-peers/default/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-peers/default/kustomization.yaml index 0ae44380dd..7191edfb80 100644 --- a/acceptance/tests/fixtures/cases/static-client-peers/default/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-peers/default/kustomization.yaml @@ -1,9 +1,5 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization resources: -- ../../../bases/static-client -patches: -- path: patch.yaml + - ../../../bases/static-client + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/static-client-peers/default/patch.yaml b/acceptance/tests/fixtures/cases/static-client-peers/default/patch.yaml index a6a2f72b37..715485e0f8 100644 --- a/acceptance/tests/fixtures/cases/static-client-peers/default/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-client-peers/default/patch.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-client-peers/non-default-namespace/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-peers/non-default-namespace/kustomization.yaml index 0ae44380dd..7191edfb80 100644 --- a/acceptance/tests/fixtures/cases/static-client-peers/non-default-namespace/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-peers/non-default-namespace/kustomization.yaml @@ -1,9 +1,5 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization resources: -- ../../../bases/static-client -patches: -- path: patch.yaml + - ../../../bases/static-client + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/static-client-peers/non-default-namespace/patch.yaml b/acceptance/tests/fixtures/cases/static-client-peers/non-default-namespace/patch.yaml index 5a7ce34e1b..fd622759a4 100644 --- a/acceptance/tests/fixtures/cases/static-client-peers/non-default-namespace/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-client-peers/non-default-namespace/patch.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-client-tproxy/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-tproxy/kustomization.yaml index 564d02a68f..9834f91903 100644 --- a/acceptance/tests/fixtures/cases/static-client-tproxy/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-client-tproxy/kustomization.yaml @@ -1,9 +1,5 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization resources: -- ../../bases/static-client -patches: -- path: patch.yaml + - ../../bases/static-client + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/static-client-tproxy/patch.yaml b/acceptance/tests/fixtures/cases/static-client-tproxy/patch.yaml index 8540fc4721..573bf93b01 100644 --- a/acceptance/tests/fixtures/cases/static-client-tproxy/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-client-tproxy/patch.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-server-inject/kustomization.yaml b/acceptance/tests/fixtures/cases/static-server-inject/kustomization.yaml index bd2c22ff5f..bac892baa0 100644 --- a/acceptance/tests/fixtures/cases/static-server-inject/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-server-inject/kustomization.yaml @@ -1,9 +1,5 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization resources: -- ../../bases/static-server -patches: -- path: patch.yaml + - ../../bases/static-server + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/static-server-inject/patch.yaml b/acceptance/tests/fixtures/cases/static-server-inject/patch.yaml index 56ef015d8f..f2bf0d6539 100644 --- a/acceptance/tests/fixtures/cases/static-server-inject/patch.yaml +++ b/acceptance/tests/fixtures/cases/static-server-inject/patch.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: apps/v1 kind: Deployment metadata: diff --git a/acceptance/tests/fixtures/cases/static-server-openshift/kustomization.yaml b/acceptance/tests/fixtures/cases/static-server-openshift/kustomization.yaml index bd2c22ff5f..bc50c78adf 100644 --- a/acceptance/tests/fixtures/cases/static-server-openshift/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/static-server-openshift/kustomization.yaml @@ -1,9 +1,8 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization resources: -- ../../bases/static-server -patches: -- path: patch.yaml + - ../../bases/static-server + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/ingress-gateway/ingress_gateway_namespaces_test.go b/acceptance/tests/ingress-gateway/ingress_gateway_namespaces_test.go index 9edb1db010..b713620f1e 100644 --- a/acceptance/tests/ingress-gateway/ingress_gateway_namespaces_test.go +++ b/acceptance/tests/ingress-gateway/ingress_gateway_namespaces_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package ingressgateway import ( @@ -69,7 +66,7 @@ func TestIngressGatewaySingleNamespace(t *testing.T) { logger.Logf(t, "creating Kubernetes namespace %s", testNamespace) k8s.RunKubectl(t, ctx.KubectlOptions(t), "create", "ns", testNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "ns", testNamespace) }) @@ -80,12 +77,12 @@ func TestIngressGatewaySingleNamespace(t *testing.T) { } logger.Logf(t, "creating server in %s namespace", testNamespace) - k8s.DeployKustomize(t, nsK8SOptions, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, nsK8SOptions, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") // We use the static-client pod so that we can make calls to the ingress gateway // via kubectl exec without needing a route into the cluster from the test machine. logger.Logf(t, "creating static-client in %s namespace", testNamespace) - k8s.DeployKustomize(t, nsK8SOptions, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-client") + k8s.DeployKustomize(t, nsK8SOptions, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-client") // With the cluster up, we can create our ingress-gateway config entry. logger.Log(t, "creating config entry") @@ -188,7 +185,7 @@ func TestIngressGatewayNamespaceMirroring(t *testing.T) { logger.Logf(t, "creating Kubernetes namespace %s", testNamespace) k8s.RunKubectl(t, ctx.KubectlOptions(t), "create", "ns", testNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "ns", testNamespace) }) @@ -199,12 +196,12 @@ func TestIngressGatewayNamespaceMirroring(t *testing.T) { } logger.Logf(t, "creating server in %s namespace", testNamespace) - k8s.DeployKustomize(t, nsK8SOptions, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, nsK8SOptions, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") // We use the static-client pod so that we can make calls to the ingress gateway // via kubectl exec without needing a route into the cluster from the test machine. logger.Logf(t, "creating static-client in %s namespace", testNamespace) - k8s.DeployKustomize(t, nsK8SOptions, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-client") + k8s.DeployKustomize(t, nsK8SOptions, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-client") consulClient, _ := consulCluster.SetupConsulClient(t, c.secure) diff --git a/acceptance/tests/ingress-gateway/ingress_gateway_test.go b/acceptance/tests/ingress-gateway/ingress_gateway_test.go index d2b3d7193e..b6535439c1 100644 --- a/acceptance/tests/ingress-gateway/ingress_gateway_test.go +++ b/acceptance/tests/ingress-gateway/ingress_gateway_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package ingressgateway import ( @@ -52,12 +49,12 @@ func TestIngressGateway(t *testing.T) { consulCluster.Create(t) logger.Log(t, "creating server") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") // We use the static-client pod so that we can make calls to the ingress gateway // via kubectl exec without needing a route into the cluster from the test machine. logger.Log(t, "creating static-client pod") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-client") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-client") // With the cluster up, we can create our ingress-gateway config entry. logger.Log(t, "creating config entry") diff --git a/acceptance/tests/ingress-gateway/main_test.go b/acceptance/tests/ingress-gateway/main_test.go index 4a74d2f361..bd927f96cb 100644 --- a/acceptance/tests/ingress-gateway/main_test.go +++ b/acceptance/tests/ingress-gateway/main_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package ingressgateway import ( diff --git a/acceptance/tests/metrics/main_test.go b/acceptance/tests/metrics/main_test.go index d3c15b811b..8717c7c4b5 100644 --- a/acceptance/tests/metrics/main_test.go +++ b/acceptance/tests/metrics/main_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package metrics import ( diff --git a/acceptance/tests/metrics/metrics_test.go b/acceptance/tests/metrics/metrics_test.go index 3d40841e36..5d05e45aa4 100644 --- a/acceptance/tests/metrics/metrics_test.go +++ b/acceptance/tests/metrics/metrics_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package metrics import ( @@ -71,7 +68,7 @@ func TestComponentMetrics(t *testing.T) { // This simulates queries that would be made by a prometheus server that runs externally to the consul // components in the cluster. logger.Log(t, "creating static-client") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-client") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-client") // Server Metrics metricsOutput, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "exec", "deploy/"+StaticClientName, "--", "curl", "--silent", "--show-error", fmt.Sprintf("http://%s:8500/v1/agent/metrics?format=prometheus", fmt.Sprintf("%s-consul-server.%s.svc", releaseName, ns))) @@ -116,13 +113,13 @@ func TestAppMetrics(t *testing.T) { // Deploy service that will emit app and envoy metrics at merged metrics endpoint logger.Log(t, "creating static-metrics-app") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-metrics-app") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-metrics-app") // Create the static-client deployment so we can use it for in-cluster calls to metrics endpoints. // This simulates queries that would be made by a prometheus server that runs externally to the consul // components in the cluster. logger.Log(t, "creating static-client") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-client") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-client") // Merged App Metrics podList, err := ctx.KubernetesClient(t).CoreV1().Pods(ns).List(context.Background(), metav1.ListOptions{LabelSelector: "app=static-metrics-app"}) @@ -132,7 +129,7 @@ func TestAppMetrics(t *testing.T) { // Retry because sometimes the merged metrics server takes a couple hundred milliseconds // to start. - retry.RunWith(&retry.Counter{Count: 20, Wait: 2 * time.Second}, t, func(r *retry.R) { + retry.RunWith(&retry.Counter{Count: 3, Wait: 1 * time.Second}, t, func(r *retry.R) { metricsOutput, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "exec", "deploy/"+StaticClientName, "--", "curl", "--silent", "--show-error", fmt.Sprintf("http://%s:20200/metrics", podIP)) require.NoError(r, err) // This assertion represents the metrics from the envoy sidecar. diff --git a/acceptance/tests/partitions/main_test.go b/acceptance/tests/partitions/main_test.go index 89833ec2cc..2052a1b74e 100644 --- a/acceptance/tests/partitions/main_test.go +++ b/acceptance/tests/partitions/main_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package partitions import ( diff --git a/acceptance/tests/partitions/partitions_connect_test.go b/acceptance/tests/partitions/partitions_connect_test.go index c53e5b6171..ae2f0688f6 100644 --- a/acceptance/tests/partitions/partitions_connect_test.go +++ b/acceptance/tests/partitions/partitions_connect_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package partitions import ( @@ -108,7 +105,6 @@ func TestPartitions_Connect(t *testing.T) { "dns.enableRedirection": strconv.FormatBool(cfg.EnableTransparentProxy), } - // Setup the default partition defaultPartitionHelmValues := make(map[string]string) // On Kind, there are no load balancers but since all clusters @@ -130,7 +126,6 @@ func TestPartitions_Connect(t *testing.T) { serverConsulCluster := consul.NewHelmCluster(t, defaultPartitionHelmValues, defaultPartitionClusterContext, cfg, releaseName) serverConsulCluster.Create(t) - // Copy secrets from the default partition to the secondary partition // Get the TLS CA certificate and key secret from the server cluster and apply it to the client cluster. caCertSecretName := fmt.Sprintf("%s-consul-ca-cert", releaseName) @@ -148,7 +143,7 @@ func TestPartitions_Connect(t *testing.T) { k8sAuthMethodHost := k8s.KubernetesAPIServerHost(t, cfg, secondaryPartitionClusterContext) - // Create secondary partition cluster. + // Create client cluster. secondaryPartitionHelmValues := map[string]string{ "global.enabled": "false", @@ -206,14 +201,14 @@ func TestPartitions_Connect(t *testing.T) { logger.Logf(t, "creating namespaces %s and %s in servers cluster", staticServerNamespace, StaticClientNamespace) k8s.RunKubectl(t, defaultPartitionClusterContext.KubectlOptions(t), "create", "ns", staticServerNamespace) k8s.RunKubectl(t, defaultPartitionClusterContext.KubectlOptions(t), "create", "ns", StaticClientNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.RunKubectl(t, defaultPartitionClusterContext.KubectlOptions(t), "delete", "ns", staticServerNamespace, StaticClientNamespace) }) logger.Logf(t, "creating namespaces %s and %s in clients cluster", staticServerNamespace, StaticClientNamespace) k8s.RunKubectl(t, secondaryPartitionClusterContext.KubectlOptions(t), "create", "ns", staticServerNamespace) k8s.RunKubectl(t, secondaryPartitionClusterContext.KubectlOptions(t), "create", "ns", StaticClientNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.RunKubectl(t, secondaryPartitionClusterContext.KubectlOptions(t), "delete", "ns", staticServerNamespace, StaticClientNamespace) }) @@ -273,37 +268,37 @@ func TestPartitions_Connect(t *testing.T) { kustomizeDir := "../fixtures/bases/mesh-gateway" k8s.KubectlApplyK(t, defaultPartitionClusterContext.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.KubectlDeleteK(t, defaultPartitionClusterContext.KubectlOptions(t), kustomizeDir) }) k8s.KubectlApplyK(t, secondaryPartitionClusterContext.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.KubectlDeleteK(t, secondaryPartitionClusterContext.KubectlOptions(t), kustomizeDir) }) // This section of the tests runs the in-partition networking tests. t.Run("in-partition", func(t *testing.T) { logger.Log(t, "test in-partition networking") logger.Log(t, "creating static-server and static-client deployments in server cluster") - k8s.DeployKustomize(t, defaultPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, defaultPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") if cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, defaultPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, defaultPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { if c.destinationNamespace == defaultNamespace { - k8s.DeployKustomize(t, defaultPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + k8s.DeployKustomize(t, defaultPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") } else { - k8s.DeployKustomize(t, defaultPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") + k8s.DeployKustomize(t, defaultPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") } } logger.Log(t, "creating static-server and static-client deployments in client cluster") - k8s.DeployKustomize(t, secondaryPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, secondaryPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") if cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { if c.destinationNamespace == defaultNamespace { - k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") } else { - k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") + k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") } } // Check that both static-server and static-client have been injected and now have 2 containers in server cluster. @@ -385,7 +380,7 @@ func TestPartitions_Connect(t *testing.T) { require.NoError(t, err) _, _, err = consulClient.ConfigEntries().Set(intention, &api.WriteOptions{Partition: secondaryPartition}) require.NoError(t, err) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { _, err := consulClient.ConfigEntries().Delete(api.ServiceIntentions, staticServerName, &api.WriteOptions{Partition: defaultPartition}) require.NoError(t, err) _, err = consulClient.ConfigEntries().Delete(api.ServiceIntentions, staticServerName, &api.WriteOptions{Partition: secondaryPartition}) @@ -426,25 +421,25 @@ func TestPartitions_Connect(t *testing.T) { t.Run("cross-partition", func(t *testing.T) { logger.Log(t, "test cross-partition networking") logger.Log(t, "creating static-server and static-client deployments in server cluster") - k8s.DeployKustomize(t, defaultPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, defaultPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") if cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, defaultPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, defaultPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { if c.destinationNamespace == defaultNamespace { - k8s.DeployKustomize(t, defaultPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-partitions/default-ns-partition") + k8s.DeployKustomize(t, defaultPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-partitions/default-ns-partition") } else { - k8s.DeployKustomize(t, defaultPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-partitions/ns-partition") + k8s.DeployKustomize(t, defaultPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-partitions/ns-partition") } } logger.Log(t, "creating static-server and static-client deployments in client cluster") - k8s.DeployKustomize(t, secondaryPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, secondaryPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") if cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { if c.destinationNamespace == defaultNamespace { - k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-partitions/default-ns-default-partition") + k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-partitions/default-ns-default-partition") } else { - k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-partitions/ns-default-partition") + k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-partitions/ns-default-partition") } } // Check that both static-server and static-client have been injected and now have 2 containers in server cluster. @@ -498,14 +493,14 @@ func TestPartitions_Connect(t *testing.T) { if c.destinationNamespace == defaultNamespace { k8s.KubectlApplyK(t, defaultPartitionClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/default-partition-default") k8s.KubectlApplyK(t, secondaryPartitionClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/secondary-partition-default") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.KubectlDeleteK(t, defaultPartitionClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/default-partition-default") k8s.KubectlDeleteK(t, secondaryPartitionClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/secondary-partition-default") }) } else { k8s.KubectlApplyK(t, defaultPartitionClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/default-partition-ns1") k8s.KubectlApplyK(t, secondaryPartitionClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/secondary-partition-ns1") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.KubectlDeleteK(t, defaultPartitionClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/default-partition-ns1") k8s.KubectlDeleteK(t, secondaryPartitionClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/secondary-partition-ns1") }) @@ -553,7 +548,7 @@ func TestPartitions_Connect(t *testing.T) { intention.Sources[0].Partition = defaultPartition _, _, err = consulClient.ConfigEntries().Set(intention, &api.WriteOptions{Partition: secondaryPartition}) require.NoError(t, err) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { _, err := consulClient.ConfigEntries().Delete(api.ServiceIntentions, staticServerName, &api.WriteOptions{Partition: defaultPartition}) require.NoError(t, err) _, err = consulClient.ConfigEntries().Delete(api.ServiceIntentions, staticServerName, &api.WriteOptions{Partition: secondaryPartition}) diff --git a/acceptance/tests/partitions/partitions_gateway_test.go b/acceptance/tests/partitions/partitions_gateway_test.go deleted file mode 100644 index a90a790cb6..0000000000 --- a/acceptance/tests/partitions/partitions_gateway_test.go +++ /dev/null @@ -1,360 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package partitions - -import ( - "context" - "fmt" - "strconv" - "testing" - "time" - - terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" - "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" - "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" - "github.com/hashicorp/consul-k8s/acceptance/framework/logger" - "github.com/hashicorp/consul/api" - "github.com/hashicorp/consul/sdk/testutil/retry" - "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -// Test that Gateway works in a default and ACLsEnabled installations for X-Partition and in-partition networking. -func TestPartitions_Gateway(t *testing.T) { - env := suite.Environment() - cfg := suite.Config() - - if !cfg.EnableEnterprise { - t.Skipf("skipping this test because -enable-enterprise is not set") - } - - const defaultPartition = "default" - const secondaryPartition = "secondary" - - defaultPartitionClusterContext := env.DefaultContext(t) - secondaryPartitionClusterContext := env.Context(t, 1) - - commonHelmValues := map[string]string{ - "global.adminPartitions.enabled": "true", - "global.enableConsulNamespaces": "true", - "global.logLevel": "debug", - - "global.tls.enabled": "true", - "global.tls.httpsOnly": "true", - - "global.acls.manageSystemACLs": "true", - - "connectInject.enabled": "true", - // When mirroringK8S is set, this setting is ignored. - "connectInject.consulNamespaces.consulDestinationNamespace": staticServerNamespace, - "connectInject.consulNamespaces.mirroringK8S": "true", - - "meshGateway.enabled": "true", - "meshGateway.replicas": "1", - - "dns.enabled": "true", - "dns.enableRedirection": strconv.FormatBool(cfg.EnableTransparentProxy), - } - - defaultPartitionHelmValues := make(map[string]string) - - // On Kind, there are no load balancers but since all clusters - // share the same node network (docker bridge), we can use - // a NodePort service so that we can access node(s) in a different Kind cluster. - if cfg.UseKind { - defaultPartitionHelmValues["meshGateway.service.type"] = "NodePort" - defaultPartitionHelmValues["meshGateway.service.nodePort"] = "30200" // todo: do we need to set this port? - defaultPartitionHelmValues["server.exposeService.type"] = "NodePort" - defaultPartitionHelmValues["server.exposeService.nodePort.https"] = "30000" - defaultPartitionHelmValues["server.exposeService.nodePort.grpc"] = "30100" - } - - releaseName := helpers.RandomName() - - helpers.MergeMaps(defaultPartitionHelmValues, commonHelmValues) - - // Install the consul cluster with servers in the default kubernetes context. - serverConsulCluster := consul.NewHelmCluster(t, defaultPartitionHelmValues, defaultPartitionClusterContext, cfg, releaseName) - serverConsulCluster.Create(t) - - // Get the TLS CA certificate and key secret from the server cluster and apply it to the client cluster. - caCertSecretName := fmt.Sprintf("%s-consul-ca-cert", releaseName) - - logger.Logf(t, "retrieving ca cert secret %s from the server cluster and applying to the client cluster", caCertSecretName) - k8s.CopySecret(t, defaultPartitionClusterContext, secondaryPartitionClusterContext, caCertSecretName) - - partitionToken := fmt.Sprintf("%s-consul-partitions-acl-token", releaseName) - logger.Logf(t, "retrieving partition token secret %s from the server cluster and applying to the client cluster", partitionToken) - k8s.CopySecret(t, defaultPartitionClusterContext, secondaryPartitionClusterContext, partitionToken) - - partitionServiceName := fmt.Sprintf("%s-consul-expose-servers", releaseName) - partitionSvcAddress := k8s.ServiceHost(t, cfg, defaultPartitionClusterContext, partitionServiceName) - - k8sAuthMethodHost := k8s.KubernetesAPIServerHost(t, cfg, secondaryPartitionClusterContext) - - // Create client cluster. - secondaryPartitionHelmValues := map[string]string{ - "global.enabled": "false", - - "global.adminPartitions.name": secondaryPartition, - - "global.tls.caCert.secretName": caCertSecretName, - "global.tls.caCert.secretKey": "tls.crt", - - "externalServers.enabled": "true", - "externalServers.hosts[0]": partitionSvcAddress, - "externalServers.tlsServerName": "server.dc1.consul", - } - - // Setup partition token and auth method host since ACLs enabled. - secondaryPartitionHelmValues["global.acls.bootstrapToken.secretName"] = partitionToken - secondaryPartitionHelmValues["global.acls.bootstrapToken.secretKey"] = "token" - secondaryPartitionHelmValues["externalServers.k8sAuthMethodHost"] = k8sAuthMethodHost - - if cfg.UseKind { - secondaryPartitionHelmValues["externalServers.httpsPort"] = "30000" - secondaryPartitionHelmValues["externalServers.grpcPort"] = "30100" - secondaryPartitionHelmValues["meshGateway.service.type"] = "NodePort" - secondaryPartitionHelmValues["meshGateway.service.nodePort"] = "30200" - } - - helpers.MergeMaps(secondaryPartitionHelmValues, commonHelmValues) - - // Install the consul cluster without servers in the client cluster kubernetes context. - clientConsulCluster := consul.NewHelmCluster(t, secondaryPartitionHelmValues, secondaryPartitionClusterContext, cfg, releaseName) - clientConsulCluster.Create(t) - - defaultPartitionClusterStaticServerOpts := &terratestk8s.KubectlOptions{ - ContextName: defaultPartitionClusterContext.KubectlOptions(t).ContextName, - ConfigPath: defaultPartitionClusterContext.KubectlOptions(t).ConfigPath, - Namespace: staticServerNamespace, - } - secondaryPartitionClusterStaticServerOpts := &terratestk8s.KubectlOptions{ - ContextName: secondaryPartitionClusterContext.KubectlOptions(t).ContextName, - ConfigPath: secondaryPartitionClusterContext.KubectlOptions(t).ConfigPath, - Namespace: staticServerNamespace, - } - secondaryPartitionClusterStaticClientOpts := &terratestk8s.KubectlOptions{ - ContextName: secondaryPartitionClusterContext.KubectlOptions(t).ContextName, - ConfigPath: secondaryPartitionClusterContext.KubectlOptions(t).ConfigPath, - Namespace: StaticClientNamespace, - } - - logger.Logf(t, "creating namespaces %s and %s in servers cluster", staticServerNamespace, StaticClientNamespace) - k8s.RunKubectl(t, defaultPartitionClusterContext.KubectlOptions(t), "create", "ns", staticServerNamespace) - k8s.RunKubectl(t, defaultPartitionClusterContext.KubectlOptions(t), "create", "ns", StaticClientNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.RunKubectl(t, defaultPartitionClusterContext.KubectlOptions(t), "delete", "ns", staticServerNamespace, StaticClientNamespace) - }) - - logger.Logf(t, "creating namespaces %s and %s in clients cluster", staticServerNamespace, StaticClientNamespace) - k8s.RunKubectl(t, secondaryPartitionClusterContext.KubectlOptions(t), "create", "ns", staticServerNamespace) - k8s.RunKubectl(t, secondaryPartitionClusterContext.KubectlOptions(t), "create", "ns", StaticClientNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.RunKubectl(t, secondaryPartitionClusterContext.KubectlOptions(t), "delete", "ns", staticServerNamespace, StaticClientNamespace) - }) - - consulClient, _ := serverConsulCluster.SetupConsulClient(t, true) - - serverQueryServerOpts := &api.QueryOptions{Namespace: staticServerNamespace, Partition: defaultPartition} - clientQueryServerOpts := &api.QueryOptions{Namespace: StaticClientNamespace, Partition: defaultPartition} - - serverQueryClientOpts := &api.QueryOptions{Namespace: staticServerNamespace, Partition: secondaryPartition} - clientQueryClientOpts := &api.QueryOptions{Namespace: StaticClientNamespace, Partition: secondaryPartition} - - // We need to register the cleanup function before we create the deployments - // because golang will execute them in reverse order i.e. the last registered - // cleanup function will be executed first. - t.Cleanup(func() { - retry.Run(t, func(r *retry.R) { - tokens, _, err := consulClient.ACL().TokenList(serverQueryServerOpts) - require.NoError(r, err) - for _, token := range tokens { - require.NotContains(r, token.Description, staticServerName) - } - - tokens, _, err = consulClient.ACL().TokenList(clientQueryServerOpts) - require.NoError(r, err) - for _, token := range tokens { - require.NotContains(r, token.Description, StaticClientName) - } - tokens, _, err = consulClient.ACL().TokenList(serverQueryClientOpts) - require.NoError(r, err) - for _, token := range tokens { - require.NotContains(r, token.Description, staticServerName) - } - - tokens, _, err = consulClient.ACL().TokenList(clientQueryClientOpts) - require.NoError(r, err) - for _, token := range tokens { - require.NotContains(r, token.Description, StaticClientName) - } - }) - }) - - // Create a ProxyDefaults resource to configure services to use the mesh - // gateways. - logger.Log(t, "creating proxy-defaults config") - kustomizeDir := "../fixtures/cases/api-gateways/mesh" - - k8s.KubectlApplyK(t, defaultPartitionClusterContext.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.KubectlDeleteK(t, defaultPartitionClusterContext.KubectlOptions(t), kustomizeDir) - }) - - k8s.KubectlApplyK(t, secondaryPartitionClusterContext.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.KubectlDeleteK(t, secondaryPartitionClusterContext.KubectlOptions(t), kustomizeDir) - }) - - // We use the static-client pod so that we can make calls to the api gateway - // via kubectl exec without needing a route into the cluster from the test machine. - // Since we're deploying the gateway in the secondary cluster, we create the static client - // in the secondary as well. - logger.Log(t, "creating static-client pod in secondary partition cluster") - k8s.DeployKustomize(t, secondaryPartitionClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-client") - - logger.Log(t, "creating api-gateway resources") - out, err := k8s.RunKubectlAndGetOutputE(t, secondaryPartitionClusterStaticServerOpts, "apply", "-k", "../fixtures/bases/api-gateway") - require.NoError(t, err, out) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - // Ignore errors here because if the test ran as expected - // the custom resources will have been deleted. - k8s.RunKubectlAndGetOutputE(t, secondaryPartitionClusterStaticServerOpts, "delete", "-k", "../fixtures/bases/api-gateway") - }) - - // Grab a kubernetes client so that we can verify binding - // behavior prior to issuing requests through the gateway. - k8sClient := secondaryPartitionClusterContext.ControllerRuntimeClient(t) - - // On startup, the controller can take upwards of 1m to perform - // leader election so we may need to wait a long time for - // the reconcile loop to run (hence the 1m timeout here). - var gatewayAddress string - counter := &retry.Counter{Count: 600, Wait: 2 * time.Second} - retry.RunWith(counter, t, func(r *retry.R) { - var gateway gwv1beta1.Gateway - err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway", Namespace: staticServerNamespace}, &gateway) - require.NoError(r, err) - - // check that we have an address to use - require.Len(r, gateway.Status.Addresses, 1) - // now we know we have an address, set it so we can use it - gatewayAddress = gateway.Status.Addresses[0].Value - }) - - targetAddress := fmt.Sprintf("http://%s/", gatewayAddress) - - // This section of the tests runs the in-partition networking tests. - t.Run("in-partition", func(t *testing.T) { - logger.Log(t, "test in-partition networking") - logger.Log(t, "creating target server in secondary partition cluster") - k8s.DeployKustomize(t, secondaryPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") - - // Check that static-server injected 2 containers. - for _, labelSelector := range []string{"app=static-server"} { - podList, err := secondaryPartitionClusterContext.KubernetesClient(t).CoreV1().Pods(metav1.NamespaceAll).List(context.Background(), metav1.ListOptions{ - LabelSelector: labelSelector, - }) - require.NoError(t, err) - require.Len(t, podList.Items, 1) - require.Len(t, podList.Items[0].Spec.Containers, 2) - } - - logger.Log(t, "patching route to target server") - k8s.RunKubectl(t, secondaryPartitionClusterStaticServerOpts, "patch", "httproute", "http-route", "-p", `{"spec":{"rules":[{"backendRefs":[{"group":"consul.hashicorp.com","kind":"MeshService","name":"mesh-service","port":80}]}]}}`, "--type=merge") - - logger.Log(t, "checking that the connection is not successful because there's no intention") - k8s.CheckStaticServerHTTPConnectionFailing(t, secondaryPartitionClusterStaticClientOpts, StaticClientName, targetAddress) - - intention := &api.ServiceIntentionsConfigEntry{ - Kind: api.ServiceIntentions, - Name: staticServerName, - Namespace: staticServerNamespace, - Sources: []*api.SourceIntention{ - { - Name: "gateway", - Namespace: staticServerNamespace, - Action: api.IntentionActionAllow, - }, - }, - } - - logger.Log(t, "creating intention") - _, _, err = consulClient.ConfigEntries().Set(intention, &api.WriteOptions{Partition: secondaryPartition}) - require.NoError(t, err) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - _, err = consulClient.ConfigEntries().Delete(api.ServiceIntentions, staticServerName, &api.WriteOptions{Partition: secondaryPartition}) - require.NoError(t, err) - }) - - logger.Log(t, "checking that connection is successful") - k8s.CheckStaticServerConnectionSuccessful(t, secondaryPartitionClusterStaticClientOpts, StaticClientName, targetAddress) - }) - - // This section of the tests runs the cross-partition networking tests. - t.Run("cross-partition", func(t *testing.T) { - logger.Log(t, "test cross-partition networking") - - logger.Log(t, "creating target server in default partition cluster") - k8s.DeployKustomize(t, defaultPartitionClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") - - // Check that static-server injected 2 containers. - for _, labelSelector := range []string{"app=static-server"} { - podList, err := defaultPartitionClusterContext.KubernetesClient(t).CoreV1().Pods(metav1.NamespaceAll).List(context.Background(), metav1.ListOptions{ - LabelSelector: labelSelector, - }) - require.NoError(t, err) - require.Len(t, podList.Items, 1) - require.Len(t, podList.Items[0].Spec.Containers, 2) - } - - logger.Log(t, "creating exported services") - k8s.KubectlApplyK(t, defaultPartitionClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/default-partition-ns1") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.KubectlDeleteK(t, defaultPartitionClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/default-partition-ns1") - }) - - logger.Log(t, "creating local service resolver") - k8s.KubectlApplyK(t, secondaryPartitionClusterStaticServerOpts, "../fixtures/cases/api-gateways/resolver") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.KubectlDeleteK(t, secondaryPartitionClusterStaticServerOpts, "../fixtures/cases/api-gateways/resolver") - }) - - logger.Log(t, "patching route to target server") - k8s.RunKubectl(t, secondaryPartitionClusterStaticServerOpts, "patch", "httproute", "http-route", "-p", `{"spec":{"rules":[{"backendRefs":[{"group":"consul.hashicorp.com","kind":"MeshService","name":"mesh-service","port":80}]}]}}`, "--type=merge") - - logger.Log(t, "checking that the connection is not successful because there's no intention") - k8s.CheckStaticServerHTTPConnectionFailing(t, secondaryPartitionClusterStaticClientOpts, StaticClientName, targetAddress) - - intention := &api.ServiceIntentionsConfigEntry{ - Kind: api.ServiceIntentions, - Name: staticServerName, - Namespace: staticServerNamespace, - Sources: []*api.SourceIntention{ - { - Name: "gateway", - Namespace: staticServerNamespace, - Action: api.IntentionActionAllow, - Partition: secondaryPartition, - }, - }, - } - - logger.Log(t, "creating intention") - _, _, err = consulClient.ConfigEntries().Set(intention, &api.WriteOptions{Partition: defaultPartition}) - require.NoError(t, err) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - _, err = consulClient.ConfigEntries().Delete(api.ServiceIntentions, staticServerName, &api.WriteOptions{Partition: defaultPartition}) - require.NoError(t, err) - }) - - logger.Log(t, "checking that connection is successful") - k8s.CheckStaticServerConnectionSuccessful(t, secondaryPartitionClusterStaticClientOpts, StaticClientName, targetAddress) - }) -} diff --git a/acceptance/tests/partitions/partitions_sync_test.go b/acceptance/tests/partitions/partitions_sync_test.go index da95d7f272..8d761d0765 100644 --- a/acceptance/tests/partitions/partitions_sync_test.go +++ b/acceptance/tests/partitions/partitions_sync_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package partitions import ( @@ -195,13 +192,13 @@ func TestPartitions_Sync(t *testing.T) { logger.Logf(t, "creating namespaces %s in servers cluster", staticServerNamespace) k8s.RunKubectl(t, primaryClusterContext.KubectlOptions(t), "create", "ns", staticServerNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.RunKubectl(t, primaryClusterContext.KubectlOptions(t), "delete", "ns", staticServerNamespace) }) logger.Logf(t, "creating namespaces %s in clients cluster", staticServerNamespace) k8s.RunKubectl(t, secondaryClusterContext.KubectlOptions(t), "create", "ns", staticServerNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.RunKubectl(t, secondaryClusterContext.KubectlOptions(t), "delete", "ns", staticServerNamespace) }) @@ -241,13 +238,13 @@ func TestPartitions_Sync(t *testing.T) { logger.Log(t, "creating a static-server with a service") // create service in default partition. - k8s.DeployKustomize(t, primaryStaticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-server") + k8s.DeployKustomize(t, primaryStaticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-server") // create service in secondary partition. - k8s.DeployKustomize(t, secondaryStaticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-server") + k8s.DeployKustomize(t, secondaryStaticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-server") logger.Log(t, "checking that the service has been synced to Consul") var services map[string][]string - counter := &retry.Counter{Count: 30, Wait: 30 * time.Second} + counter := &retry.Counter{Count: 20, Wait: 30 * time.Second} retry.RunWith(counter, t, func(r *retry.R) { var err error // list services in default partition catalog. diff --git a/acceptance/tests/peering/main_test.go b/acceptance/tests/peering/main_test.go index 075051861d..df9812171c 100644 --- a/acceptance/tests/peering/main_test.go +++ b/acceptance/tests/peering/main_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package peering import ( diff --git a/acceptance/tests/peering/peering_connect_namespaces_test.go b/acceptance/tests/peering/peering_connect_namespaces_test.go index 618248c6a9..b52d2793d0 100644 --- a/acceptance/tests/peering/peering_connect_namespaces_test.go +++ b/acceptance/tests/peering/peering_connect_namespaces_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package peering import ( @@ -166,12 +163,12 @@ func TestPeering_ConnectNamespaces(t *testing.T) { kustomizeMeshDir := "../fixtures/bases/mesh-peering" k8s.KubectlApplyK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.KubectlDeleteK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) }) k8s.KubectlApplyK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.KubectlDeleteK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) }) @@ -198,7 +195,7 @@ func TestPeering_ConnectNamespaces(t *testing.T) { // Create the peering acceptor on the client peer. k8s.KubectlApply(t, staticClientPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-acceptor.yaml") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.KubectlDelete(t, staticClientPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-acceptor.yaml") }) @@ -214,7 +211,7 @@ func TestPeering_ConnectNamespaces(t *testing.T) { // Create the peering dialer on the server peer. k8s.KubectlApply(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-dialer.yaml") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.RunKubectl(t, staticServerPeerClusterContext.KubectlOptions(t), "delete", "secret", "api-token") k8s.KubectlDelete(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-dialer.yaml") }) @@ -232,13 +229,13 @@ func TestPeering_ConnectNamespaces(t *testing.T) { logger.Logf(t, "creating namespaces %s in server peer", staticServerNamespace) k8s.RunKubectl(t, staticServerPeerClusterContext.KubectlOptions(t), "create", "ns", staticServerNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.RunKubectl(t, staticServerPeerClusterContext.KubectlOptions(t), "delete", "ns", staticServerNamespace) }) logger.Logf(t, "creating namespaces %s in client peer", staticClientNamespace) k8s.RunKubectl(t, staticClientPeerClusterContext.KubectlOptions(t), "create", "ns", staticClientNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.RunKubectl(t, staticClientPeerClusterContext.KubectlOptions(t), "delete", "ns", staticClientNamespace) }) @@ -256,26 +253,26 @@ func TestPeering_ConnectNamespaces(t *testing.T) { kustomizeDir := "../fixtures/bases/mesh-gateway" k8s.KubectlApplyK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.KubectlDeleteK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeDir) }) k8s.KubectlApplyK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.KubectlDeleteK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeDir) }) logger.Log(t, "creating static-server in server peer") - k8s.DeployKustomize(t, staticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, staticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") logger.Log(t, "creating static-client deployments in client peer") if cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { if c.destinationNamespace == defaultNamespace { - k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-peers/default-namespace") + k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-peers/default-namespace") } else { - k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-peers/non-default-namespace") + k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-peers/non-default-namespace") } } // Check that both static-server and static-client have been injected and now have 2 containers. @@ -312,12 +309,12 @@ func TestPeering_ConnectNamespaces(t *testing.T) { logger.Log(t, "creating exported services") if c.destinationNamespace == defaultNamespace { k8s.KubectlApplyK(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/cases/crd-peers/default-namespace") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.KubectlDeleteK(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/cases/crd-peers/default-namespace") }) } else { k8s.KubectlApplyK(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/cases/crd-peers/non-default-namespace") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.KubectlDeleteK(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/cases/crd-peers/non-default-namespace") }) } diff --git a/acceptance/tests/peering/peering_connect_test.go b/acceptance/tests/peering/peering_connect_test.go index d92ab59990..470ada08a1 100644 --- a/acceptance/tests/peering/peering_connect_test.go +++ b/acceptance/tests/peering/peering_connect_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package peering import ( @@ -121,12 +118,12 @@ func TestPeering_Connect(t *testing.T) { kustomizeMeshDir := "../fixtures/bases/mesh-peering" k8s.KubectlApplyK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.KubectlDeleteK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) }) k8s.KubectlApplyK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.KubectlDeleteK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) }) @@ -153,7 +150,7 @@ func TestPeering_Connect(t *testing.T) { // Create the peering acceptor on the client peer. k8s.KubectlApply(t, staticClientPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-acceptor.yaml") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.KubectlDelete(t, staticClientPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-acceptor.yaml") }) @@ -169,7 +166,7 @@ func TestPeering_Connect(t *testing.T) { // Create the peering dialer on the server peer. k8s.KubectlApply(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-dialer.yaml") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.RunKubectl(t, staticServerPeerClusterContext.KubectlOptions(t), "delete", "secret", "api-token") k8s.KubectlDelete(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-dialer.yaml") }) @@ -187,13 +184,13 @@ func TestPeering_Connect(t *testing.T) { logger.Logf(t, "creating namespaces %s in server peer", staticServerNamespace) k8s.RunKubectl(t, staticServerPeerClusterContext.KubectlOptions(t), "create", "ns", staticServerNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.RunKubectl(t, staticServerPeerClusterContext.KubectlOptions(t), "delete", "ns", staticServerNamespace) }) logger.Logf(t, "creating namespaces %s in client peer", staticClientNamespace) k8s.RunKubectl(t, staticClientPeerClusterContext.KubectlOptions(t), "create", "ns", staticClientNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.RunKubectl(t, staticClientPeerClusterContext.KubectlOptions(t), "delete", "ns", staticClientNamespace) }) @@ -203,23 +200,23 @@ func TestPeering_Connect(t *testing.T) { kustomizeDir := "../fixtures/bases/mesh-gateway" k8s.KubectlApplyK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.KubectlDeleteK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeDir) }) k8s.KubectlApplyK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.KubectlDeleteK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeDir) }) logger.Log(t, "creating static-server in server peer") - k8s.DeployKustomize(t, staticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, staticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") logger.Log(t, "creating static-client deployments in client peer") if cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { - k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-peers/default") + k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-peers/default") } // Check that both static-server and static-client have been injected and now have 2 containers. podList, err := staticServerPeerClusterContext.KubernetesClient(t).CoreV1().Pods(metav1.NamespaceAll).List(context.Background(), metav1.ListOptions{ @@ -249,7 +246,7 @@ func TestPeering_Connect(t *testing.T) { logger.Log(t, "creating exported services") k8s.KubectlApplyK(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/cases/crd-peers/default") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.KubectlDeleteK(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/cases/crd-peers/default") }) diff --git a/acceptance/tests/peering/peering_gateway_test.go b/acceptance/tests/peering/peering_gateway_test.go deleted file mode 100644 index 3ba04980a9..0000000000 --- a/acceptance/tests/peering/peering_gateway_test.go +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package peering - -import ( - "context" - "fmt" - "testing" - "time" - - terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" - "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" - "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" - "github.com/hashicorp/consul-k8s/acceptance/framework/logger" - "github.com/hashicorp/consul/api" - "github.com/hashicorp/consul/sdk/testutil/retry" - "github.com/hashicorp/go-version" - "github.com/stretchr/testify/require" - "k8s.io/apimachinery/pkg/types" - - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -func TestPeering_Gateway(t *testing.T) { - env := suite.Environment() - cfg := suite.Config() - - if !cfg.EnableEnterprise { - t.Skipf("skipping this test because -enable-enterprise is not set") - } - - ver, err := version.NewVersion("1.13.0") - require.NoError(t, err) - if cfg.ConsulVersion != nil && cfg.ConsulVersion.LessThan(ver) { - t.Skipf("skipping this test because peering is not supported in version %v", cfg.ConsulVersion.String()) - } - - const staticServerPeer = "server" - const staticClientPeer = "client" - - staticServerPeerClusterContext := env.DefaultContext(t) - staticClientPeerClusterContext := env.Context(t, 1) - - commonHelmValues := map[string]string{ - "global.peering.enabled": "true", - "global.enableConsulNamespaces": "true", - - "global.tls.enabled": "true", - "global.tls.httpsOnly": "true", - - "global.acls.manageSystemACLs": "true", - - "connectInject.enabled": "true", - - // When mirroringK8S is set, this setting is ignored. - "connectInject.consulNamespaces.mirroringK8S": "true", - - "meshGateway.enabled": "true", - "meshGateway.replicas": "1", - - "dns.enabled": "true", - } - - staticServerPeerHelmValues := map[string]string{ - "global.datacenter": staticServerPeer, - } - - if !cfg.UseKind { - staticServerPeerHelmValues["server.replicas"] = "3" - } - - // On Kind, there are no load balancers but since all clusters - // share the same node network (docker bridge), we can use - // a NodePort service so that we can access node(s) in a different Kind cluster. - if cfg.UseKind { - staticServerPeerHelmValues["server.exposeGossipAndRPCPorts"] = "true" - staticServerPeerHelmValues["meshGateway.service.type"] = "NodePort" - staticServerPeerHelmValues["meshGateway.service.nodePort"] = "30100" - } - - releaseName := helpers.RandomName() - - helpers.MergeMaps(staticServerPeerHelmValues, commonHelmValues) - - // Install the first peer where static-server will be deployed in the static-server kubernetes context. - staticServerPeerCluster := consul.NewHelmCluster(t, staticServerPeerHelmValues, staticServerPeerClusterContext, cfg, releaseName) - staticServerPeerCluster.Create(t) - - staticClientPeerHelmValues := map[string]string{ - "global.datacenter": staticClientPeer, - } - - if !cfg.UseKind { - staticClientPeerHelmValues["server.replicas"] = "3" - } - - if cfg.UseKind { - staticClientPeerHelmValues["server.exposeGossipAndRPCPorts"] = "true" - staticClientPeerHelmValues["meshGateway.service.type"] = "NodePort" - staticClientPeerHelmValues["meshGateway.service.nodePort"] = "30100" - } - - helpers.MergeMaps(staticClientPeerHelmValues, commonHelmValues) - - // Install the second peer where static-client will be deployed in the static-client kubernetes context. - staticClientPeerCluster := consul.NewHelmCluster(t, staticClientPeerHelmValues, staticClientPeerClusterContext, cfg, releaseName) - staticClientPeerCluster.Create(t) - - // Create Mesh resource to use mesh gateways. - logger.Log(t, "creating mesh config") - kustomizeMeshDir := "../fixtures/bases/mesh-peering" - - k8s.KubectlApplyK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.KubectlDeleteK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) - }) - - k8s.KubectlApplyK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.KubectlDeleteK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeMeshDir) - }) - - staticServerPeerClient, _ := staticServerPeerCluster.SetupConsulClient(t, true) - staticClientPeerClient, _ := staticClientPeerCluster.SetupConsulClient(t, true) - - // Ensure mesh config entries are created in Consul. - timer := &retry.Timer{Timeout: 1 * time.Minute, Wait: 1 * time.Second} - retry.RunWith(timer, t, func(r *retry.R) { - ceServer, _, err := staticServerPeerClient.ConfigEntries().Get(api.MeshConfig, "mesh", &api.QueryOptions{}) - require.NoError(r, err) - configEntryServer, ok := ceServer.(*api.MeshConfigEntry) - require.True(r, ok) - require.Equal(r, configEntryServer.GetName(), "mesh") - require.NoError(r, err) - - ceClient, _, err := staticClientPeerClient.ConfigEntries().Get(api.MeshConfig, "mesh", &api.QueryOptions{}) - require.NoError(r, err) - configEntryClient, ok := ceClient.(*api.MeshConfigEntry) - require.True(r, ok) - require.Equal(r, configEntryClient.GetName(), "mesh") - require.NoError(r, err) - }) - - // Create the peering acceptor on the client peer. - k8s.KubectlApply(t, staticClientPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-acceptor.yaml") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.KubectlDelete(t, staticClientPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-acceptor.yaml") - }) - - // Ensure the secret is created. - retry.RunWith(timer, t, func(r *retry.R) { - acceptorSecretName, err := k8s.RunKubectlAndGetOutputE(t, staticClientPeerClusterContext.KubectlOptions(t), "get", "peeringacceptor", "server", "-o", "jsonpath={.status.secret.name}") - require.NoError(r, err) - require.NotEmpty(r, acceptorSecretName) - }) - - // Copy secret from client peer to server peer. - k8s.CopySecret(t, staticClientPeerClusterContext, staticServerPeerClusterContext, "api-token") - - // Create the peering dialer on the server peer. - k8s.KubectlApply(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-dialer.yaml") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.RunKubectl(t, staticServerPeerClusterContext.KubectlOptions(t), "delete", "secret", "api-token") - k8s.KubectlDelete(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/bases/peering/peering-dialer.yaml") - }) - - staticServerOpts := &terratestk8s.KubectlOptions{ - ContextName: staticServerPeerClusterContext.KubectlOptions(t).ContextName, - ConfigPath: staticServerPeerClusterContext.KubectlOptions(t).ConfigPath, - Namespace: staticServerNamespace, - } - staticClientOpts := &terratestk8s.KubectlOptions{ - ContextName: staticClientPeerClusterContext.KubectlOptions(t).ContextName, - ConfigPath: staticClientPeerClusterContext.KubectlOptions(t).ConfigPath, - Namespace: staticClientNamespace, - } - - logger.Logf(t, "creating namespaces %s in server peer", staticServerNamespace) - k8s.RunKubectl(t, staticServerPeerClusterContext.KubectlOptions(t), "create", "ns", staticServerNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.RunKubectl(t, staticServerPeerClusterContext.KubectlOptions(t), "delete", "ns", staticServerNamespace) - }) - - logger.Logf(t, "creating namespaces %s in client peer", staticClientNamespace) - k8s.RunKubectl(t, staticClientPeerClusterContext.KubectlOptions(t), "create", "ns", staticClientNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.RunKubectl(t, staticClientPeerClusterContext.KubectlOptions(t), "delete", "ns", staticClientNamespace) - }) - - // Create a ProxyDefaults resource to configure services to use the mesh - // gateways. - logger.Log(t, "creating proxy-defaults config") - kustomizeDir := "../fixtures/cases/api-gateways/mesh" - - k8s.KubectlApplyK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.KubectlDeleteK(t, staticServerPeerClusterContext.KubectlOptions(t), kustomizeDir) - }) - - k8s.KubectlApplyK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.KubectlDeleteK(t, staticClientPeerClusterContext.KubectlOptions(t), kustomizeDir) - }) - - // We use the static-client pod so that we can make calls to the api gateway - // via kubectl exec without needing a route into the cluster from the test machine. - // Since we're deploying the gateway in the secondary cluster, we create the static client - // in the secondary as well. - logger.Log(t, "creating static-client pod in client peer") - k8s.DeployKustomize(t, staticClientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-peers/non-default-namespace") - - logger.Log(t, "creating static-server in server peer") - k8s.DeployKustomize(t, staticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") - - logger.Log(t, "creating exported services") - k8s.KubectlApplyK(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/cases/crd-peers/non-default-namespace") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.KubectlDeleteK(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/cases/crd-peers/non-default-namespace") - }) - - logger.Log(t, "creating api-gateway resources in client peer") - out, err := k8s.RunKubectlAndGetOutputE(t, staticClientOpts, "apply", "-k", "../fixtures/bases/api-gateway") - require.NoError(t, err, out) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - // Ignore errors here because if the test ran as expected - // the custom resources will have been deleted. - k8s.RunKubectlAndGetOutputE(t, staticClientOpts, "delete", "-k", "../fixtures/bases/api-gateway") - }) - - // Grab a kubernetes client so that we can verify binding - // behavior prior to issuing requests through the gateway. - k8sClient := staticClientPeerClusterContext.ControllerRuntimeClient(t) - - // On startup, the controller can take upwards of 1m to perform - // leader election so we may need to wait a long time for - // the reconcile loop to run (hence the 1m timeout here). - var gatewayAddress string - counter := &retry.Counter{Count: 600, Wait: 2 * time.Second} - retry.RunWith(counter, t, func(r *retry.R) { - var gateway gwv1beta1.Gateway - err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway", Namespace: staticClientNamespace}, &gateway) - require.NoError(r, err) - - // check that we have an address to use - require.Len(r, gateway.Status.Addresses, 1) - // now we know we have an address, set it so we can use it - gatewayAddress = gateway.Status.Addresses[0].Value - }) - - targetAddress := fmt.Sprintf("http://%s/", gatewayAddress) - - logger.Log(t, "creating local service resolver") - k8s.KubectlApplyK(t, staticClientOpts, "../fixtures/cases/api-gateways/peer-resolver") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.KubectlDeleteK(t, staticClientOpts, "../fixtures/cases/api-gateways/peer-resolver") - }) - - logger.Log(t, "patching route to target server") - k8s.RunKubectl(t, staticClientOpts, "patch", "httproute", "http-route", "-p", `{"spec":{"rules":[{"backendRefs":[{"group":"consul.hashicorp.com","kind":"MeshService","name":"mesh-service","port":80}]}]}}`, "--type=merge") - - logger.Log(t, "checking that the connection is not successful because there's no intention") - k8s.CheckStaticServerHTTPConnectionFailing(t, staticClientOpts, staticClientName, targetAddress) - - intention := &api.ServiceIntentionsConfigEntry{ - Kind: api.ServiceIntentions, - Name: staticServerName, - Namespace: staticServerNamespace, - Sources: []*api.SourceIntention{ - { - Name: "gateway", - Namespace: staticClientNamespace, - Action: api.IntentionActionAllow, - Peer: staticClientPeer, - }, - }, - } - - logger.Log(t, "creating intention") - _, _, err = staticServerPeerClient.ConfigEntries().Set(intention, &api.WriteOptions{}) - require.NoError(t, err) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - _, err = staticServerPeerClient.ConfigEntries().Delete(api.ServiceIntentions, staticServerName, &api.WriteOptions{}) - require.NoError(t, err) - }) - - logger.Log(t, "checking that connection is successful") - k8s.CheckStaticServerConnectionSuccessful(t, staticClientOpts, staticClientName, targetAddress) -} diff --git a/acceptance/tests/sameness/main_test.go b/acceptance/tests/sameness/main_test.go deleted file mode 100644 index ded943c6f0..0000000000 --- a/acceptance/tests/sameness/main_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package sameness - -import ( - "fmt" - "os" - "testing" - - testsuite "github.com/hashicorp/consul-k8s/acceptance/framework/suite" -) - -var suite testsuite.Suite - -func TestMain(m *testing.M) { - suite = testsuite.NewSuite(m) - - expectedNumberOfClusters := 4 - - if suite.Config().EnableMultiCluster && suite.Config().IsExpectedClusterCount(expectedNumberOfClusters) && suite.Config().UseKind { - os.Exit(suite.Run()) - } else { - fmt.Println(fmt.Sprintf("Skipping sameness tests because either -enable-multi-cluster is "+ - "not set, the number of clusters did not match the expected count of %d, or --useKind is false. "+ - "Sameness acceptance tests are currently only supported on Kind clusters", expectedNumberOfClusters)) - } -} diff --git a/acceptance/tests/sameness/sameness_test.go b/acceptance/tests/sameness/sameness_test.go deleted file mode 100644 index 5e1c268169..0000000000 --- a/acceptance/tests/sameness/sameness_test.go +++ /dev/null @@ -1,846 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package sameness - -import ( - ctx "context" - "fmt" - "strconv" - "strings" - "testing" - "time" - - terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" - "github.com/hashicorp/consul-k8s/acceptance/framework/config" - "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/environment" - "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" - "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" - "github.com/hashicorp/consul-k8s/acceptance/framework/logger" - "github.com/hashicorp/consul/api" - "github.com/hashicorp/consul/sdk/testutil/retry" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -const ( - cluster01Partition = "ap1" - cluster01Datacenter = "dc1" - cluster02Datacenter = "dc2" - cluster03Datacenter = "dc3" - staticClientNamespace = "ns1" - staticServerNamespace = "ns2" - - keyCluster01a = "cluster-01-a" - keyCluster01b = "cluster-01-b" - keyCluster02a = "cluster-02-a" - keyCluster03a = "cluster-03-a" - - staticServerName = "static-server" - staticClientName = "static-client" - - staticServerDeployment = "deploy/static-server" - staticClientDeployment = "deploy/static-client" - - peerName1a = keyCluster01a - peerName1b = keyCluster01b - peerName2a = keyCluster02a - peerName3a = keyCluster03a - - samenessGroupName = "group-01" - - cluster01Region = "us-east-1" - cluster02Region = "us-west-1" - cluster03Region = "us-east-2" - - retryTimeout = 5 * time.Minute -) - -func TestFailover_Connect(t *testing.T) { - env := suite.Environment() - cfg := suite.Config() - - if !cfg.EnableEnterprise { - t.Skipf("skipping this test because -enable-enterprise is not set") - } - - cases := []struct { - name string - ACLsEnabled bool - }{ - { - "default failover", - false, - }, - { - "secure failover", - true, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - /* - Architecture Overview: - Primary Datacenter (DC1) - Default Partition - Peer -> DC2 (cluster-02-a) - Peer -> DC3 (cluster-03-a) - AP1 Partition - Peer -> DC2 (cluster-02-a) - Peer -> DC3 (cluster-03-a) - Datacenter 2 (DC2) - Default Partition - Peer -> DC1 (cluster-01-a) - Peer -> DC1 (cluster-01-b) - Peer -> DC3 (cluster-03-a) - Datacenter 3 (DC3) - Default Partition - Peer -> DC1 (cluster-01-a) - Peer -> DC1 (cluster-01-b) - Peer -> DC2 (cluster-02-a) - - - Architecture Diagram + failover scenarios from perspective of DC1 Default Partition Static-Server - +-------------------------------------------+ - | | - | DC1 | - | | - | +-----------------------------+ | +-----------------------------------+ - | | | | | DC2 | - | | +------------------+ | | Failover 2 | +------------------+ | - | | | +-------+--------+-----------------+------>| | | - | | | Static-Server | | | | | Static-Server | | - | | | +-------+---+ | | | | | - | | | | | | | | | | | - | | | | | | | | | | | - | | | +-------+---+----+-------------+ | | | | - | | +------------------+ | | | | | +------------------+ | - | | Admin Partitions: Default | | | | | | - | | Name: cluster-01-a | | | | | Admin Partitions: Default | - | | Region: us-east-1 | | | | | Name: cluster-02-a | - | +-----------------------------+ | | | | Region: us-west-1 | - | | | | +-----------------------------------+ - | Failover 1| | Failover 3 | - | +-------------------------------+ | | | +-----------------------------------+ - | | | | | | | DC3 | - | | +------------------+ | | | | | +------------------+ | - | | | | | | | | | | Static-Server | | - | | | Static-Server | | | | | | | | | - | | | | | | | | | | | | - | | | | | | | +---+------>| | | - | | | |<------+--+ | | | | | - | | | | | | | +------------------+ | - | | +------------------+ | | | | - | | Admin Partitions: ap1 | | | Admin Partitions: Default | - | | Name: cluster-01-b | | | Name: cluster-03-a | - | | Region: us-east-1 | | | Region: us-east-2 | - | +-------------------------------+ | | | - | | +-----------------------------------+ - +-------------------------------------------+ - */ - - testClusters := clusters{ - keyCluster01a: {name: peerName1a, context: env.DefaultContext(t), hasServer: true, acceptors: []string{peerName2a, peerName3a}, locality: localityForRegion(cluster01Region)}, - keyCluster01b: {name: peerName1b, context: env.Context(t, 1), partition: cluster01Partition, hasServer: false, acceptors: []string{peerName2a, peerName3a}, locality: localityForRegion(cluster01Region)}, - keyCluster02a: {name: peerName2a, context: env.Context(t, 2), hasServer: true, acceptors: []string{peerName3a}, locality: localityForRegion(cluster02Region)}, - keyCluster03a: {name: peerName3a, context: env.Context(t, 3), hasServer: true, locality: localityForRegion(cluster03Region)}, - } - - // Setup Namespaces. - for _, v := range testClusters { - createNamespaces(t, cfg, v.context) - } - - // Create the cluster-01-a. - commonHelmValues := map[string]string{ - "global.peering.enabled": "true", - - "global.tls.enabled": "true", - "global.tls.httpsOnly": strconv.FormatBool(c.ACLsEnabled), - - "global.enableConsulNamespaces": "true", - - "global.adminPartitions.enabled": "true", - - "global.logLevel": "warn", - - "global.acls.manageSystemACLs": strconv.FormatBool(c.ACLsEnabled), - - "connectInject.enabled": "true", - "connectInject.consulNamespaces.mirroringK8S": "true", - - "meshGateway.enabled": "true", - "meshGateway.replicas": "1", - - "dns.enabled": "true", - "connectInject.sidecarProxy.lifecycle.defaultEnabled": "false", - } - - defaultPartitionHelmValues := map[string]string{ - "global.datacenter": cluster01Datacenter, - } - - // On Kind, there are no load balancers but since all clusters - // share the same node network (docker bridge), we can use - // a NodePort service so that we can access node(s) in a different Kind cluster. - if cfg.UseKind { - defaultPartitionHelmValues["meshGateway.service.type"] = "NodePort" - defaultPartitionHelmValues["meshGateway.service.nodePort"] = "30200" - defaultPartitionHelmValues["server.exposeService.type"] = "NodePort" - defaultPartitionHelmValues["server.exposeService.nodePort.https"] = "30000" - defaultPartitionHelmValues["server.exposeService.nodePort.grpc"] = "30100" - } - helpers.MergeMaps(defaultPartitionHelmValues, commonHelmValues) - - releaseName := helpers.RandomName() - testClusters[keyCluster01a].helmCluster = consul.NewHelmCluster(t, defaultPartitionHelmValues, testClusters[keyCluster01a].context, cfg, releaseName) - testClusters[keyCluster01a].helmCluster.Create(t) - - // Get the TLS CA certificate and key secret from the server cluster and apply it to the client cluster. - caCertSecretName := fmt.Sprintf("%s-consul-ca-cert", releaseName) - - logger.Logf(t, "retrieving ca cert secret %s from the server cluster and applying to the client cluster", caCertSecretName) - k8s.CopySecret(t, testClusters[keyCluster01a].context, testClusters[keyCluster01b].context, caCertSecretName) - - // Create Secondary Partition Cluster (cluster-01-b) which will apply the primary (dc1) datacenter. - partitionToken := fmt.Sprintf("%s-consul-partitions-acl-token", releaseName) - if c.ACLsEnabled { - logger.Logf(t, "retrieving partition token secret %s from the server cluster and applying to the client cluster", partitionToken) - k8s.CopySecret(t, testClusters[keyCluster01a].context, testClusters[keyCluster01b].context, partitionToken) - } - - partitionServiceName := fmt.Sprintf("%s-consul-expose-servers", releaseName) - partitionSvcAddress := k8s.ServiceHost(t, cfg, testClusters[keyCluster01a].context, partitionServiceName) - - k8sAuthMethodHost := k8s.KubernetesAPIServerHost(t, cfg, testClusters[keyCluster01b].context) - - secondaryPartitionHelmValues := map[string]string{ - "global.enabled": "false", - "global.datacenter": cluster01Datacenter, - - "global.adminPartitions.name": cluster01Partition, - - "global.tls.caCert.secretName": caCertSecretName, - "global.tls.caCert.secretKey": "tls.crt", - - "externalServers.enabled": "true", - "externalServers.hosts[0]": partitionSvcAddress, - "externalServers.tlsServerName": fmt.Sprintf("server.%s.consul", cluster01Datacenter), - "global.server.enabled": "false", - } - - if c.ACLsEnabled { - // Setup partition token and auth method host if ACLs enabled. - secondaryPartitionHelmValues["global.acls.bootstrapToken.secretName"] = partitionToken - secondaryPartitionHelmValues["global.acls.bootstrapToken.secretKey"] = "token" - secondaryPartitionHelmValues["externalServers.k8sAuthMethodHost"] = k8sAuthMethodHost - } - - if cfg.UseKind { - secondaryPartitionHelmValues["externalServers.httpsPort"] = "30000" - secondaryPartitionHelmValues["externalServers.grpcPort"] = "30100" - secondaryPartitionHelmValues["meshGateway.service.type"] = "NodePort" - secondaryPartitionHelmValues["meshGateway.service.nodePort"] = "30200" - } - helpers.MergeMaps(secondaryPartitionHelmValues, commonHelmValues) - - testClusters[keyCluster01b].helmCluster = consul.NewHelmCluster(t, secondaryPartitionHelmValues, testClusters[keyCluster01b].context, cfg, releaseName) - testClusters[keyCluster01b].helmCluster.Create(t) - - // Create cluster-02-a Cluster. - PeerOneHelmValues := map[string]string{ - "global.datacenter": cluster02Datacenter, - } - - if cfg.UseKind { - PeerOneHelmValues["server.exposeGossipAndRPCPorts"] = "true" - PeerOneHelmValues["meshGateway.service.type"] = "NodePort" - PeerOneHelmValues["meshGateway.service.nodePort"] = "30100" - } - helpers.MergeMaps(PeerOneHelmValues, commonHelmValues) - - testClusters[keyCluster02a].helmCluster = consul.NewHelmCluster(t, PeerOneHelmValues, testClusters[keyCluster02a].context, cfg, releaseName) - testClusters[keyCluster02a].helmCluster.Create(t) - - // Create cluster-03-a Cluster. - PeerTwoHelmValues := map[string]string{ - "global.datacenter": cluster03Datacenter, - } - - if cfg.UseKind { - PeerTwoHelmValues["server.exposeGossipAndRPCPorts"] = "true" - PeerTwoHelmValues["meshGateway.service.type"] = "NodePort" - PeerTwoHelmValues["meshGateway.service.nodePort"] = "30100" - } - helpers.MergeMaps(PeerTwoHelmValues, commonHelmValues) - - testClusters[keyCluster03a].helmCluster = consul.NewHelmCluster(t, PeerTwoHelmValues, testClusters[keyCluster03a].context, cfg, releaseName) - testClusters[keyCluster03a].helmCluster.Create(t) - - // Create a ProxyDefaults resource to configure services to use the mesh - // gateways and set server and client opts. - for k, v := range testClusters { - logger.Logf(t, "applying resources on %s", v.context.KubectlOptions(t).ContextName) - - // Client will use the client namespace. - testClusters[k].clientOpts = &terratestk8s.KubectlOptions{ - ContextName: v.context.KubectlOptions(t).ContextName, - ConfigPath: v.context.KubectlOptions(t).ConfigPath, - Namespace: staticClientNamespace, - } - - // Server will use the server namespace. - testClusters[k].serverOpts = &terratestk8s.KubectlOptions{ - ContextName: v.context.KubectlOptions(t).ContextName, - ConfigPath: v.context.KubectlOptions(t).ConfigPath, - Namespace: staticServerNamespace, - } - - // Sameness Defaults need to be applied first so that the sameness group exists. - applyResources(t, cfg, "../fixtures/bases/mesh-gateway", v.context.KubectlOptions(t)) - applyResources(t, cfg, "../fixtures/bases/sameness/override-ns", v.serverOpts) - - // Only assign a client if the cluster is running a Consul server. - if v.hasServer { - testClusters[k].client, _ = testClusters[k].helmCluster.SetupConsulClient(t, c.ACLsEnabled) - } - } - - // Assign the client default partition client to the partition - testClusters[keyCluster01b].client = testClusters[keyCluster01a].client - - // Apply Mesh resource to default partition and peers - for _, v := range testClusters { - if v.hasServer { - applyResources(t, cfg, "../fixtures/bases/sameness/peering/mesh", v.context.KubectlOptions(t)) - } - } - - // Apply locality to clusters - for _, v := range testClusters { - setK8sNodeLocality(t, v.context, v) - } - - // Peering/Dialer relationship - /* - cluster-01-a cluster-02-a - Dialer -> 2a 1a -> acceptor - Dialer -> 3a 1b -> acceptor - Dialer -> 3a - - cluster-01-b cluster-03-a - Dialer -> 2a 1a -> acceptor - Dialer -> 3a 1b -> acceptor - 2a -> acceptor - */ - for _, v := range []*cluster{testClusters[keyCluster02a], testClusters[keyCluster03a]} { - logger.Logf(t, "creating acceptor on %s", v.name) - // Create an acceptor token on the cluster - applyResources(t, cfg, fmt.Sprintf("../fixtures/bases/sameness/peering/%s-acceptor", v.name), v.context.KubectlOptions(t)) - - // Copy secrets to the necessary peers to be used for dialing later - for _, vv := range testClusters { - if isAcceptor(v.name, vv.acceptors) { - acceptorSecretName := getPeeringAcceptorSecret(t, cfg, v, vv.name) - logger.Logf(t, "acceptor %s created on %s", acceptorSecretName, v.name) - - logger.Logf(t, "copying acceptor token %s from %s to %s", acceptorSecretName, v.name, vv.name) - copySecret(t, cfg, v.context, vv.context, acceptorSecretName) - } - } - } - - // Create the dialers - for _, v := range []*cluster{testClusters[keyCluster01a], testClusters[keyCluster01b], testClusters[keyCluster02a]} { - applyResources(t, cfg, fmt.Sprintf("../fixtures/bases/sameness/peering/%s-dialer", v.name), v.context.KubectlOptions(t)) - } - - // If ACLs are enabled, we need to create the intentions - if c.ACLsEnabled { - intention := &api.ServiceIntentionsConfigEntry{ - Name: staticServerName, - Kind: api.ServiceIntentions, - Namespace: staticServerNamespace, - Sources: []*api.SourceIntention{ - { - Name: staticClientName, - Namespace: staticClientNamespace, - SamenessGroup: samenessGroupName, - Action: api.IntentionActionAllow, - }, - }, - } - - for _, v := range testClusters { - logger.Logf(t, "creating intentions on server %s", v.name) - _, _, err := v.client.ConfigEntries().Set(intention, &api.WriteOptions{Partition: v.partition}) - require.NoError(t, err) - } - } - - logger.Log(t, "creating exported services") - for _, v := range testClusters { - if v.hasServer { - applyResources(t, cfg, "../fixtures/cases/sameness/exported-services/default-partition", v.context.KubectlOptions(t)) - } else { - applyResources(t, cfg, "../fixtures/cases/sameness/exported-services/ap1-partition", v.context.KubectlOptions(t)) - } - } - - // Create sameness group after exporting the services, this will reduce flakiness in an automated test - for _, v := range testClusters { - applyResources(t, cfg, fmt.Sprintf("../fixtures/bases/sameness/%s-default-ns", v.name), v.context.KubectlOptions(t)) - } - - // Setup DNS. - for _, v := range testClusters { - dnsService, err := v.context.KubernetesClient(t).CoreV1().Services("default").Get(ctx.Background(), fmt.Sprintf("%s-%s", releaseName, "consul-dns"), metav1.GetOptions{}) - require.NoError(t, err) - v.dnsIP = &dnsService.Spec.ClusterIP - logger.Logf(t, "%s dnsIP: %s", v.name, *v.dnsIP) - } - - // Setup Prepared Query. - definition := &api.PreparedQueryDefinition{ - Name: "my-query", - Service: api.ServiceQuery{ - Service: staticServerName, - SamenessGroup: samenessGroupName, - Namespace: staticServerNamespace, - OnlyPassing: false, - }, - } - - for k, v := range testClusters { - if v.hasServer { - pqID, _, err := v.client.PreparedQuery().Create(definition, &api.WriteOptions{}) - require.NoError(t, err) - logger.Logf(t, "%s PQ ID: %s", v.name, pqID) - testClusters[k].pqID = &pqID - testClusters[k].pqName = &definition.Name - } - } - - // Create static server/client after the rest of the config is setup for a more stable testing experience - // Create static server deployments. - logger.Log(t, "creating static-server and static-client deployments") - k8s.DeployKustomize(t, testClusters[keyCluster01a].serverOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, - "../fixtures/cases/sameness/static-server/dc1-default") - k8s.DeployKustomize(t, testClusters[keyCluster01b].serverOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, - "../fixtures/cases/sameness/static-server/dc1-partition") - k8s.DeployKustomize(t, testClusters[keyCluster02a].serverOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, - "../fixtures/cases/sameness/static-server/dc2") - k8s.DeployKustomize(t, testClusters[keyCluster03a].serverOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, - "../fixtures/cases/sameness/static-server/dc3") - - // Create static client deployments. - staticClientKustomizeDirDefault := "../fixtures/cases/sameness/static-client/default-partition" - staticClientKustomizeDirAP1 := "../fixtures/cases/sameness/static-client/ap1-partition" - - // If transparent proxy is enabled create clients without explicit upstreams - if cfg.EnableTransparentProxy { - staticClientKustomizeDirDefault = fmt.Sprintf("%s-%s", staticClientKustomizeDirDefault, "tproxy") - staticClientKustomizeDirAP1 = fmt.Sprintf("%s-%s", staticClientKustomizeDirAP1, "tproxy") - } - - k8s.DeployKustomize(t, testClusters[keyCluster01a].clientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, - staticClientKustomizeDirDefault) - k8s.DeployKustomize(t, testClusters[keyCluster02a].clientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, - staticClientKustomizeDirDefault) - k8s.DeployKustomize(t, testClusters[keyCluster03a].clientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, - staticClientKustomizeDirDefault) - k8s.DeployKustomize(t, testClusters[keyCluster01b].clientOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, - staticClientKustomizeDirAP1) - - // Verify that both static-server and static-client have been injected and now have 2 containers in each cluster. - // Also get the server IP - testClusters.setServerIP(t) - - // Everything should be up and running now - testClusters.verifyServerUpState(t, cfg.EnableTransparentProxy) - logger.Log(t, "all infrastructure up and running") - - // Verify locality is set on services based on node labels previously applied. - // - // This is currently the only locality testing we do for k8s and ensures that single-partition - // locality-aware routing will function in consul-k8s. In the future, this test will be expanded - // to test multi-cluster locality-based failover with sameness groups. - for _, v := range testClusters { - checkLocalities(t, v) - } - - // Verify all the failover Scenarios - logger.Log(t, "verifying failover scenarios") - - subCases := []struct { - name string - server *cluster - failovers []struct { - failoverServer *cluster - expectedPQ expectedPQ - } - checkDNSPQ bool - }{ - { - name: "cluster-01-a perspective", // This matches the diagram at the beginning of the test - server: testClusters[keyCluster01a], - failovers: []struct { - failoverServer *cluster - expectedPQ expectedPQ - }{ - {failoverServer: testClusters[keyCluster01a], expectedPQ: expectedPQ{partition: "default", peerName: "", namespace: "ns2"}}, - {failoverServer: testClusters[keyCluster01b], expectedPQ: expectedPQ{partition: "ap1", peerName: "", namespace: "ns2"}}, - {failoverServer: testClusters[keyCluster02a], expectedPQ: expectedPQ{partition: "default", peerName: testClusters[keyCluster02a].name, namespace: "ns2"}}, - {failoverServer: testClusters[keyCluster03a], expectedPQ: expectedPQ{partition: "default", peerName: testClusters[keyCluster03a].name, namespace: "ns2"}}, - }, - checkDNSPQ: true, - }, - { - name: "cluster-01-b partition perspective", - server: testClusters[keyCluster01b], - failovers: []struct { - failoverServer *cluster - expectedPQ expectedPQ - }{ - {failoverServer: testClusters[keyCluster01b], expectedPQ: expectedPQ{partition: "ap1", peerName: "", namespace: "ns2"}}, - {failoverServer: testClusters[keyCluster01a], expectedPQ: expectedPQ{partition: "default", peerName: "", namespace: "ns2"}}, - {failoverServer: testClusters[keyCluster02a], expectedPQ: expectedPQ{partition: "default", peerName: testClusters[keyCluster02a].name, namespace: "ns2"}}, - {failoverServer: testClusters[keyCluster03a], expectedPQ: expectedPQ{partition: "default", peerName: testClusters[keyCluster03a].name, namespace: "ns2"}}, - }, - checkDNSPQ: false, - }, - { - name: "cluster-02-a perspective", - server: testClusters[keyCluster02a], - failovers: []struct { - failoverServer *cluster - expectedPQ expectedPQ - }{ - {failoverServer: testClusters[keyCluster02a], expectedPQ: expectedPQ{partition: "default", peerName: "", namespace: "ns2"}}, - {failoverServer: testClusters[keyCluster01a], expectedPQ: expectedPQ{partition: "default", peerName: testClusters[keyCluster01a].name, namespace: "ns2"}}, - {failoverServer: testClusters[keyCluster01b], expectedPQ: expectedPQ{partition: "default", peerName: testClusters[keyCluster01b].name, namespace: "ns2"}}, - {failoverServer: testClusters[keyCluster03a], expectedPQ: expectedPQ{partition: "default", peerName: testClusters[keyCluster03a].name, namespace: "ns2"}}, - }, - checkDNSPQ: true, - }, - { - name: "cluster-03-a perspective", - server: testClusters[keyCluster03a], - failovers: []struct { - failoverServer *cluster - expectedPQ expectedPQ - }{ - {failoverServer: testClusters[keyCluster03a], expectedPQ: expectedPQ{partition: "default", peerName: "", namespace: "ns2"}}, - {failoverServer: testClusters[keyCluster01a], expectedPQ: expectedPQ{partition: "default", peerName: testClusters[keyCluster01a].name, namespace: "ns2"}}, - {failoverServer: testClusters[keyCluster01b], expectedPQ: expectedPQ{partition: "default", peerName: testClusters[keyCluster01b].name, namespace: "ns2"}}, - {failoverServer: testClusters[keyCluster02a], expectedPQ: expectedPQ{partition: "default", peerName: testClusters[keyCluster02a].name, namespace: "ns2"}}, - }, - checkDNSPQ: true, - }, - } - for _, sc := range subCases { - t.Run(sc.name, func(t *testing.T) { - // Reset the scale of all servers - testClusters.resetScale(t) - testClusters.verifyServerUpState(t, cfg.EnableTransparentProxy) - // We're resetting the scale, so make sure we have all the new IP addresses saved - testClusters.setServerIP(t) - - for _, v := range sc.failovers { - // Verify Failover (If this is the first check, then just verifying we're starting with the right server) - logger.Log(t, "checking service failover") - - if cfg.EnableTransparentProxy { - serviceTargetCheck(t, sc.server, v.failoverServer.name, fmt.Sprintf("http://static-server.virtual.ns2.ns.%s.ap.consul", sc.server.fullTextPartition())) - } else { - serviceTargetCheck(t, sc.server, v.failoverServer.name, "localhost:8080") - } - - // Verify DNS - if sc.checkDNSPQ { - logger.Log(t, "verifying dns") - dnsFailoverCheck(t, cfg, releaseName, *sc.server.dnsIP, sc.server, v.failoverServer) - - // Verify PQ - logger.Log(t, "verifying prepared query") - preparedQueryFailoverCheck(t, releaseName, *sc.server.dnsIP, v.expectedPQ, sc.server, v.failoverServer) - } else { - // We currently skip running DNS and PQ tests for a couple of reasons - // 1. The admin partition does not contain a server, so DNS service will not resolve on the admin partition cluster - // 2. A workaround to perform the DNS and PQ queries on the primary datacenter cluster by specifying the admin partition - // e.g kubectl --context kind-dc1 --namespace ns1 exec -i deploy/static-client -c static-client \ - // -- dig @test-3lmypr-consul-dns.default static-server.service.ns2.ns.mine.sg.ap1.ap.consul - // is not possible at the moment due to a bug. The workaround will be used once this bug is fixed. - logger.Logf(t, "skipping DNS and PQ checks for %s", sc.name) - } - - // Scale down static-server on the current failover, will fail over to the next. - logger.Logf(t, "scaling server down on %s", v.failoverServer.name) - k8s.KubectlScale(t, v.failoverServer.serverOpts, staticServerDeployment, 0) - } - }) - } - }) - } -} - -type expectedPQ struct { - partition string - peerName string - namespace string -} - -type cluster struct { - name string - partition string - locality api.Locality - context environment.TestContext - helmCluster *consul.HelmCluster - client *api.Client - hasServer bool - serverOpts *terratestk8s.KubectlOptions - clientOpts *terratestk8s.KubectlOptions - staticServerIP *string - pqID *string - pqName *string - dnsIP *string - acceptors []string -} - -func (c cluster) fullTextPartition() string { - if c.partition == "" { - return "default" - } else { - return c.partition - } -} - -type clusters map[string]*cluster - -func (c clusters) resetScale(t *testing.T) { - for _, v := range c { - k8s.KubectlScale(t, v.serverOpts, staticServerDeployment, 1) - } -} - -// setServerIP makes sure everything is up and running and then saves the -// static-server IP to the appropriate cluster. IP addresses can change when -// services are scaled up and down. -func (c clusters) setServerIP(t *testing.T) { - for _, labelSelector := range []string{"app=static-server", "app=static-client"} { - for k, v := range c { - podList, err := v.context.KubernetesClient(t).CoreV1().Pods(metav1.NamespaceAll).List(ctx.Background(), - metav1.ListOptions{LabelSelector: labelSelector}) - require.NoError(t, err) - require.Len(t, podList.Items, 1) - require.Len(t, podList.Items[0].Spec.Containers, 2) - if labelSelector == "app=static-server" { - ip := &podList.Items[0].Status.PodIP - require.NotNil(t, ip) - logger.Logf(t, "%s-static-server-ip: %s", v.name, *ip) - c[k].staticServerIP = ip - } - } - } -} - -// verifyServerUpState will verify that the static-servers are all up and running as -// expected by curling them from their local datacenters. -func (c clusters) verifyServerUpState(t *testing.T, isTproxyEnabled bool) { - logger.Logf(t, "verifying that static-servers are up") - for _, v := range c { - // Query using a client and expect its own name, no failover should occur - if isTproxyEnabled { - serviceTargetCheck(t, v, v.name, fmt.Sprintf("http://static-server.virtual.ns2.ns.%s.ap.consul", v.fullTextPartition())) - } else { - serviceTargetCheck(t, v, v.name, "localhost:8080") - } - } -} - -func copySecret(t *testing.T, cfg *config.TestConfig, sourceContext, destContext environment.TestContext, secretName string) { - k8s.CopySecret(t, sourceContext, destContext, secretName) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.RunKubectl(t, destContext.KubectlOptions(t), "delete", "secret", secretName) - }) -} - -func createNamespaces(t *testing.T, cfg *config.TestConfig, context environment.TestContext) { - logger.Logf(t, "creating namespaces in %s", context.KubectlOptions(t).ContextName) - k8s.RunKubectl(t, context.KubectlOptions(t), "create", "ns", staticServerNamespace) - k8s.RunKubectl(t, context.KubectlOptions(t), "create", "ns", staticClientNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.RunKubectl(t, context.KubectlOptions(t), "delete", "ns", staticClientNamespace, staticServerNamespace) - }) -} - -func applyResources(t *testing.T, cfg *config.TestConfig, kustomizeDir string, opts *terratestk8s.KubectlOptions) { - k8s.KubectlApplyK(t, opts, kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.KubectlDeleteK(t, opts, kustomizeDir) - }) -} - -// setK8sNodeLocality labels the k8s node corresponding to the given cluster with standard labels indicating the -// locality of that node. These are propagated by connect-inject to registered Consul services. -func setK8sNodeLocality(t *testing.T, context environment.TestContext, c *cluster) { - nodeList, err := context.KubernetesClient(t).CoreV1().Nodes().List(ctx.Background(), metav1.ListOptions{}) - require.NoError(t, err) - // Get the name of the (only) node from the Kind cluster. - node := nodeList.Items[0].Name - k8s.KubectlLabel(t, context.KubectlOptions(t), "node", node, corev1.LabelTopologyRegion, c.locality.Region) - k8s.KubectlLabel(t, context.KubectlOptions(t), "node", node, corev1.LabelTopologyZone, c.locality.Zone) -} - -// serviceTargetCheck verifies that curling the `static-server` using the `static-client` responds with the expected -// cluster name. Each static-server responds with a unique name so that we can verify failover occured as expected. -func serviceTargetCheck(t *testing.T, server *cluster, expectedName string, curlAddress string) { - timer := &retry.Timer{Timeout: retryTimeout, Wait: 5 * time.Second} - var resp string - var err error - retry.RunWith(timer, t, func(r *retry.R) { - // Use -s/--silent and -S/--show-error flags w/ curl to reduce noise during retries. - // This silences extra output like the request progress bar, but preserves errors. - resp, err = k8s.RunKubectlAndGetOutputE(t, server.clientOpts, "exec", "-i", - staticClientDeployment, "-c", staticClientName, "--", "curl", "-sS", curlAddress) - require.NoError(r, err) - assert.Contains(r, resp, expectedName) - }) - logger.Log(t, resp) -} - -// preparedQueryFailoverCheck verifies that failover occurs when executing the prepared query. It also assures that -// executing the prepared query via DNS also provides expected results. -func preparedQueryFailoverCheck(t *testing.T, releaseName string, dnsIP string, epq expectedPQ, server, failover *cluster) { - timer := &retry.Timer{Timeout: retryTimeout, Wait: 5 * time.Second} - resp, _, err := server.client.PreparedQuery().Execute(*server.pqID, &api.QueryOptions{Namespace: staticServerNamespace, Partition: server.partition}) - require.NoError(t, err) - require.Len(t, resp.Nodes, 1) - - assert.Equal(t, epq.partition, resp.Nodes[0].Service.Partition) - assert.Equal(t, epq.peerName, resp.Nodes[0].Service.PeerName) - assert.Equal(t, epq.namespace, resp.Nodes[0].Service.Namespace) - assert.Equal(t, *failover.staticServerIP, resp.Nodes[0].Service.Address) - - // Verify that dns lookup is successful, there is no guarantee that the ip address is unique, so for PQ this is - // just verifying that we can query using DNS and that the ip address is correct. It does not however prove - // that failover occured, that is left to client `Execute` - dnsPQLookup := []string{fmt.Sprintf("%s.query.consul", *server.pqName)} - retry.RunWith(timer, t, func(r *retry.R) { - logs := dnsQuery(t, releaseName, dnsPQLookup, server, failover) - assert.Contains(r, logs, fmt.Sprintf("SERVER: %s", dnsIP)) - assert.Contains(r, logs, "ANSWER SECTION:") - assert.Contains(r, logs, *failover.staticServerIP) - }) -} - -// DNS failover check verifies that failover occurred when querying the DNS. -func dnsFailoverCheck(t *testing.T, cfg *config.TestConfig, releaseName string, dnsIP string, server, failover *cluster) { - timer := &retry.Timer{Timeout: retryTimeout, Wait: 5 * time.Second} - dnsLookup := []string{fmt.Sprintf("static-server.service.ns2.ns.%s.sg.consul", samenessGroupName), "+tcp", "SRV"} - retry.RunWith(timer, t, func(r *retry.R) { - logs := dnsQuery(t, releaseName, dnsLookup, server, failover) - - assert.Contains(r, logs, fmt.Sprintf("SERVER: %s", dnsIP)) - assert.Contains(r, logs, "ANSWER SECTION:") - assert.Contains(r, logs, *failover.staticServerIP) - - // Additional checks - // When accessing the SRV record for DNS we can get more information. In the case of Kind, - // the context can be used to determine that failover occured to the expected kubernetes cluster - // hosting Consul - assert.Contains(r, logs, "ADDITIONAL SECTION:") - expectedName := failover.context.KubectlOptions(t).ContextName - if cfg.UseKind { - expectedName = strings.Replace(expectedName, "kind-", "", -1) - } - assert.Contains(r, logs, expectedName) - }) -} - -// dnsQuery performs a dns query with the provided query string. -func dnsQuery(t *testing.T, releaseName string, dnsQuery []string, server, failover *cluster) string { - timer := &retry.Timer{Timeout: retryTimeout, Wait: 1 * time.Second} - var logs string - retry.RunWith(timer, t, func(r *retry.R) { - args := []string{"exec", "-i", - staticClientDeployment, "-c", staticClientName, "--", "dig", fmt.Sprintf("@%s-consul-dns.default", - releaseName)} - args = append(args, dnsQuery...) - var err error - logs, err = k8s.RunKubectlAndGetOutputE(t, server.clientOpts, args...) - require.NoError(r, err) - }) - logger.Logf(t, "%s: %s", failover.name, logs) - return logs -} - -// isAcceptor iterates through the provided acceptor list of cluster names and determines if -// any match the provided name. Returns true if a match is found, false otherwise. -func isAcceptor(name string, acceptorList []string) bool { - for _, v := range acceptorList { - if name == v { - return true - } - } - return false -} - -// getPeeringAcceptorSecret assures that the secret is created and retrieves the secret from the provided acceptor. -func getPeeringAcceptorSecret(t *testing.T, cfg *config.TestConfig, server *cluster, acceptorName string) string { - // Ensure the secrets are created. - var acceptorSecretName string - timer := &retry.Timer{Timeout: retryTimeout, Wait: 1 * time.Second} - retry.RunWith(timer, t, func(r *retry.R) { - var err error - acceptorSecretName, err = k8s.RunKubectlAndGetOutputE(t, server.context.KubectlOptions(t), "get", "peeringacceptor", acceptorName, "-o", "jsonpath={.status.secret.name}") - require.NoError(r, err) - require.NotEmpty(r, acceptorSecretName) - }) - - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.RunKubectl(t, server.context.KubectlOptions(t), "delete", "secret", acceptorSecretName) - }) - - return acceptorSecretName -} - -// localityForRegion returns the full api.Locality to use in tests for a given region string. -func localityForRegion(r string) api.Locality { - return api.Locality{ - Region: r, - Zone: r + "a", - } -} - -// checkLocalities checks the given cluster for `static-client` and `static-server` instances matching the locality -// expected for the cluster. -func checkLocalities(t *testing.T, c *cluster) { - for ns, svcs := range map[string][]string{ - staticClientNamespace: { - staticClientName, - staticClientName + "-sidecar-proxy", - }, - staticServerNamespace: { - staticServerName, - staticServerName + "-sidecar-proxy", - }, - } { - for _, svc := range svcs { - cs := getCatalogService(t, c, svc, ns, c.partition) - assert.NotNil(t, cs.ServiceLocality, "service %s in %s did not have locality set", svc, c.name) - assert.Equal(t, c.locality, *cs.ServiceLocality, "locality for service %s in %s did not match expected", svc, c.name) - } - } -} - -func getCatalogService(t *testing.T, c *cluster, svc, ns, partition string) *api.CatalogService { - resp, _, err := c.client.Catalog().Service(svc, "", &api.QueryOptions{Namespace: ns, Partition: partition}) - require.NoError(t, err) - assert.NotEmpty(t, resp, "did not find service %s in cluster %s (partition=%s ns=%s)", svc, c.name, partition, ns) - return resp[0] -} diff --git a/acceptance/tests/snapshot-agent/main_test.go b/acceptance/tests/snapshot-agent/main_test.go index 3d70823059..daa389d4c4 100644 --- a/acceptance/tests/snapshot-agent/main_test.go +++ b/acceptance/tests/snapshot-agent/main_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package snapshotagent import ( diff --git a/acceptance/tests/snapshot-agent/snapshot_agent_k8s_secret_test.go b/acceptance/tests/snapshot-agent/snapshot_agent_k8s_secret_test.go index b5613fe76d..a4c1ef7494 100644 --- a/acceptance/tests/snapshot-agent/snapshot_agent_k8s_secret_test.go +++ b/acceptance/tests/snapshot-agent/snapshot_agent_k8s_secret_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package snapshotagent import ( diff --git a/acceptance/tests/snapshot-agent/snapshot_agent_vault_test.go b/acceptance/tests/snapshot-agent/snapshot_agent_vault_test.go index 10cceb5952..ee20ffd9d7 100644 --- a/acceptance/tests/snapshot-agent/snapshot_agent_vault_test.go +++ b/acceptance/tests/snapshot-agent/snapshot_agent_vault_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package snapshotagent import ( diff --git a/acceptance/tests/sync/main_test.go b/acceptance/tests/sync/main_test.go index a26ecc9670..80c394e561 100644 --- a/acceptance/tests/sync/main_test.go +++ b/acceptance/tests/sync/main_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package sync import ( diff --git a/acceptance/tests/sync/sync_catalog_namespaces_test.go b/acceptance/tests/sync/sync_catalog_namespaces_test.go index 67123d6e4f..79f592033c 100644 --- a/acceptance/tests/sync/sync_catalog_namespaces_test.go +++ b/acceptance/tests/sync/sync_catalog_namespaces_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package sync import ( @@ -97,12 +94,12 @@ func TestSyncCatalogNamespaces(t *testing.T) { logger.Logf(t, "creating namespace %s", staticServerNamespace) k8s.RunKubectl(t, ctx.KubectlOptions(t), "create", "ns", staticServerNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "ns", staticServerNamespace) }) logger.Log(t, "creating a static-server with a service") - k8s.DeployKustomize(t, staticServerOpts, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-server") + k8s.DeployKustomize(t, staticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-server") consulClient, _ := consulCluster.SetupConsulClient(t, c.secure) diff --git a/acceptance/tests/sync/sync_catalog_test.go b/acceptance/tests/sync/sync_catalog_test.go index dc30570422..3e3880b618 100644 --- a/acceptance/tests/sync/sync_catalog_test.go +++ b/acceptance/tests/sync/sync_catalog_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package sync import ( @@ -50,7 +47,7 @@ func TestSyncCatalog(t *testing.T) { consulCluster.Create(t) logger.Log(t, "creating a static-server with a service") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), suite.Config().NoCleanupOnFailure, suite.Config().NoCleanup, suite.Config().DebugDirectory, "../fixtures/bases/static-server") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), suite.Config().NoCleanupOnFailure, suite.Config().DebugDirectory, "../fixtures/bases/static-server") consulClient, _ := consulCluster.SetupConsulClient(t, c.secure) @@ -120,7 +117,7 @@ func TestSyncCatalogWithIngress(t *testing.T) { // endpoint fails initially. out, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "apply", "-k", "../fixtures/bases/ingress") require.NoError(r, err, out) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { // Ignore errors here because if the test ran as expected // the custom resources will have been deleted. k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-k", "../fixtures/bases/ingress") @@ -130,7 +127,7 @@ func TestSyncCatalogWithIngress(t *testing.T) { consulCluster.Create(t) logger.Log(t, "creating a static-server with a service") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), suite.Config().NoCleanupOnFailure, suite.Config().NoCleanup, suite.Config().DebugDirectory, "../fixtures/bases/static-server") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), suite.Config().NoCleanupOnFailure, suite.Config().DebugDirectory, "../fixtures/bases/static-server") consulClient, _ := consulCluster.SetupConsulClient(t, c.secure) diff --git a/acceptance/tests/terminating-gateway/common.go b/acceptance/tests/terminating-gateway/common.go index 908e6cbec1..b94dd8a897 100644 --- a/acceptance/tests/terminating-gateway/common.go +++ b/acceptance/tests/terminating-gateway/common.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package terminatinggateway import ( diff --git a/acceptance/tests/terminating-gateway/main_test.go b/acceptance/tests/terminating-gateway/main_test.go index 0c8127623d..477e125f94 100644 --- a/acceptance/tests/terminating-gateway/main_test.go +++ b/acceptance/tests/terminating-gateway/main_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package terminatinggateway import ( diff --git a/acceptance/tests/terminating-gateway/terminating_gateway_destinations_test.go b/acceptance/tests/terminating-gateway/terminating_gateway_destinations_test.go index 62485c6e44..4ff4ae7bd4 100644 --- a/acceptance/tests/terminating-gateway/terminating_gateway_destinations_test.go +++ b/acceptance/tests/terminating-gateway/terminating_gateway_destinations_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package terminatinggateway import ( @@ -73,7 +70,7 @@ func TestTerminatingGatewayDestinations(t *testing.T) { // Deploy a static-server that will play the role of an external service. logger.Log(t, "creating static-server deployment") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-server-https") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-server-https") // If ACLs are enabled we need to update the role of the terminating gateway // with service:write permissions to the static-server service @@ -91,7 +88,7 @@ func TestTerminatingGatewayDestinations(t *testing.T) { // Deploy the static client logger.Log(t, "deploying static client") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") staticServerIP, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "get", "po", "-l", "app=static-server", `-o=jsonpath={.items[0].status.podIP}`) require.NoError(t, err) diff --git a/acceptance/tests/terminating-gateway/terminating_gateway_namespaces_test.go b/acceptance/tests/terminating-gateway/terminating_gateway_namespaces_test.go index 73250ea810..8c4435ae75 100644 --- a/acceptance/tests/terminating-gateway/terminating_gateway_namespaces_test.go +++ b/acceptance/tests/terminating-gateway/terminating_gateway_namespaces_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package terminatinggateway import ( @@ -62,7 +59,7 @@ func TestTerminatingGatewaySingleNamespace(t *testing.T) { logger.Logf(t, "creating Kubernetes namespace %s", testNamespace) k8s.RunKubectl(t, ctx.KubectlOptions(t), "create", "ns", testNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "ns", testNamespace) }) @@ -74,7 +71,7 @@ func TestTerminatingGatewaySingleNamespace(t *testing.T) { // Deploy a static-server that will play the role of an external service. logger.Log(t, "creating static-server deployment") - k8s.DeployKustomize(t, nsK8SOptions, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-server") + k8s.DeployKustomize(t, nsK8SOptions, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-server") // Register the external service. registerExternalService(t, consulClient, testNamespace) @@ -91,7 +88,7 @@ func TestTerminatingGatewaySingleNamespace(t *testing.T) { // Deploy the static client. logger.Log(t, "deploying static client") - k8s.DeployKustomize(t, nsK8SOptions, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") + k8s.DeployKustomize(t, nsK8SOptions, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") // If ACLs are enabled, test that intentions prevent connections. if c.secure { @@ -159,14 +156,14 @@ func TestTerminatingGatewayNamespaceMirroring(t *testing.T) { logger.Logf(t, "creating Kubernetes namespace %s", testNamespace) k8s.RunKubectl(t, ctx.KubectlOptions(t), "create", "ns", testNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "ns", testNamespace) }) StaticClientNamespace := "ns2" logger.Logf(t, "creating Kubernetes namespace %s", StaticClientNamespace) k8s.RunKubectl(t, ctx.KubectlOptions(t), "create", "ns", StaticClientNamespace) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "ns", StaticClientNamespace) }) @@ -183,7 +180,7 @@ func TestTerminatingGatewayNamespaceMirroring(t *testing.T) { // Deploy a static-server that will play the role of an external service. logger.Log(t, "creating static-server deployment") - k8s.DeployKustomize(t, ns1K8SOptions, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-server") + k8s.DeployKustomize(t, ns1K8SOptions, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-server") // Register the external service registerExternalService(t, consulClient, testNamespace) @@ -200,7 +197,7 @@ func TestTerminatingGatewayNamespaceMirroring(t *testing.T) { // Deploy the static client logger.Log(t, "deploying static client") - k8s.DeployKustomize(t, ns2K8SOptions, cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") + k8s.DeployKustomize(t, ns2K8SOptions, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") // If ACLs are enabled, test that intentions prevent connections. if c.secure { diff --git a/acceptance/tests/terminating-gateway/terminating_gateway_test.go b/acceptance/tests/terminating-gateway/terminating_gateway_test.go index 6e7e95032f..16809de5e2 100644 --- a/acceptance/tests/terminating-gateway/terminating_gateway_test.go +++ b/acceptance/tests/terminating-gateway/terminating_gateway_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package terminatinggateway import ( @@ -51,7 +48,7 @@ func TestTerminatingGateway(t *testing.T) { // Deploy a static-server that will play the role of an external service. logger.Log(t, "creating static-server deployment") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-server") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-server") // Once the cluster is up, register the external service, then create the config entry. consulClient, _ := consulCluster.SetupConsulClient(t, c.secure) @@ -71,7 +68,7 @@ func TestTerminatingGateway(t *testing.T) { // Deploy the static client logger.Log(t, "deploying static client") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") // If ACLs are enabled, test that intentions prevent connections. if c.secure { diff --git a/acceptance/tests/vault/main_test.go b/acceptance/tests/vault/main_test.go index 02a22c2b79..6b38cad022 100644 --- a/acceptance/tests/vault/main_test.go +++ b/acceptance/tests/vault/main_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package vault import ( diff --git a/acceptance/tests/vault/vault_namespaces_test.go b/acceptance/tests/vault/vault_namespaces_test.go index 4a9ca092d0..8d0beefdc0 100644 --- a/acceptance/tests/vault/vault_namespaces_test.go +++ b/acceptance/tests/vault/vault_namespaces_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package vault import ( @@ -265,13 +262,13 @@ func TestVault_VaultNamespace(t *testing.T) { // Deploy two services and check that they can talk to each other. logger.Log(t, "creating static-server and static-client deployments") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") if cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") } - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.KubectlDeleteK(t, ctx.KubectlOptions(t), "../fixtures/bases/intention") }) k8s.KubectlApplyK(t, ctx.KubectlOptions(t), "../fixtures/bases/intention") diff --git a/acceptance/tests/vault/vault_partitions_test.go b/acceptance/tests/vault/vault_partitions_test.go index 63002993a6..609147b676 100644 --- a/acceptance/tests/vault/vault_partitions_test.go +++ b/acceptance/tests/vault/vault_partitions_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package vault import ( diff --git a/acceptance/tests/vault/vault_test.go b/acceptance/tests/vault/vault_test.go index 9dab0a3e71..fdde364b5b 100644 --- a/acceptance/tests/vault/vault_test.go +++ b/acceptance/tests/vault/vault_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package vault import ( @@ -17,7 +14,6 @@ import ( "github.com/hashicorp/consul-k8s/acceptance/framework/logger" "github.com/hashicorp/consul-k8s/acceptance/framework/portforward" "github.com/hashicorp/consul-k8s/acceptance/framework/vault" - "github.com/hashicorp/consul/sdk/testutil/retry" "github.com/hashicorp/go-uuid" "github.com/hashicorp/go-version" "github.com/stretchr/testify/require" @@ -35,29 +31,6 @@ const ( // TestVault installs Vault, bootstraps it with secrets, policies, and Kube Auth Method. // It then configures Consul to use vault as the backend and checks that it works. func TestVault(t *testing.T) { - cases := map[string]struct { - autoBootstrap bool - }{ - "manual ACL bootstrap": {}, - "automatic ACL bootstrap": { - autoBootstrap: true, - }, - } - for name, c := range cases { - c := c - t.Run(name, func(t *testing.T) { - testVault(t, c.autoBootstrap) - }) - } -} - -// testVault is the implementation for TestVault: -// -// - testAutoBootstrap = false. Test when ACL bootstrapping has already occurred. -// The test pre-populates a Vault secret with the bootstrap token. -// - testAutoBootstrap = true. Test that server-acl-init automatically ACL bootstraps -// consul and writes the bootstrap token to Vault. -func testVault(t *testing.T, testAutoBootstrap bool) { cfg := suite.Config() ctx := suite.Environment().DefaultContext(t) kubectlOptions := ctx.KubectlOptions(t) @@ -111,6 +84,20 @@ func testVault(t *testing.T, testAutoBootstrap bool) { } serverPKIConfig.ConfigurePKIAndAuthRole(t, vaultClient) + // Configure controller webhook PKI + controllerWebhookPKIConfig := &vault.PKIAndAuthRoleConfiguration{ + BaseURL: "controller", + PolicyName: "controller-ca-policy", + RoleName: "controller-ca-role", + KubernetesNamespace: ns, + DataCenter: "dc1", + ServiceAccountName: fmt.Sprintf("%s-consul-%s", consulReleaseName, "controller"), + AllowedSubdomain: fmt.Sprintf("%s-consul-%s", consulReleaseName, "controller-webhook"), + MaxTTL: fmt.Sprintf("%ds", expirationInSeconds), + AuthMethodPath: KubernetesAuthMethodPath, + } + controllerWebhookPKIConfig.ConfigurePKIAndAuthRole(t, vaultClient) + // Configure connect injector webhook PKI connectInjectorWebhookPKIConfig := &vault.PKIAndAuthRoleConfiguration{ BaseURL: "connect", @@ -150,22 +137,16 @@ func testVault(t *testing.T, testAutoBootstrap bool) { licenseSecret.SaveSecretAndAddReadPolicy(t, vaultClient) } + // Bootstrap Token + bootstrapToken, err := uuid.GenerateUUID() + require.NoError(t, err) bootstrapTokenSecret := &vault.KV2Secret{ Path: "consul/data/secret/bootstrap", Key: "token", - Value: "", + Value: bootstrapToken, PolicyName: "bootstrap", } - if testAutoBootstrap { - bootstrapTokenSecret.SaveSecretAndAddUpdatePolicy(t, vaultClient) - } else { - id, err := uuid.GenerateUUID() - require.NoError(t, err) - bootstrapTokenSecret.Value = id - bootstrapTokenSecret.SaveSecretAndAddReadPolicy(t, vaultClient) - } - - bootstrapToken := bootstrapTokenSecret.Value + bootstrapTokenSecret.SaveSecretAndAddReadPolicy(t, vaultClient) // ------------------------- // Additional Auth Roles @@ -231,12 +212,15 @@ func testVault(t *testing.T, testAutoBootstrap bool) { "connectInject.replicas": "1", "global.secretsBackend.vault.connectInject.tlsCert.secretName": connectInjectorWebhookPKIConfig.CertPath, "global.secretsBackend.vault.connectInject.caCert.secretName": connectInjectorWebhookPKIConfig.CAPath, + "global.secretsBackend.vault.controller.tlsCert.secretName": controllerWebhookPKIConfig.CertPath, + "global.secretsBackend.vault.controller.caCert.secretName": controllerWebhookPKIConfig.CAPath, "global.secretsBackend.vault.enabled": "true", "global.secretsBackend.vault.consulServerRole": consulServerRole, "global.secretsBackend.vault.consulClientRole": consulClientRole, "global.secretsBackend.vault.consulCARole": serverPKIConfig.RoleName, "global.secretsBackend.vault.connectInjectRole": connectInjectorWebhookPKIConfig.RoleName, + "global.secretsBackend.vault.controllerRole": controllerWebhookPKIConfig.RoleName, "global.secretsBackend.vault.manageSystemACLsRole": manageSystemACLsRole, "global.secretsBackend.vault.ca.secretName": vaultCASecret, @@ -298,26 +282,6 @@ func testVault(t *testing.T, testAutoBootstrap bool) { logger.Logf(t, "Wait %d seconds for certificates to rotate....", expirationInSeconds) time.Sleep(time.Duration(expirationInSeconds) * time.Second) - if testAutoBootstrap { - logger.Logf(t, "Validating the ACL bootstrap token was stored in Vault.") - timer := &retry.Timer{Timeout: 10 * time.Second, Wait: 1 * time.Second} - retry.RunWith(timer, t, func(r *retry.R) { - secret, err := vaultClient.Logical().Read("consul/data/secret/bootstrap") - require.NoError(r, err) - - data, ok := secret.Data["data"].(map[string]interface{}) - require.True(r, ok) - require.NotNil(r, data) - - tok, ok := data["token"].(string) - require.True(r, ok) - require.NotEmpty(r, tok) - - // Set bootstrapToken for subsequent validations. - bootstrapToken = tok - }) - } - // Validate that the gossip encryption key is set correctly. logger.Log(t, "Validating the gossip key has been set correctly.") consulCluster.ACLToken = bootstrapToken @@ -350,13 +314,13 @@ func testVault(t *testing.T, testAutoBootstrap bool) { // Deploy two services and check that they can talk to each other. logger.Log(t, "creating static-server and static-client deployments") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") if cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") } - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.KubectlDeleteK(t, ctx.KubectlOptions(t), "../fixtures/bases/intention") }) k8s.KubectlApplyK(t, ctx.KubectlOptions(t), "../fixtures/bases/intention") diff --git a/acceptance/tests/vault/vault_tls_auto_reload_test.go b/acceptance/tests/vault/vault_tls_auto_reload_test.go index d5d4d33c4c..f079ee7492 100644 --- a/acceptance/tests/vault/vault_tls_auto_reload_test.go +++ b/acceptance/tests/vault/vault_tls_auto_reload_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package vault import ( @@ -246,13 +243,13 @@ func TestVault_TLSAutoReload(t *testing.T) { // Deploy two services and check that they can talk to each other. logger.Log(t, "creating static-server and static-client deployments") - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") if cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { - k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") } - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.KubectlDeleteK(t, ctx.KubectlOptions(t), "../fixtures/bases/intention") }) k8s.KubectlApplyK(t, ctx.KubectlOptions(t), "../fixtures/bases/intention") diff --git a/acceptance/tests/vault/vault_wan_fed_test.go b/acceptance/tests/vault/vault_wan_fed_test.go index fa63c4d5fb..a65702907f 100644 --- a/acceptance/tests/vault/vault_wan_fed_test.go +++ b/acceptance/tests/vault/vault_wan_fed_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package vault import ( @@ -31,7 +28,6 @@ import ( // in the secondary that will treat the Vault server in the primary as an external server. func TestVault_WANFederationViaGateways(t *testing.T) { cfg := suite.Config() - if cfg.UseKind { t.Skipf("Skipping this test because it's currently flaky on kind") } @@ -492,18 +488,16 @@ func TestVault_WANFederationViaGateways(t *testing.T) { logger.Log(t, "creating proxy-defaults config") kustomizeDir := "../fixtures/bases/mesh-gateway" k8s.KubectlApplyK(t, primaryCtx.KubectlOptions(t), kustomizeDir) - - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.KubectlDeleteK(t, primaryCtx.KubectlOptions(t), kustomizeDir) }) // Check that we can connect services over the mesh gateways. - logger.Log(t, "creating static-server in dc2") - k8s.DeployKustomize(t, secondaryCtx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, secondaryCtx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") logger.Log(t, "creating static-client in dc1") - k8s.DeployKustomize(t, primaryCtx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-multi-dc") + k8s.DeployKustomize(t, primaryCtx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-multi-dc") logger.Log(t, "creating intention") _, _, err = primaryClient.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{ @@ -520,7 +514,6 @@ func TestVault_WANFederationViaGateways(t *testing.T) { logger.Log(t, "checking that connection is successful") k8s.CheckStaticServerConnectionSuccessful(t, primaryCtx.KubectlOptions(t), StaticClientName, "http://localhost:1234") - } // vaultAddress returns Vault's server URL depending on test configuration. diff --git a/acceptance/tests/wan-federation/main_test.go b/acceptance/tests/wan-federation/main_test.go index 4a47a8a00f..4f6b0a7b54 100644 --- a/acceptance/tests/wan-federation/main_test.go +++ b/acceptance/tests/wan-federation/main_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package wanfederation import ( diff --git a/acceptance/tests/wan-federation/wan_federation_gateway_test.go b/acceptance/tests/wan-federation/wan_federation_gateway_test.go deleted file mode 100644 index 6abacda438..0000000000 --- a/acceptance/tests/wan-federation/wan_federation_gateway_test.go +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package wanfederation - -import ( - "context" - "fmt" - "testing" - "time" - - "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/environment" - "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" - "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" - "github.com/hashicorp/consul-k8s/acceptance/framework/logger" - "github.com/hashicorp/consul/api" - "github.com/hashicorp/serf/testutil/retry" - "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -func TestWANFederation_Gateway(t *testing.T) { - env := suite.Environment() - cfg := suite.Config() - - if cfg.UseKind { - // the only way this test can currently run on kind, at least on a Mac, is via leveraging MetalLB, which - // isn't in CI, so we just skip for now. - t.Skipf("skipping wan federation tests as they currently fail on Kind even though they work on other clouds.") - } - - primaryContext := env.DefaultContext(t) - secondaryContext := env.Context(t, 1) - - primaryHelmValues := map[string]string{ - "global.datacenter": "dc1", - - "global.tls.enabled": "true", - "global.tls.httpsOnly": "true", - - "global.federation.enabled": "true", - "global.federation.createFederationSecret": "true", - - "global.acls.manageSystemACLs": "true", - "global.acls.createReplicationToken": "true", - - "connectInject.enabled": "true", - "connectInject.replicas": "1", - - "meshGateway.enabled": "true", - "meshGateway.replicas": "1", - } - - releaseName := helpers.RandomName() - - // Install the primary consul cluster in the default kubernetes context - primaryConsulCluster := consul.NewHelmCluster(t, primaryHelmValues, primaryContext, cfg, releaseName) - primaryConsulCluster.Create(t) - - // Get the federation secret from the primary cluster and apply it to secondary cluster - federationSecretName := fmt.Sprintf("%s-consul-federation", releaseName) - logger.Logf(t, "retrieving federation secret %s from the primary cluster and applying to the secondary", federationSecretName) - federationSecret, err := primaryContext.KubernetesClient(t).CoreV1().Secrets(primaryContext.KubectlOptions(t).Namespace).Get(context.Background(), federationSecretName, metav1.GetOptions{}) - require.NoError(t, err) - federationSecret.ResourceVersion = "" - _, err = secondaryContext.KubernetesClient(t).CoreV1().Secrets(secondaryContext.KubectlOptions(t).Namespace).Create(context.Background(), federationSecret, metav1.CreateOptions{}) - require.NoError(t, err) - - var k8sAuthMethodHost string - // When running on kind, the kube API address in kubeconfig will have a localhost address - // which will not work from inside the container. That's why we need to use the endpoints address instead - // which will point the node IP. - if cfg.UseKind { - // The Kubernetes AuthMethod host is read from the endpoints for the Kubernetes service. - kubernetesEndpoint, err := secondaryContext.KubernetesClient(t).CoreV1().Endpoints("default").Get(context.Background(), "kubernetes", metav1.GetOptions{}) - require.NoError(t, err) - k8sAuthMethodHost = fmt.Sprintf("%s:%d", kubernetesEndpoint.Subsets[0].Addresses[0].IP, kubernetesEndpoint.Subsets[0].Ports[0].Port) - } else { - k8sAuthMethodHost = k8s.KubernetesAPIServerHostFromOptions(t, secondaryContext.KubectlOptions(t)) - } - - // Create secondary cluster - secondaryHelmValues := map[string]string{ - "global.datacenter": "dc2", - - "global.tls.enabled": "true", - "global.tls.httpsOnly": "false", - "global.acls.manageSystemACLs": "true", - "global.tls.caCert.secretName": federationSecretName, - "global.tls.caCert.secretKey": "caCert", - "global.tls.caKey.secretName": federationSecretName, - "global.tls.caKey.secretKey": "caKey", - - "global.federation.enabled": "true", - - "server.extraVolumes[0].type": "secret", - "server.extraVolumes[0].name": federationSecretName, - "server.extraVolumes[0].load": "true", - "server.extraVolumes[0].items[0].key": "serverConfigJSON", - "server.extraVolumes[0].items[0].path": "config.json", - - "connectInject.enabled": "true", - "connectInject.replicas": "1", - - "meshGateway.enabled": "true", - "meshGateway.replicas": "1", - - "global.acls.replicationToken.secretName": federationSecretName, - "global.acls.replicationToken.secretKey": "replicationToken", - "global.federation.k8sAuthMethodHost": k8sAuthMethodHost, - "global.federation.primaryDatacenter": "dc1", - } - - // Install the secondary consul cluster in the secondary kubernetes context - secondaryConsulCluster := consul.NewHelmCluster(t, secondaryHelmValues, secondaryContext, cfg, releaseName) - secondaryConsulCluster.Create(t) - - primaryClient, _ := primaryConsulCluster.SetupConsulClient(t, true) - secondaryClient, _ := secondaryConsulCluster.SetupConsulClient(t, true) - - // Verify federation between servers - logger.Log(t, "verifying federation was successful") - helpers.VerifyFederation(t, primaryClient, secondaryClient, releaseName, true) - - // Create a ProxyDefaults resource to configure services to use the mesh - // gateways. - logger.Log(t, "creating proxy-defaults config in dc1") - kustomizeDir := "../fixtures/cases/api-gateways/mesh" - k8s.KubectlApplyK(t, primaryContext.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.KubectlDeleteK(t, primaryContext.KubectlOptions(t), kustomizeDir) - }) - - // these clients are just there so we can exec in and curl on them. - logger.Log(t, "creating static-client in dc1") - k8s.DeployKustomize(t, primaryContext.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-multi-dc") - - logger.Log(t, "creating static-client in dc2") - k8s.DeployKustomize(t, secondaryContext.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-multi-dc") - - t.Run("from primary to secondary", func(t *testing.T) { - logger.Log(t, "creating static-server in dc2") - k8s.DeployKustomize(t, secondaryContext.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") - - logger.Log(t, "creating api-gateway resources in dc1") - out, err := k8s.RunKubectlAndGetOutputE(t, primaryContext.KubectlOptions(t), "apply", "-k", "../fixtures/bases/api-gateway") - require.NoError(t, err, out) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - // Ignore errors here because if the test ran as expected - // the custom resources will have been deleted. - k8s.RunKubectlAndGetOutputE(t, primaryContext.KubectlOptions(t), "delete", "-k", "../fixtures/bases/api-gateway") - }) - - // create a service resolver for doing cross-dc redirects. - k8s.KubectlApplyK(t, secondaryContext.KubectlOptions(t), "../fixtures/cases/api-gateways/dc1-to-dc2-resolver") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.KubectlDeleteK(t, secondaryContext.KubectlOptions(t), "../fixtures/cases/api-gateways/dc1-to-dc2-resolver") - }) - - // patching the route to target a MeshService since we don't have the corresponding Kubernetes service in this - // cluster. - k8s.RunKubectl(t, primaryContext.KubectlOptions(t), "patch", "httproute", "http-route", "-p", `{"spec":{"rules":[{"backendRefs":[{"group":"consul.hashicorp.com","kind":"MeshService","name":"mesh-service","port":80}]}]}}`, "--type=merge") - - checkConnectivity(t, primaryContext, primaryClient) - }) - - t.Run("from secondary to primary", func(t *testing.T) { - // Check that we can connect services over the mesh gateways - logger.Log(t, "creating static-server in dc1") - k8s.DeployKustomize(t, primaryContext.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") - - logger.Log(t, "creating api-gateway resources in dc2") - out, err := k8s.RunKubectlAndGetOutputE(t, secondaryContext.KubectlOptions(t), "apply", "-k", "../fixtures/bases/api-gateway") - require.NoError(t, err, out) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - // Ignore errors here because if the test ran as expected - // the custom resources will have been deleted. - k8s.RunKubectlAndGetOutputE(t, secondaryContext.KubectlOptions(t), "delete", "-k", "../fixtures/bases/api-gateway") - }) - - // create a service resolver for doing cross-dc redirects. - k8s.KubectlApplyK(t, secondaryContext.KubectlOptions(t), "../fixtures/cases/api-gateways/dc2-to-dc1-resolver") - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { - k8s.KubectlDeleteK(t, secondaryContext.KubectlOptions(t), "../fixtures/cases/api-gateways/dc2-to-dc1-resolver") - }) - - // patching the route to target a MeshService since we don't have the corresponding Kubernetes service in this - // cluster. - k8s.RunKubectl(t, secondaryContext.KubectlOptions(t), "patch", "httproute", "http-route", "-p", `{"spec":{"rules":[{"backendRefs":[{"group":"consul.hashicorp.com","kind":"MeshService","name":"mesh-service","port":80}]}]}}`, "--type=merge") - - checkConnectivity(t, secondaryContext, primaryClient) - }) -} - -func checkConnectivity(t *testing.T, ctx environment.TestContext, client *api.Client) { - k8sClient := ctx.ControllerRuntimeClient(t) - - // On startup, the controller can take upwards of 1m to perform - // leader election so we may need to wait a long time for - // the reconcile loop to run (hence the 1m timeout here). - var gatewayAddress string - counter := &retry.Counter{Count: 600, Wait: 2 * time.Second} - retry.RunWith(counter, t, func(r *retry.R) { - var gateway gwv1beta1.Gateway - err := k8sClient.Get(context.Background(), types.NamespacedName{Name: "gateway", Namespace: "default"}, &gateway) - require.NoError(r, err) - - // check that we have an address to use - require.Len(r, gateway.Status.Addresses, 1) - // now we know we have an address, set it so we can use it - gatewayAddress = gateway.Status.Addresses[0].Value - }) - - targetAddress := fmt.Sprintf("http://%s/", gatewayAddress) - - logger.Log(t, "checking that the connection is not successful because there's no intention") - k8s.CheckStaticServerHTTPConnectionFailing(t, ctx.KubectlOptions(t), StaticClientName, targetAddress) - - logger.Log(t, "creating intention") - _, _, err := client.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{ - Kind: api.ServiceIntentions, - Name: "static-server", - Sources: []*api.SourceIntention{ - { - Name: "gateway", - Action: api.IntentionActionAllow, - }, - }, - }, nil) - require.NoError(t, err) - defer func() { - _, err := client.ConfigEntries().Delete(api.ServiceIntentions, "static-server", &api.WriteOptions{}) - require.NoError(t, err) - }() - - logger.Log(t, "checking that connection is successful") - k8s.CheckStaticServerConnectionSuccessful(t, ctx.KubectlOptions(t), StaticClientName, targetAddress) -} diff --git a/acceptance/tests/wan-federation/wan_federation_test.go b/acceptance/tests/wan-federation/wan_federation_test.go index 8edc1f5d03..d347695faa 100644 --- a/acceptance/tests/wan-federation/wan_federation_test.go +++ b/acceptance/tests/wan-federation/wan_federation_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package wanfederation import ( @@ -154,7 +151,7 @@ func TestWANFederation(t *testing.T) { logger.Log(t, "creating proxy-defaults config") kustomizeDir := "../fixtures/bases/mesh-gateway" k8s.KubectlApplyK(t, secondaryContext.KubectlOptions(t), kustomizeDir) - helpers.Cleanup(t, cfg.NoCleanupOnFailure, cfg.NoCleanup, func() { + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { k8s.KubectlDeleteK(t, secondaryContext.KubectlOptions(t), kustomizeDir) }) @@ -184,10 +181,10 @@ func TestWANFederation(t *testing.T) { // Check that we can connect services over the mesh gateways logger.Log(t, "creating static-server in dc2") - k8s.DeployKustomize(t, secondaryHelper.KubectlOptsForApp(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, secondaryHelper.KubectlOptsForApp(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") logger.Log(t, "creating static-client in dc1") - k8s.DeployKustomize(t, primaryHelper.KubectlOptsForApp(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/cases/static-client-multi-dc") + k8s.DeployKustomize(t, primaryHelper.KubectlOptsForApp(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-multi-dc") if c.secure { primaryHelper.CreateIntention(t) diff --git a/charts/consul/.helmignore b/charts/consul/.helmignore index 3fa2f24edf..d1180d2fb7 100644 --- a/charts/consul/.helmignore +++ b/charts/consul/.helmignore @@ -2,4 +2,3 @@ .terraform/ bin/ test/ -crds/kustomization.yaml diff --git a/charts/consul/Chart.yaml b/charts/consul/Chart.yaml index de8e6e9df6..84642eb7ac 100644 --- a/charts/consul/Chart.yaml +++ b/charts/consul/Chart.yaml @@ -1,11 +1,8 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: v2 name: consul -version: 1.3.0-dev -appVersion: 1.17-dev -kubeVersion: ">=1.22.0-0" +version: 1.0.10-dev +appVersion: 1.14-dev +kubeVersion: ">=1.21.0-0" description: Official HashiCorp Consul Chart home: https://www.consul.io icon: https://raw.githubusercontent.com/hashicorp/consul-k8s/main/assets/icon.png @@ -16,13 +13,13 @@ annotations: artifacthub.io/prerelease: true artifacthub.io/images: | - name: consul - image: docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.17-dev + image: docker.mirror.hashicorp.services/hashicorppreview/consul:1.14-dev - name: consul-k8s-control-plane - image: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.3.0-dev + image: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.0.10-dev - name: consul-dataplane - image: docker.mirror.hashicorp.services/hashicorppreview/consul-dataplane:1.3-dev + image: docker.mirror.hashicorp.services/hashicorppreview/consul-dataplane:1.0-dev - name: envoy - image: envoyproxy/envoy:v1.26.2 + image: envoyproxy/envoy:v1.24.10 artifacthub.io/license: MPL-2.0 artifacthub.io/links: | - name: Documentation diff --git a/charts/consul/addons/gen.sh b/charts/consul/addons/gen.sh index 1d03390bed..967b368c63 100755 --- a/charts/consul/addons/gen.sh +++ b/charts/consul/addons/gen.sh @@ -1,7 +1,4 @@ #!/usr/bin/env bash -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - WD=$(dirname "$0") WD=$(cd "$WD"; pwd) diff --git a/charts/consul/addons/values/prometheus.yaml b/charts/consul/addons/values/prometheus.yaml index 1f90636f2e..9ffe9af5ae 100644 --- a/charts/consul/addons/values/prometheus.yaml +++ b/charts/consul/addons/values/prometheus.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - # Disable non-essential components alertmanager: enabled: false diff --git a/charts/consul/templates/_helpers.tpl b/charts/consul/templates/_helpers.tpl index 18f57b188c..cbb49ffaef 100644 --- a/charts/consul/templates/_helpers.tpl +++ b/charts/consul/templates/_helpers.tpl @@ -15,8 +15,23 @@ as well as the global.name setting. {{- end -}} {{- end -}} + {{- define "consul.restrictedSecurityContext" -}} {{- if not .Values.global.enablePodSecurityPolicies -}} +{{/* +To be compatible with the 'restricted' Pod Security Standards profile, we +should set this securityContext on containers whenever possible. + +In OpenShift < 4.11 the restricted SCC disallows setting most of these fields, +so we do not set any for simplicity (and because that's how it was configured +prior to adding restricted PSA support here). In OpenShift >= 4.11, the new +restricted-v2 SCC allows setting these in the securityContext, and by setting +them we avoid PSA warnings that are enabled by default. + +We use the K8s version as a proxy for the OpenShift version because there is a +1:1 mapping of versions. OpenShift 4.11 corresponds to K8s 1.24.x. +*/}} +{{- if (or (not .Values.global.openshift.enabled) (and (ge .Capabilities.KubeVersion.Major "1") (ge .Capabilities.KubeVersion.Minor "24"))) -}} securityContext: allowPrivilegeEscalation: false capabilities: @@ -25,11 +40,12 @@ securityContext: runAsNonRoot: true seccompProfile: type: RuntimeDefault +{{- end -}} {{- if not .Values.global.openshift.enabled -}} {{/* We must set runAsUser or else the root user will be used in some cases and containers will fail to start due to runAsNonRoot above (e.g. -tls-init-cleanup). On OpenShift, runAsUser is automatically. We pick user 100 +tls-init-cleanup). On OpenShift, runAsUser is set automatically. We pick user 100 because it is a non-root user id that exists in the consul, consul-dataplane, and consul-k8s-control-plane images. */}} @@ -96,6 +112,22 @@ and consul-k8s-control-plane images. {{ "{{" }}- end -{{ "}}" }} {{- end -}} +{{- define "consul.controllerWebhookTLSCertTemplate" -}} + | + {{ "{{" }}- with secret "{{ .Values.global.secretsBackend.vault.controller.tlsCert.secretName }}" "{{- $name := include "consul.fullname" . -}}{{ printf "common_name=%s-controller-webhook" $name }}" + "alt_names={{ include "consul.controllerWebhookTLSAltNames" . }}" -{{ "}}" }} + {{ "{{" }}- .Data.certificate -{{ "}}" }} + {{ "{{" }}- end -{{ "}}" }} +{{- end -}} + +{{- define "consul.controllerWebhookTLSKeyTemplate" -}} + | + {{ "{{" }}- with secret "{{ .Values.global.secretsBackend.vault.controller.tlsCert.secretName }}" "{{- $name := include "consul.fullname" . -}}{{ printf "common_name=%s-controller-webhook" $name }}" + "alt_names={{ include "consul.controllerWebhookTLSAltNames" . }}" -{{ "}}" }} + {{ "{{" }}- .Data.private_key -{{ "}}" }} + {{ "{{" }}- end -{{ "}}" }} +{{- end -}} + {{- define "consul.serverTLSAltNames" -}} {{- $name := include "consul.fullname" . -}} {{- $ns := .Release.Namespace -}} @@ -116,6 +148,12 @@ and consul-k8s-control-plane images. {{ printf "%s-connect-injector,%s-connect-injector.%s,%s-connect-injector.%s.svc,%s-connect-injector.%s.svc.cluster.local" $name $name $ns $name $ns $name $ns}} {{- end -}} +{{- define "consul.controllerWebhookTLSAltNames" -}} +{{- $name := include "consul.fullname" . -}} +{{- $ns := .Release.Namespace -}} +{{ printf "%s-controller-webhook,%s-controller-webhook.%s,%s-controller-webhook.%s.svc,%s-controller-webhook.%s.svc.cluster.local" $name $name $ns $name $ns $name $ns}} +{{- end -}} + {{- define "consul.vaultReplicationTokenTemplate" -}} | {{ "{{" }}- with secret "{{ .Values.global.acls.replicationToken.secretName }}" -{{ "}}" }} @@ -286,17 +324,20 @@ Fails when at least one but not all of the following have been set: - global.secretsBackend.vault.connectInjectRole - global.secretsBackend.vault.connectInject.tlsCert.secretName - global.secretsBackend.vault.connectInject.caCert.secretName +- global.secretsBackend.vault.controllerRole +- global.secretsBackend.vault.controller.tlsCert.secretName +- global.secretsBackend.vault.controller.caCert.secretName The above values are needed in full to turn off web cert manager and allow -connect inject to manage its own webhook certs. +connect inject and controller to manage its own webhook certs. Usage: {{ template "consul.validateVaultWebhookCertConfiguration" . }} */}} {{- define "consul.validateVaultWebhookCertConfiguration" -}} -{{- if or .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName}} -{{- if or (not .Values.global.secretsBackend.vault.connectInjectRole) (not .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName) (not .Values.global.secretsBackend.vault.connectInject.caCert.secretName) }} -{{fail "When one of the following has been set, all must be set: global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName"}} +{{- if or .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName .Values.global.secretsBackend.vault.controllerRole .Values.global.secretsBackend.vault.controller.tlsCert.secretName .Values.global.secretsBackend.vault.controller.caCert.secretName}} +{{- if or (not .Values.global.secretsBackend.vault.connectInjectRole) (not .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName) (not .Values.global.secretsBackend.vault.connectInject.caCert.secretName) (not .Values.global.secretsBackend.vault.controllerRole) (not .Values.global.secretsBackend.vault.controller.tlsCert.secretName) (not .Values.global.secretsBackend.vault.controller.caCert.secretName) }} +{{fail "When one of the following has been set, all must be set: global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName, global.secretsBackend.vault.controllerRole, global.secretsBackend.vault.controller.tlsCert.secretName, and global.secretsBackend.vault.controller.caCert.secretName."}} {{ end }} {{ end }} {{- end -}} @@ -358,7 +399,7 @@ Consul server environment variables for consul-k8s commands. {{- end }} {{- if and .Values.externalServers.enabled .Values.externalServers.skipServerWatch }} - name: CONSUL_SKIP_SERVER_WATCH - value: "true" + value: "true" {{- end }} {{- end -}} @@ -389,7 +430,7 @@ Usage: {{ template "consul.validateCloudSecretKeys" . }} */}} {{- define "consul.validateCloudSecretKeys" -}} -{{- if and .Values.global.cloud.enabled }} +{{- if and .Values.global.cloud.enabled }} {{- if or (and .Values.global.cloud.resourceId.secretName (not .Values.global.cloud.resourceId.secretKey)) (and .Values.global.cloud.resourceId.secretKey (not .Values.global.cloud.resourceId.secretName)) }} {{fail "When either global.cloud.resourceId.secretName or global.cloud.resourceId.secretKey is defined, both must be set."}} {{- end }} @@ -411,38 +452,3 @@ Usage: {{ template "consul.validateCloudSecretKeys" . }} {{- end }} {{- end -}} - -{{/* -Fails if temeletryCollector.clientId or telemetryCollector.clientSecret exist and one of other secrets is nil or empty. -- telemetryCollector.cloud.clientId.secretName -- telemetryCollector.cloud.clientSecret.secretName -- global.cloud.resourceId.secretName - -Usage: {{ template "consul.validateTelemetryCollectorCloud" . }} - -*/}} -{{- define "consul.validateTelemetryCollectorCloud" -}} -{{- if (and .Values.telemetryCollector.cloud.clientId.secretName (or (not .Values.global.cloud.resourceId.secretName) (not .Values.telemetryCollector.cloud.clientSecret.secretName))) }} -{{fail "When telemetryCollector.cloud.clientId.secretName is set, global.cloud.resourceId.secretName, telemetryCollector.cloud.clientSecret.secretName must also be set."}} -{{- end }} -{{- if (and .Values.telemetryCollector.cloud.clientSecret.secretName (or (not .Values.global.cloud.resourceId.secretName) (not .Values.telemetryCollector.cloud.clientSecret.secretName))) }} -{{fail "When telemetryCollector.cloud.clientSecret.secretName is set, global.cloud.resourceId.secretName,telemetryCollector.cloud.clientId.secretName must also be set."}} -{{- end }} -{{- end }} - -{{/**/}} - -{{- define "consul.validateTelemetryCollectorCloudSecretKeys" -}} -{{- if or (and .Values.telemetryCollector.cloud.clientId.secretName (not .Values.telemetryCollector.cloud.clientId.secretKey)) (and .Values.telemetryCollector.cloud.clientId.secretKey (not .Values.telemetryCollector.cloud.clientId.secretName)) }} -{{fail "When either telemetryCollector.cloud.clientId.secretName or telemetryCollector.cloud.clientId.secretKey is defined, both must be set."}} -{{- end }} -{{- if or (and .Values.telemetryCollector.cloud.clientSecret.secretName (not .Values.telemetryCollector.cloud.clientSecret.secretKey)) (and .Values.telemetryCollector.cloud.clientSecret.secretKey (not .Values.telemetryCollector.cloud.clientSecret.secretName)) }} -{{fail "When either telemetryCollector.cloud.clientSecret.secretName or telemetryCollector.cloud.clientSecret.secretKey is defined, both must be set."}} -{{- end }} -{{- if or (and .Values.telemetryCollector.cloud.clientSecret.secretName .Values.telemetryCollector.cloud.clientSecret.secretKey .Values.telemetryCollector.cloud.clientId.secretName .Values.telemetryCollector.cloud.clientId.secretKey (not .Values.global.cloud.resourceId.secretName)) }} -{{fail "When telemetryCollector has clientId and clientSecret global.cloud.resourceId.secretName must be set"}} -{{- end }} -{{- if or (and .Values.telemetryCollector.cloud.clientSecret.secretName .Values.telemetryCollector.cloud.clientSecret.secretKey .Values.telemetryCollector.cloud.clientId.secretName .Values.telemetryCollector.cloud.clientId.secretKey (not .Values.global.cloud.resourceId.secretKey)) }} -{{fail "When telemetryCollector has clientId and clientSecret .global.cloud.resourceId.secretKey must be set"}} -{{- end }} -{{- end -}} diff --git a/charts/consul/templates/connect-inject-clusterrole.yaml b/charts/consul/templates/connect-inject-clusterrole.yaml index c95b7c143a..f2e12f0ad9 100644 --- a/charts/consul/templates/connect-inject-clusterrole.yaml +++ b/charts/consul/templates/connect-inject-clusterrole.yaml @@ -24,17 +24,10 @@ rules: - serviceintentions - ingressgateways - terminatinggateways - - gatewayclassconfigs - - meshservices - - samenessgroups - - controlplanerequestlimits - - routeretryfilters - - routetimeoutfilters {{- if .Values.global.peering.enabled }} - peeringacceptors - peeringdialers {{- end }} - - jwtproviders verbs: - create - delete @@ -56,35 +49,26 @@ rules: - serviceintentions/status - ingressgateways/status - terminatinggateways/status - - samenessgroups/status - - controlplanerequestlimits/status {{- if .Values.global.peering.enabled }} - peeringacceptors/status - peeringdialers/status {{- end }} - - jwtproviders/status verbs: - get - patch - update +{{- if .Values.global.acls.manageSystemACLs }} - apiGroups: [ "" ] - resources: [ "secrets", "serviceaccounts", "endpoints", "services", "namespaces", "nodes" ] + resources: [ "serviceaccounts", "secrets" ] verbs: - - create - get - - list - - watch - - delete - - update -- apiGroups: [ "rbac.authorization.k8s.io" ] - resources: [ "roles", "rolebindings" ] +{{- end }} +- apiGroups: [ "" ] + resources: [ "endpoints", "services", "namespaces", "nodes" ] verbs: - - get - - list - - watch - - delete - - create - - update + - "get" + - "list" + - "watch" - apiGroups: [ "" ] resources: - pods @@ -124,78 +108,12 @@ rules: - "update" - "delete" {{- end }} +{{- if .Values.global.enablePodSecurityPolicies }} - apiGroups: [ "policy" ] resources: [ "podsecuritypolicies" ] - verbs: - - use -- apiGroups: - - gateway.networking.k8s.io - resources: - - gatewayclasses - - gateways - - httproutes - - tcproutes - - referencegrants - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - gateway.networking.k8s.io - resources: - - gatewayclasses/finalizers - - gateways/finalizers - - httproutes/finalizers - - tcproutes/finalizers - verbs: - - update -- apiGroups: - - gateway.networking.k8s.io - resources: - - gatewayclasses/status - - gateways/status - - httproutes/status - - tcproutes/status - verbs: - - get - - patch - - update -- apiGroups: - - apps - resources: - - deployments - verbs: - - create - - get - - list - - update - - watch - - delete -- apiGroups: - - core - resources: - - services - verbs: - - watch - - list -- apiGroups: [ "" ] - resources: [ "secrets" ] - verbs: - - "get" - - "list" - - "watch" -{{- if .Values.global.openshift.enabled }} -- apiGroups: - - security.openshift.io - resources: - - securitycontextconstraints resourceNames: - - {{ .Values.connectInject.apiGateway.managedGatewayClass.openshiftSCCName }} + - {{ template "consul.fullname" . }}-connect-injector verbs: - - use - {{- end }} + - use +{{- end }} {{- end }} diff --git a/charts/consul/templates/connect-inject-deployment.yaml b/charts/consul/templates/connect-inject-deployment.yaml index e726c9ecc9..f9e79ced52 100644 --- a/charts/consul/templates/connect-inject-deployment.yaml +++ b/charts/consul/templates/connect-inject-deployment.yaml @@ -271,7 +271,6 @@ spec: {{- if and .Values.global.tls.enabled .Values.global.tls.enableAutoEncrypt }} -enable-auto-encrypt \ {{- end }} - -enable-telemetry-collector={{ .Values.global.metrics.enableTelemetryCollector}} \ startupProbe: httpGet: path: /readyz/ready diff --git a/charts/consul/templates/connect-inject-mutatingwebhookconfiguration.yaml b/charts/consul/templates/connect-inject-mutatingwebhookconfiguration.yaml index e4fe79f621..afcfd3800f 100644 --- a/charts/consul/templates/connect-inject-mutatingwebhookconfiguration.yaml +++ b/charts/consul/templates/connect-inject-mutatingwebhookconfiguration.yaml @@ -222,27 +222,6 @@ webhooks: resources: - exportedservices sideEffects: None -- clientConfig: - service: - name: {{ template "consul.fullname" . }}-connect-injector - namespace: {{ .Release.Namespace }} - path: /mutate-v1alpha1-controlplanerequestlimits - failurePolicy: Fail - admissionReviewVersions: - - "v1beta1" - - "v1" - name: mutate-controlplanerequestlimit.consul.hashicorp.com - rules: - - apiGroups: - - consul.hashicorp.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - controlplanerequestlimits - sideEffects: None - name: {{ template "consul.fullname" . }}-connect-injector.consul.hashicorp.com # The webhook will fail scheduling all pods that are not part of consul if all replicas of the webhook are unhealthy. objectSelector: @@ -312,47 +291,5 @@ webhooks: admissionReviewVersions: - "v1beta1" - "v1" -- admissionReviewVersions: - - v1beta1 - - v1 - clientConfig: - service: - name: {{ template "consul.fullname" . }}-connect-injector - namespace: {{ .Release.Namespace }} - path: /mutate-v1alpha1-samenessgroup - failurePolicy: Fail - name: mutate-samenessgroup.consul.hashicorp.com - rules: - - apiGroups: - - consul.hashicorp.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - samenessgroups - sideEffects: None {{- end }} -- admissionReviewVersions: - - v1beta1 - - v1 - clientConfig: - service: - name: {{ template "consul.fullname" . }}-connect-injector - namespace: {{ .Release.Namespace }} - path: /mutate-v1alpha1-jwtprovider - failurePolicy: Fail - name: mutate-jwtprovider.consul.hashicorp.com - rules: - - apiGroups: - - consul.hashicorp.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - jwtproviders - sideEffects: None {{- end }} diff --git a/charts/consul/templates/crd-controlplanerequestlimits.yaml b/charts/consul/templates/crd-controlplanerequestlimits.yaml deleted file mode 100644 index 2b0c45a621..0000000000 --- a/charts/consul/templates/crd-controlplanerequestlimits.yaml +++ /dev/null @@ -1,203 +0,0 @@ -{{- if .Values.connectInject.enabled }} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: controlplanerequestlimits.consul.hashicorp.com - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: crd -spec: - group: consul.hashicorp.com - names: - kind: ControlPlaneRequestLimit - listKind: ControlPlaneRequestLimitList - plural: controlplanerequestlimits - singular: controlplanerequestlimit - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The sync status of the resource with Consul - jsonPath: .status.conditions[?(@.type=="Synced")].status - name: Synced - type: string - - description: The age of the resource - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: ControlPlaneRequestLimit is the Schema for the controlplanerequestlimits - API. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: ControlPlaneRequestLimitSpec defines the desired state of - ControlPlaneRequestLimit. - properties: - acl: - properties: - readRate: - type: number - writeRate: - type: number - type: object - catalog: - properties: - readRate: - type: number - writeRate: - type: number - type: object - configEntry: - properties: - readRate: - type: number - writeRate: - type: number - type: object - connectCA: - properties: - readRate: - type: number - writeRate: - type: number - type: object - coordinate: - properties: - readRate: - type: number - writeRate: - type: number - type: object - discoveryChain: - properties: - readRate: - type: number - writeRate: - type: number - type: object - health: - properties: - readRate: - type: number - writeRate: - type: number - type: object - intention: - properties: - readRate: - type: number - writeRate: - type: number - type: object - kv: - properties: - readRate: - type: number - writeRate: - type: number - type: object - mode: - type: string - perparedQuery: - properties: - readRate: - type: number - writeRate: - type: number - type: object - readRate: - type: number - session: - properties: - readRate: - type: number - writeRate: - type: number - type: object - tenancy: - properties: - readRate: - type: number - writeRate: - type: number - type: object - txn: - properties: - readRate: - type: number - writeRate: - type: number - type: object - writeRate: - type: number - type: object - status: - properties: - conditions: - description: Conditions indicate the latest available observations - of a resource's current state. - items: - description: 'Conditions define a readiness condition for a Consul - resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' - properties: - lastTransitionTime: - description: LastTransitionTime is the last time the condition - transitioned from one status to another. - format: date-time - type: string - message: - description: A human readable message indicating details about - the transition. - type: string - reason: - description: The reason for the condition's last transition. - type: string - status: - description: Status of the condition, one of True, False, Unknown. - type: string - type: - description: Type of condition. - type: string - required: - - status - - type - type: object - type: array - lastSyncedTime: - description: LastSyncedTime is the last time the resource successfully - synced with Consul. - format: date-time - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] -{{- end }} diff --git a/charts/consul/templates/crd-exportedservices.yaml b/charts/consul/templates/crd-exportedservices.yaml index 591500cb12..007990372c 100644 --- a/charts/consul/templates/crd-exportedservices.yaml +++ b/charts/consul/templates/crd-exportedservices.yaml @@ -76,12 +76,8 @@ spec: the service to. type: string peer: - description: Peer is the name of the peer to export the - service to. - type: string - samenessGroup: - description: SamenessGroup is the name of the sameness - group to export the service to. + description: '[Experimental] Peer is the name of the peer + to export the service to.' type: string type: object type: array diff --git a/charts/consul/templates/crd-gatewayclassconfigs.yaml b/charts/consul/templates/crd-gatewayclassconfigs.yaml deleted file mode 100644 index 8140902f78..0000000000 --- a/charts/consul/templates/crd-gatewayclassconfigs.yaml +++ /dev/null @@ -1,164 +0,0 @@ -{{- if .Values.connectInject.enabled }} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: gatewayclassconfigs.consul.hashicorp.com - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: crd -spec: - group: consul.hashicorp.com - names: - kind: GatewayClassConfig - listKind: GatewayClassConfigList - plural: gatewayclassconfigs - singular: gatewayclassconfig - scope: Cluster - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: GatewayClassConfig defines the values that may be set on a GatewayClass - for Consul API Gateway. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of GatewayClassConfig. - properties: - copyAnnotations: - description: Annotation Information to copy to services or deployments - properties: - service: - description: List of annotations to copy to the gateway service. - items: - type: string - type: array - type: object - deployment: - description: Deployment defines the deployment configuration for the - gateway. - properties: - defaultInstances: - default: 1 - description: Number of gateway instances that should be deployed - by default - format: int32 - maximum: 8 - minimum: 1 - type: integer - maxInstances: - default: 8 - description: Max allowed number of gateway instances - format: int32 - maximum: 8 - minimum: 1 - type: integer - minInstances: - default: 1 - description: Minimum allowed number of gateway instances - format: int32 - maximum: 8 - minimum: 1 - type: integer - type: object - nodeSelector: - additionalProperties: - type: string - description: 'NodeSelector is a selector which must be true for the - pod to fit on a node. Selector which must match a node''s labels - for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' - type: object - podSecurityPolicy: - description: The name of an existing Kubernetes PodSecurityPolicy - to bind to the managed ServiceAccount if ACLs are managed. - type: string - serviceType: - description: Service Type string describes ingress methods for a service - enum: - - ClusterIP - - NodePort - - LoadBalancer - type: string - tolerations: - description: 'Tolerations allow the scheduler to schedule nodes with - matching taints. More Info: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/' - items: - description: The pod this Toleration is attached to tolerates any - taint that matches the triple using the matching - operator . - properties: - effect: - description: Effect indicates the taint effect to match. Empty - means match all taint effects. When specified, allowed values - are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: Key is the taint key that the toleration applies - to. Empty means match all taint keys. If the key is empty, - operator must be Exists; this combination means to match all - values and all keys. - type: string - operator: - description: Operator represents a key's relationship to the - value. Valid operators are Exists and Equal. Defaults to Equal. - Exists is equivalent to wildcard for value, so that a pod - can tolerate all taints of a particular category. - type: string - tolerationSeconds: - description: TolerationSeconds represents the period of time - the toleration (which must be of effect NoExecute, otherwise - this field is ignored) tolerates the taint. By default, it - is not set, which means tolerate the taint forever (do not - evict). Zero and negative values will be treated as 0 (evict - immediately) by the system. - format: int64 - type: integer - value: - description: Value is the taint value the toleration matches - to. If the operator is Exists, the value should be empty, - otherwise just a regular string. - type: string - type: object - type: array - openshiftSCCName: - description: The name of an existing SecurityContextConstraints - resource to bind to the managed role when running on OpenShift. - type: string - mapPrivilegedContainerPorts: - type: integer - format: int32 - minimum: 0 - maximum: 64512 - description: mapPrivilegedContainerPorts is the value which Consul will add to privileged container port - values (ports < 1024) defined on a Gateway when the number is greater than 0. This cannot be more than - 64512 as the highest privileged port is 1023, which would then map to 65535, which is the highest - valid port number. - type: object - type: object - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] -{{- end }} diff --git a/charts/consul/templates/crd-gatewayclasses.yaml b/charts/consul/templates/crd-gatewayclasses.yaml deleted file mode 100644 index b0725c2baf..0000000000 --- a/charts/consul/templates/crd-gatewayclasses.yaml +++ /dev/null @@ -1,330 +0,0 @@ -{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 - gateway.networking.k8s.io/bundle-version: v0.6.2 - gateway.networking.k8s.io/channel: experimental - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: crd - name: gatewayclasses.gateway.networking.k8s.io -spec: - group: gateway.networking.k8s.io - names: - categories: - - gateway-api - kind: GatewayClass - listKind: GatewayClassList - plural: gatewayclasses - shortNames: - - gc - singular: gatewayclass - scope: Cluster - versions: - - additionalPrinterColumns: - - jsonPath: .spec.controllerName - name: Controller - type: string - - jsonPath: .status.conditions[?(@.type=="Accepted")].status - name: Accepted - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .spec.description - name: Description - priority: 1 - type: string - deprecated: true - deprecationWarning: The v1alpha2 version of GatewayClass has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1. - name: v1alpha2 - schema: - openAPIV3Schema: - description: "GatewayClass describes a class of Gateways available to the user for creating Gateway resources. \n It is recommended that this resource be used as a template for Gateways. This means that a Gateway is based on the state of the GatewayClass at the time it was created and changes to the GatewayClass or associated parameters are not propagated down to existing Gateways. This recommendation is intended to limit the blast radius of changes to GatewayClass or associated parameters. If implementations choose to propagate GatewayClass changes to existing Gateways, that MUST be clearly documented by the implementation. \n Whenever one or more Gateways are using a GatewayClass, implementations MUST add the `gateway-exists-finalizer.gateway.networking.k8s.io` finalizer on the associated GatewayClass. This ensures that a GatewayClass associated with a Gateway is not deleted while in use. \n GatewayClass is a Cluster level resource." - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of GatewayClass. - properties: - controllerName: - description: "ControllerName is the name of the controller that is managing Gateways of this class. The value of this field MUST be a domain prefixed path. \n Example: \"example.net/gateway-controller\". \n This field is not mutable and cannot be empty. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - description: - description: Description helps describe a GatewayClass with more details. - maxLength: 64 - type: string - parametersRef: - description: "ParametersRef is a reference to a resource that contains the configuration parameters corresponding to the GatewayClass. This is optional if the controller does not require any additional configuration. \n ParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap, or an implementation-specific custom resource. The resource can be cluster-scoped or namespace-scoped. \n If the referent cannot be found, the GatewayClass's \"InvalidParameters\" status condition will be true. \n Support: Implementation-specific" - properties: - group: - description: Group is the group of the referent. - 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. - 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 - namespace: - description: Namespace is the namespace of the referent. This field is required when referring to a Namespace-scoped resource and MUST be unset when referring to a Cluster-scoped resource. - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - group - - kind - - name - type: object - required: - - controllerName - type: object - status: - default: - conditions: - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: Waiting - status: Unknown - type: Accepted - description: Status defines the current state of GatewayClass. - properties: - conditions: - default: - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: Pending - status: Unknown - type: Accepted - description: "Conditions is the current status from the controller for this GatewayClass. \n Controllers should prefer to publish conditions using values of GatewayClassConditionType for the type of each Condition." - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - type: object - required: - - spec - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .spec.controllerName - name: Controller - type: string - - jsonPath: .status.conditions[?(@.type=="Accepted")].status - name: Accepted - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .spec.description - name: Description - priority: 1 - type: string - name: v1beta1 - schema: - openAPIV3Schema: - description: "GatewayClass describes a class of Gateways available to the user for creating Gateway resources. \n It is recommended that this resource be used as a template for Gateways. This means that a Gateway is based on the state of the GatewayClass at the time it was created and changes to the GatewayClass or associated parameters are not propagated down to existing Gateways. This recommendation is intended to limit the blast radius of changes to GatewayClass or associated parameters. If implementations choose to propagate GatewayClass changes to existing Gateways, that MUST be clearly documented by the implementation. \n Whenever one or more Gateways are using a GatewayClass, implementations MUST add the `gateway-exists-finalizer.gateway.networking.k8s.io` finalizer on the associated GatewayClass. This ensures that a GatewayClass associated with a Gateway is not deleted while in use. \n GatewayClass is a Cluster level resource." - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of GatewayClass. - properties: - controllerName: - description: "ControllerName is the name of the controller that is managing Gateways of this class. The value of this field MUST be a domain prefixed path. \n Example: \"example.net/gateway-controller\". \n This field is not mutable and cannot be empty. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - description: - description: Description helps describe a GatewayClass with more details. - maxLength: 64 - type: string - parametersRef: - description: "ParametersRef is a reference to a resource that contains the configuration parameters corresponding to the GatewayClass. This is optional if the controller does not require any additional configuration. \n ParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap, or an implementation-specific custom resource. The resource can be cluster-scoped or namespace-scoped. \n If the referent cannot be found, the GatewayClass's \"InvalidParameters\" status condition will be true. \n Support: Implementation-specific" - properties: - group: - description: Group is the group of the referent. - 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. - 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 - namespace: - description: Namespace is the namespace of the referent. This field is required when referring to a Namespace-scoped resource and MUST be unset when referring to a Cluster-scoped resource. - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - group - - kind - - name - type: object - required: - - controllerName - type: object - status: - default: - conditions: - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: Waiting - status: Unknown - type: Accepted - description: Status defines the current state of GatewayClass. - properties: - conditions: - default: - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: Pending - status: Unknown - type: Accepted - description: "Conditions is the current status from the controller for this GatewayClass. \n Controllers should prefer to publish conditions using values of GatewayClassConditionType for the type of each Condition." - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] -{{- end }} diff --git a/charts/consul/templates/crd-gateways.yaml b/charts/consul/templates/crd-gateways.yaml deleted file mode 100644 index 923a75477d..0000000000 --- a/charts/consul/templates/crd-gateways.yaml +++ /dev/null @@ -1,884 +0,0 @@ -{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 - gateway.networking.k8s.io/bundle-version: v0.6.2 - gateway.networking.k8s.io/channel: experimental - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: crd - name: gateways.gateway.networking.k8s.io -spec: - group: gateway.networking.k8s.io - names: - categories: - - gateway-api - kind: Gateway - listKind: GatewayList - plural: gateways - shortNames: - - gtw - singular: gateway - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.gatewayClassName - name: Class - type: string - - jsonPath: .status.addresses[*].value - name: Address - type: string - - jsonPath: .status.conditions[?(@.type=="Programmed")].status - name: Programmed - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - deprecated: true - deprecationWarning: The v1alpha2 version of Gateway has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1. - name: v1alpha2 - schema: - openAPIV3Schema: - description: Gateway represents an instance of a service-traffic handling infrastructure by binding Listeners to a set of IP addresses. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of Gateway. - properties: - addresses: - description: "Addresses requested for this Gateway. This is optional and behavior can depend on the implementation. If a value is set in the spec and the requested address is invalid or unavailable, the implementation MUST indicate this in the associated entry in GatewayStatus.Addresses. \n The Addresses field represents a request for the address(es) on the \"outside of the Gateway\", that traffic bound for this Gateway will use. This could be the IP address or hostname of an external load balancer or other networking infrastructure, or some other address that traffic will be sent to. \n The .listener.hostname field is used to route traffic that has already arrived at the Gateway to the correct in-cluster destination. \n If no Addresses are specified, the implementation MAY schedule the Gateway in an implementation-specific manner, assigning an appropriate set of Addresses. \n The implementation MUST bind all Listeners to every GatewayAddress that it assigns to the Gateway and add a corresponding entry in GatewayStatus.Addresses. \n Support: Extended" - items: - description: GatewayAddress describes an address that can be bound to a Gateway. - properties: - type: - default: IPAddress - description: Type of the address. - maxLength: 253 - minLength: 1 - pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - value: - description: "Value of the address. The validity of the values will depend on the type and support by the controller. \n Examples: `1.2.3.4`, `128::1`, `my-ip-address`." - maxLength: 253 - minLength: 1 - type: string - required: - - value - type: object - maxItems: 16 - type: array - gatewayClassName: - description: GatewayClassName used for this Gateway. This is the name of a GatewayClass resource. - maxLength: 253 - minLength: 1 - type: string - listeners: - description: "Listeners associated with this Gateway. Listeners define logical endpoints that are bound on this Gateway's addresses. At least one Listener MUST be specified. \n Each listener in a Gateway must have a unique combination of Hostname, Port, and Protocol. \n An implementation MAY group Listeners by Port and then collapse each group of Listeners into a single Listener if the implementation determines that the Listeners in the group are \"compatible\". An implementation MAY also group together and collapse compatible Listeners belonging to different Gateways. \n For example, an implementation might consider Listeners to be compatible with each other if all of the following conditions are met: \n 1. Either each Listener within the group specifies the \"HTTP\" Protocol or each Listener within the group specifies either the \"HTTPS\" or \"TLS\" Protocol. \n 2. Each Listener within the group specifies a Hostname that is unique within the group. \n 3. As a special case, one Listener within a group may omit Hostname, in which case this Listener matches when no other Listener matches. \n If the implementation does collapse compatible Listeners, the hostname provided in the incoming client request MUST be matched to a Listener to find the correct set of Routes. The incoming hostname MUST be matched using the Hostname field for each Listener in order of most to least specific. That is, exact matches must be processed before wildcard matches. \n If this field specifies multiple Listeners that have the same Port value but are not compatible, the implementation must raise a \"Conflicted\" condition in the Listener status. \n Support: Core" - items: - description: Listener embodies the concept of a logical endpoint where a Gateway accepts network connections. - properties: - allowedRoutes: - default: - namespaces: - from: Same - description: "AllowedRoutes defines the types of routes that MAY be attached to a Listener and the trusted namespaces where those Route resources MAY be present. \n Although a client request may match multiple route rules, only one rule may ultimately receive the request. Matching precedence MUST be determined in order of the following criteria: \n * The most specific match as defined by the Route type. * The oldest Route based on creation timestamp. For example, a Route with a creation timestamp of \"2020-09-08 01:02:03\" is given precedence over a Route with a creation timestamp of \"2020-09-08 01:02:04\". * If everything else is equivalent, the Route appearing first in alphabetical order (namespace/name) should be given precedence. For example, foo/bar is given precedence over foo/baz. \n All valid rules within a Route attached to this Listener should be implemented. Invalid Route rules can be ignored (sometimes that will mean the full Route). If a Route rule transitions from valid to invalid, support for that Route rule should be dropped to ensure consistency. For example, even if a filter specified by a Route rule is invalid, the rest of the rules within that Route should still be supported. \n Support: Core" - properties: - kinds: - description: "Kinds specifies the groups and kinds of Routes that are allowed to bind to this Gateway Listener. When unspecified or empty, the kinds of Routes selected are determined using the Listener protocol. \n A RouteGroupKind MUST correspond to kinds of Routes that are compatible with the application protocol specified in the Listener's Protocol field. If an implementation does not support or recognize this resource type, it MUST set the \"ResolvedRefs\" condition to False for this Listener with the \"InvalidRouteKinds\" reason. \n Support: Core" - items: - description: RouteGroupKind indicates the group and kind of a Route resource. - properties: - group: - default: gateway.networking.k8s.io - description: Group is the group of the Route. - 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 the kind of the Route. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - required: - - kind - type: object - maxItems: 8 - type: array - namespaces: - default: - from: Same - description: "Namespaces indicates namespaces from which Routes may be attached to this Listener. This is restricted to the namespace of this Gateway by default. \n Support: Core" - properties: - from: - default: Same - description: "From indicates where Routes will be selected for this Gateway. Possible values are: * All: Routes in all namespaces may be used by this Gateway. * Selector: Routes in namespaces selected by the selector may be used by this Gateway. * Same: Only Routes in the same namespace may be used by this Gateway. \n Support: Core" - enum: - - All - - Selector - - Same - type: string - selector: - description: "Selector must be specified when From is set to \"Selector\". In that case, only Routes in Namespaces matching this Selector will be selected by this Gateway. This field is ignored for other values of \"From\". \n Support: Core" - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - type: object - type: object - hostname: - description: "Hostname specifies the virtual hostname to match for protocol types that define this concept. When unspecified, all hostnames are matched. This field is ignored for protocols that don't require hostname based matching. \n Implementations MUST apply Hostname matching appropriately for each of the following protocols: \n * TLS: The Listener Hostname MUST match the SNI. * HTTP: The Listener Hostname MUST match the Host header of the request. * HTTPS: The Listener Hostname SHOULD match at both the TLS and HTTP protocol layers as described above. If an implementation does not ensure that both the SNI and Host header match the Listener hostname, it MUST clearly document that. \n For HTTPRoute and TLSRoute resources, there is an interaction with the `spec.hostnames` array. When both listener and route specify hostnames, there MUST be an intersection between the values for a Route to be accepted. For more information, refer to the Route specific Hostnames documentation. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - name: - description: "Name is the name of the Listener. This name MUST be unique within a Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - port: - description: "Port is the network port. Multiple listeners may use the same port, subject to the Listener compatibility rules. \n Support: Core" - format: int32 - maximum: 65535 - minimum: 1 - type: integer - protocol: - description: "Protocol specifies the network protocol this listener expects to receive. \n Support: Core" - maxLength: 255 - minLength: 1 - pattern: ^[a-zA-Z0-9]([-a-zSA-Z0-9]*[a-zA-Z0-9])?$|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9]+$ - type: string - tls: - description: "TLS is the TLS configuration for the Listener. This field is required if the Protocol field is \"HTTPS\" or \"TLS\". It is invalid to set this field if the Protocol field is \"HTTP\", \"TCP\", or \"UDP\". \n The association of SNIs to Certificate defined in GatewayTLSConfig is defined based on the Hostname field for this listener. \n The GatewayClass MUST use the longest matching SNI out of all available certificates for any TLS handshake. \n Support: Core" - properties: - certificateRefs: - description: "CertificateRefs contains a series of references to Kubernetes objects that contains TLS certificates and private keys. These certificates are used to establish a TLS handshake for requests that match the hostname of the associated listener. \n A single CertificateRef to a Kubernetes Secret has \"Core\" support. Implementations MAY choose to support attaching multiple certificates to a Listener, but this behavior is implementation-specific. \n References to a resource in different namespace are invalid UNLESS there is a ReferenceGrant in the target namespace that allows the certificate to be attached. If a ReferenceGrant does not allow this reference, the \"ResolvedRefs\" condition MUST be set to False for this listener with the \"RefNotPermitted\" reason. \n This field is required to have at least one element when the mode is set to \"Terminate\" (default) and is optional otherwise. \n CertificateRefs can reference to standard Kubernetes resources, i.e. Secret, or implementation-specific custom resources. \n Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls \n Support: Implementation-specific (More than one reference or other resource types)" - items: - description: "SecretObjectReference identifies an API object including its namespace, defaulting to Secret. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. \n References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object." - properties: - group: - default: "" - 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: - default: Secret - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - name - type: object - maxItems: 64 - type: array - mode: - default: Terminate - description: "Mode defines the TLS behavior for the TLS session initiated by the client. There are two possible modes: \n - Terminate: The TLS session between the downstream client and the Gateway is terminated at the Gateway. This mode requires certificateRefs to be set and contain at least one element. - Passthrough: The TLS session is NOT terminated by the Gateway. This implies that the Gateway can't decipher the TLS stream except for the ClientHello message of the TLS protocol. CertificateRefs field is ignored in this mode. \n Support: Core" - enum: - - Terminate - - Passthrough - type: string - options: - additionalProperties: - description: AnnotationValue is the value of an annotation in Gateway API. This is used for validation of maps such as TLS options. This roughly matches Kubernetes annotation validation, although the length validation in that case is based on the entire size of the annotations struct. - maxLength: 4096 - minLength: 0 - type: string - description: "Options are a list of key/value pairs to enable extended TLS configuration for each implementation. For example, configuring the minimum TLS version or supported cipher suites. \n A set of common keys MAY be defined by the API in the future. To avoid any ambiguity, implementation-specific definitions MUST use domain-prefixed names, such as `example.com/my-custom-option`. Un-prefixed names are reserved for key names defined by Gateway API. \n Support: Implementation-specific" - maxProperties: 16 - type: object - type: object - required: - - name - - port - - protocol - type: object - maxItems: 64 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - required: - - gatewayClassName - - listeners - type: object - status: - default: - conditions: - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: NotReconciled - status: Unknown - type: Accepted - description: Status defines the current state of Gateway. - properties: - addresses: - description: Addresses lists the IP addresses that have actually been bound to the Gateway. These addresses may differ from the addresses in the Spec, e.g. if the Gateway automatically assigns an address from a reserved pool. - items: - description: GatewayAddress describes an address that can be bound to a Gateway. - properties: - type: - default: IPAddress - description: Type of the address. - maxLength: 253 - minLength: 1 - pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - value: - description: "Value of the address. The validity of the values will depend on the type and support by the controller. \n Examples: `1.2.3.4`, `128::1`, `my-ip-address`." - maxLength: 253 - minLength: 1 - type: string - required: - - value - type: object - maxItems: 16 - type: array - conditions: - default: - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: Pending - status: Unknown - type: Accepted - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: Pending - status: Unknown - type: Programmed - description: "Conditions describe the current conditions of the Gateway. \n Implementations should prefer to express Gateway conditions using the `GatewayConditionType` and `GatewayConditionReason` constants so that operators and tools can converge on a common vocabulary to describe Gateway state. \n Known condition types are: \n * \"Accepted\" * \"Ready\"" - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - listeners: - description: Listeners provide status for each unique listener port defined in the Spec. - items: - description: ListenerStatus is the status associated with a Listener. - properties: - attachedRoutes: - description: AttachedRoutes represents the total number of Routes that have been successfully attached to this Listener. - format: int32 - type: integer - conditions: - description: Conditions describe the current condition of this listener. - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - name: - description: Name is the name of the Listener that this status corresponds to. - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - supportedKinds: - description: "SupportedKinds is the list indicating the Kinds supported by this listener. This MUST represent the kinds an implementation supports for that Listener configuration. \n If kinds are specified in Spec that are not supported, they MUST NOT appear in this list and an implementation MUST set the \"ResolvedRefs\" condition to \"False\" with the \"InvalidRouteKinds\" reason. If both valid and invalid Route kinds are specified, the implementation MUST reference the valid Route kinds that have been specified." - items: - description: RouteGroupKind indicates the group and kind of a Route resource. - properties: - group: - default: gateway.networking.k8s.io - description: Group is the group of the Route. - 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 the kind of the Route. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - required: - - kind - type: object - maxItems: 8 - type: array - required: - - attachedRoutes - - conditions - - name - - supportedKinds - type: object - maxItems: 64 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - required: - - spec - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .spec.gatewayClassName - name: Class - type: string - - jsonPath: .status.addresses[*].value - name: Address - type: string - - jsonPath: .status.conditions[?(@.type=="Programmed")].status - name: Programmed - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: Gateway represents an instance of a service-traffic handling infrastructure by binding Listeners to a set of IP addresses. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of Gateway. - properties: - addresses: - description: "Addresses requested for this Gateway. This is optional and behavior can depend on the implementation. If a value is set in the spec and the requested address is invalid or unavailable, the implementation MUST indicate this in the associated entry in GatewayStatus.Addresses. \n The Addresses field represents a request for the address(es) on the \"outside of the Gateway\", that traffic bound for this Gateway will use. This could be the IP address or hostname of an external load balancer or other networking infrastructure, or some other address that traffic will be sent to. \n The .listener.hostname field is used to route traffic that has already arrived at the Gateway to the correct in-cluster destination. \n If no Addresses are specified, the implementation MAY schedule the Gateway in an implementation-specific manner, assigning an appropriate set of Addresses. \n The implementation MUST bind all Listeners to every GatewayAddress that it assigns to the Gateway and add a corresponding entry in GatewayStatus.Addresses. \n Support: Extended" - items: - description: GatewayAddress describes an address that can be bound to a Gateway. - properties: - type: - default: IPAddress - description: Type of the address. - maxLength: 253 - minLength: 1 - pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - value: - description: "Value of the address. The validity of the values will depend on the type and support by the controller. \n Examples: `1.2.3.4`, `128::1`, `my-ip-address`." - maxLength: 253 - minLength: 1 - type: string - required: - - value - type: object - maxItems: 16 - type: array - gatewayClassName: - description: GatewayClassName used for this Gateway. This is the name of a GatewayClass resource. - maxLength: 253 - minLength: 1 - type: string - listeners: - description: "Listeners associated with this Gateway. Listeners define logical endpoints that are bound on this Gateway's addresses. At least one Listener MUST be specified. \n Each listener in a Gateway must have a unique combination of Hostname, Port, and Protocol. \n An implementation MAY group Listeners by Port and then collapse each group of Listeners into a single Listener if the implementation determines that the Listeners in the group are \"compatible\". An implementation MAY also group together and collapse compatible Listeners belonging to different Gateways. \n For example, an implementation might consider Listeners to be compatible with each other if all of the following conditions are met: \n 1. Either each Listener within the group specifies the \"HTTP\" Protocol or each Listener within the group specifies either the \"HTTPS\" or \"TLS\" Protocol. \n 2. Each Listener within the group specifies a Hostname that is unique within the group. \n 3. As a special case, one Listener within a group may omit Hostname, in which case this Listener matches when no other Listener matches. \n If the implementation does collapse compatible Listeners, the hostname provided in the incoming client request MUST be matched to a Listener to find the correct set of Routes. The incoming hostname MUST be matched using the Hostname field for each Listener in order of most to least specific. That is, exact matches must be processed before wildcard matches. \n If this field specifies multiple Listeners that have the same Port value but are not compatible, the implementation must raise a \"Conflicted\" condition in the Listener status. \n Support: Core" - items: - description: Listener embodies the concept of a logical endpoint where a Gateway accepts network connections. - properties: - allowedRoutes: - default: - namespaces: - from: Same - description: "AllowedRoutes defines the types of routes that MAY be attached to a Listener and the trusted namespaces where those Route resources MAY be present. \n Although a client request may match multiple route rules, only one rule may ultimately receive the request. Matching precedence MUST be determined in order of the following criteria: \n * The most specific match as defined by the Route type. * The oldest Route based on creation timestamp. For example, a Route with a creation timestamp of \"2020-09-08 01:02:03\" is given precedence over a Route with a creation timestamp of \"2020-09-08 01:02:04\". * If everything else is equivalent, the Route appearing first in alphabetical order (namespace/name) should be given precedence. For example, foo/bar is given precedence over foo/baz. \n All valid rules within a Route attached to this Listener should be implemented. Invalid Route rules can be ignored (sometimes that will mean the full Route). If a Route rule transitions from valid to invalid, support for that Route rule should be dropped to ensure consistency. For example, even if a filter specified by a Route rule is invalid, the rest of the rules within that Route should still be supported. \n Support: Core" - properties: - kinds: - description: "Kinds specifies the groups and kinds of Routes that are allowed to bind to this Gateway Listener. When unspecified or empty, the kinds of Routes selected are determined using the Listener protocol. \n A RouteGroupKind MUST correspond to kinds of Routes that are compatible with the application protocol specified in the Listener's Protocol field. If an implementation does not support or recognize this resource type, it MUST set the \"ResolvedRefs\" condition to False for this Listener with the \"InvalidRouteKinds\" reason. \n Support: Core" - items: - description: RouteGroupKind indicates the group and kind of a Route resource. - properties: - group: - default: gateway.networking.k8s.io - description: Group is the group of the Route. - 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 the kind of the Route. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - required: - - kind - type: object - maxItems: 8 - type: array - namespaces: - default: - from: Same - description: "Namespaces indicates namespaces from which Routes may be attached to this Listener. This is restricted to the namespace of this Gateway by default. \n Support: Core" - properties: - from: - default: Same - description: "From indicates where Routes will be selected for this Gateway. Possible values are: * All: Routes in all namespaces may be used by this Gateway. * Selector: Routes in namespaces selected by the selector may be used by this Gateway. * Same: Only Routes in the same namespace may be used by this Gateway. \n Support: Core" - enum: - - All - - Selector - - Same - type: string - selector: - description: "Selector must be specified when From is set to \"Selector\". In that case, only Routes in Namespaces matching this Selector will be selected by this Gateway. This field is ignored for other values of \"From\". \n Support: Core" - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - type: object - type: object - hostname: - description: "Hostname specifies the virtual hostname to match for protocol types that define this concept. When unspecified, all hostnames are matched. This field is ignored for protocols that don't require hostname based matching. \n Implementations MUST apply Hostname matching appropriately for each of the following protocols: \n * TLS: The Listener Hostname MUST match the SNI. * HTTP: The Listener Hostname MUST match the Host header of the request. * HTTPS: The Listener Hostname SHOULD match at both the TLS and HTTP protocol layers as described above. If an implementation does not ensure that both the SNI and Host header match the Listener hostname, it MUST clearly document that. \n For HTTPRoute and TLSRoute resources, there is an interaction with the `spec.hostnames` array. When both listener and route specify hostnames, there MUST be an intersection between the values for a Route to be accepted. For more information, refer to the Route specific Hostnames documentation. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - name: - description: "Name is the name of the Listener. This name MUST be unique within a Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - port: - description: "Port is the network port. Multiple listeners may use the same port, subject to the Listener compatibility rules. \n Support: Core" - format: int32 - maximum: 65535 - minimum: 1 - type: integer - protocol: - description: "Protocol specifies the network protocol this listener expects to receive. \n Support: Core" - maxLength: 255 - minLength: 1 - pattern: ^[a-zA-Z0-9]([-a-zSA-Z0-9]*[a-zA-Z0-9])?$|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9]+$ - type: string - tls: - description: "TLS is the TLS configuration for the Listener. This field is required if the Protocol field is \"HTTPS\" or \"TLS\". It is invalid to set this field if the Protocol field is \"HTTP\", \"TCP\", or \"UDP\". \n The association of SNIs to Certificate defined in GatewayTLSConfig is defined based on the Hostname field for this listener. \n The GatewayClass MUST use the longest matching SNI out of all available certificates for any TLS handshake. \n Support: Core" - properties: - certificateRefs: - description: "CertificateRefs contains a series of references to Kubernetes objects that contains TLS certificates and private keys. These certificates are used to establish a TLS handshake for requests that match the hostname of the associated listener. \n A single CertificateRef to a Kubernetes Secret has \"Core\" support. Implementations MAY choose to support attaching multiple certificates to a Listener, but this behavior is implementation-specific. \n References to a resource in different namespace are invalid UNLESS there is a ReferenceGrant in the target namespace that allows the certificate to be attached. If a ReferenceGrant does not allow this reference, the \"ResolvedRefs\" condition MUST be set to False for this listener with the \"RefNotPermitted\" reason. \n This field is required to have at least one element when the mode is set to \"Terminate\" (default) and is optional otherwise. \n CertificateRefs can reference to standard Kubernetes resources, i.e. Secret, or implementation-specific custom resources. \n Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls \n Support: Implementation-specific (More than one reference or other resource types)" - items: - description: "SecretObjectReference identifies an API object including its namespace, defaulting to Secret. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. \n References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object." - properties: - group: - default: "" - 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: - default: Secret - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - name - type: object - maxItems: 64 - type: array - mode: - default: Terminate - description: "Mode defines the TLS behavior for the TLS session initiated by the client. There are two possible modes: \n - Terminate: The TLS session between the downstream client and the Gateway is terminated at the Gateway. This mode requires certificateRefs to be set and contain at least one element. - Passthrough: The TLS session is NOT terminated by the Gateway. This implies that the Gateway can't decipher the TLS stream except for the ClientHello message of the TLS protocol. CertificateRefs field is ignored in this mode. \n Support: Core" - enum: - - Terminate - - Passthrough - type: string - options: - additionalProperties: - description: AnnotationValue is the value of an annotation in Gateway API. This is used for validation of maps such as TLS options. This roughly matches Kubernetes annotation validation, although the length validation in that case is based on the entire size of the annotations struct. - maxLength: 4096 - minLength: 0 - type: string - description: "Options are a list of key/value pairs to enable extended TLS configuration for each implementation. For example, configuring the minimum TLS version or supported cipher suites. \n A set of common keys MAY be defined by the API in the future. To avoid any ambiguity, implementation-specific definitions MUST use domain-prefixed names, such as `example.com/my-custom-option`. Un-prefixed names are reserved for key names defined by Gateway API. \n Support: Implementation-specific" - maxProperties: 16 - type: object - type: object - required: - - name - - port - - protocol - type: object - maxItems: 64 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - required: - - gatewayClassName - - listeners - type: object - status: - default: - conditions: - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: NotReconciled - status: Unknown - type: Accepted - description: Status defines the current state of Gateway. - properties: - addresses: - description: Addresses lists the IP addresses that have actually been bound to the Gateway. These addresses may differ from the addresses in the Spec, e.g. if the Gateway automatically assigns an address from a reserved pool. - items: - description: GatewayAddress describes an address that can be bound to a Gateway. - properties: - type: - default: IPAddress - description: Type of the address. - maxLength: 253 - minLength: 1 - pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - value: - description: "Value of the address. The validity of the values will depend on the type and support by the controller. \n Examples: `1.2.3.4`, `128::1`, `my-ip-address`." - maxLength: 253 - minLength: 1 - type: string - required: - - value - type: object - maxItems: 16 - type: array - conditions: - default: - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: Pending - status: Unknown - type: Accepted - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: Pending - status: Unknown - type: Programmed - description: "Conditions describe the current conditions of the Gateway. \n Implementations should prefer to express Gateway conditions using the `GatewayConditionType` and `GatewayConditionReason` constants so that operators and tools can converge on a common vocabulary to describe Gateway state. \n Known condition types are: \n * \"Accepted\" * \"Ready\"" - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - listeners: - description: Listeners provide status for each unique listener port defined in the Spec. - items: - description: ListenerStatus is the status associated with a Listener. - properties: - attachedRoutes: - description: AttachedRoutes represents the total number of Routes that have been successfully attached to this Listener. - format: int32 - type: integer - conditions: - description: Conditions describe the current condition of this listener. - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - name: - description: Name is the name of the Listener that this status corresponds to. - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - supportedKinds: - description: "SupportedKinds is the list indicating the Kinds supported by this listener. This MUST represent the kinds an implementation supports for that Listener configuration. \n If kinds are specified in Spec that are not supported, they MUST NOT appear in this list and an implementation MUST set the \"ResolvedRefs\" condition to \"False\" with the \"InvalidRouteKinds\" reason. If both valid and invalid Route kinds are specified, the implementation MUST reference the valid Route kinds that have been specified." - items: - description: RouteGroupKind indicates the group and kind of a Route resource. - properties: - group: - default: gateway.networking.k8s.io - description: Group is the group of the Route. - 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 the kind of the Route. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - required: - - kind - type: object - maxItems: 8 - type: array - required: - - attachedRoutes - - conditions - - name - - supportedKinds - type: object - maxItems: 64 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] -{{- end }} diff --git a/charts/consul/templates/crd-grpcroutes.yaml b/charts/consul/templates/crd-grpcroutes.yaml deleted file mode 100644 index 07412ba60b..0000000000 --- a/charts/consul/templates/crd-grpcroutes.yaml +++ /dev/null @@ -1,768 +0,0 @@ -{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 - gateway.networking.k8s.io/bundle-version: v0.6.2 - gateway.networking.k8s.io/channel: experimental - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: crd - name: grpcroutes.gateway.networking.k8s.io -spec: - group: gateway.networking.k8s.io - names: - categories: - - gateway-api - kind: GRPCRoute - listKind: GRPCRouteList - plural: grpcroutes - singular: grpcroute - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.hostnames - name: Hostnames - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha2 - schema: - openAPIV3Schema: - description: "GRPCRoute provides a way to route gRPC requests. This includes the capability to match requests by hostname, gRPC service, gRPC method, or HTTP/2 header. Filters can be used to specify additional processing steps. Backends specify where matching requests will be routed. \n GRPCRoute falls under extended support within the Gateway API. Within the following specification, the word \"MUST\" indicates that an implementation supporting GRPCRoute must conform to the indicated requirement, but an implementation not supporting this route type need not follow the requirement unless explicitly indicated. \n Implementations supporting `GRPCRoute` with the `HTTPS` `ProtocolType` MUST accept HTTP/2 connections without an initial upgrade from HTTP/1.1, i.e. via ALPN. If the implementation does not support this, then it MUST set the \"Accepted\" condition to \"False\" for the affected listener with a reason of \"UnsupportedProtocol\". Implementations MAY also accept HTTP/2 connections with an upgrade from HTTP/1. \n Implementations supporting `GRPCRoute` with the `HTTP` `ProtocolType` MUST support HTTP/2 over cleartext TCP (h2c, https://www.rfc-editor.org/rfc/rfc7540#section-3.1) without an initial upgrade from HTTP/1.1, i.e. with prior knowledge (https://www.rfc-editor.org/rfc/rfc7540#section-3.4). If the implementation does not support this, then it MUST set the \"Accepted\" condition to \"False\" for the affected listener with a reason of \"UnsupportedProtocol\". Implementations MAY also accept HTTP/2 connections with an upgrade from HTTP/1, i.e. without prior knowledge. \n Support: Extended" - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of GRPCRoute. - properties: - hostnames: - description: "Hostnames defines a set of hostnames to match against the GRPC Host header to select a GRPCRoute to process the request. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label MUST appear by itself as the first label. \n If a hostname is specified by both the Listener and GRPCRoute, there MUST be at least one intersecting hostname for the GRPCRoute to be attached to the Listener. For example: \n * A Listener with `test.example.com` as the hostname matches GRPCRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches GRPCRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `test.example.com` and `*.example.com` would both match. On the other hand, `example.com` and `test.example.net` would not match. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n If both the Listener and GRPCRoute have specified hostnames, any GRPCRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the GRPCRoute specified `test.example.com` and `test.example.net`, `test.example.net` MUST NOT be considered for a match. \n If both the Listener and GRPCRoute have specified hostnames, and none match with the criteria above, then the GRPCRoute MUST NOT be accepted by the implementation. The implementation MUST raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. \n If a Route (A) of type HTTPRoute or GRPCRoute is attached to a Listener and that listener already has another Route (B) of the other type attached and the intersection of the hostnames of A and B is non-empty, then the implementation MUST accept exactly one of these two routes, determined by the following criteria, in order: \n * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n The rejected Route MUST raise an 'Accepted' condition with a status of 'False' in the corresponding RouteParentStatus. \n Support: Core" - items: - description: "Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n Hostname can be \"precise\" which is a domain name without the terminating dot of a network host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). \n Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed." - maxLength: 253 - minLength: 1 - pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - maxItems: 16 - type: array - parentRefs: - description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." - items: - description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - 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. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - maxItems: 32 - type: array - rules: - default: - - matches: - - method: - type: Exact - description: Rules are a list of GRPC matchers, filters and actions. - items: - description: GRPCRouteRule defines the semantics for matching an gRPC request based on conditions (matches), processing it (filters), and forwarding the request to an API object (backendRefs). - properties: - backendRefs: - description: "BackendRefs defines the backend(s) where matching requests should be sent. \n Failure behavior here depends on how many BackendRefs are specified and how many are invalid. \n If *all* entries in BackendRefs are invalid, and there are also no filters specified in this route rule, *all* traffic which matches this rule MUST receive an `UNAVAILABLE` status. \n See the GRPCBackendRef definition for the rules about what makes a single GRPCBackendRef invalid. \n When a GRPCBackendRef is invalid, `UNAVAILABLE` statuses MUST be returned for requests that would have otherwise been routed to an invalid backend. If multiple backends are specified, and some are invalid, the proportion of requests that would otherwise have been routed to an invalid backend MUST receive an `UNAVAILABLE` status. \n For example, if two backends are specified with equal weights, and one is invalid, 50 percent of traffic MUST receive an `UNAVAILABLE` status. Implementations may choose how that 50 percent is determined. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Core" - items: - description: GRPCBackendRef defines how a GRPCRoute forwards a gRPC request. - properties: - filters: - description: "Filters defined at this level MUST be executed if and only if the request is being forwarded to the backend defined here. \n Support: Implementation-specific (For broader support of filters, use the Filters field in GRPCRouteRule.)" - items: - description: GRPCRouteFilter defines processing steps that must be completed during the request or response lifecycle. GRPCRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. - properties: - extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" - 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 - requestHeaderModifier: - description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" - properties: - add: - description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" - items: - type: string - maxItems: 16 - type: array - set: - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - requestMirror: - description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" - properties: - backendRef: - description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" - properties: - group: - default: "" - 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: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name - type: object - required: - - backendRef - type: object - responseHeaderModifier: - description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " - properties: - add: - description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" - items: - type: string - maxItems: 16 - type: array - set: - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: - description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations supporting GRPCRoute MUST support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` MUST be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n " - enum: - - ResponseHeaderModifier - - RequestHeaderModifier - - RequestMirror - - ExtensionRef - type: string - required: - - type - type: object - maxItems: 16 - type: array - group: - default: "" - 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: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - weight: - default: 1 - description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support for this field varies based on the context where used." - format: int32 - maximum: 1000000 - minimum: 0 - type: integer - required: - - name - type: object - maxItems: 16 - type: array - filters: - description: "Filters define the filters that are applied to requests that match this rule. \n The effects of ordering of multiple behaviors are currently unspecified. This can change in the future based on feedback during the alpha stage. \n Conformance-levels at this level are defined based on the type of filter: \n - ALL core filters MUST be supported by all implementations that support GRPCRoute. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. \n Specifying a core filter multiple times has unspecified or implementation-specific conformance. Support: Core" - items: - description: GRPCRouteFilter defines processing steps that must be completed during the request or response lifecycle. GRPCRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. - properties: - extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" - 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 - requestHeaderModifier: - description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" - properties: - add: - description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" - items: - type: string - maxItems: 16 - type: array - set: - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - requestMirror: - description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" - properties: - backendRef: - description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" - properties: - group: - default: "" - 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: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name - type: object - required: - - backendRef - type: object - responseHeaderModifier: - description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " - properties: - add: - description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" - items: - type: string - maxItems: 16 - type: array - set: - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: - description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations supporting GRPCRoute MUST support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` MUST be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n " - enum: - - ResponseHeaderModifier - - RequestHeaderModifier - - RequestMirror - - ExtensionRef - type: string - required: - - type - type: object - maxItems: 16 - type: array - matches: - default: - - method: - type: Exact - description: "Matches define conditions used for matching the rule against incoming gRPC requests. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied. \n For example, take the following matches configuration: \n ``` matches: - method: service: foo.bar headers: values: version: 2 - method: service: foo.bar.v2 ``` \n For a request to match against this rule, it MUST satisfy EITHER of the two conditions: \n - service of foo.bar AND contains the header `version: 2` - service of foo.bar.v2 \n See the documentation for GRPCRouteMatch on how to specify multiple match conditions to be ANDed together. \n If no matches are specified, the implementation MUST match every gRPC request. \n Proxy or Load Balancer routing configuration generated from GRPCRoutes MUST prioritize rules based on the following criteria, continuing on ties. Merging MUST not be done between GRPCRoutes and HTTPRoutes. Precedence MUST be given to the rule with the largest number of: \n * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. * Characters in a matching service. * Characters in a matching method. * Header matches. \n If ties still exist across multiple Routes, matching precedence MUST be determined in order of the following criteria, continuing on ties: \n * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n If ties still exist within the Route that has been given precedence, matching precedence MUST be granted to the first matching rule meeting the above criteria." - items: - description: "GRPCRouteMatch defines the predicate used to match requests to a given action. Multiple match types are ANDed together, i.e. the match will evaluate to true only if all conditions are satisfied. \n For example, the match below will match a gRPC request only if its service is `foo` AND it contains the `version: v1` header: \n ``` matches: - method: type: Exact service: \"foo\" headers: - name: \"version\" value \"v1\" \n ```" - properties: - headers: - description: Headers specifies gRPC request header matchers. Multiple match values are ANDed together, meaning, a request MUST match all the specified headers to select the route. - items: - description: GRPCHeaderMatch describes how to select a gRPC route by matching gRPC request headers. - properties: - name: - description: "Name is the name of the gRPC Header to be matched. \n If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: Type specifies how to match against the value of the header. - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of the gRPC Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - method: - default: - type: Exact - description: Method specifies a gRPC request service/method matcher. If this field is not specified, all services and methods will match. - properties: - method: - description: "Value of the method to match against. If left empty or omitted, will match all services. \n At least one of Service and Method MUST be a non-empty string. \n A GRPC Method must be a valid Protobuf Method (https://protobuf.com/docs/language-spec#methods)." - maxLength: 1024 - pattern: ^[A-Za-z_][A-Za-z_0-9]*$ - type: string - service: - description: "Value of the service to match against. If left empty or omitted, will match any service. \n At least one of Service and Method MUST be a non-empty string. \n A GRPC Service must be a valid Protobuf Type Name (https://protobuf.com/docs/language-spec#type-references)." - maxLength: 1024 - pattern: ^(?i)\.?[a-z_][a-z_0-9]*(\.[a-z_][a-z_0-9]*)*$ - type: string - type: - default: Exact - description: "Type specifies how to match against the service and/or method. Support: Core (Exact with service and method specified) \n Support: Implementation-specific (Exact with method specified but no service specified) \n Support: Implementation-specific (RegularExpression)" - enum: - - Exact - - RegularExpression - type: string - type: object - type: object - maxItems: 8 - type: array - type: object - maxItems: 16 - type: array - type: object - status: - description: Status defines the current state of GRPCRoute. - properties: - parents: - description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." - items: - description: RouteParentStatus describes the status of a route with respect to an associated Parent. - properties: - conditions: - description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - controllerName: - description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/gateway-controller\". \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - parentRef: - description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - 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. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - required: - - controllerName - - parentRef - type: object - maxItems: 32 - type: array - required: - - parents - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] -{{- end }} diff --git a/charts/consul/templates/crd-httproutes.yaml b/charts/consul/templates/crd-httproutes.yaml deleted file mode 100644 index d9055e7406..0000000000 --- a/charts/consul/templates/crd-httproutes.yaml +++ /dev/null @@ -1,1916 +0,0 @@ -{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 - gateway.networking.k8s.io/bundle-version: v0.6.2 - gateway.networking.k8s.io/channel: experimental - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: crd - name: httproutes.gateway.networking.k8s.io -spec: - group: gateway.networking.k8s.io - names: - categories: - - gateway-api - kind: HTTPRoute - listKind: HTTPRouteList - plural: httproutes - singular: httproute - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.hostnames - name: Hostnames - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - deprecated: true - deprecationWarning: The v1alpha2 version of HTTPRoute has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1. - name: v1alpha2 - schema: - openAPIV3Schema: - description: HTTPRoute provides a way to route HTTP requests. This includes the capability to match requests by hostname, path, header, or query param. Filters can be used to specify additional processing steps. Backends specify where matching requests should be routed. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of HTTPRoute. - properties: - hostnames: - description: "Hostnames defines a set of hostname that should match against the HTTP Host header to select a HTTPRoute to process the request. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n If a hostname is specified by both the Listener and HTTPRoute, there must be at least one intersecting hostname for the HTTPRoute to be attached to the Listener. For example: \n * A Listener with `test.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `*.example.com`, `test.example.com`, and `foo.test.example.com` would all match. On the other hand, `example.com` and `test.example.net` would not match. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n If both the Listener and HTTPRoute have specified hostnames, any HTTPRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the HTTPRoute specified `test.example.com` and `test.example.net`, `test.example.net` must not be considered for a match. \n If both the Listener and HTTPRoute have specified hostnames, and none match with the criteria above, then the HTTPRoute is not accepted. The implementation must raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. \n In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. overlapping wildcard matching and exact matching hostnames), precedence must be given to rules from the HTTPRoute with the largest number of: \n * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. \n If ties exist across multiple Routes, the matching precedence rules for HTTPRouteMatches takes over. \n Support: Core" - items: - description: "Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n Hostname can be \"precise\" which is a domain name without the terminating dot of a network host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). \n Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed." - maxLength: 253 - minLength: 1 - pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - maxItems: 16 - type: array - parentRefs: - description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." - items: - description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - 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. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - maxItems: 32 - type: array - rules: - default: - - matches: - - path: - type: PathPrefix - value: / - description: Rules are a list of HTTP matchers, filters and actions. - items: - description: HTTPRouteRule defines semantics for matching an HTTP request based on conditions (matches), processing it (filters), and forwarding the request to an API object (backendRefs). - properties: - backendRefs: - description: "BackendRefs defines the backend(s) where matching requests should be sent. \n Failure behavior here depends on how many BackendRefs are specified and how many are invalid. \n If *all* entries in BackendRefs are invalid, and there are also no filters specified in this route rule, *all* traffic which matches this rule MUST receive a 500 status code. \n See the HTTPBackendRef definition for the rules about what makes a single HTTPBackendRef invalid. \n When a HTTPBackendRef is invalid, 500 status codes MUST be returned for requests that would have otherwise been routed to an invalid backend. If multiple backends are specified, and some are invalid, the proportion of requests that would otherwise have been routed to an invalid backend MUST receive a 500 status code. \n For example, if two backends are specified with equal weights, and one is invalid, 50 percent of traffic must receive a 500. Implementations may choose how that 50 percent is determined. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Core" - items: - description: HTTPBackendRef defines how a HTTPRoute should forward an HTTP request. - properties: - filters: - description: "Filters defined at this level should be executed if and only if the request is being forwarded to the backend defined here. \n Support: Implementation-specific (For broader support of filters, use the Filters field in HTTPRouteRule.)" - items: - description: HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. - properties: - extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" - 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 - requestHeaderModifier: - description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" - properties: - add: - description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" - items: - type: string - maxItems: 16 - type: array - set: - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - requestMirror: - description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" - properties: - backendRef: - description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" - properties: - group: - default: "" - 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: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name - type: object - required: - - backendRef - type: object - requestRedirect: - description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n Support: Core" - properties: - hostname: - description: "Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname of the request is used. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - path: - description: "Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. \n Support: Extended \n " - properties: - replaceFullPath: - description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " - maxLength: 1024 - type: string - replacePrefixMatch: - description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " - maxLength: 1024 - type: string - type: - description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - port: - description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n Support: Extended" - format: int32 - maximum: 65535 - minimum: 1 - type: integer - scheme: - description: "Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" - enum: - - http - - https - type: string - statusCode: - default: 302 - description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" - enum: - - 301 - - 302 - type: integer - type: object - responseHeaderModifier: - description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " - properties: - add: - description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" - items: - type: string - maxItems: 16 - type: array - set: - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: - description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - RequestHeaderModifier - - ResponseHeaderModifier - - RequestMirror - - RequestRedirect - - URLRewrite - - ExtensionRef - type: string - urlRewrite: - description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " - properties: - hostname: - description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - path: - description: "Path defines a path rewrite. \n Support: Extended \n " - properties: - replaceFullPath: - description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " - maxLength: 1024 - type: string - replacePrefixMatch: - description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " - maxLength: 1024 - type: string - type: - description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - type: object - required: - - type - type: object - maxItems: 16 - type: array - group: - default: "" - 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: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - weight: - default: 1 - description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support for this field varies based on the context where used." - format: int32 - maximum: 1000000 - minimum: 0 - type: integer - required: - - name - type: object - maxItems: 16 - type: array - filters: - description: "Filters define the filters that are applied to requests that match this rule. \n The effects of ordering of multiple behaviors are currently unspecified. This can change in the future based on feedback during the alpha stage. \n Conformance-levels at this level are defined based on the type of filter: \n - ALL core filters MUST be supported by all implementations. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. \n Specifying a core filter multiple times has unspecified or implementation-specific conformance. \n All filters are expected to be compatible with each other except for the URLRewrite and RequestRedirect filters, which may not be combined. If an implementation can not support other combinations of filters, they must clearly document that limitation. In all cases where incompatible or unsupported filters are specified, implementations MUST add a warning condition to status. \n Support: Core" - items: - description: HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. - properties: - extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" - 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 - requestHeaderModifier: - description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" - properties: - add: - description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" - items: - type: string - maxItems: 16 - type: array - set: - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - requestMirror: - description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" - properties: - backendRef: - description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" - properties: - group: - default: "" - 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: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name - type: object - required: - - backendRef - type: object - requestRedirect: - description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n Support: Core" - properties: - hostname: - description: "Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname of the request is used. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - path: - description: "Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. \n Support: Extended \n " - properties: - replaceFullPath: - description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " - maxLength: 1024 - type: string - replacePrefixMatch: - description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " - maxLength: 1024 - type: string - type: - description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - port: - description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n Support: Extended" - format: int32 - maximum: 65535 - minimum: 1 - type: integer - scheme: - description: "Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" - enum: - - http - - https - type: string - statusCode: - default: 302 - description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" - enum: - - 301 - - 302 - type: integer - type: object - responseHeaderModifier: - description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " - properties: - add: - description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" - items: - type: string - maxItems: 16 - type: array - set: - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: - description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - RequestHeaderModifier - - ResponseHeaderModifier - - RequestMirror - - RequestRedirect - - URLRewrite - - ExtensionRef - type: string - urlRewrite: - description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " - properties: - hostname: - description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - path: - description: "Path defines a path rewrite. \n Support: Extended \n " - properties: - replaceFullPath: - description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " - maxLength: 1024 - type: string - replacePrefixMatch: - description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " - maxLength: 1024 - type: string - type: - description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - type: object - required: - - type - type: object - maxItems: 16 - type: array - matches: - default: - - path: - type: PathPrefix - value: / - description: "Matches define conditions used for matching the rule against incoming HTTP requests. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied. \n For example, take the following matches configuration: \n ``` matches: - path: value: \"/foo\" headers: - name: \"version\" value: \"v2\" - path: value: \"/v2/foo\" ``` \n For a request to match against this rule, a request must satisfy EITHER of the two conditions: \n - path prefixed with `/foo` AND contains the header `version: v2` - path prefix of `/v2/foo` \n See the documentation for HTTPRouteMatch on how to specify multiple match conditions that should be ANDed together. \n If no matches are specified, the default is a prefix path match on \"/\", which has the effect of matching every HTTP request. \n Proxy or Load Balancer routing configuration generated from HTTPRoutes MUST prioritize matches based on the following criteria, continuing on ties. Across all rules specified on applicable Routes, precedence must be given to the match with the largest number of: \n * Characters in a matching path. * Header matches. * Query param matches. \n If ties still exist across multiple Routes, matching precedence MUST be determined in order of the following criteria, continuing on ties: \n * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n If ties still exist within an HTTPRoute, matching precedence MUST be granted to the FIRST matching rule (in list order) with a match meeting the above criteria. \n When no rules matching a request have been successfully attached to the parent a request is coming from, a HTTP 404 status code MUST be returned." - items: - description: "HTTPRouteMatch defines the predicate used to match requests to a given action. Multiple match types are ANDed together, i.e. the match will evaluate to true only if all conditions are satisfied. \n For example, the match below will match a HTTP request only if its path starts with `/foo` AND it contains the `version: v1` header: \n ``` match: \n \tpath: \t value: \"/foo\" \theaders: \t- name: \"version\" \t value \"v1\" \n ```" - properties: - headers: - description: Headers specifies HTTP request header matchers. Multiple match values are ANDed together, meaning, a request must match all the specified headers to select the route. - items: - description: HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request headers. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent. \n When a header is repeated in an HTTP request, it is implementation-specific behavior as to how this is represented. Generally, proxies should follow the guidance from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding processing a repeated header, with special handling for \"Set-Cookie\"." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: "Type specifies how to match against the value of the header. \n Support: Core (Exact) \n Support: Implementation-specific (RegularExpression) \n Since RegularExpression HeaderMatchType has implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect." - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - method: - description: "Method specifies HTTP method matcher. When specified, this route will be matched only if the request has the specified method. \n Support: Extended" - enum: - - GET - - HEAD - - POST - - PUT - - DELETE - - CONNECT - - OPTIONS - - TRACE - - PATCH - type: string - path: - default: - type: PathPrefix - value: / - description: Path specifies a HTTP request path matcher. If this field is not specified, a default prefix match on the "/" path is provided. - properties: - type: - default: PathPrefix - description: "Type specifies how to match against the path Value. \n Support: Core (Exact, PathPrefix) \n Support: Implementation-specific (RegularExpression)" - enum: - - Exact - - PathPrefix - - RegularExpression - type: string - value: - default: / - description: Value of the HTTP path to match against. - maxLength: 1024 - type: string - type: object - queryParams: - description: "QueryParams specifies HTTP query parameter matchers. Multiple match values are ANDed together, meaning, a request must match all the specified query parameters to select the route. \n Support: Extended" - items: - description: HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP query parameters. - properties: - name: - description: "Name is the name of the HTTP query param to be matched. This must be an exact string match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). \n If multiple entries specify equivalent query param names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent query param name MUST be ignored. \n If a query param is repeated in an HTTP request, the behavior is purposely left undefined, since different data planes have different capabilities. However, it is *recommended* that implementations should match against the first value of the param if the data plane supports it, as this behavior is expected in other load balancing contexts outside of the Gateway API. \n Users SHOULD NOT route traffic based on repeated query params to guard themselves against potential differences in the implementations." - maxLength: 256 - minLength: 1 - type: string - type: - default: Exact - description: "Type specifies how to match against the value of the query parameter. \n Support: Extended (Exact) \n Support: Implementation-specific (RegularExpression) \n Since RegularExpression QueryParamMatchType has Implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect." - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP query param to be matched. - maxLength: 1024 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - maxItems: 8 - type: array - type: object - maxItems: 16 - type: array - type: object - status: - description: Status defines the current state of HTTPRoute. - properties: - parents: - description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." - items: - description: RouteParentStatus describes the status of a route with respect to an associated Parent. - properties: - conditions: - description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - controllerName: - description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/gateway-controller\". \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - parentRef: - description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - 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. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - required: - - controllerName - - parentRef - type: object - maxItems: 32 - type: array - required: - - parents - type: object - required: - - spec - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .spec.hostnames - name: Hostnames - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: HTTPRoute provides a way to route HTTP requests. This includes the capability to match requests by hostname, path, header, or query param. Filters can be used to specify additional processing steps. Backends specify where matching requests should be routed. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of HTTPRoute. - properties: - hostnames: - description: "Hostnames defines a set of hostname that should match against the HTTP Host header to select a HTTPRoute to process the request. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n If a hostname is specified by both the Listener and HTTPRoute, there must be at least one intersecting hostname for the HTTPRoute to be attached to the Listener. For example: \n * A Listener with `test.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `*.example.com`, `test.example.com`, and `foo.test.example.com` would all match. On the other hand, `example.com` and `test.example.net` would not match. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n If both the Listener and HTTPRoute have specified hostnames, any HTTPRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the HTTPRoute specified `test.example.com` and `test.example.net`, `test.example.net` must not be considered for a match. \n If both the Listener and HTTPRoute have specified hostnames, and none match with the criteria above, then the HTTPRoute is not accepted. The implementation must raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. \n In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. overlapping wildcard matching and exact matching hostnames), precedence must be given to rules from the HTTPRoute with the largest number of: \n * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. \n If ties exist across multiple Routes, the matching precedence rules for HTTPRouteMatches takes over. \n Support: Core" - items: - description: "Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n Hostname can be \"precise\" which is a domain name without the terminating dot of a network host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). \n Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed." - maxLength: 253 - minLength: 1 - pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - maxItems: 16 - type: array - parentRefs: - description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." - items: - description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - 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. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - maxItems: 32 - type: array - rules: - default: - - matches: - - path: - type: PathPrefix - value: / - description: Rules are a list of HTTP matchers, filters and actions. - items: - description: HTTPRouteRule defines semantics for matching an HTTP request based on conditions (matches), processing it (filters), and forwarding the request to an API object (backendRefs). - properties: - backendRefs: - description: "BackendRefs defines the backend(s) where matching requests should be sent. \n Failure behavior here depends on how many BackendRefs are specified and how many are invalid. \n If *all* entries in BackendRefs are invalid, and there are also no filters specified in this route rule, *all* traffic which matches this rule MUST receive a 500 status code. \n See the HTTPBackendRef definition for the rules about what makes a single HTTPBackendRef invalid. \n When a HTTPBackendRef is invalid, 500 status codes MUST be returned for requests that would have otherwise been routed to an invalid backend. If multiple backends are specified, and some are invalid, the proportion of requests that would otherwise have been routed to an invalid backend MUST receive a 500 status code. \n For example, if two backends are specified with equal weights, and one is invalid, 50 percent of traffic must receive a 500. Implementations may choose how that 50 percent is determined. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Core" - items: - description: HTTPBackendRef defines how a HTTPRoute should forward an HTTP request. - properties: - filters: - description: "Filters defined at this level should be executed if and only if the request is being forwarded to the backend defined here. \n Support: Implementation-specific (For broader support of filters, use the Filters field in HTTPRouteRule.)" - items: - description: HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. - properties: - extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" - 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 - requestHeaderModifier: - description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" - properties: - add: - description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" - items: - type: string - maxItems: 16 - type: array - set: - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - requestMirror: - description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" - properties: - backendRef: - description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" - properties: - group: - default: "" - 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: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name - type: object - required: - - backendRef - type: object - requestRedirect: - description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n Support: Core" - properties: - hostname: - description: "Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname of the request is used. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - path: - description: "Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. \n Support: Extended \n " - properties: - replaceFullPath: - description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " - maxLength: 1024 - type: string - replacePrefixMatch: - description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " - maxLength: 1024 - type: string - type: - description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - port: - description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n Support: Extended" - format: int32 - maximum: 65535 - minimum: 1 - type: integer - scheme: - description: "Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" - enum: - - http - - https - type: string - statusCode: - default: 302 - description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" - enum: - - 301 - - 302 - type: integer - type: object - responseHeaderModifier: - description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " - properties: - add: - description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" - items: - type: string - maxItems: 16 - type: array - set: - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: - description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - RequestHeaderModifier - - ResponseHeaderModifier - - RequestMirror - - RequestRedirect - - URLRewrite - - ExtensionRef - type: string - urlRewrite: - description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " - properties: - hostname: - description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - path: - description: "Path defines a path rewrite. \n Support: Extended \n " - properties: - replaceFullPath: - description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " - maxLength: 1024 - type: string - replacePrefixMatch: - description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " - maxLength: 1024 - type: string - type: - description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - type: object - required: - - type - type: object - maxItems: 16 - type: array - group: - default: "" - 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: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - weight: - default: 1 - description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support for this field varies based on the context where used." - format: int32 - maximum: 1000000 - minimum: 0 - type: integer - required: - - name - type: object - maxItems: 16 - type: array - filters: - description: "Filters define the filters that are applied to requests that match this rule. \n The effects of ordering of multiple behaviors are currently unspecified. This can change in the future based on feedback during the alpha stage. \n Conformance-levels at this level are defined based on the type of filter: \n - ALL core filters MUST be supported by all implementations. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. \n Specifying a core filter multiple times has unspecified or implementation-specific conformance. \n All filters are expected to be compatible with each other except for the URLRewrite and RequestRedirect filters, which may not be combined. If an implementation can not support other combinations of filters, they must clearly document that limitation. In all cases where incompatible or unsupported filters are specified, implementations MUST add a warning condition to status. \n Support: Core" - items: - description: HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. - properties: - extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" - 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 - requestHeaderModifier: - description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" - properties: - add: - description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" - items: - type: string - maxItems: 16 - type: array - set: - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - requestMirror: - description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" - properties: - backendRef: - description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" - properties: - group: - default: "" - 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: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name - type: object - required: - - backendRef - type: object - requestRedirect: - description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n Support: Core" - properties: - hostname: - description: "Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname of the request is used. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - path: - description: "Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. \n Support: Extended \n " - properties: - replaceFullPath: - description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " - maxLength: 1024 - type: string - replacePrefixMatch: - description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " - maxLength: 1024 - type: string - type: - description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - port: - description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n Support: Extended" - format: int32 - maximum: 65535 - minimum: 1 - type: integer - scheme: - description: "Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" - enum: - - http - - https - type: string - statusCode: - default: 302 - description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" - enum: - - 301 - - 302 - type: integer - type: object - responseHeaderModifier: - description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " - properties: - add: - description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" - items: - type: string - maxItems: 16 - type: array - set: - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: - description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - RequestHeaderModifier - - ResponseHeaderModifier - - RequestMirror - - RequestRedirect - - URLRewrite - - ExtensionRef - type: string - urlRewrite: - description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " - properties: - hostname: - description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - path: - description: "Path defines a path rewrite. \n Support: Extended \n " - properties: - replaceFullPath: - description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " - maxLength: 1024 - type: string - replacePrefixMatch: - description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " - maxLength: 1024 - type: string - type: - description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - type: object - required: - - type - type: object - maxItems: 16 - type: array - matches: - default: - - path: - type: PathPrefix - value: / - description: "Matches define conditions used for matching the rule against incoming HTTP requests. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied. \n For example, take the following matches configuration: \n ``` matches: - path: value: \"/foo\" headers: - name: \"version\" value: \"v2\" - path: value: \"/v2/foo\" ``` \n For a request to match against this rule, a request must satisfy EITHER of the two conditions: \n - path prefixed with `/foo` AND contains the header `version: v2` - path prefix of `/v2/foo` \n See the documentation for HTTPRouteMatch on how to specify multiple match conditions that should be ANDed together. \n If no matches are specified, the default is a prefix path match on \"/\", which has the effect of matching every HTTP request. \n Proxy or Load Balancer routing configuration generated from HTTPRoutes MUST prioritize matches based on the following criteria, continuing on ties. Across all rules specified on applicable Routes, precedence must be given to the match with the largest number of: \n * Characters in a matching path. * Header matches. * Query param matches. \n If ties still exist across multiple Routes, matching precedence MUST be determined in order of the following criteria, continuing on ties: \n * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n If ties still exist within an HTTPRoute, matching precedence MUST be granted to the FIRST matching rule (in list order) with a match meeting the above criteria. \n When no rules matching a request have been successfully attached to the parent a request is coming from, a HTTP 404 status code MUST be returned." - items: - description: "HTTPRouteMatch defines the predicate used to match requests to a given action. Multiple match types are ANDed together, i.e. the match will evaluate to true only if all conditions are satisfied. \n For example, the match below will match a HTTP request only if its path starts with `/foo` AND it contains the `version: v1` header: \n ``` match: \n \tpath: \t value: \"/foo\" \theaders: \t- name: \"version\" \t value \"v1\" \n ```" - properties: - headers: - description: Headers specifies HTTP request header matchers. Multiple match values are ANDed together, meaning, a request must match all the specified headers to select the route. - items: - description: HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request headers. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent. \n When a header is repeated in an HTTP request, it is implementation-specific behavior as to how this is represented. Generally, proxies should follow the guidance from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding processing a repeated header, with special handling for \"Set-Cookie\"." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: "Type specifies how to match against the value of the header. \n Support: Core (Exact) \n Support: Implementation-specific (RegularExpression) \n Since RegularExpression HeaderMatchType has implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect." - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - method: - description: "Method specifies HTTP method matcher. When specified, this route will be matched only if the request has the specified method. \n Support: Extended" - enum: - - GET - - HEAD - - POST - - PUT - - DELETE - - CONNECT - - OPTIONS - - TRACE - - PATCH - type: string - path: - default: - type: PathPrefix - value: / - description: Path specifies a HTTP request path matcher. If this field is not specified, a default prefix match on the "/" path is provided. - properties: - type: - default: PathPrefix - description: "Type specifies how to match against the path Value. \n Support: Core (Exact, PathPrefix) \n Support: Implementation-specific (RegularExpression)" - enum: - - Exact - - PathPrefix - - RegularExpression - type: string - value: - default: / - description: Value of the HTTP path to match against. - maxLength: 1024 - type: string - type: object - queryParams: - description: "QueryParams specifies HTTP query parameter matchers. Multiple match values are ANDed together, meaning, a request must match all the specified query parameters to select the route. \n Support: Extended" - items: - description: HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP query parameters. - properties: - name: - description: "Name is the name of the HTTP query param to be matched. This must be an exact string match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). \n If multiple entries specify equivalent query param names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent query param name MUST be ignored. \n If a query param is repeated in an HTTP request, the behavior is purposely left undefined, since different data planes have different capabilities. However, it is *recommended* that implementations should match against the first value of the param if the data plane supports it, as this behavior is expected in other load balancing contexts outside of the Gateway API. \n Users SHOULD NOT route traffic based on repeated query params to guard themselves against potential differences in the implementations." - maxLength: 256 - minLength: 1 - type: string - type: - default: Exact - description: "Type specifies how to match against the value of the query parameter. \n Support: Extended (Exact) \n Support: Implementation-specific (RegularExpression) \n Since RegularExpression QueryParamMatchType has Implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect." - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP query param to be matched. - maxLength: 1024 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - maxItems: 8 - type: array - type: object - maxItems: 16 - type: array - type: object - status: - description: Status defines the current state of HTTPRoute. - properties: - parents: - description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." - items: - description: RouteParentStatus describes the status of a route with respect to an associated Parent. - properties: - conditions: - description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - controllerName: - description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/gateway-controller\". \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - parentRef: - description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - 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. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - required: - - controllerName - - parentRef - type: object - maxItems: 32 - type: array - required: - - parents - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] -{{- end }} diff --git a/charts/consul/templates/crd-jwtproviders.yaml b/charts/consul/templates/crd-jwtproviders.yaml deleted file mode 100644 index 8a51d16b68..0000000000 --- a/charts/consul/templates/crd-jwtproviders.yaml +++ /dev/null @@ -1,265 +0,0 @@ -{{- if .Values.connectInject.enabled }} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: jwtproviders.consul.hashicorp.com - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: crd -spec: - group: consul.hashicorp.com - names: - kind: JWTProvider - listKind: JWTProviderList - plural: jwtproviders - singular: jwtprovider - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: JWTProvider is the Schema for the jwtproviders API. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: JWTProviderSpec defines the desired state of JWTProvider - properties: - audiences: - description: Audiences is the set of audiences the JWT is allowed - to access. If specified, all JWTs verified with this provider must - address at least one of these to be considered valid. - items: - type: string - type: array - cacheConfig: - description: CacheConfig defines configuration for caching the validation - result for previously seen JWTs. Caching results can speed up verification - when individual tokens are expected to be handled multiple times. - properties: - size: - description: "Size specifies the maximum number of JWT verification - results to cache. \n Defaults to 0, meaning that JWT caching - is disabled." - type: integer - type: object - clockSkewSeconds: - description: "ClockSkewSeconds specifies the maximum allowable time - difference from clock skew when validating the \"exp\" (Expiration) - and \"nbf\" (Not Before) claims. \n Default value is 30 seconds." - type: integer - forwarding: - description: Forwarding defines rules for forwarding verified JWTs - to the backend. - properties: - headerName: - description: "HeaderName is a header name to use when forwarding - a verified JWT to the backend. The verified JWT could have been - extracted from any location (query param, header, or cookie). - \n The header value will be base64-URL-encoded, and will not - be padded unless PadForwardPayloadHeader is true." - type: string - padForwardPayloadHeader: - description: "PadForwardPayloadHeader determines whether padding - should be added to the base64 encoded token forwarded with ForwardPayloadHeader. - \n Default value is false." - type: boolean - type: object - issuer: - description: Issuer is the entity that must have issued the JWT. This - value must match the "iss" claim of the token. - type: string - jsonWebKeySet: - description: JSONWebKeySet defines a JSON Web Key Set, its location - on disk, or the means with which to fetch a key set from a remote - server. - properties: - local: - description: Local specifies a local source for the key set. - properties: - filename: - description: Filename configures a location on disk where - the JWKS can be found. If specified, the file must be present - on the disk of ALL proxies with intentions referencing this - provider. - type: string - jwks: - description: JWKS contains a base64 encoded JWKS. - type: string - type: object - remote: - description: Remote specifies how to fetch a key set from a remote - server. - properties: - cacheDuration: - description: "CacheDuration is the duration after which cached - keys should be expired. \n Default value is 5 minutes." - format: int64 - type: integer - fetchAsynchronously: - description: "FetchAsynchronously indicates that the JWKS - should be fetched when a client request arrives. Client - requests will be paused until the JWKS is fetched. If false, - the proxy listener will wait for the JWKS to be fetched - before being activated. \n Default value is false." - type: boolean - requestTimeoutMs: - description: RequestTimeoutMs is the number of milliseconds - to time out when making a request for the JWKS. - type: integer - retryPolicy: - description: "RetryPolicy defines a retry policy for fetching - JWKS. \n There is no retry by default." - properties: - numRetries: - description: "NumRetries is the number of times to retry - fetching the JWKS. The retry strategy uses jittered - exponential backoff with a base interval of 1s and max - of 10s. \n Default value is 0." - type: integer - retryPolicyBackOff: - description: "Backoff policy \n Defaults to Envoy's backoff - policy" - properties: - baseInterval: - description: "BaseInterval to be used for the next - back off computation \n The default value from envoy - is 1s" - format: int64 - type: integer - maxInterval: - description: "MaxInternal to be used to specify the - maximum interval between retries. Optional but should - be greater or equal to BaseInterval. \n Defaults - to 10 times BaseInterval" - format: int64 - type: integer - type: object - type: object - uri: - description: URI is the URI of the server to query for the - JWKS. - type: string - type: object - type: object - locations: - description: 'Locations where the JWT will be present in requests. - Envoy will check all of these locations to extract a JWT. If no - locations are specified Envoy will default to: 1. Authorization - header with Bearer schema: "Authorization: Bearer " 2. accessToken - query parameter.' - items: - description: "JWTLocation is a location where the JWT could be present - in requests. \n Only one of Header, QueryParam, or Cookie can - be specified." - properties: - cookie: - description: Cookie defines how to extract a JWT from an HTTP - request cookie. - properties: - name: - description: Name is the name of the cookie containing the - token. - type: string - type: object - header: - description: Header defines how to extract a JWT from an HTTP - request header. - properties: - forward: - description: "Forward defines whether the header with the - JWT should be forwarded after the token has been verified. - If false, the header will not be forwarded to the backend. - \n Default value is false." - type: boolean - name: - description: Name is the name of the header containing the - token. - type: string - valuePrefix: - description: 'ValuePrefix is an optional prefix that precedes - the token in the header value. For example, "Bearer " - is a standard value prefix for a header named "Authorization", - but the prefix is not part of the token itself: "Authorization: - Bearer "' - type: string - type: object - queryParam: - description: QueryParam defines how to extract a JWT from an - HTTP request query parameter. - properties: - name: - description: Name is the name of the query param containing - the token. - type: string - type: object - type: object - type: array - type: object - status: - properties: - conditions: - description: Conditions indicate the latest available observations - of a resource's current state. - items: - description: 'Conditions define a readiness condition for a Consul - resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' - properties: - lastTransitionTime: - description: LastTransitionTime is the last time the condition - transitioned from one status to another. - format: date-time - type: string - message: - description: A human readable message indicating details about - the transition. - type: string - reason: - description: The reason for the condition's last transition. - type: string - status: - description: Status of the condition, one of True, False, Unknown. - type: string - type: - description: Type of condition. - type: string - required: - - status - - type - type: object - type: array - lastSyncedTime: - description: LastSyncedTime is the last time the resource successfully - synced with Consul. - format: date-time - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] -{{- end }} diff --git a/charts/consul/templates/crd-meshes.yaml b/charts/consul/templates/crd-meshes.yaml index 0710d41280..2e33eb9653 100644 --- a/charts/consul/templates/crd-meshes.yaml +++ b/charts/consul/templates/crd-meshes.yaml @@ -55,11 +55,6 @@ spec: spec: description: MeshSpec defines the desired state of Mesh. properties: - allowEnablingPermissiveMutualTLS: - description: AllowEnablingPermissiveMutualTLS must be true in order - to allow setting MutualTLSMode=permissive in either service-defaults - or proxy-defaults. - type: boolean http: description: HTTP defines the HTTP configuration for the service mesh. properties: diff --git a/charts/consul/templates/crd-meshservices.yaml b/charts/consul/templates/crd-meshservices.yaml deleted file mode 100644 index df8f673bdc..0000000000 --- a/charts/consul/templates/crd-meshservices.yaml +++ /dev/null @@ -1,64 +0,0 @@ -{{- if .Values.connectInject.enabled }} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: meshservices.consul.hashicorp.com - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: crd -spec: - group: consul.hashicorp.com - names: - kind: MeshService - listKind: MeshServiceList - plural: meshservices - singular: meshservice - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: MeshService holds a reference to an externally managed Consul - Service Mesh service. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of MeshService. - properties: - name: - description: Name holds the service name for a Consul service. - type: string - peer: - description: Peer optionally specifies the name of the peer exporting - the Consul service. If not specified, the Consul service is assumed - to be in the local datacenter. - type: string - type: object - type: object - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] -{{- end }} diff --git a/charts/consul/templates/crd-proxydefaults.yaml b/charts/consul/templates/crd-proxydefaults.yaml index 1e931dd888..e66543637f 100644 --- a/charts/consul/templates/crd-proxydefaults.yaml +++ b/charts/consul/templates/crd-proxydefaults.yaml @@ -57,60 +57,12 @@ spec: spec: description: ProxyDefaultsSpec defines the desired state of ProxyDefaults. properties: - accessLogs: - description: AccessLogs controls all envoy instances' access logging - configuration. - properties: - disableListenerLogs: - description: DisableListenerLogs turns off just listener logs - for connections rejected by Envoy because they don't have a - matching listener filter. - type: boolean - enabled: - description: Enabled turns on all access logging - type: boolean - jsonFormat: - description: 'JSONFormat is a JSON-formatted string of an Envoy - access log format dictionary. See for more info on formatting: - https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#format-dictionaries - Defining JSONFormat and TextFormat is invalid.' - type: string - path: - description: Path is the output file to write logs for file-type - logging - type: string - textFormat: - description: 'TextFormat is a representation of Envoy access logs - format. See for more info on formatting: https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#format-strings - Defining JSONFormat and TextFormat is invalid.' - type: string - type: - description: Type selects the output for logs one of "file", "stderr". - "stdout" - type: string - type: object config: description: Config is an arbitrary map of configuration values used by Connect proxies. Any values that your proxy allows can be configured globally here. Supports JSON config values. See https://www.consul.io/docs/connect/proxies/envoy#configuration-formatting type: object x-kubernetes-preserve-unknown-fields: true - envoyExtensions: - description: EnvoyExtensions are a list of extensions to modify Envoy - proxy configuration. - items: - description: EnvoyExtension has configuration for an extension that - patches Envoy resources. - properties: - arguments: - type: object - x-kubernetes-preserve-unknown-fields: true - name: - type: string - required: - type: boolean - type: object - type: array expose: description: Expose controls the default expose path configuration for Envoy. @@ -143,32 +95,6 @@ spec: type: object type: array type: object - failoverPolicy: - description: FailoverPolicy specifies the exact mechanism used for - failover. - properties: - mode: - description: Mode specifies the type of failover that will be - performed. Valid values are "sequential", "" (equivalent to - "sequential") and "order-by-locality". - type: string - regions: - description: Regions is the ordered list of the regions of the - failover targets. Valid values can be "us-west-1", "us-west-2", - and so on. - items: - type: string - type: array - type: object - prioritizeByLocality: - description: PrioritizeByLocality contains the configuration for - locality aware routing. - properties: - mode: - description: Mode specifies the behavior of PrioritizeByLocality - routing. Valid values are "", "none", and "failover". - type: string - type: object meshGateway: description: MeshGateway controls the default mesh gateway configuration for this service. @@ -189,18 +115,6 @@ spec: CRD and should be set using annotations on the services that are part of the mesh.' type: string - mutualTLSMode: - description: 'MutualTLSMode controls whether mutual TLS is required - for all incoming connections when transparent proxy is enabled. - This can be set to "permissive" or "strict". "strict" is the default - which requires mutual TLS for incoming connections. In the insecure - "permissive" mode, connections to the sidecar proxy public listener - port require mutual TLS, but connections to the service port do - not require mutual TLS and are proxied to the application unmodified. - Note: Intentions are not enforced for non-mTLS connections. To keep - your services secure, we recommend using "strict" mode whenever - possible and enabling "permissive" mode only when necessary.' - type: string transparentProxy: description: 'TransparentProxy controls configuration specific to proxies in transparent mode. Note: This cannot be set using the diff --git a/charts/consul/templates/crd-referencegrants.yaml b/charts/consul/templates/crd-referencegrants.yaml deleted file mode 100644 index 6ae177d987..0000000000 --- a/charts/consul/templates/crd-referencegrants.yaml +++ /dev/null @@ -1,211 +0,0 @@ -{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: crd - gateway.networking.k8s.io/bundle-version: v0.6.2 - gateway.networking.k8s.io/channel: experimental - creationTimestamp: null - name: referencegrants.gateway.networking.k8s.io -spec: - group: gateway.networking.k8s.io - names: - categories: - - gateway-api - kind: ReferenceGrant - listKind: ReferenceGrantList - plural: referencegrants - shortNames: - - refgrant - singular: referencegrant - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha2 - schema: - openAPIV3Schema: - description: "ReferenceGrant identifies kinds of resources in other namespaces that are trusted to reference the specified kinds of resources in the same namespace as the policy. \n Each ReferenceGrant can be used to represent a unique trust relationship. Additional Reference Grants can be used to add to the set of trusted sources of inbound references for the namespace they are defined within. \n All cross-namespace references in Gateway API (with the exception of cross-namespace Gateway-route attachment) require a ReferenceGrant. \n ReferenceGrant is a form of runtime verification allowing users to assert which cross-namespace object references are permitted. Implementations that support ReferenceGrant MUST NOT permit cross-namespace references which have no grant, and MUST respond to the removal of a grant by revoking the access that the grant allowed. \n Support: Core" - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of ReferenceGrant. - properties: - from: - description: "From describes the trusted namespaces and kinds that can reference the resources described in \"To\". Each entry in this list MUST be considered to be an additional place that references can be valid from, or to put this another way, entries MUST be combined using OR. \n Support: Core" - items: - description: ReferenceGrantFrom describes trusted namespaces and kinds. - properties: - group: - description: "Group is the group of the referent. When empty, the Kubernetes core API group is inferred. \n Support: Core" - 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 the kind of the referent. Although implementations may support additional resources, the following types are part of the \"Core\" support level for this field. \n When used to permit a SecretObjectReference: \n * Gateway \n When used to permit a BackendObjectReference: \n * GRPCRoute * HTTPRoute * TCPRoute * TLSRoute * UDPRoute" - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - namespace: - description: "Namespace is the namespace of the referent. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - group - - kind - - namespace - type: object - maxItems: 16 - minItems: 1 - type: array - to: - description: "To describes the resources that may be referenced by the resources described in \"From\". Each entry in this list MUST be considered to be an additional place that references can be valid to, or to put this another way, entries MUST be combined using OR. \n Support: Core" - items: - description: ReferenceGrantTo describes what Kinds are allowed as targets of the references. - properties: - group: - description: "Group is the group of the referent. When empty, the Kubernetes core API group is inferred. \n Support: Core" - 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 the kind of the referent. Although implementations may support additional resources, the following types are part of the \"Core\" support level for this field: \n * Secret when used to permit a SecretObjectReference * Service when used to permit a BackendObjectReference" - 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. When unspecified, this policy refers to all resources of the specified Group and Kind in the local namespace. - maxLength: 253 - minLength: 1 - type: string - required: - - group - - kind - type: object - maxItems: 16 - minItems: 1 - type: array - required: - - from - - to - type: object - type: object - served: true - storage: true - subresources: {} - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: "ReferenceGrant identifies kinds of resources in other namespaces that are trusted to reference the specified kinds of resources in the same namespace as the policy. \n Each ReferenceGrant can be used to represent a unique trust relationship. Additional Reference Grants can be used to add to the set of trusted sources of inbound references for the namespace they are defined within. \n All cross-namespace references in Gateway API (with the exception of cross-namespace Gateway-route attachment) require a ReferenceGrant. \n ReferenceGrant is a form of runtime verification allowing users to assert which cross-namespace object references are permitted. Implementations that support ReferenceGrant MUST NOT permit cross-namespace references which have no grant, and MUST respond to the removal of a grant by revoking the access that the grant allowed. \n Support: Core" - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of ReferenceGrant. - properties: - from: - description: "From describes the trusted namespaces and kinds that can reference the resources described in \"To\". Each entry in this list MUST be considered to be an additional place that references can be valid from, or to put this another way, entries MUST be combined using OR. \n Support: Core" - items: - description: ReferenceGrantFrom describes trusted namespaces and kinds. - properties: - group: - description: "Group is the group of the referent. When empty, the Kubernetes core API group is inferred. \n Support: Core" - 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 the kind of the referent. Although implementations may support additional resources, the following types are part of the \"Core\" support level for this field. \n When used to permit a SecretObjectReference: \n * Gateway \n When used to permit a BackendObjectReference: \n * GRPCRoute * HTTPRoute * TCPRoute * TLSRoute * UDPRoute" - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - namespace: - description: "Namespace is the namespace of the referent. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - group - - kind - - namespace - type: object - maxItems: 16 - minItems: 1 - type: array - to: - description: "To describes the resources that may be referenced by the resources described in \"From\". Each entry in this list MUST be considered to be an additional place that references can be valid to, or to put this another way, entries MUST be combined using OR. \n Support: Core" - items: - description: ReferenceGrantTo describes what Kinds are allowed as targets of the references. - properties: - group: - description: "Group is the group of the referent. When empty, the Kubernetes core API group is inferred. \n Support: Core" - 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 the kind of the referent. Although implementations may support additional resources, the following types are part of the \"Core\" support level for this field: \n * Secret when used to permit a SecretObjectReference * Service when used to permit a BackendObjectReference" - 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. When unspecified, this policy refers to all resources of the specified Group and Kind in the local namespace. - maxLength: 253 - minLength: 1 - type: string - required: - - group - - kind - type: object - maxItems: 16 - minItems: 1 - type: array - required: - - from - - to - type: object - type: object - served: true - storage: false - subresources: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] -{{- end }} diff --git a/charts/consul/templates/crd-routeretryfilters.yaml b/charts/consul/templates/crd-routeretryfilters.yaml deleted file mode 100644 index 3c69a5a2ae..0000000000 --- a/charts/consul/templates/crd-routeretryfilters.yaml +++ /dev/null @@ -1,117 +0,0 @@ -{{- if .Values.connectInject.enabled }} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null - name: routeretryfilters.consul.hashicorp.com - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: crd -spec: - group: consul.hashicorp.com - names: - kind: RouteRetryFilter - listKind: RouteRetryFilterList - plural: routeretryfilters - singular: routeretryfilter - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The sync status of the resource with Consul - jsonPath: .status.conditions[?(@.type=="Synced")].status - name: Synced - type: string - - description: The last successful synced time of the resource with Consul - jsonPath: .status.lastSyncedTime - name: Last Synced - type: date - - description: The age of the resource - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: RouteRetryFilter is the Schema for the routeretryfilters API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: RouteRetryFilterSpec defines the desired state of RouteRetryFilter - properties: - numRetries: - format: int32 - minimum: 0 - type: integer - retryOn: - items: - type: string - type: array - retryOnConnectFailure: - type: boolean - retryOnStatusCodes: - items: - format: int32 - type: integer - type: array - type: object - status: - properties: - conditions: - description: Conditions indicate the latest available observations - of a resource's current state. - items: - description: 'Conditions define a readiness condition for a Consul - resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' - properties: - lastTransitionTime: - description: LastTransitionTime is the last time the condition - transitioned from one status to another. - format: date-time - type: string - message: - description: A human readable message indicating details about - the transition. - type: string - reason: - description: The reason for the condition's last transition. - type: string - status: - description: Status of the condition, one of True, False, Unknown. - type: string - type: - description: Type of condition. - type: string - required: - - status - - type - type: object - type: array - lastSyncedTime: - description: LastSyncedTime is the last time the resource successfully - synced with Consul. - format: date-time - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -{{- end }} diff --git a/charts/consul/templates/crd-routetimeoutfilters.yaml b/charts/consul/templates/crd-routetimeoutfilters.yaml deleted file mode 100644 index 992d21b35b..0000000000 --- a/charts/consul/templates/crd-routetimeoutfilters.yaml +++ /dev/null @@ -1,115 +0,0 @@ -{{- if .Values.connectInject.enabled }} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null - name: routetimeoutfilters.consul.hashicorp.com - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: crd -spec: - group: consul.hashicorp.com - names: - kind: RouteTimeoutFilter - listKind: RouteTimeoutFilterList - plural: routetimeoutfilters - singular: routetimeoutfilter - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The sync status of the resource with Consul - jsonPath: .status.conditions[?(@.type=="Synced")].status - name: Synced - type: string - - description: The last successful synced time of the resource with Consul - jsonPath: .status.lastSyncedTime - name: Last Synced - type: date - - description: The age of the resource - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: RouteTimeoutFilter is the Schema for the httproutetimeoutfilters - API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: RouteTimeoutFilterSpec defines the desired state of RouteTimeoutFilter - properties: - idleTimeout: - description: A Duration represents the elapsed time between two instants - as an int64 nanosecond count. The representation limits the largest - representable duration to approximately 290 years. - format: int64 - type: integer - requestTimeout: - description: A Duration represents the elapsed time between two instants - as an int64 nanosecond count. The representation limits the largest - representable duration to approximately 290 years. - format: int64 - type: integer - type: object - status: - properties: - conditions: - description: Conditions indicate the latest available observations - of a resource's current state. - items: - description: 'Conditions define a readiness condition for a Consul - resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' - properties: - lastTransitionTime: - description: LastTransitionTime is the last time the condition - transitioned from one status to another. - format: date-time - type: string - message: - description: A human readable message indicating details about - the transition. - type: string - reason: - description: The reason for the condition's last transition. - type: string - status: - description: Status of the condition, one of True, False, Unknown. - type: string - type: - description: Type of condition. - type: string - required: - - status - - type - type: object - type: array - lastSyncedTime: - description: LastSyncedTime is the last time the resource successfully - synced with Consul. - format: date-time - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -{{- end }} diff --git a/charts/consul/templates/crd-samenessgroups.yaml b/charts/consul/templates/crd-samenessgroups.yaml deleted file mode 100644 index 60beb5662c..0000000000 --- a/charts/consul/templates/crd-samenessgroups.yaml +++ /dev/null @@ -1,137 +0,0 @@ -{{- if .Values.connectInject.enabled }} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: samenessgroups.consul.hashicorp.com - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: crd -spec: - group: consul.hashicorp.com - names: - kind: SamenessGroup - listKind: SamenessGroupList - plural: samenessgroups - shortNames: - - sameness-group - singular: samenessgroup - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The sync status of the resource with Consul - jsonPath: .status.conditions[?(@.type=="Synced")].status - name: Synced - type: string - - description: The last successful synced time of the resource with Consul - jsonPath: .status.lastSyncedTime - name: Last Synced - type: date - - description: The age of the resource - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: SamenessGroup is the Schema for the samenessgroups API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: SamenessGroupSpec defines the desired state of SamenessGroup. - properties: - defaultForFailover: - description: DefaultForFailover indicates that upstream requests to - members of the given sameness group will implicitly failover between - members of this sameness group. When DefaultForFailover is true, - the local partition must be a member of the sameness group or IncludeLocal - must be set to true. - type: boolean - includeLocal: - description: IncludeLocal is used to include the local partition as - the first member of the sameness group. The local partition can - only be a member of a single sameness group. - type: boolean - members: - description: Members are the partitions and peers that are part of - the sameness group. If a member of a sameness group does not exist, - it will be ignored. - items: - properties: - partition: - description: The partitions and peers that are part of the sameness - group. A sameness group member cannot define both peer and - partition at the same time. - type: string - peer: - type: string - type: object - type: array - type: object - status: - properties: - conditions: - description: Conditions indicate the latest available observations - of a resource's current state. - items: - description: 'Conditions define a readiness condition for a Consul - resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' - properties: - lastTransitionTime: - description: LastTransitionTime is the last time the condition - transitioned from one status to another. - format: date-time - type: string - message: - description: A human readable message indicating details about - the transition. - type: string - reason: - description: The reason for the condition's last transition. - type: string - status: - description: Status of the condition, one of True, False, Unknown. - type: string - type: - description: Type of condition. - type: string - required: - - status - - type - type: object - type: array - lastSyncedTime: - description: LastSyncedTime is the last time the resource successfully - synced with Consul. - format: date-time - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] -{{- end }} diff --git a/charts/consul/templates/crd-servicedefaults.yaml b/charts/consul/templates/crd-servicedefaults.yaml index 870f5ad86c..3b5503eebe 100644 --- a/charts/consul/templates/crd-servicedefaults.yaml +++ b/charts/consul/templates/crd-servicedefaults.yaml @@ -57,12 +57,6 @@ spec: spec: description: ServiceDefaultsSpec defines the desired state of ServiceDefaults. properties: - balanceInboundConnections: - description: BalanceInboundConnections sets the strategy for allocating - inbound connections to the service across proxy threads. The only - supported value is exact_balance. By default, no connection balancing - is used. Refer to the Envoy Connection Balance config for details. - type: string destination: description: Destination is an address(es)/port combination that represents an endpoint outside the mesh. This is only valid when the mesh is @@ -82,22 +76,6 @@ spec: format: int32 type: integer type: object - envoyExtensions: - description: EnvoyExtensions are a list of extensions to modify Envoy - proxy configuration. - items: - description: EnvoyExtension has configuration for an extension that - patches Envoy resources. - properties: - arguments: - type: object - x-kubernetes-preserve-unknown-fields: true - name: - type: string - required: - type: boolean - type: object - type: array expose: description: Expose controls the default expose path configuration for Envoy. @@ -136,15 +114,15 @@ spec: with an external system. type: string localConnectTimeoutMs: - description: LocalConnectTimeoutMs is the number of milliseconds allowed - to make connections to the local application instance before timing - out. Defaults to 5000. + description: The number of milliseconds allowed to make connections + to the local application instance before timing out. Defaults to + 5000. type: integer localRequestTimeoutMs: - description: LocalRequestTimeoutMs is the timeout for HTTP requests - to the local application instance in milliseconds. Applies to HTTP-based - protocols only. If not specified, inherits the Envoy default for - route timeouts (15s). + description: In milliseconds, the timeout for HTTP requests to the + local application instance. Applies to HTTP-based protocols only. + If not specified, inherits the Envoy default for route timeouts + (15s). type: integer maxInboundConnections: description: MaxInboundConnections is the maximum number of concurrent @@ -171,18 +149,6 @@ spec: CRD and should be set using annotations on the services that are part of the mesh.' type: string - mutualTLSMode: - description: 'MutualTLSMode controls whether mutual TLS is required - for all incoming connections when transparent proxy is enabled. - This can be set to "permissive" or "strict". "strict" is the default - which requires mutual TLS for incoming connections. In the insecure - "permissive" mode, connections to the sidecar proxy public listener - port require mutual TLS, but connections to the service port do - not require mutual TLS and are proxied to the application unmodified. - Note: Intentions are not enforced for non-mTLS connections. To keep - your services secure, we recommend using "strict" mode whenever - possible and enabling "permissive" mode only when necessary.' - type: string protocol: description: Protocol sets the protocol of the service. This is used by Connect proxies for things like observability features and to @@ -270,15 +236,15 @@ spec: type: string type: object name: - description: Name is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides + description: Name is only accepted within a service-defaults config entry. type: string namespace: - description: Namespace is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides + description: Namespace is only accepted within a service-defaults config entry. type: string partition: - description: Partition is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides + description: Partition is only accepted within a service-defaults config entry. type: string passiveHealthCheck: @@ -317,10 +283,6 @@ spec: format: int32 type: integer type: object - peer: - description: Peer is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides - config entry. - type: string protocol: description: Protocol describes the upstream's service protocol. Valid values are "tcp", "http" and "grpc". Anything else @@ -387,15 +349,15 @@ spec: type: string type: object name: - description: Name is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides + description: Name is only accepted within a service-defaults config entry. type: string namespace: - description: Namespace is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides + description: Namespace is only accepted within a service-defaults config entry. type: string partition: - description: Partition is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides + description: Partition is only accepted within a service-defaults config entry. type: string passiveHealthCheck: @@ -436,10 +398,6 @@ spec: format: int32 type: integer type: object - peer: - description: Peer is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides - config entry. - type: string protocol: description: Protocol describes the upstream's service protocol. Valid values are "tcp", "http" and "grpc". Anything else diff --git a/charts/consul/templates/crd-serviceintentions.yaml b/charts/consul/templates/crd-serviceintentions.yaml index c4d2b5f20d..cdbb5413b0 100644 --- a/charts/consul/templates/crd-serviceintentions.yaml +++ b/charts/consul/templates/crd-serviceintentions.yaml @@ -74,43 +74,6 @@ spec: have intentions defined. type: string type: object - jwt: - description: JWT specifies the configuration to validate a JSON Web - Token for all incoming requests. - properties: - providers: - description: Providers is a list of providers to consider when - verifying a JWT. - items: - properties: - name: - description: Name is the name of the JWT provider. There - MUST be a corresponding "jwt-provider" config entry with - this name. - type: string - verifyClaims: - description: VerifyClaims is a list of additional claims - to verify in a JWT's payload. - items: - properties: - path: - description: Path is the path to the claim in the - token JSON. - items: - type: string - type: array - value: - description: Value is the expected value at the given - path. If the type at the path is a list then we - verify that this value is contained in the list. - If the type at the path is a string then we verify - that this value matches. - type: string - type: object - type: array - type: object - type: array - type: object sources: description: Sources is the list of all intention sources and the authorization granted to those sources. The order of this list does @@ -139,7 +102,8 @@ spec: description: Partition is the Admin Partition for the Name parameter. type: string peer: - description: Peer is the peer name for the Name parameter. + description: '[Experimental] Peer is the peer name for the Name + parameter.' type: string permissions: description: Permissions is the list of all additional L7 attributes @@ -220,50 +184,8 @@ spec: match on the HTTP request path. type: string type: object - jwt: - description: JWT specifies configuration to validate a - JSON Web Token for incoming requests. - properties: - providers: - description: Providers is a list of providers to consider - when verifying a JWT. - items: - properties: - name: - description: Name is the name of the JWT provider. - There MUST be a corresponding "jwt-provider" - config entry with this name. - type: string - verifyClaims: - description: VerifyClaims is a list of additional - claims to verify in a JWT's payload. - items: - properties: - path: - description: Path is the path to the claim - in the token JSON. - items: - type: string - type: array - value: - description: Value is the expected value - at the given path. If the type at the - path is a list then we verify that this - value is contained in the list. If the - type at the path is a string then we - verify that this value matches. - type: string - type: object - type: array - type: object - type: array - type: object type: object type: array - samenessGroup: - description: SamenessGroup is the name of the sameness group, - if applicable. - type: string type: object type: array type: object diff --git a/charts/consul/templates/crd-serviceresolvers.yaml b/charts/consul/templates/crd-serviceresolvers.yaml index eb5643fe49..e058052e97 100644 --- a/charts/consul/templates/crd-serviceresolvers.yaml +++ b/charts/consul/templates/crd-serviceresolvers.yaml @@ -79,26 +79,6 @@ spec: service from to form the failover group of instances. If empty the current namespace is used. type: string - policy: - description: Policy specifies the exact mechanism used for failover. - properties: - mode: - description: Mode specifies the type of failover that will - be performed. Valid values are "sequential", "" (equivalent - to "sequential") and "order-by-locality". - type: string - regions: - description: Regions is the ordered list of the regions - of the failover targets. Valid values can be "us-west-1", - "us-west-2", and so on. - items: - type: string - type: array - type: object - samenessGroup: - description: SamenessGroup is the name of the sameness group - to try during failover. - type: string service: description: Service is the service to resolve instead of the default as the failover group of instances during failover. @@ -227,16 +207,6 @@ spec: type: integer type: object type: object - prioritizeByLocality: - description: PrioritizeByLocality controls whether the locality of - services within the local partition will be used to prioritize connectivity. - properties: - mode: - description: 'Mode specifies the type of prioritization that will - be performed when selecting nodes in the local partition. Valid - values are: "" (default "none"), "none", and "failover".' - type: string - type: object redirect: description: Redirect when configured, all attempts to resolve the service this resolver defines will be substituted for the supplied @@ -262,10 +232,6 @@ spec: description: Peer is the name of the cluster peer to resolve the service from instead of the current one. type: string - samenessGroup: - description: SamenessGroup is the name of the sameness group to - resolve the service from instead of the current one. - type: string service: description: Service is a service to resolve instead of the current service. @@ -276,10 +242,6 @@ spec: If empty the default subset is used. type: string type: object - requestTimeout: - description: RequestTimeout is the timeout for receiving an HTTP response - from this service before the connection is terminated. - type: string subsets: additionalProperties: properties: diff --git a/charts/consul/templates/crd-tcproutes.yaml b/charts/consul/templates/crd-tcproutes.yaml deleted file mode 100644 index a17f457a78..0000000000 --- a/charts/consul/templates/crd-tcproutes.yaml +++ /dev/null @@ -1,284 +0,0 @@ -{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 - gateway.networking.k8s.io/bundle-version: v0.6.2 - gateway.networking.k8s.io/channel: experimental - creationTimestamp: null - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: crd - name: tcproutes.gateway.networking.k8s.io -spec: - group: gateway.networking.k8s.io - names: - categories: - - gateway-api - kind: TCPRoute - listKind: TCPRouteList - plural: tcproutes - singular: tcproute - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha2 - schema: - openAPIV3Schema: - description: TCPRoute provides a way to route TCP requests. When combined with a Gateway listener, it can be used to forward connections on the port specified by the listener to a set of backends specified by the TCPRoute. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of TCPRoute. - properties: - parentRefs: - description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." - items: - description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - 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. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - maxItems: 32 - type: array - rules: - description: Rules are a list of TCP matchers and actions. - items: - description: TCPRouteRule is the configuration for a given rule. - properties: - backendRefs: - description: "BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a non-existent resource or a Service with no endpoints), the underlying implementation MUST actively reject connection attempts to this backend. Connection rejections must respect weight; if an invalid backend is requested to have 80% of connections, then 80% of connections must be rejected instead. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Extended" - items: - description: "BackendRef defines how a Route should forward a request to a Kubernetes resource. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details." - properties: - group: - default: "" - 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: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - weight: - default: 1 - description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support for this field varies based on the context where used." - format: int32 - maximum: 1000000 - minimum: 0 - type: integer - required: - - name - type: object - maxItems: 16 - minItems: 1 - type: array - type: object - maxItems: 16 - minItems: 1 - type: array - required: - - rules - type: object - status: - description: Status defines the current state of TCPRoute. - properties: - parents: - description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." - items: - description: RouteParentStatus describes the status of a route with respect to an associated Parent. - properties: - conditions: - description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - controllerName: - description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/gateway-controller\". \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - parentRef: - description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - 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. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - required: - - controllerName - - parentRef - type: object - maxItems: 32 - type: array - required: - - parents - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] -{{- end }} diff --git a/charts/consul/templates/crd-tlsroutes.yaml b/charts/consul/templates/crd-tlsroutes.yaml deleted file mode 100644 index be72f47d65..0000000000 --- a/charts/consul/templates/crd-tlsroutes.yaml +++ /dev/null @@ -1,294 +0,0 @@ -{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 - gateway.networking.k8s.io/bundle-version: v0.6.2 - gateway.networking.k8s.io/channel: experimental - creationTimestamp: null - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: crd - name: tlsroutes.gateway.networking.k8s.io -spec: - group: gateway.networking.k8s.io - names: - categories: - - gateway-api - kind: TLSRoute - listKind: TLSRouteList - plural: tlsroutes - singular: tlsroute - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha2 - schema: - openAPIV3Schema: - description: "The TLSRoute resource is similar to TCPRoute, but can be configured to match against TLS-specific metadata. This allows more flexibility in matching streams for a given TLS listener. \n If you need to forward traffic to a single target for a TLS listener, you could choose to use a TCPRoute with a TLS listener." - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of TLSRoute. - properties: - hostnames: - description: "Hostnames defines a set of SNI names that should match against the SNI attribute of TLS ClientHello message in TLS handshake. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed in SNI names per RFC 6066. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n If a hostname is specified by both the Listener and TLSRoute, there must be at least one intersecting hostname for the TLSRoute to be attached to the Listener. For example: \n * A Listener with `test.example.com` as the hostname matches TLSRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches TLSRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `test.example.com` and `*.example.com` would both match. On the other hand, `example.com` and `test.example.net` would not match. \n If both the Listener and TLSRoute have specified hostnames, any TLSRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the TLSRoute specified `test.example.com` and `test.example.net`, `test.example.net` must not be considered for a match. \n If both the Listener and TLSRoute have specified hostnames, and none match with the criteria above, then the TLSRoute is not accepted. The implementation must raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. \n Support: Core" - items: - description: "Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n Hostname can be \"precise\" which is a domain name without the terminating dot of a network host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). \n Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed." - maxLength: 253 - minLength: 1 - pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - maxItems: 16 - type: array - parentRefs: - description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." - items: - description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - 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. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - maxItems: 32 - type: array - rules: - description: Rules are a list of TLS matchers and actions. - items: - description: TLSRouteRule is the configuration for a given rule. - properties: - backendRefs: - description: "BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a non-existent resource or a Service with no endpoints), the rule performs no forwarding; if no filters are specified that would result in a response being sent, the underlying implementation must actively reject request attempts to this backend, by rejecting the connection or returning a 500 status code. Request rejections must respect weight; if an invalid backend is requested to have 80% of requests, then 80% of requests must be rejected instead. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Extended" - items: - description: "BackendRef defines how a Route should forward a request to a Kubernetes resource. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details." - properties: - group: - default: "" - 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: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - weight: - default: 1 - description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support for this field varies based on the context where used." - format: int32 - maximum: 1000000 - minimum: 0 - type: integer - required: - - name - type: object - maxItems: 16 - minItems: 1 - type: array - type: object - maxItems: 16 - minItems: 1 - type: array - required: - - rules - type: object - status: - description: Status defines the current state of TLSRoute. - properties: - parents: - description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." - items: - description: RouteParentStatus describes the status of a route with respect to an associated Parent. - properties: - conditions: - description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - controllerName: - description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/gateway-controller\". \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - parentRef: - description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - 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. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - required: - - controllerName - - parentRef - type: object - maxItems: 32 - type: array - required: - - parents - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] -{{- end }} diff --git a/charts/consul/templates/crd-udproutes.yaml b/charts/consul/templates/crd-udproutes.yaml deleted file mode 100644 index fe331cca30..0000000000 --- a/charts/consul/templates/crd-udproutes.yaml +++ /dev/null @@ -1,284 +0,0 @@ -{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 - gateway.networking.k8s.io/bundle-version: v0.6.2 - gateway.networking.k8s.io/channel: experimental - creationTimestamp: null - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: crd - name: udproutes.gateway.networking.k8s.io -spec: - group: gateway.networking.k8s.io - names: - categories: - - gateway-api - kind: UDPRoute - listKind: UDPRouteList - plural: udproutes - singular: udproute - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha2 - schema: - openAPIV3Schema: - description: UDPRoute provides a way to route UDP traffic. When combined with a Gateway listener, it can be used to forward traffic on the port specified by the listener to a set of backends specified by the UDPRoute. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of UDPRoute. - properties: - parentRefs: - description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." - items: - description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - 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. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - maxItems: 32 - type: array - rules: - description: Rules are a list of UDP matchers and actions. - items: - description: UDPRouteRule is the configuration for a given rule. - properties: - backendRefs: - description: "BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a non-existent resource or a Service with no endpoints), the underlying implementation MUST actively reject connection attempts to this backend. Packet drops must respect weight; if an invalid backend is requested to have 80% of the packets, then 80% of packets must be dropped instead. \n Support: Core for Kubernetes Service Support: Implementation-specific for any other resource \n Support for weight: Extended" - items: - description: "BackendRef defines how a Route should forward a request to a Kubernetes resource. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details." - properties: - group: - default: "" - 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: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - weight: - default: 1 - description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support for this field varies based on the context where used." - format: int32 - maximum: 1000000 - minimum: 0 - type: integer - required: - - name - type: object - maxItems: 16 - minItems: 1 - type: array - type: object - maxItems: 16 - minItems: 1 - type: array - required: - - rules - type: object - status: - description: Status defines the current state of UDPRoute. - properties: - parents: - description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." - items: - description: RouteParentStatus describes the status of a route with respect to an associated Parent. - properties: - conditions: - description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - controllerName: - description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/gateway-controller\". \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - parentRef: - description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - 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. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - required: - - controllerName - - parentRef - type: object - maxItems: 32 - type: array - required: - - parents - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] -{{- end }} diff --git a/charts/consul/templates/gateway-cleanup-clusterrole.yaml b/charts/consul/templates/gateway-cleanup-clusterrole.yaml deleted file mode 100644 index c533a882f5..0000000000 --- a/charts/consul/templates/gateway-cleanup-clusterrole.yaml +++ /dev/null @@ -1,35 +0,0 @@ -{{- if .Values.connectInject.enabled }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: {{ template "consul.fullname" . }}-gateway-cleanup - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: gateway-cleanup -rules: - - apiGroups: - - consul.hashicorp.com - resources: - - gatewayclassconfigs - verbs: - - get - - delete - - apiGroups: - - gateway.networking.k8s.io - resources: - - gatewayclasses - verbs: - - get - - delete -{{- if .Values.global.enablePodSecurityPolicies }} - - apiGroups: ["policy"] - resources: ["podsecuritypolicies"] - resourceNames: - - {{ template "consul.fullname" . }}-gateway-cleanup - verbs: - - use -{{- end }} -{{- end }} diff --git a/charts/consul/templates/gateway-cleanup-clusterrolebinding.yaml b/charts/consul/templates/gateway-cleanup-clusterrolebinding.yaml deleted file mode 100644 index 9235f32101..0000000000 --- a/charts/consul/templates/gateway-cleanup-clusterrolebinding.yaml +++ /dev/null @@ -1,20 +0,0 @@ -{{- if .Values.connectInject.enabled }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: {{ template "consul.fullname" . }}-gateway-cleanup - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: gateway-cleanup -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ template "consul.fullname" . }}-gateway-cleanup -subjects: - - kind: ServiceAccount - name: {{ template "consul.fullname" . }}-gateway-cleanup - namespace: {{ .Release.Namespace }} -{{- end }} \ No newline at end of file diff --git a/charts/consul/templates/gateway-cleanup-job.yaml b/charts/consul/templates/gateway-cleanup-job.yaml deleted file mode 100644 index a987c3b591..0000000000 --- a/charts/consul/templates/gateway-cleanup-job.yaml +++ /dev/null @@ -1,65 +0,0 @@ -{{- if .Values.connectInject.enabled }} -apiVersion: batch/v1 -kind: Job -metadata: - name: {{ template "consul.fullname" . }}-gateway-cleanup - namespace: {{ .Release.Namespace }} - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: gateway-cleanup - {{- if .Values.global.extraLabels }} - {{- toYaml .Values.global.extraLabels | nindent 4 }} - {{- end }} - annotations: - "helm.sh/hook": pre-delete - "helm.sh/hook-weight": "0" - "helm.sh/hook-delete-policy": hook-succeeded,hook-failed -spec: - template: - metadata: - name: {{ template "consul.fullname" . }}-gateway-cleanup - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - release: {{ .Release.Name }} - component: gateway-cleanup - {{- if .Values.global.extraLabels }} - {{- toYaml .Values.global.extraLabels | nindent 8 }} - {{- end }} - annotations: - "consul.hashicorp.com/connect-inject": "false" - {{- if .Values.global.acls.annotations }} - {{- tpl .Values.global.acls.annotations . | nindent 8 }} - {{- end }} - spec: - restartPolicy: Never - serviceAccountName: {{ template "consul.fullname" . }}-gateway-cleanup - containers: - - name: gateway-cleanup - image: {{ .Values.global.imageK8S }} - {{- include "consul.restrictedSecurityContext" . | nindent 10 }} - command: - - consul-k8s-control-plane - args: - - gateway-cleanup - - -gateway-class-name=consul - - -gateway-class-config-name=consul-api-gateway - resources: - requests: - memory: "50Mi" - cpu: "50m" - limits: - memory: "50Mi" - cpu: "50m" - {{- if .Values.global.acls.tolerations }} - tolerations: - {{ tpl .Values.global.acls.tolerations . | indent 8 | trim }} - {{- end }} - {{- if .Values.global.acls.nodeSelector }} - nodeSelector: - {{ tpl .Values.global.acls.nodeSelector . | indent 8 | trim }} - {{- end }} -{{- end }} diff --git a/charts/consul/templates/gateway-cleanup-podsecuritypolicy.yaml b/charts/consul/templates/gateway-cleanup-podsecuritypolicy.yaml deleted file mode 100644 index ffbad130cc..0000000000 --- a/charts/consul/templates/gateway-cleanup-podsecuritypolicy.yaml +++ /dev/null @@ -1,32 +0,0 @@ -{{- if (and .Values.connectInject.enabled .Values.global.enablePodSecurityPolicies)}} -apiVersion: policy/v1beta1 -kind: PodSecurityPolicy -metadata: - name: {{ template "consul.fullname" . }}-gateway-cleanup - namespace: {{ .Release.Namespace }} - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: gateway-cleanup -spec: - privileged: false - allowPrivilegeEscalation: false - # This is redundant with non-root + disallow privilege escalation, - # but we can provide it for defense in depth. - requiredDropCapabilities: - - ALL - hostNetwork: false - hostIPC: false - hostPID: false - runAsUser: - rule: 'RunAsAny' - seLinux: - rule: 'RunAsAny' - supplementalGroups: - rule: 'RunAsAny' - fsGroup: - rule: 'RunAsAny' - readOnlyRootFilesystem: false -{{- end }} diff --git a/charts/consul/templates/gateway-cleanup-serviceaccount.yaml b/charts/consul/templates/gateway-cleanup-serviceaccount.yaml deleted file mode 100644 index f50eb72d97..0000000000 --- a/charts/consul/templates/gateway-cleanup-serviceaccount.yaml +++ /dev/null @@ -1,13 +0,0 @@ -{{- if .Values.connectInject.enabled }} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ template "consul.fullname" . }}-gateway-cleanup - namespace: {{ .Release.Namespace }} - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: gateway-cleanup -{{- end }} diff --git a/charts/consul/templates/gateway-resources-clusterrole.yaml b/charts/consul/templates/gateway-resources-clusterrole.yaml deleted file mode 100644 index c3bdfeb4a3..0000000000 --- a/charts/consul/templates/gateway-resources-clusterrole.yaml +++ /dev/null @@ -1,37 +0,0 @@ -{{- if .Values.connectInject.enabled }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: {{ template "consul.fullname" . }}-gateway-resources - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: gateway-resources -rules: - - apiGroups: - - consul.hashicorp.com - resources: - - gatewayclassconfigs - verbs: - - get - - update - - create - - apiGroups: - - gateway.networking.k8s.io - resources: - - gatewayclasses - verbs: - - get - - update - - create -{{- if .Values.global.enablePodSecurityPolicies }} - - apiGroups: ["policy"] - resources: ["podsecuritypolicies"] - resourceNames: - - {{ template "consul.fullname" . }}-gateway-resources - verbs: - - use -{{- end }} -{{- end }} diff --git a/charts/consul/templates/gateway-resources-clusterrolebinding.yaml b/charts/consul/templates/gateway-resources-clusterrolebinding.yaml deleted file mode 100644 index 921df23239..0000000000 --- a/charts/consul/templates/gateway-resources-clusterrolebinding.yaml +++ /dev/null @@ -1,20 +0,0 @@ -{{- if .Values.connectInject.enabled }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: {{ template "consul.fullname" . }}-gateway-resources - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: gateway-resources -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ template "consul.fullname" . }}-gateway-resources -subjects: - - kind: ServiceAccount - name: {{ template "consul.fullname" . }}-gateway-resources - namespace: {{ .Release.Namespace }} -{{- end }} \ No newline at end of file diff --git a/charts/consul/templates/gateway-resources-job.yaml b/charts/consul/templates/gateway-resources-job.yaml deleted file mode 100644 index de64e2d70d..0000000000 --- a/charts/consul/templates/gateway-resources-job.yaml +++ /dev/null @@ -1,122 +0,0 @@ -{{- if .Values.connectInject.enabled }} -apiVersion: batch/v1 -kind: Job -metadata: - name: {{ template "consul.fullname" . }}-gateway-resources - namespace: {{ .Release.Namespace }} - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: gateway-resources - {{- if .Values.global.extraLabels }} - {{- toYaml .Values.global.extraLabels | nindent 4 }} - {{- end }} - annotations: - "helm.sh/hook": post-install,post-upgrade - "helm.sh/hook-weight": "0" - "helm.sh/hook-delete-policy": hook-succeeded -spec: - template: - metadata: - name: {{ template "consul.fullname" . }}-gateway-resources - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - release: {{ .Release.Name }} - component: gateway-resources - {{- if .Values.global.extraLabels }} - {{- toYaml .Values.global.extraLabels | nindent 8 }} - {{- end }} - annotations: - "consul.hashicorp.com/connect-inject": "false" - {{- if .Values.global.acls.annotations }} - {{- tpl .Values.global.acls.annotations . | nindent 8 }} - {{- end }} - spec: - restartPolicy: Never - serviceAccountName: {{ template "consul.fullname" . }}-gateway-resources - containers: - - name: gateway-resources - image: {{ .Values.global.imageK8S }} - {{- include "consul.restrictedSecurityContext" . | nindent 10 }} - command: - - consul-k8s-control-plane - args: - - gateway-resources - - -gateway-class-name=consul - - -gateway-class-config-name=consul-api-gateway - - -controller-name=consul.hashicorp.com/gateway-controller - - -app={{template "consul.name" .}} - - -chart={{template "consul.chart" .}} - - -heritage={{ .Release.Service }} - - -release-name={{ .Release.Name }} - - -component=api-gateway - {{- if .Values.apiGateway.enabled }} # Overide values from the old stanza. To be removed in 1.17 (t-eckert 2023-05-19) - {{- if .Values.apiGateway.managedGatewayClass.deployment }} - {{- if .Values.apiGateway.managedGatewayClass.deployment.defaultInstances }} - - -deployment-default-instances={{ .Values.apiGateway.managedGatewayClass.deployment.defaultInstances }} - {{- end}} - {{- if .Values.apiGateway.managedGatewayClass.deployment.maxInstances }} - - -deployment-max-instances={{ .Values.apiGateway.managedGatewayClass.deployment.maxInstances }} - {{- end}} - {{- if .Values.apiGateway.managedGatewayClass.deployment.minInstances }} - - -deployment-min-instances={{ .Values.apiGateway.managedGatewayClass.deployment.minInstances }} - {{- end}} - {{- end}} - {{- if .Values.apiGateway.managedGatewayClass.nodeSelector }} - - -node-selector={{ .Values.apiGateway.managedGatewayClass.nodeSelector }} - {{- end }} - {{- if .Values.apiGateway.managedGatewayClass.tolerations }} - - -tolerations={{ .Values.apiGateway.managedGatewayClass.tolerations }} - {{- end }} - {{- if .Values.apiGateway.managedGatewayClass.copyAnnotations.service }} - - -service-annotations={{ .Values.apiGateway.managedGatewayClass.copyAnnotations.service.annotations }} - {{- end }} - - -service-type={{ .Values.apiGateway.managedGatewayClass.serviceType }} - {{- else }} # the new stanza - {{- if .Values.connectInject.apiGateway.managedGatewayClass.deployment }} - {{- if .Values.connectInject.apiGateway.managedGatewayClass.deployment.defaultInstances }} - - -deployment-default-instances={{ .Values.connectInject.apiGateway.managedGatewayClass.deployment.defaultInstances }} - {{- end}} - {{- if .Values.connectInject.apiGateway.managedGatewayClass.deployment.maxInstances }} - - -deployment-max-instances={{ .Values.connectInject.apiGateway.managedGatewayClass.deployment.maxInstances }} - {{- end}} - {{- if .Values.connectInject.apiGateway.managedGatewayClass.deployment.minInstances }} - - -deployment-min-instances={{ .Values.connectInject.apiGateway.managedGatewayClass.deployment.minInstances }} - {{- end}} - {{- end}} - {{- if .Values.connectInject.apiGateway.managedGatewayClass.nodeSelector }} - - -node-selector - - {{- toYaml .Values.connectInject.apiGateway.managedGatewayClass.nodeSelector | nindent 14 -}} - {{- end }} - {{- if .Values.connectInject.apiGateway.managedGatewayClass.tolerations }} - - -tolerations={{ .Values.connectInject.apiGateway.managedGatewayClass.tolerations }} - {{- end }} - {{- if .Values.connectInject.apiGateway.managedGatewayClass.copyAnnotations.service }} - - -service-annotations - - {{- toYaml .Values.connectInject.apiGateway.managedGatewayClass.copyAnnotations.service.annotations | nindent 14 -}} - {{- end }} - - -service-type={{ .Values.connectInject.apiGateway.managedGatewayClass.serviceType }} - {{- if .Values.global.openshift.enabled }} - - -openshift-scc-name={{ .Values.connectInject.apiGateway.managedGatewayClass.openshiftSCCName }} - {{- end }} - - -map-privileged-container-ports={{ .Values.connectInject.apiGateway.managedGatewayClass.mapPrivilegedContainerPorts }} - {{- end}} - resources: - requests: - memory: "50Mi" - cpu: "50m" - limits: - memory: "50Mi" - cpu: "50m" - {{- if .Values.global.acls.tolerations }} - tolerations: - {{ tpl .Values.global.acls.tolerations . | indent 8 | trim }} - {{- end }} - {{- if .Values.global.acls.nodeSelector }} - nodeSelector: - {{ tpl .Values.global.acls.nodeSelector . | indent 8 | trim }} - {{- end }} -{{- end }} diff --git a/charts/consul/templates/gateway-resources-podsecuritypolicy.yaml b/charts/consul/templates/gateway-resources-podsecuritypolicy.yaml deleted file mode 100644 index da5299194c..0000000000 --- a/charts/consul/templates/gateway-resources-podsecuritypolicy.yaml +++ /dev/null @@ -1,32 +0,0 @@ -{{- if (and .Values.global.enablePodSecurityPolicies .Values.connectInject.enabled)}} -apiVersion: policy/v1beta1 -kind: PodSecurityPolicy -metadata: - name: {{ template "consul.fullname" . }}-gateway-resources - namespace: {{ .Release.Namespace }} - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: gateway-resources -spec: - privileged: false - allowPrivilegeEscalation: false - # This is redundant with non-root + disallow privilege escalation, - # but we can provide it for defense in depth. - requiredDropCapabilities: - - ALL - hostNetwork: false - hostIPC: false - hostPID: false - runAsUser: - rule: 'RunAsAny' - seLinux: - rule: 'RunAsAny' - supplementalGroups: - rule: 'RunAsAny' - fsGroup: - rule: 'RunAsAny' - readOnlyRootFilesystem: false -{{- end }} diff --git a/charts/consul/templates/gateway-resources-serviceaccount.yaml b/charts/consul/templates/gateway-resources-serviceaccount.yaml deleted file mode 100644 index 4611dc38e1..0000000000 --- a/charts/consul/templates/gateway-resources-serviceaccount.yaml +++ /dev/null @@ -1,13 +0,0 @@ -{{- if .Values.connectInject.enabled }} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ template "consul.fullname" . }}-gateway-resources - namespace: {{ .Release.Namespace }} - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: gateway-resources -{{- end }} diff --git a/charts/consul/templates/server-acl-init-job.yaml b/charts/consul/templates/server-acl-init-job.yaml index e8a06cf7aa..282aa7258f 100644 --- a/charts/consul/templates/server-acl-init-job.yaml +++ b/charts/consul/templates/server-acl-init-job.yaml @@ -50,23 +50,14 @@ spec: {{- tpl .Values.global.acls.annotations . | nindent 8 }} {{- end }} {{- if .Values.global.secretsBackend.vault.enabled }} - - {{- /* Run the Vault agent as both an init container and sidecar. - The Vault agent sidecar is needed when server-acl-init bootstraps ACLs - and writes the bootstrap token back to Vault. - * agent-pre-populate: true - Run the Vault agent init container. - * agent-pre-populate-only: false - Also, run the Vault agent sidecar. - * agent-cache-enable: true - Enable the Agent cache listener. - * agent-cache-listener-port: 8200 - (optional) Listen on 127.0.0.1:8200. - * agent-enable-quit: true - Enable a "quit" endpoint. server-acl-init - tells the Vault agent to stop (without this the Job will not complete). - */}} - "vault.hashicorp.com/agent-pre-populate": "true" - "vault.hashicorp.com/agent-pre-populate-only": "false" - "vault.hashicorp.com/agent-cache-enable": "true" - "vault.hashicorp.com/agent-cache-listener-port": "8200" - "vault.hashicorp.com/agent-enable-quit": "true" + "vault.hashicorp.com/agent-pre-populate-only": "true" "vault.hashicorp.com/agent-inject": "true" + {{- if .Values.global.acls.bootstrapToken.secretName }} + {{- with .Values.global.acls.bootstrapToken }} + "vault.hashicorp.com/agent-inject-secret-bootstrap-token": "{{ .secretName }}" + "vault.hashicorp.com/agent-inject-template-bootstrap-token": {{ template "consul.vaultSecretTemplate" . }} + {{- end }} + {{- end }} {{- if .Values.global.acls.partitionToken.secretName }} {{- with .Values.global.acls.partitionToken }} "vault.hashicorp.com/agent-inject-secret-partition-token": "{{ .secretName }}" @@ -117,7 +108,14 @@ spec: path: tls.crt {{- end }} {{- end }} - {{- if and .Values.global.acls.replicationToken.secretName (not .Values.global.secretsBackend.vault.enabled) }} + {{- if (and .Values.global.acls.bootstrapToken.secretName (not .Values.global.secretsBackend.vault.enabled)) }} + - name: bootstrap-token + secret: + secretName: {{ .Values.global.acls.bootstrapToken.secretName }} + items: + - key: {{ .Values.global.acls.bootstrapToken.secretKey }} + path: bootstrap-token + {{- else if and .Values.global.acls.replicationToken.secretName (not .Values.global.secretsBackend.vault.enabled) }} - name: acl-replication-token secret: secretName: {{ .Values.global.acls.replicationToken.secretName }} @@ -141,13 +139,6 @@ spec: valueFrom: fieldRef: fieldPath: metadata.name - # Extract the Vault namespace from the Vault agent annotations. - {{- if .Values.global.secretsBackend.vault.enabled }} - {{- if .Values.global.secretsBackend.vault.agentAnnotations }} - - name: VAULT_NAMESPACE - value: {{ get (tpl .Values.global.secretsBackend.vault.agentAnnotations . | fromYaml) "vault.hashicorp.com/namespace" }} - {{- end }} - {{- end }} {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 8 }} {{- if (or .Values.global.tls.enabled .Values.global.acls.replicationToken.secretName .Values.global.acls.bootstrapToken.secretName) }} volumeMounts: @@ -158,7 +149,11 @@ spec: readOnly: true {{- end }} {{- end }} - {{- if and .Values.global.acls.replicationToken.secretName (not .Values.global.secretsBackend.vault.enabled) }} + {{- if (and .Values.global.acls.bootstrapToken.secretName (not .Values.global.secretsBackend.vault.enabled)) }} + - name: bootstrap-token + mountPath: /consul/acl/tokens + readOnly: true + {{- else if and .Values.global.acls.replicationToken.secretName (not .Values.global.secretsBackend.vault.enabled) }} - name: acl-replication-token mountPath: /consul/acl/tokens readOnly: true @@ -176,15 +171,13 @@ spec: -resource-prefix=${CONSUL_FULLNAME} \ -k8s-namespace={{ .Release.Namespace }} \ -set-server-tokens={{ $serverEnabled }} \ + + {{- if .Values.global.acls.bootstrapToken.secretName }} {{- if .Values.global.secretsBackend.vault.enabled }} - -secrets-backend=vault \ + -bootstrap-token-file=/vault/secrets/bootstrap-token \ {{- else }} - -secrets-backend=kubernetes \ + -bootstrap-token-file=/consul/acl/tokens/bootstrap-token \ {{- end }} - - {{- if .Values.global.acls.bootstrapToken.secretName }} - -bootstrap-token-secret-name={{ .Values.global.acls.bootstrapToken.secretName }} \ - -bootstrap-token-secret-key={{ .Values.global.acls.bootstrapToken.secretKey }} \ {{- end }} {{- if .Values.syncCatalog.enabled }} diff --git a/charts/consul/templates/server-clusterrole.yaml b/charts/consul/templates/server-clusterrole.yaml deleted file mode 100644 index c22f562264..0000000000 --- a/charts/consul/templates/server-clusterrole.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: {{ template "consul.fullname" . }}-server - namespace: {{ .Release.Namespace }} - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: server -rules: -- apiGroups: [""] - resources: ["nodes"] - verbs: - - get diff --git a/charts/consul/templates/server-clusterrolebinding.yaml b/charts/consul/templates/server-clusterrolebinding.yaml deleted file mode 100644 index 854fda870e..0000000000 --- a/charts/consul/templates/server-clusterrolebinding.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: {{ template "consul.fullname" . }}-server - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: server -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ template "consul.fullname" . }}-server -subjects: -- kind: ServiceAccount - name: {{ template "consul.fullname" . }}-server - namespace: {{ .Release.Namespace }} diff --git a/charts/consul/templates/server-config-configmap.yaml b/charts/consul/templates/server-config-configmap.yaml index 6c102f0ae3..f4f787cb24 100644 --- a/charts/consul/templates/server-config-configmap.yaml +++ b/charts/consul/templates/server-config-configmap.yaml @@ -1,5 +1,4 @@ {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} -{{- if (not (or (eq .Values.server.limits.requestLimits.mode "disabled") (eq .Values.server.limits.requestLimits.mode "permissive") (eq .Values.server.limits.requestLimits.mode "enforce"))) }}{{fail "server.limits.requestLimits.mode must be one of the following values: disabled, permissive, and enforce." }}{{ end -}} {{- if and .Values.server.auditLogs.enabled (not .Values.global.acls.manageSystemACLs) }}{{fail "ACLs must be enabled inorder to configure audit logs"}}{{ end -}} # StatefulSet to run the actual Consul server cluster. apiVersion: v1 @@ -31,13 +30,6 @@ data: "log_level": "{{ .Values.server.logLevel | upper }}", {{- end }} "domain": "{{ .Values.global.domain }}", - "limits": { - "request_limits": { - "mode": "{{ .Values.server.limits.requestLimits.mode }}", - "read_rate": {{ .Values.server.limits.requestLimits.readRate }}, - "write_rate": {{ .Values.server.limits.requestLimits.writeRate }} - } - }, "ports": { {{- if not .Values.global.tls.enabled }} "grpc": 8502, diff --git a/charts/consul/templates/server-statefulset.yaml b/charts/consul/templates/server-statefulset.yaml index 2ad04f0755..8987bd68c0 100644 --- a/charts/consul/templates/server-statefulset.yaml +++ b/charts/consul/templates/server-statefulset.yaml @@ -198,11 +198,6 @@ spec: medium: "Memory" {{- end }} {{- end }} - {{- if .Values.global.trustedCAs }} - - name: trusted-cas - emptyDir: - medium: "Memory" - {{- end }} {{- range .Values.server.extraVolumes }} - name: userconfig-{{ .name }} {{ .type }}: @@ -222,27 +217,9 @@ spec: {{- if .Values.server.priorityClassName }} priorityClassName: {{ .Values.server.priorityClassName | quote }} {{- end }} - initContainers: - - name: locality-init - image: {{ .Values.global.imageK8S }} - env: - - name: NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - command: - - "/bin/sh" - - "-ec" - - | - consul-k8s-control-plane fetch-server-region -node-name "$NODE_NAME" -output-file /consul/extra-config/locality.json - volumeMounts: - - name: extra-config - mountPath: /consul/extra-config - {{- include "consul.restrictedSecurityContext" . | nindent 8 }} containers: - name: consul image: "{{ default .Values.global.image .Values.server.image }}" - imagePullPolicy: {{ .Values.global.imagePullPolicy }} env: - name: ADVERTISE_IP valueFrom: @@ -314,9 +291,9 @@ spec: {{- end }} {{- if .Values.global.cloud.enabled}} # These are mounted as secrets so that the consul server agent can use them. - # - the hcp-go-sdk in consul agent will already look for HCP_CLIENT_ID, HCP_CLIENT_SECRET, HCP_AUTH_URL, + # - the hcp-go-sdk in consul agent will already look for HCP_CLIENT_ID, HCP_CLIENT_SECRET, HCP_AUTH_URL, # HCP_SCADA_ADDRESS, and HCP_API_HOST. so nothing more needs to be done. - # - HCP_RESOURCE_ID is created for use in the + # - HCP_RESOURCE_ID is created for use in the # `-hcl="cloud { resource_id = \"${HCP_RESOURCE_ID}\" }"` logic in the command below. {{- if .Values.global.cloud.clientId.secretName }} - name: HCP_CLIENT_ID @@ -351,7 +328,7 @@ spec: valueFrom: secretKeyRef: name: {{ .Values.global.cloud.apiHost.secretName }} - key: {{ .Values.global.cloud.apiHost.secretKey }} + key: {{ .Values.global.cloud.apiHost.secretKey }} {{- end}} {{- if .Values.global.cloud.scadaAddress.secretName }} - name: HCP_SCADA_ADDRESS @@ -359,25 +336,13 @@ spec: secretKeyRef: name: {{ .Values.global.cloud.scadaAddress.secretName }} key: {{ .Values.global.cloud.scadaAddress.secretKey }} - {{- end}} - {{- end }} - {{- if .Values.global.trustedCAs }} - - name: SSL_CERT_DIR - value: "/etc/ssl/certs:/trusted-cas" + {{- end}} {{- end }} {{- include "consul.extraEnvironmentVars" .Values.server | nindent 12 }} command: - "/bin/sh" - "-ec" - | - {{- if .Values.global.trustedCAs }} - {{- range $i, $cert := .Values.global.trustedCAs }} - cat < /trusted-cas/custom-ca-{{$i}}.pem - {{- $cert | nindent 14 }} - EOF - {{- end }} - {{- end }} - {{- if and .Values.global.secretsBackend.vault.enabled .Values.global.gossipEncryption.secretName }} GOSSIP_KEY=`cat /vault/secrets/gossip.txt` {{- end }} @@ -410,8 +375,7 @@ spec: -config-dir=/consul/userconfig/{{ .name }} \ {{- end }} {{- end }} - -config-file=/consul/extra-config/extra-from-values.json \ - -config-file=/consul/extra-config/locality.json + -config-file=/consul/extra-config/extra-from-values.json {{- if and .Values.global.cloud.enabled .Values.global.cloud.resourceId.secretName }} -hcl="cloud { resource_id = \"${HCP_RESOURCE_ID}\" }" {{- end }} @@ -445,11 +409,6 @@ spec: mountPath: /consul/vault-ca/ readOnly: true {{- end }} - {{- if .Values.global.trustedCAs }} - - name: trusted-cas - mountPath: /trusted-cas - readOnly: false - {{- end }} ports: {{- if (or (not .Values.global.tls.enabled) (not .Values.global.tls.httpsOnly)) }} - name: http diff --git a/charts/consul/templates/telemetry-collector-configmap.yaml b/charts/consul/templates/telemetry-collector-configmap.yaml deleted file mode 100644 index 0bf5b8753c..0000000000 --- a/charts/consul/templates/telemetry-collector-configmap.yaml +++ /dev/null @@ -1,18 +0,0 @@ -{{- if (and .Values.telemetryCollector.enabled .Values.telemetryCollector.customExporterConfig) }} -# Immutable ConfigMap which saves the partition name. Attempting to update this configmap -# with a new Admin Partition name will cause the helm upgrade to fail -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ template "consul.fullname" . }}-telemetry-collector - namespace: {{ .Release.Namespace }} - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: consul-telemetry-collector -data: - config.json: |- - {{ tpl .Values.telemetryCollector.customExporterConfig . | trimAll "\"" | indent 4 }} -{{- end }} diff --git a/charts/consul/templates/telemetry-collector-deployment.yaml b/charts/consul/templates/telemetry-collector-deployment.yaml deleted file mode 100644 index 4b5bb6df7b..0000000000 --- a/charts/consul/templates/telemetry-collector-deployment.yaml +++ /dev/null @@ -1,391 +0,0 @@ -{{- if .Values.telemetryCollector.enabled }} -{{- if not .Values.telemetryCollector.image}}{{ fail "telemetryCollector.image must be set to enable consul-telemetry-collector" }}{{ end }} -{{- if not .Values.connectInject.enabled }}{{ fail "connectInject.enabled must be true" }}{{ end -}} -{{- if and .Values.global.adminPartitions.enabled (not .Values.global.enableConsulNamespaces) }}{{ fail "global.enableConsulNamespaces must be true if global.adminPartitions.enabled=true" }}{{ end }} -{{ template "consul.validateCloudSecretKeys" . }} -{{ template "consul.validateTelemetryCollectorCloud" . }} -{{ template "consul.validateTelemetryCollectorCloudSecretKeys" . }} -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ template "consul.fullname" . }}-telemetry-collector - namespace: {{ .Release.Namespace }} - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: consul-telemetry-collector - {{- if .Values.global.extraLabels }} - {{- toYaml .Values.global.extraLabels | nindent 4 }} - {{- end }} -spec: - replicas: {{ .Values.telemetryCollector.replicas }} - selector: - matchLabels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - release: {{ .Release.Name }} - component: consul-telemetry-collector - template: - metadata: - annotations: - "consul.hashicorp.com/connect-inject": "false" - # This annotation tells the endpoints controller that this pod was injected even though it wasn't. The - # endpoints controller would then sync the endpoint into Consul - "consul.hashicorp.com/connect-inject-status": "injected" - # We aren't using tproxy and we don't have an original pod. This would be simpler if we made a path similar - # to gateways - "consul.hashicorp.com/connect-service-port": "metricsserver" - "consul.hashicorp.com/transparent-proxy": "false" - "consul.hashicorp.com/transparent-proxy-overwrite-probes": "false" - "consul.hashicorp.com/connect-k8s-version": {{ $.Chart.Version }} - {{- if .Values.telemetryCollector.customExporterConfig }} - # configmap checksum - "consul.hashicorp.com/config-checksum": {{ include (print $.Template.BasePath "/telemetry-collector-configmap.yaml") . | sha256sum }} - {{- end }} - # vault annotations - {{- if (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) }} - "vault.hashicorp.com/agent-init-first": "true" - "vault.hashicorp.com/agent-inject": "true" - "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.consulCARole }} - "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ .Values.global.tls.caCert.secretName }} - "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" . }} - {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} - "vault.hashicorp.com/agent-extra-secret": "{{ .Values.global.secretsBackend.vault.ca.secretName }}" - "vault.hashicorp.com/ca-cert": "/vault/custom/{{ .Values.global.secretsBackend.vault.ca.secretKey }}" - {{- end }} - {{- if .Values.global.secretsBackend.vault.agentAnnotations }} - {{ tpl .Values.global.secretsBackend.vault.agentAnnotations . | nindent 8 | trim }} - {{- end }} - {{- end }} - - labels: - consul.hashicorp.com/connect-inject-managed-by: consul-k8s-endpoints-controller - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - release: {{ .Release.Name }} - component: consul-telemetry-collector - {{- if .Values.global.extraLabels }} - {{- toYaml .Values.global.extraLabels | nindent 8 }} - {{- end }} - spec: - # This needs to explicitly be consul-telemetry-collector because we look this up from each service consul-dataplane - # to forward metrics to it. - serviceAccountName: consul-telemetry-collector - initContainers: - # We're manually managing this init container instead of using the connect injector so that we don't run into - # any race conditions on the connect-injector deployment or upgrade - - name: consul-connect-init - env: - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - {{- if .Values.global.acls.manageSystemACLs }} - - name: CONSUL_LOGIN_AUTH_METHOD - value: {{ template "consul.fullname" . }}-k8s-auth-method - - name: CONSUL_LOGIN_META - value: "component=consul-telemetry-collector,pod=$(NAMESPACE)/$(POD_NAME)" - {{- end }} - - name: CONSUL_NODE_NAME - value: $(NODE_NAME)-virtual - {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 10 }} - {{- if .Values.global.enableConsulNamespaces }} - - name: CONSUL_NAMESPACE - value: {{ .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} - {{- if .Values.syncCatalog.consulNamespaces.mirroringK8S }} - - name: CONSUL_LOGIN_NAMESPACE - value: "default" - {{- else }} - - name: CONSUL_LOGIN_NAMESPACE - value: {{ .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} - {{- end }} - {{- end }} - command: - - /bin/sh - - -ec - - |- - consul-k8s-control-plane connect-init -pod-name=${POD_NAME} -pod-namespace=${POD_NAMESPACE} \ - -log-level={{ default .Values.global.logLevel .Values.telemetryCollector.logLevel }} \ - -log-json={{ .Values.global.logJSON }} \ - -service-account-name="consul-telemetry-collector" \ - -service-name="" \ - -proxy-id-file="/consul/connect-inject/proxyid" - - image: {{ .Values.global.imageK8S }} - imagePullPolicy: IfNotPresent - {{- if .Values.telemetryCollector.initContainer.resources }} - resources: - {{- toYaml .Values.telemetryCollector.initContainer.resources | nindent 12 }} - {{- else }} - resources: - limits: - cpu: 50m - memory: 150Mi - requests: - cpu: 50m - memory: 25Mi - {{- end }} - terminationMessagePath: /dev/termination-log - terminationMessagePolicy: File - volumeMounts: - - mountPath: /consul/connect-inject - name: consul-connect-inject-data - {{- if .Values.global.tls.enabled }} - {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} - - name: consul-ca-cert - mountPath: /consul/tls/ca - readOnly: true - {{- end }} - {{- end }} - containers: - - name: consul-telemetry-collector - image: {{ .Values.telemetryCollector.image }} - imagePullPolicy: {{ .Values.global.imagePullPolicy }} - ports: - - containerPort: 9090 - name: metrics - protocol: TCP - - containerPort: 9356 - name: metricsserver - protocol: TCP - env: - # These are mounted as secrets so that the telemetry-collector can use them when cloud is enabled. - # - the hcp-go-sdk in consul agent will already look for HCP_CLIENT_ID, HCP_CLIENT_SECRET, HCP_AUTH_URL, - # HCP_SCADA_ADDRESS, and HCP_API_HOST. so nothing more needs to be done. - # - HCP_RESOURCE_ID is created for use in the global cloud section but we will share it here - {{- if .Values.telemetryCollector.cloud.clientId.secretName }} - - name: HCP_CLIENT_ID - valueFrom: - secretKeyRef: - name: {{ .Values.telemetryCollector.cloud.clientId.secretName }} - key: {{ .Values.telemetryCollector.cloud.clientId.secretKey }} - {{- end }} - {{- if .Values.telemetryCollector.cloud.clientSecret.secretName }} - - name: HCP_CLIENT_SECRET - valueFrom: - secretKeyRef: - name: {{ .Values.telemetryCollector.cloud.clientSecret.secretName }} - key: {{ .Values.telemetryCollector.cloud.clientSecret.secretKey }} - {{- end}} - {{- if .Values.global.cloud.resourceId.secretName }} - - name: HCP_RESOURCE_ID - valueFrom: - secretKeyRef: - name: {{ .Values.global.cloud.resourceId.secretName }} - key: {{ .Values.global.cloud.resourceId.secretKey }} - {{- end }} - {{- if .Values.global.cloud.authUrl.secretName }} - - name: HCP_AUTH_URL - valueFrom: - secretKeyRef: - name: {{ .Values.global.cloud.authUrl.secretName }} - key: {{ .Values.global.cloud.authUrl.secretKey }} - {{- end}} - {{- if .Values.global.cloud.apiHost.secretName }} - - name: HCP_API_HOST - valueFrom: - secretKeyRef: - name: {{ .Values.global.cloud.apiHost.secretName }} - key: {{ .Values.global.cloud.apiHost.secretKey }} - {{- end}} - {{- if .Values.global.cloud.scadaAddress.secretName }} - - name: HCP_SCADA_ADDRESS - valueFrom: - secretKeyRef: - name: {{ .Values.global.cloud.scadaAddress.secretName }} - key: {{ .Values.global.cloud.scadaAddress.secretKey }} - {{- end}} - {{- if .Values.global.trustedCAs }} - - name: SSL_CERT_DIR - value: "/etc/ssl/certs:/trusted-cas" - {{- end }} - {{- include "consul.extraEnvironmentVars" .Values.telemetryCollector | nindent 12 }} - command: - - "/bin/sh" - - "-ec" - - | - {{- if .Values.global.trustedCAs }} - {{- range $i, $cert := .Values.global.trustedCAs }} - cat < /trusted-cas/custom-ca-{{$i}}.pem - {{- $cert | nindent 10 }} - EOF - {{- end }} - {{- end }} - - consul-telemetry-collector agent \ - {{- if .Values.telemetryCollector.customExporterConfig }} - -config-file-path /consul/config/config.json \ - {{ end }} - volumeMounts: - {{- if .Values.telemetryCollector.customExporterConfig }} - - name: config - mountPath: /consul/config - {{- end }} - {{- if .Values.global.trustedCAs }} - - name: trusted-cas - mountPath: /trusted-cas - readOnly: false - {{- end }} - resources: - {{- if .Values.telemetryCollector.resources }} - {{- toYaml .Values.telemetryCollector.resources | nindent 12 }} - {{- end }} - # consul-dataplane container - - name: consul-dataplane - image: "{{ .Values.global.imageConsulDataplane }}" - imagePullPolicy: IfNotPresent - command: - - consul-dataplane - args: - # addresses - {{- if .Values.externalServers.enabled }} - - -addresses={{ .Values.externalServers.hosts | first }} - {{- else }} - - -addresses={{ template "consul.fullname" . }}-server.{{ .Release.Namespace }}.svc - {{- end }} - # grpc - {{- if .Values.externalServers.enabled }} - - -grpc-port={{ .Values.externalServers.grpcPort }} - {{- else }} - - -grpc-port=8502 - {{- end }} - - -proxy-service-id-path=/consul/connect-inject/proxyid - # tls - {{- if .Values.global.tls.enabled }} - {{- if (not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots)) }} - {{- if .Values.global.secretsBackend.vault.enabled }} - - -ca-certs=/vault/secrets/serverca.crt - {{- else }} - - -ca-certs=/consul/tls/ca/tls.crt - {{- end }} - {{- end }} - {{- if and .Values.externalServers.enabled .Values.externalServers.tlsServerName }} - - -tls-server-name={{.Values.externalServers.tlsServerName }} - {{- else if .Values.global.cloud.enabled }} - - -tls-server-name=server.{{ .Values.global.datacenter}}.{{ .Values.global.domain}} - {{- end }} - {{- else }} - - -tls-disabled - {{- end }} - # credentials - {{- if .Values.global.acls.manageSystemACLs }} - - -credential-type=login - - -login-bearer-token-path=/var/run/secrets/kubernetes.io/serviceaccount/token - - -login-auth-method={{ template "consul.fullname" . }}-k8s-auth-method - {{- if .Values.global.enableConsulNamespaces }} - {{- if .Values.syncCatalog.consulNamespaces.mirroringK8S }} - - -login-namespace="default" - {{- else }} - - -login-namespace={{ .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} - {{- end }} - {{- end }} - {{- if .Values.global.adminPartitions.enabled }} - - foo - - -login-partition={{ .Values.global.adminPartitions.name }} - {{- end }} - {{- end }} - {{- if .Values.global.enableConsulNamespaces }} - - -service-namespace={{ .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} - {{- end }} - {{- if .Values.global.adminPartitions.enabled }} - - -service-partition={{ .Values.global.adminPartitions.name }} - {{- end }} - {{- if .Values.global.metrics.enabled }} - - -telemetry-prom-scrape-path=/metrics - {{- end }} - - -log-level={{ default .Values.global.logLevel .Values.telemetryCollector.logLevel }} - - -log-json={{ .Values.global.logJSON }} - - -envoy-concurrency=2 - {{- if and .Values.externalServers.enabled .Values.externalServers.skipServerWatch }} - - -server-watch-disabled=true - {{- end }} - env: - - name: NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - - name: DP_CREDENTIAL_LOGIN_META1 - value: pod=$(NAMESPACE)/$(POD_NAME) - - name: DP_CREDENTIAL_LOGIN_META2 - value: component=consul-telemetry-collector - - name: DP_SERVICE_NODE_NAME - value: $(NODE_NAME)-virtual - - name: TMPDIR - value: /consul/connect-inject - readinessProbe: - failureThreshold: 3 - initialDelaySeconds: 1 - periodSeconds: 10 - successThreshold: 1 - tcpSocket: - port: 20000 - timeoutSeconds: 1 - securityContext: - readOnlyRootFilesystem: true - runAsGroup: 5995 - runAsNonRoot: true - runAsUser: 5995 - # dataplane volume mounts - volumeMounts: - - mountPath: /consul/connect-inject - name: consul-connect-inject-data - {{- if .Values.global.tls.enabled }} - {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} - - name: consul-ca-cert - mountPath: /consul/tls/ca - readOnly: true - {{- end }} - {{- end }} - - {{- if .Values.telemetryCollector.nodeSelector }} - nodeSelector: - {{ tpl .Values.telemetryCollector.nodeSelector . | indent 8 | trim }} - {{- end }} - {{- if .Values.telemetryCollector.priorityClassName }} - priorityClassName: {{ .Values.telemetryCollector.priorityClassName }} - {{- end }} - volumes: - - emptyDir: - medium: Memory - name: consul-connect-inject-data - {{- if .Values.global.trustedCAs }} - - name: trusted-cas - emptyDir: - medium: "Memory" - {{- end }} - {{- if .Values.global.tls.enabled }} - {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} - - name: consul-ca-cert - secret: - {{- if .Values.global.tls.caCert.secretName }} - secretName: {{ .Values.global.tls.caCert.secretName }} - {{- else }} - secretName: {{ template "consul.fullname" . }}-ca-cert - {{- end }} - items: - - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} - path: tls.crt - {{- end }} - {{- end }} - - name: config - configMap: - name: {{ template "consul.fullname" . }}-telemetry-collector -{{- end }} diff --git a/charts/consul/templates/telemetry-collector-podsecuritypolicy.yaml b/charts/consul/templates/telemetry-collector-podsecuritypolicy.yaml deleted file mode 100644 index 286a92d0bd..0000000000 --- a/charts/consul/templates/telemetry-collector-podsecuritypolicy.yaml +++ /dev/null @@ -1,40 +0,0 @@ -{{- if and .Values.global.enablePodSecurityPolicies .Values.telemetryCollector.enabled }} -apiVersion: policy/v1beta1 -kind: PodSecurityPolicy -metadata: - name: {{ template "consul.fullname" . }}-telemetry-collector - namespace: {{ .Release.Namespace }} - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: telemetry-collector -spec: - privileged: false - # Required to prevent escalations to root. - allowPrivilegeEscalation: false - # This is redundant with non-root + disallow privilege escalation, - # but we can provide it for defense in depth. - requiredDropCapabilities: - - ALL - # Allow core volume types. - volumes: - - 'configMap' - - 'emptyDir' - - 'projected' - - 'secret' - - 'downwardAPI' - hostNetwork: false - hostIPC: false - hostPID: false - runAsUser: - rule: 'RunAsAny' - seLinux: - rule: 'RunAsAny' - supplementalGroups: - rule: 'RunAsAny' - fsGroup: - rule: 'RunAsAny' - readOnlyRootFilesystem: false -{{- end }} diff --git a/charts/consul/templates/telemetry-collector-role.yaml b/charts/consul/templates/telemetry-collector-role.yaml deleted file mode 100644 index f89373252d..0000000000 --- a/charts/consul/templates/telemetry-collector-role.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{- if and .Values.global.enablePodSecurityPolicies .Values.telemetryCollector.enabled }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: {{ template "consul.fullname" . }}-telemetry-collector - namespace: {{ .Release.Namespace }} - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: consul-telemetry-collector -rules: - - apiGroups: ["policy"] - resources: ["podsecuritypolicies"] - resourceNames: - - {{ template "consul.fullname" . }}-telemetry-collector - verbs: - - use -{{- end }} - diff --git a/charts/consul/templates/telemetry-collector-rolebinding.yaml b/charts/consul/templates/telemetry-collector-rolebinding.yaml deleted file mode 100644 index 1f9a896997..0000000000 --- a/charts/consul/templates/telemetry-collector-rolebinding.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{- if and .Values.global.enablePodSecurityPolicies .Values.telemetryCollector.enabled }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: {{ template "consul.fullname" . }}-telemetry-collector - namespace: {{ .Release.Namespace }} - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - component: consul-telemetry-collector -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: {{ template "consul.fullname" . }}-telemetry-collector -subjects: - - kind: ServiceAccount - name: {{ template "consul.fullname" . }}-telemetry-collector -{{- end }} - diff --git a/charts/consul/templates/telemetry-collector-service.yaml b/charts/consul/templates/telemetry-collector-service.yaml deleted file mode 100644 index 266c80b4bf..0000000000 --- a/charts/consul/templates/telemetry-collector-service.yaml +++ /dev/null @@ -1,24 +0,0 @@ -{{- if .Values.telemetryCollector.enabled }} -apiVersion: v1 -kind: Service -metadata: - name: consul-telemetry-collector - namespace: {{ .Release.Namespace }} - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - release: {{ .Release.Name }} - component: consul-telemetry-collector - {{ if .Values.telemetryCollector.service.annotations }} - annotations: - {{ tpl .Values.telemetryCollector.service.annotations . | nindent 4 | trim }} - {{- end }} -spec: - type: ClusterIP - ports: - - port: 9356 - targetPort: 9356 - selector: - app: consul - component: consul-telemetry-collector -{{- end }} \ No newline at end of file diff --git a/charts/consul/templates/telemetry-collector-serviceaccount.yaml b/charts/consul/templates/telemetry-collector-serviceaccount.yaml deleted file mode 100644 index fca58eede9..0000000000 --- a/charts/consul/templates/telemetry-collector-serviceaccount.yaml +++ /dev/null @@ -1,23 +0,0 @@ -{{- if .Values.telemetryCollector.enabled }} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: consul-telemetry-collector - namespace: {{ .Release.Namespace }} - labels: - app: {{ template "consul.name" . }} - chart: {{ template "consul.chart" . }} - release: {{ .Release.Name }} - component: consul-telemetry-collector - {{- if .Values.telemetryCollector.serviceAccount.annotations }} - annotations: - {{ tpl .Values.telemetryCollector.serviceAccount.annotations . | nindent 4 | trim }} - {{- end }} -automountServiceAccountToken: true -{{- with .Values.global.imagePullSecrets }} -imagePullSecrets: -{{- range . }} - - name: {{ .name }} -{{- end }} -{{- end }} -{{- end }} \ No newline at end of file diff --git a/charts/consul/templates/webhook-cert-manager-clusterrole.yaml b/charts/consul/templates/webhook-cert-manager-clusterrole.yaml index e13e2dc741..c2a2422d02 100644 --- a/charts/consul/templates/webhook-cert-manager-clusterrole.yaml +++ b/charts/consul/templates/webhook-cert-manager-clusterrole.yaml @@ -1,4 +1,4 @@ -{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} +{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName .Values.global.secretsBackend.vault.controllerRole .Values.global.secretsBackend.vault.controller.tlsCert.secretName .Values.global.secretsBackend.vault.controller.caCert.secretName) -}} {{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/charts/consul/templates/webhook-cert-manager-clusterrolebinding.yaml b/charts/consul/templates/webhook-cert-manager-clusterrolebinding.yaml index 472ef4ee1d..ca2bb84bda 100644 --- a/charts/consul/templates/webhook-cert-manager-clusterrolebinding.yaml +++ b/charts/consul/templates/webhook-cert-manager-clusterrolebinding.yaml @@ -1,4 +1,4 @@ -{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} +{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName .Values.global.secretsBackend.vault.controllerRole .Values.global.secretsBackend.vault.controller.tlsCert.secretName .Values.global.secretsBackend.vault.controller.caCert.secretName) -}} {{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/charts/consul/templates/webhook-cert-manager-configmap.yaml b/charts/consul/templates/webhook-cert-manager-configmap.yaml index 293dd32d9f..914d2b87dd 100644 --- a/charts/consul/templates/webhook-cert-manager-configmap.yaml +++ b/charts/consul/templates/webhook-cert-manager-configmap.yaml @@ -1,4 +1,4 @@ -{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} +{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName .Values.global.secretsBackend.vault.controllerRole .Values.global.secretsBackend.vault.controller.tlsCert.secretName .Values.global.secretsBackend.vault.controller.caCert.secretName) -}} {{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} apiVersion: v1 kind: ConfigMap diff --git a/charts/consul/templates/webhook-cert-manager-deployment.yaml b/charts/consul/templates/webhook-cert-manager-deployment.yaml index 7ba25b330c..ffa68216f3 100644 --- a/charts/consul/templates/webhook-cert-manager-deployment.yaml +++ b/charts/consul/templates/webhook-cert-manager-deployment.yaml @@ -1,4 +1,4 @@ -{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} +{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName .Values.global.secretsBackend.vault.controllerRole .Values.global.secretsBackend.vault.controller.tlsCert.secretName .Values.global.secretsBackend.vault.controller.caCert.secretName) -}} {{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} apiVersion: apps/v1 kind: Deployment diff --git a/charts/consul/templates/webhook-cert-manager-podsecuritypolicy.yaml b/charts/consul/templates/webhook-cert-manager-podsecuritypolicy.yaml index 4d685edc39..b67dbda510 100644 --- a/charts/consul/templates/webhook-cert-manager-podsecuritypolicy.yaml +++ b/charts/consul/templates/webhook-cert-manager-podsecuritypolicy.yaml @@ -1,4 +1,4 @@ -{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} +{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName .Values.global.secretsBackend.vault.controllerRole .Values.global.secretsBackend.vault.controller.tlsCert.secretName .Values.global.secretsBackend.vault.controller.caCert.secretName) -}} {{- if (and .Values.global.enablePodSecurityPolicies (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled))) }} {{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} apiVersion: policy/v1beta1 diff --git a/charts/consul/templates/webhook-cert-manager-serviceaccount.yaml b/charts/consul/templates/webhook-cert-manager-serviceaccount.yaml index 68c54f3c27..fa4b24ef8b 100644 --- a/charts/consul/templates/webhook-cert-manager-serviceaccount.yaml +++ b/charts/consul/templates/webhook-cert-manager-serviceaccount.yaml @@ -1,4 +1,4 @@ -{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName) -}} +{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName .Values.global.secretsBackend.vault.controllerRole .Values.global.secretsBackend.vault.controller.tlsCert.secretName .Values.global.secretsBackend.vault.controller.caCert.secretName) -}} {{- if (and .Values.connectInject.enabled (not $hasConfiguredWebhookCertsUsingVault)) }} apiVersion: v1 kind: ServiceAccount diff --git a/charts/consul/test/docker/Test.dockerfile b/charts/consul/test/docker/Test.dockerfile index e6a4caa6e0..85f3a607e3 100644 --- a/charts/consul/test/docker/Test.dockerfile +++ b/charts/consul/test/docker/Test.dockerfile @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - # This Dockerfile installs all the dependencies necessary to run the unit and # acceptance tests. This image also contains gcloud so you can run tests # against a GKE cluster easily. diff --git a/charts/consul/test/terraform/eks/main.tf b/charts/consul/test/terraform/eks/main.tf index 3bc8b40451..c404d0bb72 100644 --- a/charts/consul/test/terraform/eks/main.tf +++ b/charts/consul/test/terraform/eks/main.tf @@ -68,7 +68,7 @@ module "eks" { kubeconfig_api_version = "client.authentication.k8s.io/v1beta1" cluster_name = "consul-k8s-${random_id.suffix[count.index].dec}" - cluster_version = "1.26" + cluster_version = "1.25" subnets = module.vpc[count.index].private_subnets enable_irsa = true @@ -124,13 +124,12 @@ resource "aws_iam_role_policy_attachment" "csi" { } resource "aws_eks_addon" "csi-driver" { - count = var.cluster_count - cluster_name = module.eks[count.index].cluster_id - addon_name = "aws-ebs-csi-driver" - addon_version = "v1.15.0-eksbuild.1" - service_account_role_arn = aws_iam_role.csi-driver-role[count.index].arn - resolve_conflicts_on_create = "OVERWRITE" - resolve_conflicts_on_update = "OVERWRITE" + count = var.cluster_count + cluster_name = module.eks[count.index].cluster_id + addon_name = "aws-ebs-csi-driver" + addon_version = "v1.15.0-eksbuild.1" + service_account_role_arn = aws_iam_role.csi-driver-role[count.index].arn + resolve_conflicts = "OVERWRITE" } data "aws_eks_cluster" "cluster" { diff --git a/charts/consul/test/terraform/eks/outputs.tf b/charts/consul/test/terraform/eks/outputs.tf index 1d971bf0b5..986a917f6a 100644 --- a/charts/consul/test/terraform/eks/outputs.tf +++ b/charts/consul/test/terraform/eks/outputs.tf @@ -3,4 +3,4 @@ output "kubeconfigs" { value = [for cl in module.eks : pathexpand(format("~/.kube/%s", cl.cluster_id))] -} +} \ No newline at end of file diff --git a/charts/consul/test/terraform/eks/variables.tf b/charts/consul/test/terraform/eks/variables.tf index 548cddeb33..7536668442 100644 --- a/charts/consul/test/terraform/eks/variables.tf +++ b/charts/consul/test/terraform/eks/variables.tf @@ -27,4 +27,4 @@ variable "tags" { type = map(any) default = {} description = "Tags to attach to the created resources." -} +} \ No newline at end of file diff --git a/charts/consul/test/terraform/gke/main.tf b/charts/consul/test/terraform/gke/main.tf index 34bb07906f..fe5adc5e8d 100644 --- a/charts/consul/test/terraform/gke/main.tf +++ b/charts/consul/test/terraform/gke/main.tf @@ -21,7 +21,7 @@ resource "random_id" "suffix" { data "google_container_engine_versions" "main" { location = var.zone - version_prefix = "1.25.9" + version_prefix = "1.25." } # We assume that the subnets are already created to save time. diff --git a/charts/consul/test/terraform/gke/outputs.tf b/charts/consul/test/terraform/gke/outputs.tf index a0ffac907f..17a2aac4fd 100644 --- a/charts/consul/test/terraform/gke/outputs.tf +++ b/charts/consul/test/terraform/gke/outputs.tf @@ -11,4 +11,4 @@ output "cluster_names" { output "kubeconfigs" { value = [for cl in google_container_cluster.cluster : format("$HOME/.kube/%s", cl.name)] -} +} \ No newline at end of file diff --git a/charts/consul/test/terraform/gke/variables.tf b/charts/consul/test/terraform/gke/variables.tf index f33952850a..ba580a540d 100644 --- a/charts/consul/test/terraform/gke/variables.tf +++ b/charts/consul/test/terraform/gke/variables.tf @@ -42,4 +42,4 @@ variable "subnet" { type = string default = "default" description = "Subnet to create the cluster in. Currently all clusters use the default subnet and we are running out of IPs" -} +} \ No newline at end of file diff --git a/charts/consul/test/terraform/openshift/main.tf b/charts/consul/test/terraform/openshift/main.tf index 3605c37a7b..88da265c17 100644 --- a/charts/consul/test/terraform/openshift/main.tf +++ b/charts/consul/test/terraform/openshift/main.tf @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - provider "azurerm" { features {} } diff --git a/charts/consul/test/terraform/openshift/oc-login.sh b/charts/consul/test/terraform/openshift/oc-login.sh index f463c03af7..e7db48761d 100755 --- a/charts/consul/test/terraform/openshift/oc-login.sh +++ b/charts/consul/test/terraform/openshift/oc-login.sh @@ -1,7 +1,4 @@ #!/usr/bin/env bash -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - resource_group=$1 cluster_name=$2 diff --git a/charts/consul/test/terraform/openshift/outputs.tf b/charts/consul/test/terraform/openshift/outputs.tf index 7a395e33b7..66fc892934 100644 --- a/charts/consul/test/terraform/openshift/outputs.tf +++ b/charts/consul/test/terraform/openshift/outputs.tf @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - output "kubeconfigs" { value = [for rg in azurerm_resource_group.test : format("$HOME/.kube/%s", rg.name)] } \ No newline at end of file diff --git a/charts/consul/test/terraform/openshift/variables.tf b/charts/consul/test/terraform/openshift/variables.tf index 5aa3cc0180..1df518f8ed 100644 --- a/charts/consul/test/terraform/openshift/variables.tf +++ b/charts/consul/test/terraform/openshift/variables.tf @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - variable "location" { default = "westus2" description = "The Azure Region to create all resources in." diff --git a/charts/consul/test/unit/_helpers.bash b/charts/consul/test/unit/_helpers.bash index d57d2a0fbf..1b9d1aa4d2 100644 --- a/charts/consul/test/unit/_helpers.bash +++ b/charts/consul/test/unit/_helpers.bash @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - # chart_dir returns the directory for the chart chart_dir() { echo ${BATS_TEST_DIRNAME}/../.. diff --git a/charts/consul/test/unit/connect-inject-clusterrole.bats b/charts/consul/test/unit/connect-inject-clusterrole.bats index d02b9eacde..b1ad10a80c 100644 --- a/charts/consul/test/unit/connect-inject-clusterrole.bats +++ b/charts/consul/test/unit/connect-inject-clusterrole.bats @@ -77,7 +77,7 @@ load _helpers --set 'client.enabled=true' \ --set 'connectInject.enabled=true' \ . | tee /dev/stderr | - yq -r '.rules[4]' | tee /dev/stderr) + yq -r '.rules[3]' | tee /dev/stderr) local actual=$(echo $object | yq -r '.resources[| index("pods")' | tee /dev/stderr) [ "${actual}" != null ] @@ -106,7 +106,7 @@ load _helpers --set 'client.enabled=true' \ --set 'connectInject.enabled=true' \ . | tee /dev/stderr | - yq -r '.rules[5]' | tee /dev/stderr) + yq -r '.rules[4]' | tee /dev/stderr) local actual=$(echo $object | yq -r '.resources[| index("leases")' | tee /dev/stderr) [ "${actual}" != null ] @@ -154,7 +154,7 @@ load _helpers #-------------------------------------------------------------------- # global.enablePodSecurityPolicies -@test "connectInject/ClusterRole: allows podsecuritypolicies access with global.enablePodSecurityPolicies=false" { +@test "connectInject/ClusterRole: no podsecuritypolicies access with global.enablePodSecurityPolicies=false" { cd `chart_dir` local actual=$(helm template \ -s templates/connect-inject-clusterrole.yaml \ @@ -162,7 +162,7 @@ load _helpers --set 'global.enablePodSecurityPolicies=false' \ . | tee /dev/stderr | yq -r '.rules | map(select(.resources[0] == "podsecuritypolicies")) | length' | tee /dev/stderr) - [ "${actual}" = "1" ] + [ "${actual}" = "0" ] } @test "connectInject/ClusterRole: allows podsecuritypolicies access with global.enablePodSecurityPolicies=true" { @@ -193,11 +193,14 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=inject-ca-role' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=pki/issue/connect-webhook-cert-dc1' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=pki/issue/connect-webhook-cert-dc1' \ + --set 'global.secretsBackend.vault.controllerRole=test' \ + --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ + --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ --set 'global.secretsBackend.vault.consulClientRole=foo' \ --set 'global.secretsBackend.vault.consulServerRole=bar' \ --set 'global.secretsBackend.vault.consulCARole=test2' \ . | tee /dev/stderr | - yq -r '.rules[6]' | tee /dev/stderr) + yq -r '.rules[5]' | tee /dev/stderr) local actual=$(echo $object | yq -r '.resources[0]' | tee /dev/stderr) [ "${actual}" = "mutatingwebhookconfigurations" ] @@ -217,27 +220,3 @@ load _helpers local actual=$(echo $object | yq -r '.verbs | index("watch")' | tee /dev/stderr) [ "${actual}" != null ] } - -#-------------------------------------------------------------------- -# openshift - -@test "connectInject/ClusterRole: adds permission to securitycontextconstraints for Openshift with global.openshift.enabled=true with default apiGateway Openshift SCC Name" { - cd `chart_dir` - local object=$(helm template \ - -s templates/connect-inject-clusterrole.yaml \ - --set 'global.openshift.enabled=true' \ - . | tee /dev/stderr | - yq '.rules[13].resourceNames | index("restricted-v2")' | tee /dev/stderr) - [ "${object}" == 0 ] -} - -@test "connectInject/ClusterRole: adds permission to securitycontextconstraints for Openshift with global.openshift.enabled=true and sets apiGateway Openshift SCC Name" { - cd `chart_dir` - local object=$(helm template \ - -s templates/connect-inject-clusterrole.yaml \ - --set 'global.openshift.enabled=true' \ - --set 'connectInject.apiGateway.managedGatewayClass.openshiftSCCName=fakescc' \ - . | tee /dev/stderr | - yq '.rules[13].resourceNames | index("fakescc")' | tee /dev/stderr) - [ "${object}" == 0 ] -} diff --git a/charts/consul/test/unit/connect-inject-deployment.bats b/charts/consul/test/unit/connect-inject-deployment.bats index ccc6eca68c..8a4716889a 100755 --- a/charts/consul/test/unit/connect-inject-deployment.bats +++ b/charts/consul/test/unit/connect-inject-deployment.bats @@ -211,19 +211,6 @@ load _helpers [ "${actual}" = "true" ] } -@test "connectInject/Deployment: metrics.enableTelemetryCollector can be configured" { - cd `chart_dir` - local cmd=$(helm template \ - -s templates/connect-inject-deployment.yaml \ - --set 'connectInject.enabled=true' \ - --set 'global.metrics.enableTelemetryCollector=true' \ - . | tee /dev/stderr | - yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) - - local actual=$(echo "$cmd" | - yq 'any(contains("-enable-telemetry-collector=true"))' | tee /dev/stderr) - [ "${actual}" = "true" ] -} #-------------------------------------------------------------------- # consul and consul-dataplane images @@ -1813,6 +1800,9 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=test' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=foo/ca' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=foo/tls' \ + --set 'global.secretsBackend.vault.controllerRole=test' \ + --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ + --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ . | tee /dev/stderr | yq '.spec.template.spec.containers[0].command | any(contains("-enable-webhook-ca-update"))' | tee /dev/stderr) [ "${actual}" = "true" ] @@ -1919,7 +1909,7 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=connectinjectcarole' \ --set 'global.secretsBackend.vault.agentAnnotations=foo: bar' . [ "$status" -eq 1 ] - [[ "$output" =~ "When one of the following has been set, all must be set: global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName" ]] + [[ "$output" =~ "When one of the following has been set, all must be set: global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName, global.secretsBackend.vault.controllerRole, global.secretsBackend.vault.controller.tlsCert.secretName, and global.secretsBackend.vault.controller.caCert.secretName." ]] } @test "connectInject/Deployment: fails if vault is enabled and global.secretsBackend.vault.connectInject.tlsCert.secretName is set but global.secretsBackend.vault.connectInjectRole and global.secretsBackend.vault.connectInject.caCert.secretName are not" { @@ -1936,7 +1926,7 @@ load _helpers --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=foo/tls' \ --set 'global.secretsBackend.vault.agentAnnotations=foo: bar' . [ "$status" -eq 1 ] - [[ "$output" =~ "When one of the following has been set, all must be set: global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName" ]] + [[ "$output" =~ "When one of the following has been set, all must be set: global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName, global.secretsBackend.vault.controllerRole, global.secretsBackend.vault.controller.tlsCert.secretName, and global.secretsBackend.vault.controller.caCert.secretName." ]] } @test "connectInject/Deployment: fails if vault is enabled and global.secretsBackend.vault.connectInject.caCert.secretName is set but global.secretsBackend.vault.connectInjectRole and global.secretsBackend.vault.connectInject.tlsCert.secretName are not" { @@ -1953,7 +1943,7 @@ load _helpers --set 'global.secretsBackend.vault.connectInject.caCert.secretName=foo/ca' \ --set 'global.secretsBackend.vault.agentAnnotations=foo: bar' . [ "$status" -eq 1 ] - [[ "$output" =~ "When one of the following has been set, all must be set: global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName" ]] + [[ "$output" =~ "When one of the following has been set, all must be set: global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName, global.secretsBackend.vault.controllerRole, global.secretsBackend.vault.controller.tlsCert.secretName, and global.secretsBackend.vault.controller.caCert.secretName." ]] } @test "connectInject/Deployment: vault tls annotations are set when tls is enabled" { @@ -1971,6 +1961,9 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=test' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=foo/ca' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=pki/issue/connect-webhook-cert-dc1' \ + --set 'global.secretsBackend.vault.controllerRole=test' \ + --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ + --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ . | tee /dev/stderr | yq -r '.spec.template.metadata' | tee /dev/stderr) @@ -2044,6 +2037,9 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=inject-ca-role' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=pki/issue/connect-webhook-cert-dc1' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=pki/issue/connect-webhook-cert-dc1' \ + --set 'global.secretsBackend.vault.controllerRole=test' \ + --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ + --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ --set 'global.secretsBackend.vault.consulClientRole=foo' \ --set 'global.secretsBackend.vault.consulServerRole=bar' \ --set 'global.secretsBackend.vault.consulCARole=test2' \ @@ -2066,6 +2062,9 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=inject-ca-role' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=pki/issue/connect-webhook-cert-dc1' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=pki/issue/connect-webhook-cert-dc1' \ + --set 'global.secretsBackend.vault.controllerRole=test' \ + --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ + --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ --set 'server.serverCert.secretName=pki_int/issue/test' \ --set 'global.tls.caCert.secretName=pki_int/cert/ca' \ . | tee /dev/stderr | @@ -2095,6 +2094,9 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=inject-ca-role' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=pki/issue/connect-webhook-cert-dc1' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=pki/issue/connect-webhook-cert-dc1' \ + --set 'global.secretsBackend.vault.controllerRole=test' \ + --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ + --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ . | tee /dev/stderr | yq '.spec.template.spec.volumes[] | select(.name == "certs")' | tee /dev/stderr) [ "${actual}" == "" ] @@ -2114,6 +2116,9 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=inject-ca-role' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=pki/issue/connect-webhook-cert-dc1' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=pki/issue/connect-webhook-cert-dc1' \ + --set 'global.secretsBackend.vault.controllerRole=test' \ + --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ + --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ . | tee /dev/stderr | yq '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "certs")' | tee /dev/stderr) [ "${actual}" == "" ] diff --git a/charts/consul/test/unit/connect-inject-mutatingwebhookconfiguration.bats b/charts/consul/test/unit/connect-inject-mutatingwebhookconfiguration.bats index bc0876586c..81eda87875 100755 --- a/charts/consul/test/unit/connect-inject-mutatingwebhookconfiguration.bats +++ b/charts/consul/test/unit/connect-inject-mutatingwebhookconfiguration.bats @@ -60,7 +60,7 @@ load _helpers --set 'meshGateway.enabled=true' \ --set 'global.peering.enabled=true' \ . | tee /dev/stderr | - yq '.webhooks[12].name | contains("peeringacceptors.consul.hashicorp.com")' | tee /dev/stderr) + yq '.webhooks[11].name | contains("peeringacceptors.consul.hashicorp.com")' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(helm template \ -s templates/connect-inject-mutatingwebhookconfiguration.yaml \ @@ -69,6 +69,6 @@ load _helpers --set 'meshGateway.enabled=true' \ --set 'global.peering.enabled=true' \ . | tee /dev/stderr | - yq '.webhooks[13].name | contains("peeringdialers.consul.hashicorp.com")' | tee /dev/stderr) + yq '.webhooks[12].name | contains("peeringdialers.consul.hashicorp.com")' | tee /dev/stderr) [ "${actual}" = "true" ] } diff --git a/charts/consul/test/unit/crd-controlplanerequestlimits.bats b/charts/consul/test/unit/crd-controlplanerequestlimits.bats deleted file mode 100644 index ed98fc539f..0000000000 --- a/charts/consul/test/unit/crd-controlplanerequestlimits.bats +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "controlPlaneRequestLimit/CustomResourceDefinition: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/crd-controlplanerequestlimits.yaml \ - . | tee /dev/stderr | - yq -s 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "controlPlaneRequestLimit/CustomResourceDefinition: enabled with connectInject.enabled=true" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/crd-controlplanerequestlimits.yaml \ - --set 'connectInject.enabled=true' \ - . | tee /dev/stderr | - # The generated CRDs have "---" at the top which results in two objects - # being detected by yq, the first of which is null. We must therefore use - # yq -s so that length operates on both objects at once rather than - # individually, which would output false\ntrue and fail the test. - yq -s 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} diff --git a/charts/consul/test/unit/crd-exportedservices.bats b/charts/consul/test/unit/crd-exportedservices.bats index 235fe6bd24..1b8f4430b5 100644 --- a/charts/consul/test/unit/crd-exportedservices.bats +++ b/charts/consul/test/unit/crd-exportedservices.bats @@ -7,7 +7,7 @@ load _helpers local actual=$(helm template \ -s templates/crd-exportedservices.yaml \ . | tee /dev/stderr | - yq -s 'length > 0' | tee /dev/stderr) + yq 'length > 0' | tee /dev/stderr) [ "${actual}" = "true" ] } diff --git a/charts/consul/test/unit/crd-gatewayclassconfigs.bats b/charts/consul/test/unit/crd-gatewayclassconfigs.bats deleted file mode 100644 index 0228110b6b..0000000000 --- a/charts/consul/test/unit/crd-gatewayclassconfigs.bats +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "gatewayclassconfigs/CustomResourceDefinition: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/crd-gatewayclassconfigs.yaml \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "$actual" = "true" ] -} - -@test "gatewayclassconfigs/CustomResourceDefinition: disabled with connectInject.enabled=false" { - cd `chart_dir` - assert_empty helm template \ - -s templates/crd-gatewayclassconfigs.yaml \ - --set 'connectInject.enabled=false' \ - . -} diff --git a/charts/consul/test/unit/crd-gatewayclasses.bats b/charts/consul/test/unit/crd-gatewayclasses.bats deleted file mode 100644 index 8400590606..0000000000 --- a/charts/consul/test/unit/crd-gatewayclasses.bats +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "gatewayclasses/CustomResourceDefinition: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/crd-gatewayclasses.yaml \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "$actual" = "true" ] -} - -@test "gatewayclasses/CustomResourceDefinition: disabled with connectInject.enabled=false" { - cd `chart_dir` - assert_empty helm template \ - -s templates/crd-gatewayclasses.yaml \ - --set 'connectInject.enabled=false' \ - . -} - -@test "gatewayclasses/CustomResourceDefinition: disabled with connectInject.apiGateway.manageExternalCRDs=false" { - cd `chart_dir` - assert_empty helm template \ - -s templates/crd-gatewayclasses.yaml \ - --set 'connectInject.apiGateway.manageExternalCRDs=false' \ - . -} diff --git a/charts/consul/test/unit/crd-gateways.bats b/charts/consul/test/unit/crd-gateways.bats deleted file mode 100644 index 8a7f0284e2..0000000000 --- a/charts/consul/test/unit/crd-gateways.bats +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "gateways/CustomResourceDefinition: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/crd-gateways.yaml \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "$actual" = "true" ] -} - -@test "gateways/CustomResourceDefinition: disabled with connectInject.enabled=false" { - cd `chart_dir` - assert_empty helm template \ - -s templates/crd-gateways.yaml \ - --set 'connectInject.enabled=false' \ - . -} - -@test "gateways/CustomResourceDefinition: disabled with connectInject.apiGateway.manageExternalCRDs=false" { - cd `chart_dir` - assert_empty helm template \ - -s templates/crd-gateways.yaml \ - --set 'connectInject.apiGateway.manageExternalCRDs=false' \ - . -} diff --git a/charts/consul/test/unit/crd-grpcroutes.bats b/charts/consul/test/unit/crd-grpcroutes.bats deleted file mode 100644 index d5e3e298d7..0000000000 --- a/charts/consul/test/unit/crd-grpcroutes.bats +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "grpcroutes/CustomResourceDefinition: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/crd-grpcroutes.yaml \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "$actual" = "true" ] -} - -@test "grpcroutes/CustomResourceDefinition: disabled with connectInject.enabled=false" { - cd `chart_dir` - assert_empty helm template \ - -s templates/crd-grpcroutes.yaml \ - --set 'connectInject.enabled=false' \ - . -} - -@test "grpcroutes/CustomResourceDefinition: disabled with connectInject.apiGateway.manageExternalCRDs=false" { - cd `chart_dir` - assert_empty helm template \ - -s templates/crd-grpcroutes.yaml \ - --set 'connectInject.apiGateway.manageExternalCRDs=false' \ - . -} diff --git a/charts/consul/test/unit/crd-httproutes.bats b/charts/consul/test/unit/crd-httproutes.bats deleted file mode 100644 index 99c58e0492..0000000000 --- a/charts/consul/test/unit/crd-httproutes.bats +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "httproutes/CustomResourceDefinition: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/crd-httproutes.yaml \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "$actual" = "true" ] -} - -@test "httproutes/CustomResourceDefinition: disabled with connectInject.enabled=false" { - cd `chart_dir` - assert_empty helm template \ - -s templates/crd-httproutes.yaml \ - --set 'connectInject.enabled=false' \ - . -} - -@test "httproutes/CustomResourceDefinition: disabled with connectInject.apiGateway.manageExternalCRDs=false" { - cd `chart_dir` - assert_empty helm template \ - -s templates/crd-httproutes.yaml \ - --set 'connectInject.apiGateway.manageExternalCRDs=false' \ - . -} diff --git a/charts/consul/test/unit/crd-meshservices.bats b/charts/consul/test/unit/crd-meshservices.bats deleted file mode 100644 index c1ee806ad4..0000000000 --- a/charts/consul/test/unit/crd-meshservices.bats +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "meshservices/CustomResourceDefinition: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/crd-meshservices.yaml \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "$actual" = "true" ] -} - -@test "meshservices/CustomResourceDefinition: disabled with connectInject.enabled=false" { - cd `chart_dir` - assert_empty helm template \ - -s templates/crd-meshservices.yaml \ - --set 'connectInject.enabled=false' \ - . -} - diff --git a/charts/consul/test/unit/crd-tcproutes.bats b/charts/consul/test/unit/crd-tcproutes.bats deleted file mode 100644 index 6916efdc6f..0000000000 --- a/charts/consul/test/unit/crd-tcproutes.bats +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "tcproutes/CustomResourceDefinition: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/crd-tcproutes.yaml \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "$actual" = "true" ] -} - -@test "tcproutes/CustomResourceDefinition: disabled with connectInject.enabled=false" { - cd `chart_dir` - assert_empty helm template \ - -s templates/crd-tcproutes.yaml \ - --set 'connectInject.enabled=false' \ - . -} - -@test "tcproutes/CustomResourceDefinition: disabled with connectInject.apiGateway.manageExternalCRDs=false" { - cd `chart_dir` - assert_empty helm template \ - -s templates/crd-tcproutes.yaml \ - --set 'connectInject.apiGateway.manageExternalCRDs=false' \ - . -} diff --git a/charts/consul/test/unit/crd-tlsroutes.bats b/charts/consul/test/unit/crd-tlsroutes.bats deleted file mode 100644 index 7e1d5c471f..0000000000 --- a/charts/consul/test/unit/crd-tlsroutes.bats +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "tlsroutes/CustomResourceDefinition: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/crd-tlsroutes.yaml \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "$actual" = "true" ] -} - -@test "tlsroutes/CustomResourceDefinition: disabled with connectInject.enabled=false" { - cd `chart_dir` - assert_empty helm template \ - -s templates/crd-tlsroutes.yaml \ - --set 'connectInject.enabled=false' \ - . -} - -@test "tlsroutes/CustomResourceDefinition: disabled with connectInject.apiGateway.manageExternalCRDs=false" { - cd `chart_dir` - assert_empty helm template \ - -s templates/crd-tlsroutes.yaml \ - --set 'connectInject.apiGateway.manageExternalCRDs=false' \ - . -} diff --git a/charts/consul/test/unit/crd-udproutes.bats b/charts/consul/test/unit/crd-udproutes.bats deleted file mode 100644 index 407592a770..0000000000 --- a/charts/consul/test/unit/crd-udproutes.bats +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "udproutes/CustomResourceDefinition: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/crd-udproutes.yaml \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "$actual" = "true" ] -} - -@test "udproutes/CustomResourceDefinition: disabled with connectInject.enabled=false" { - cd `chart_dir` - assert_empty helm template \ - -s templates/crd-udproutes.yaml \ - --set 'connectInject.enabled=false' \ - . -} - -@test "udproutes/CustomResourceDefinition: disabled with connectInject.apiGateway.manageExternalCRDs=false" { - cd `chart_dir` - assert_empty helm template \ - -s templates/crd-udproutes.yaml \ - --set 'connectInject.apiGateway.manageExternalCRDs=false' \ - . -} diff --git a/charts/consul/test/unit/gateway-cleanup-clusterrole.bats b/charts/consul/test/unit/gateway-cleanup-clusterrole.bats deleted file mode 100644 index c672ac5593..0000000000 --- a/charts/consul/test/unit/gateway-cleanup-clusterrole.bats +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -target=templates/gateway-cleanup-clusterrole.yaml - -@test "gatewaycleanup/ClusterRole: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - -s $target \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "$actual" = "true" ] -} - -@test "gatewaycleanup/ClusterRole: disabled with connectInject.enabled=false" { - cd `chart_dir` - assert_empty helm template \ - -s $target \ - --set 'connectInject.enabled=false' \ - . -} - -@test "gatewaycleanup/ClusterRole: can use podsecuritypolicies with global.enablePodSecurityPolicy=true" { - cd `chart_dir` - local actual=$(helm template \ - -s $target \ - --set "global.enablePodSecurityPolicies=true" \ - . | tee /dev/stderr | - yq '.rules[] | select((.resources[0] == "podsecuritypolicies") and (.verbs[0] == "use")) | length > 0' | tee /dev/stderr) - [ "$actual" = "true" ] -} - diff --git a/charts/consul/test/unit/gateway-cleanup-clusterrolebinding.bats b/charts/consul/test/unit/gateway-cleanup-clusterrolebinding.bats deleted file mode 100644 index a6e4af5d2c..0000000000 --- a/charts/consul/test/unit/gateway-cleanup-clusterrolebinding.bats +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -target=templates/gateway-cleanup-clusterrolebinding.yaml - -@test "gatewaycleanup/ClusterRoleBinding: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - -s $target \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "$actual" = "true" ] -} - -@test "gatewaycleanup/ClusterRoleBinding: disabled with connectInject.enabled=false" { - cd `chart_dir` - assert_empty helm template \ - -s $target \ - --set 'connectInject.enabled=false' \ - . -} - diff --git a/charts/consul/test/unit/gateway-cleanup-job.bats b/charts/consul/test/unit/gateway-cleanup-job.bats deleted file mode 100644 index 657bf4a791..0000000000 --- a/charts/consul/test/unit/gateway-cleanup-job.bats +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -target=templates/gateway-cleanup-job.yaml - -@test "gatewaycleanup/Job: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - -s $target \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "$actual" = "true" ] -} - -@test "gatewaycleanup/Job: disabled with connectInject.enabled=false" { - cd `chart_dir` - assert_empty helm template \ - -s $target \ - --set 'connectInject.enabled=false' \ - . -} - - -#-------------------------------------------------------------------- -# annotations - -@test "gatewaycleanup/Job: no annotations defined by default" { - cd `chart_dir` - local actual=$(helm template \ - -s $target \ - . | tee /dev/stderr | - yq -r '.spec.template.metadata.annotations | del(."consul.hashicorp.com/connect-inject") | del(."consul.hashicorp.com/config-checksum")' | tee /dev/stderr) - [ "${actual}" = "{}" ] -} - -@test "gatewaycleanup/Job: annotations can be set" { - cd `chart_dir` - local actual=$(helm template \ - -s $target \ - --set 'global.acls.annotations=foo: bar' \ - . | tee /dev/stderr | - yq -r '.spec.template.metadata.annotations.foo' | tee /dev/stderr) - [ "${actual}" = "bar" ] -} diff --git a/charts/consul/test/unit/gateway-cleanup-podsecuritypolicy.bats b/charts/consul/test/unit/gateway-cleanup-podsecuritypolicy.bats deleted file mode 100644 index 66974da2fd..0000000000 --- a/charts/consul/test/unit/gateway-cleanup-podsecuritypolicy.bats +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -target=templates/gateway-cleanup-podsecuritypolicy.yaml - -@test "gatewaycleanup/PodSecurityPolicy: disabled by default" { - cd `chart_dir` - assert_empty helm template \ - -s $target \ - --set 'connectInject.enabled=false' \ - . -} - -@test "gatewaycleanup/PodSecurityPolicy: disabled with connectInject.enabled=false" { - cd `chart_dir` - assert_empty helm template \ - -s $target \ - --set 'connectInject.enabled=false' \ - . -} - -@test "gatewaycleanup/PodSecurityPolicy: disabled with global.enablePodSecurityPolicies=false" { - cd `chart_dir` - assert_empty helm template \ - -s $target \ - --set 'global.enablePodSecurityPolicies=false' \ - . -} - - -@test "gatewaycleanup/PodSecurityPolicy: enabled with connectInject.enabled=true and global.enablePodSecurityPolicies=true" { - cd `chart_dir` - local actual=$(helm template \ - -s $target \ - --set 'connectInject.enabled=true' \ - --set 'global.enablePodSecurityPolicies=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "$actual" = "true" ] -} diff --git a/charts/consul/test/unit/gateway-cleanup-serviceaccount.bats b/charts/consul/test/unit/gateway-cleanup-serviceaccount.bats deleted file mode 100644 index 50d01b99e9..0000000000 --- a/charts/consul/test/unit/gateway-cleanup-serviceaccount.bats +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -target=templates/gateway-cleanup-serviceaccount.yaml - -@test "gatewaycleanup/ServiceAccount: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - -s $target \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "$actual" = "true" ] -} - -@test "gatewaycleanup/ServiceAccount: disabled with connectInject.enabled=false" { - cd `chart_dir` - assert_empty helm template \ - -s $target \ - --set 'connectInject.enabled=false' \ - . -} - diff --git a/charts/consul/test/unit/gateway-resources-clusterrole.bats b/charts/consul/test/unit/gateway-resources-clusterrole.bats deleted file mode 100644 index 152209a1b5..0000000000 --- a/charts/consul/test/unit/gateway-resources-clusterrole.bats +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -target=templates/gateway-resources-clusterrole.yaml - -@test "gatewayresources/ClusterRole: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - -s $target \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "$actual" = "true" ] -} - -@test "gatewayresources/ClusterRole: disabled with connectInject.enabled=false" { - cd `chart_dir` - assert_empty helm template \ - -s $target \ - --set 'connectInject.enabled=false' \ - . -} - -@test "gatewayresources/ClusterRole: can use podsecuritypolicies with global.enablePodSecurityPolicy=true" { - cd `chart_dir` - local actual=$(helm template \ - -s $target \ - --set "global.enablePodSecurityPolicies=true" \ - . | tee /dev/stderr | - yq '.rules[] | select((.resources[0] == "podsecuritypolicies") and (.verbs[0] == "use")) | length > 0' | tee /dev/stderr) - [ "$actual" = "true" ] -} - diff --git a/charts/consul/test/unit/gateway-resources-clusterrolebinding.bats b/charts/consul/test/unit/gateway-resources-clusterrolebinding.bats deleted file mode 100644 index efc1429e20..0000000000 --- a/charts/consul/test/unit/gateway-resources-clusterrolebinding.bats +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -target=templates/gateway-resources-clusterrolebinding.yaml - -@test "gatewayresources/ClusterRoleBinding: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - -s $target \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "$actual" = "true" ] -} - -@test "gatewayresources/ClusterRoleBinding: disabled with connectInject.enabled=false" { - cd `chart_dir` - assert_empty helm template \ - -s $target \ - --set 'connectInject.enabled=false' \ - . -} - diff --git a/charts/consul/test/unit/gateway-resources-job.bats b/charts/consul/test/unit/gateway-resources-job.bats deleted file mode 100644 index 09322bd2a7..0000000000 --- a/charts/consul/test/unit/gateway-resources-job.bats +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -target=templates/gateway-resources-job.yaml - -@test "gatewayresources/Job: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - -s $target \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "$actual" = "true" ] -} - -@test "gatewayresources/Job: disabled with connectInject.enabled=false" { - cd `chart_dir` - assert_empty helm template \ - -s $target \ - --set 'connectInject.enabled=false' \ - . -} - -@test "gatewayresources/Job: imageK8S set properly" { - cd `chart_dir` - local actual=$(helm template \ - -s $target \ - --set 'global.imageK8S=foo' \ - . | tee /dev/stderr | - yq '.spec.template.spec.containers[0].image == "foo"' | tee /dev/stderr) - [ "$actual" = "true" ] -} - -#-------------------------------------------------------------------- -# fallback configuration -# to be removed in 1.17 (t-eckert 2023-05-23) - -@test "gatewayresources/Job: fallback configuration is used when apiGateway.enabled is true" { - cd `chart_dir` - local spec=$(helm template \ - -s $target \ - --set 'apiGateway.enabled=true' \ - --set 'apiGateway.image=testing' \ - --set 'apiGateway.managedGatewayClass.nodeSelector=foo: bar' \ - --set 'apiGateway.managedGatewayClass.tolerations=- key: bar' \ - --set 'apiGateway.managedGatewayClass.copyAnnotations.service.annotations=- bingo' \ - --set 'apiGateway.managedGatewayClass.serviceType=LoadBalancer' \ - . | tee /dev/stderr | - yq '.spec.template.spec.containers[0].args' | tee /dev/stderr) - - local actual=$(echo "$spec" | jq '.[9] | ."-node-selector=foo"') - [ "${actual}" = "\"bar\"" ] - - local actual=$(echo "$spec" | jq '.[10] | ."-tolerations=- key"') - [ "${actual}" = "\"bar\"" ] - - local actual=$(echo "$spec" | jq '.[11]') - [ "${actual}" = "\"-service-annotations=- bingo\"" ] -} - -#-------------------------------------------------------------------- -# configuration - -@test "gatewayresources/Job: default configuration" { - cd `chart_dir` - local spec=$(helm template \ - -s $target \ - . | tee /dev/stderr | - yq '.spec.template.spec.containers[0].args' | tee /dev/stderr) - - local actual=$(echo "$spec" | jq 'any(index("-deployment-default-instances=1"))') - [ "${actual}" = "true" ] - - local actual=$(echo "$spec" | jq 'any(index("-deployment-max-instances=1"))') - [ "${actual}" = "true" ] - - local actual=$(echo "$spec" | jq 'any(index("-deployment-min-instances=1"))') - [ "${actual}" = "true" ] - - local actual=$(echo "$spec" | jq 'any(index("-service-type=LoadBalancer"))') - [ "${actual}" = "true" ] -} - -@test "apiGateway/GatewayClassConfig: custom configuration" { - cd `chart_dir` - local spec=$(helm template \ - -s $target \ - --set 'connectInject.apiGateway.managedGatewayClass.deployment.defaultInstances=2' \ - --set 'connectInject.apiGateway.managedGatewayClass.deployment.minInstances=1' \ - --set 'connectInject.apiGateway.managedGatewayClass.deployment.maxInstances=3' \ - --set 'connectInject.apiGateway.managedGatewayClass.nodeSelector=foo: bar' \ - --set 'connectInject.apiGateway.managedGatewayClass.tolerations=- key: bar' \ - --set 'connectInject.apiGateway.managedGatewayClass.copyAnnotations.service.annotations=- bingo' \ - --set 'connectInject.apiGateway.managedGatewayClass.serviceType=Foo' \ - --set 'connectInject.apiGateway.managedGatewayClass.openshiftSCCName=hello' \ - . | tee /dev/stderr | - yq '.spec.template.spec.containers[0].args' | tee /dev/stderr) - - local actual=$(echo "$spec" | jq 'any(index("-deployment-default-instances=2"))') - [ "${actual}" = "true" ] - - local actual=$(echo "$spec" | jq 'any(index("-deployment-max-instances=3"))') - [ "${actual}" = "true" ] - - local actual=$(echo "$spec" | jq 'any(index("-deployment-min-instances=1"))') - [ "${actual}" = "true" ] - - local actual=$(echo "$spec" | jq 'any(index("-service-type=Foo"))') - [ "${actual}" = "true" ] - - local actual=$(echo "$spec" | jq '.[12]') - [ "${actual}" = "\"-node-selector\"" ] - - local actual=$(echo "$spec" | jq '.[13]') - [ "${actual}" = "\"foo: bar\"" ] - - local actual=$(echo "$spec" | jq '.[14] | ."-tolerations=- key"') - [ "${actual}" = "\"bar\"" ] - - local actual=$(echo "$spec" | jq '.[15]') - [ "${actual}" = "\"-service-annotations\"" ] - - local actual=$(echo "$spec" | jq '.[16]') - [ "${actual}" = "\"- bingo\"" ] - - local actual=$(echo "$spec" | jq '.[17]') - [ "${actual}" = "\"-service-type=Foo\"" ] -} - -@test "apiGateway/GatewayClassConfig: custom configuration openshift enabled" { - cd `chart_dir` - local spec=$(helm template \ - -s $target \ - --set 'global.openshift.enabled=true' \ - --set 'connectInject.apiGateway.managedGatewayClass.openshiftSCCName=hello' \ - . | tee /dev/stderr | - yq '.spec.template.spec.containers[0].args' | tee /dev/stderr) - - local actual=$(echo "$spec" | jq '.[13]') - [ "${actual}" = "\"-openshift-scc-name=hello\"" ] -} - - -#-------------------------------------------------------------------- -# annotations - -@test "gatewayresources/Job: no annotations defined by default" { - cd `chart_dir` - local actual=$(helm template \ - -s $target \ - . | tee /dev/stderr | - yq -r '.spec.template.metadata.annotations | del(."consul.hashicorp.com/connect-inject") | del(."consul.hashicorp.com/config-checksum")' | tee /dev/stderr) - [ "${actual}" = "{}" ] -} - -@test "gatewayresources/Job: annotations can be set" { - cd `chart_dir` - local actual=$(helm template \ - -s $target \ - --set 'global.acls.annotations=foo: bar' \ - . | tee /dev/stderr | - yq -r '.spec.template.metadata.annotations.foo' | tee /dev/stderr) - [ "${actual}" = "bar" ] -} diff --git a/charts/consul/test/unit/gateway-resources-podsecuritypolicy.bats b/charts/consul/test/unit/gateway-resources-podsecuritypolicy.bats deleted file mode 100644 index 81818c525a..0000000000 --- a/charts/consul/test/unit/gateway-resources-podsecuritypolicy.bats +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -target=templates/gateway-resources-podsecuritypolicy.yaml - -@test "gatewayresources/PodSecurityPolicy: disabled by default" { - cd `chart_dir` - assert_empty helm template \ - -s $target \ - --set 'connectInject.enabled=false' \ - . -} - -@test "gatewayresources/PodSecurityPolicy: disabled with connectInject.enabled=false" { - cd `chart_dir` - assert_empty helm template \ - -s $target \ - --set 'connectInject.enabled=false' \ - . -} - -@test "gatewayresources/PodSecurityPolicy: disabled with global.enablePodSecurityPolicies=false" { - cd `chart_dir` - assert_empty helm template \ - -s $target \ - --set 'global.enablePodSecurityPolicies=false' \ - . -} - - -@test "gatewayresources/PodSecurityPolicy: enabled with connectInject.enabled=true and global.enablePodSecurityPolicies=true" { - cd `chart_dir` - local actual=$(helm template \ - -s $target \ - --set 'connectInject.enabled=true' \ - --set 'global.enablePodSecurityPolicies=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "$actual" = "true" ] -} diff --git a/charts/consul/test/unit/gateway-resources-serviceaccount.bats b/charts/consul/test/unit/gateway-resources-serviceaccount.bats deleted file mode 100644 index 90011e226b..0000000000 --- a/charts/consul/test/unit/gateway-resources-serviceaccount.bats +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -target=templates/gateway-resources-serviceaccount.yaml - -@test "gatewayresources/ServiceAccount: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - -s $target \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "$actual" = "true" ] -} - -@test "gatewayresources/ServiceAccount: disabled with connectInject.enabled=false" { - cd `chart_dir` - assert_empty helm template \ - -s $target \ - --set 'connectInject.enabled=false' \ - . -} - diff --git a/charts/consul/test/unit/helpers.bats b/charts/consul/test/unit/helpers.bats index efcbc116b5..4245b519c4 100644 --- a/charts/consul/test/unit/helpers.bats +++ b/charts/consul/test/unit/helpers.bats @@ -115,7 +115,7 @@ load _helpers @test "helper/namespace: used everywhere" { cd `chart_dir` # Grep for files that don't have 'namespace: ' in them - local actual=$(grep -L 'namespace: ' templates/*.yaml | grep -v 'crd' | grep -v 'clusterrole' | grep -v 'gateway-gateway' | tee /dev/stderr ) + local actual=$(grep -L 'namespace: ' templates/*.yaml | grep -v 'crd' | grep -v 'clusterrole' | grep -v 'api-gateway-gateway' | tee /dev/stderr ) [ "${actual}" = '' ] } diff --git a/charts/consul/test/unit/server-acl-init-job.bats b/charts/consul/test/unit/server-acl-init-job.bats index 1dc55a9551..9b15f9e869 100644 --- a/charts/consul/test/unit/server-acl-init-job.bats +++ b/charts/consul/test/unit/server-acl-init-job.bats @@ -650,19 +650,7 @@ load _helpers yq -r '.spec.template' | tee /dev/stderr) # Check annotations - actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-pre-populate"]' | tee /dev/stderr) - [ "${actual}" = "true" ] - actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-pre-populate-only"]' | tee /dev/stderr) - [ "${actual}" = "false" ] - - actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-cache-enable"]' | tee /dev/stderr) - [ "${actual}" = "true" ] - - actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-cache-listener-port"]' | tee /dev/stderr) - [ "${actual}" = "8200" ] - - actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-enable-quit"]' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject"]' | tee /dev/stderr) @@ -671,13 +659,15 @@ load _helpers local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/role"]' | tee /dev/stderr) [ "${actual}" = "aclrole" ] - local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-secrets-backend=vault"))') - [ "${actual}" = "true" ] + local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject-secret-bootstrap-token"]' | tee /dev/stderr) + [ "${actual}" = "foo" ] - local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-secret-name=foo"))') - [ "${actual}" = "true" ] + local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject-template-bootstrap-token"]' | tee /dev/stderr) + local expected=$'{{- with secret \"foo\" -}}\n{{- .Data.data.bar -}}\n{{- end -}}' + [ "${actual}" = "${expected}" ] - local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-secret-key=bar"))') + # Check that the bootstrap token flag is set to the path of the Vault secret. + local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-file=/vault/secrets/bootstrap-token"))') [ "${actual}" = "true" ] # Check that no (secret) volumes are not attached @@ -708,31 +698,20 @@ load _helpers yq -r '.spec.template' | tee /dev/stderr) # Check annotations - local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-pre-populate"]' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-pre-populate-only"]' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-cache-enable"]' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-cache-listener-port"]' | tee /dev/stderr) - [ "${actual}" = "8200" ] - - local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-enable-quit"]' | tee /dev/stderr) + local actual + actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-pre-populate-only"]' | tee /dev/stderr) [ "${actual}" = "true" ] - - local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject"]' | tee /dev/stderr) + local actual + actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject"]' | tee /dev/stderr) [ "${actual}" = "true" ] - - local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/role"]' | tee /dev/stderr) + local actual + actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/role"]' | tee /dev/stderr) [ "${actual}" = "aclrole" ] - - local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject-secret-serverca.crt"]' | tee /dev/stderr) + local actual + actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject-secret-serverca.crt"]' | tee /dev/stderr) [ "${actual}" = "foo" ] - - local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject-template-serverca.crt"]' | tee /dev/stderr) + local actual + actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject-template-serverca.crt"]' | tee /dev/stderr) [ "${actual}" = $'{{- with secret \"foo\" -}}\n{{- .Data.certificate -}}\n{{- end -}}' ] # Check that the consul-ca-cert volume is not attached @@ -918,14 +897,12 @@ load _helpers local expected=$'{{- with secret \"/vault/replication\" -}}\n{{- .Data.data.token -}}\n{{- end -}}' [ "${actual}" = "${expected}" ] - local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-secrets-backend=vault"))') - [ "${actual}" = "true" ] + local actual=$(echo $object | yq -r '.metadata.annotations."vault.hashicorp.com/agent-inject-secret-bootstrap-token"') + [ "${actual}" = "/vault/bootstrap" ] - local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-secret-name=/vault/bootstrap"))') - [ "${actual}" = "true" ] - - local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-secret-key=token"))') - [ "${actual}" = "true" ] + local actual=$(echo $object | yq -r '.metadata.annotations."vault.hashicorp.com/agent-inject-template-bootstrap-token"') + local expected=$'{{- with secret \"/vault/bootstrap\" -}}\n{{- .Data.data.token -}}\n{{- end -}}' + [ "${actual}" = "${expected}" ] # Check that replication token Kubernetes secret volumes and volumeMounts are not attached. local actual=$(echo $object | jq -r '.spec.volumes') @@ -934,9 +911,12 @@ load _helpers local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").volumeMounts') [ "${actual}" = "null" ] - # Replication token file is passed. + # Check that the replication and bootstrap token flags are set to the path of the Vault secret. local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-acl-replication-token-file=/vault/secrets/replication-token"))') [ "${actual}" = "true" ] + + local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-file=/vault/secrets/bootstrap-token"))') + [ "${actual}" = "true" ] } #-------------------------------------------------------------------- @@ -989,7 +969,7 @@ load _helpers #-------------------------------------------------------------------- # Vault agent annotations -@test "serverACLInit/Job: default vault agent annotations" { +@test "serverACLInit/Job: no vault agent annotations defined by default" { cd `chart_dir` local actual=$(helm template \ -s templates/server-acl-init-job.yaml \ @@ -1003,21 +983,8 @@ load _helpers --set 'global.secretsBackend.vault.consulCARole=carole' \ --set 'global.secretsBackend.vault.manageSystemACLsRole=aclrole' \ . | tee /dev/stderr | - yq -r .spec.template.metadata.annotations | tee /dev/stderr) - - local expected=$(echo '{ - "consul.hashicorp.com/connect-inject": "false", - "vault.hashicorp.com/agent-inject": "true", - "vault.hashicorp.com/agent-pre-populate": "true", - "vault.hashicorp.com/agent-pre-populate-only": "false", - "vault.hashicorp.com/agent-cache-enable": "true", - "vault.hashicorp.com/agent-cache-listener-port": "8200", - "vault.hashicorp.com/agent-enable-quit": "true", - "vault.hashicorp.com/role": "aclrole" - }' | tee /dev/stderr) - - local equal=$(jq -n --argjson a "$actual" --argjson b "$expected" '$a == $b') - [ "$equal" = "true" ] + yq -r '.spec.template.metadata.annotations | del(."consul.hashicorp.com/connect-inject") | del(."vault.hashicorp.com/agent-inject") | del(."vault.hashicorp.com/agent-pre-populate-only") | del(."vault.hashicorp.com/role") | del(."vault.hashicorp.com/agent-inject-secret-bootstrap-token") | del(."vault.hashicorp.com/agent-inject-template-bootstrap-token")' | tee /dev/stderr) + [ "${actual}" = "{}" ] } @test "serverACLInit/Job: vault agent annotations can be set" { @@ -1856,23 +1823,55 @@ load _helpers [[ "$output" =~ "both global.acls.bootstrapToken.secretKey and global.acls.bootstrapToken.secretName must be set if one of them is provided" ]] } -@test "serverACLInit/Job: bootstrap token secret is passed when acls.bootstrapToken.secretKey and secretName are set" { +@test "serverACLInit/Job: -bootstrap-token-file is not set by default" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr) + + # Test the flag is not set. + local actual=$(echo "$object" | + yq '.spec.template.spec.containers[0].command | any(contains("-bootstrap-token-file"))' | tee /dev/stderr) + [ "${actual}" = "false" ] + + # Test the volume doesn't exist + local actual=$(echo "$object" | + yq '.spec.template.spec.volumes | length == 0' | tee /dev/stderr) + [ "${actual}" = "true" ] + + # Test the volume mount doesn't exist + local actual=$(echo "$object" | + yq '.spec.template.spec.containers[0].volumeMounts | length == 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "serverACLInit/Job: -bootstrap-token-file is set when acls.bootstrapToken.secretKey and secretName are set" { cd `chart_dir` local object=$(helm template \ -s templates/server-acl-init-job.yaml \ --set 'global.acls.manageSystemACLs=true' \ --set 'global.acls.bootstrapToken.secretName=name' \ --set 'global.acls.bootstrapToken.secretKey=key' \ - . | yq .spec.template | tee /dev/stderr) + . | tee /dev/stderr) - local actual=$(echo "$object" | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-secret-name=name"))') + # Test the -bootstrap-token-file flag is set. + local actual=$(echo "$object" | + yq '.spec.template.spec.containers[0].command | any(contains("-bootstrap-token-file=/consul/acl/tokens/bootstrap-token"))' | tee /dev/stderr) [ "${actual}" = "true" ] - local actual=$(echo "$object" | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-secret-key=key"))') + # Test the volume exists + local actual=$(echo "$object" | + yq '.spec.template.spec.volumes | map(select(.name == "bootstrap-token")) | length == 1' | tee /dev/stderr) + [ "${actual}" = "true" ] + + # Test the volume mount exists + local actual=$(echo "$object" | + yq '.spec.template.spec.containers[0].volumeMounts | map(select(.name == "bootstrap-token")) | length == 1' | tee /dev/stderr) [ "${actual}" = "true" ] } -@test "serverACLInit/Job: bootstrap token secret is passed when both acl.bootstrapToken and acls.replicationToken are set" { +@test "serverACLInit/Job: -bootstrap-token-file is preferred when both acls.bootstrapToken and acls.replicationToken are set" { cd `chart_dir` local object=$(helm template \ -s templates/server-acl-init-job.yaml \ @@ -1881,12 +1880,21 @@ load _helpers --set 'global.acls.bootstrapToken.secretKey=key' \ --set 'global.acls.replicationToken.secretName=replication' \ --set 'global.acls.replicationToken.secretKey=token' \ - . | yq .spec.template | tee /dev/stderr) + . | tee /dev/stderr) - local actual=$(echo "$object" | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-secret-name=name"))') + # Test the -bootstrap-token-file flag is set. + local actual=$(echo "$object" | + yq '.spec.template.spec.containers[0].command | any(contains("-bootstrap-token-file=/consul/acl/tokens/bootstrap-token"))' | tee /dev/stderr) [ "${actual}" = "true" ] - local actual=$(echo "$object" | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-secret-key=key"))') + # Test the volume exists + local actual=$(echo "$object" | + yq '.spec.template.spec.volumes | map(select(.name == "bootstrap-token")) | length == 1' | tee /dev/stderr) + [ "${actual}" = "true" ] + + # Test the volume mount exists + local actual=$(echo "$object" | + yq '.spec.template.spec.containers[0].volumeMounts | map(select(.name == "bootstrap-token")) | length == 1' | tee /dev/stderr) [ "${actual}" = "true" ] } diff --git a/charts/consul/test/unit/server-config-configmap.bats b/charts/consul/test/unit/server-config-configmap.bats index 643caeb0a1..ad1c79d248 100755 --- a/charts/consul/test/unit/server-config-configmap.bats +++ b/charts/consul/test/unit/server-config-configmap.bats @@ -966,98 +966,6 @@ load _helpers [ "${actual}" = "true" ] } -#-------------------------------------------------------------------- -# server.limits.requestLimits - -@test "server/ConfigMap: server.limits.requestLimits.mode is disabled by default" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/server-config-configmap.yaml \ - . | tee /dev/stderr | - yq -r '.data["server.json"]' | jq -r .limits.request_limits.mode | tee /dev/stderr) - - [ "${actual}" = "disabled" ] -} - -@test "server/ConfigMap: server.limits.requestLimits.mode accepts disabled, permissive, and enforce" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/server-config-configmap.yaml \ - --set 'server.limits.requestLimits.mode=disabled' \ - . | tee /dev/stderr | - yq -r '.data["server.json"]' | jq -r .limits.request_limits.mode | tee /dev/stderr) - - [ "${actual}" = "disabled" ] - - local actual=$(helm template \ - -s templates/server-config-configmap.yaml \ - --set 'server.limits.requestLimits.mode=permissive' \ - . | tee /dev/stderr | - yq -r '.data["server.json"]' | jq -r .limits.request_limits.mode | tee /dev/stderr) - - [ "${actual}" = "permissive" ] - - local actual=$(helm template \ - -s templates/server-config-configmap.yaml \ - --set 'server.limits.requestLimits.mode=enforce' \ - . | tee /dev/stderr | - yq -r '.data["server.json"]' | jq -r .limits.request_limits.mode | tee /dev/stderr) - - [ "${actual}" = "enforce" ] -} - -@test "server/ConfigMap: server.limits.requestLimits.mode errors with value other than disabled, permissive, and enforce" { - cd `chart_dir` - run helm template \ - -s templates/server-config-configmap.yaml \ - --set 'server.limits.requestLimits.mode=notvalid' \ - . - [ "$status" -eq 1 ] - [[ "$output" =~ "server.limits.requestLimits.mode must be one of the following values: disabled, permissive, and enforce" ]] -} - -@test "server/ConfigMap: server.limits.request_limits.read_rate is -1 by default" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/server-config-configmap.yaml \ - . | tee /dev/stderr | - yq -r '.data["server.json"]' | jq -r .limits.request_limits.read_rate | tee /dev/stderr) - - [ "${actual}" = "-1" ] -} - -@test "server/ConfigMap: server.limits.request_limits.read_rate is set properly when specified " { - cd `chart_dir` - local actual=$(helm template \ - -s templates/server-config-configmap.yaml \ - --set 'server.limits.requestLimits.readRate=100' \ - . | tee /dev/stderr | - yq -r '.data["server.json"]' | jq -r .limits.request_limits.read_rate | tee /dev/stderr) - - [ "${actual}" = "100" ] -} - -@test "server/ConfigMap: server.limits.request_limits.write_rate is -1 by default" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/server-config-configmap.yaml \ - . | tee /dev/stderr | - yq -r '.data["server.json"]' | jq -r .limits.request_limits.write_rate | tee /dev/stderr) - - [ "${actual}" = "-1" ] -} - -@test "server/ConfigMap: server.limits.request_limits.write_rate is set properly when specified " { - cd `chart_dir` - local actual=$(helm template \ - -s templates/server-config-configmap.yaml \ - --set 'server.limits.requestLimits.writeRate=100' \ - . | tee /dev/stderr | - yq -r '.data["server.json"]' | jq -r .limits.request_limits.write_rate | tee /dev/stderr) - - [ "${actual}" = "100" ] -} - #-------------------------------------------------------------------- # server.auditLogs @@ -1175,7 +1083,7 @@ load _helpers local actual=$(echo $object | jq -r .audit.sink.MySink1.path | tee /dev/stderr) [ "${actual}" = "/tmp/audit.json" ] - + local actual=$(echo $object | jq -r .audit.sink.MySink3.path | tee /dev/stderr) [ "${actual}" = "/tmp/audit-3.json" ] diff --git a/charts/consul/test/unit/server-statefulset.bats b/charts/consul/test/unit/server-statefulset.bats index a60884d20c..a2b2539066 100755 --- a/charts/consul/test/unit/server-statefulset.bats +++ b/charts/consul/test/unit/server-statefulset.bats @@ -686,7 +686,7 @@ load _helpers -s templates/server-statefulset.yaml \ . | tee /dev/stderr | yq -r '.spec.template.metadata.annotations."consul.hashicorp.com/config-checksum"' | tee /dev/stderr) - [ "${actual}" = 3714bf1fbca840dcd10b5aeb40c6ef35e349bd534727abe80b831c77f88da7da ] + [ "${actual}" = dc165411861bb45d37e20a0a337697336f333407f5fb29fca9252cdba652339c ] } @test "server/StatefulSet: adds config-checksum annotation when extraConfig is provided" { @@ -696,7 +696,7 @@ load _helpers --set 'server.extraConfig="{\"hello\": \"world\"}"' \ . | tee /dev/stderr | yq -r '.spec.template.metadata.annotations."consul.hashicorp.com/config-checksum"' | tee /dev/stderr) - [ "${actual}" = 87126fac3c7704fba1d4265201ad0345f4d972d2123df6e6fb416b40c5823d80 ] + [ "${actual}" = d69874f33a862f6728265246a3e38b3f64702e013ecd4afc5dcdc33d34a66954 ] } @test "server/StatefulSet: adds config-checksum annotation when config is updated" { @@ -706,7 +706,7 @@ load _helpers --set 'global.acls.manageSystemACLs=true' \ . | tee /dev/stderr | yq -r '.spec.template.metadata.annotations."consul.hashicorp.com/config-checksum"' | tee /dev/stderr) - [ "${actual}" = d98ff135c5dee661058f33e29970675761ecf235676db0d4d24f354908eee425 ] + [ "${actual}" = 95a3d3b4816a0f183b8a9aac41ff386e659cd2a465f3030f01c5c4a2b9052a6c ] } #-------------------------------------------------------------------- @@ -846,9 +846,11 @@ load _helpers #-------------------------------------------------------------------- # global.openshift.enabled -@test "server/StatefulSet: restricted container securityContexts are set when global.openshift.enabled=true" { +@test "server/StatefulSet: restricted container securityContexts are set when global.openshift.enabled=true on OpenShift >= 4.11" { cd `chart_dir` + # OpenShift 4.11 == Kube 1.24 local manifest=$(helm template \ + --kube-version '1.24' \ -s templates/server-statefulset.yaml \ --set 'global.openshift.enabled=true' \ . | tee /dev/stderr) @@ -868,11 +870,20 @@ load _helpers local actual=$(echo "$manifest" | yq -r '.spec.template.spec.containers | map(select(.name == "consul")) | .[0].securityContext') local equal=$(jq -n --argjson a "$actual" --argjson b "$expected" '$a == $b') [ "$equal" == "true" ] +} - # Check locality-init container - local actual=$(echo "$manifest" | yq -r '.spec.template.spec.initContainers | map(select(.name == "locality-init")) | .[0].securityContext') - local equal=$(jq -n --argjson a "$actual" --argjson b "$expected" '$a == $b') - [ "$equal" == "true" ] +@test "server/StatefulSet: restricted container securityContexts are not set when global.openshift.enabled=true on OpenShift < 4.11" { + cd `chart_dir` + # OpenShift 4.11 == Kube 1.24 + local manifest=$(helm template \ + --kube-version '1.23' \ + -s templates/server-statefulset.yaml \ + --set 'global.openshift.enabled=true' \ + . | tee /dev/stderr) + + # Check consul container + local actual=$(echo "$manifest" | yq -r '.spec.template.spec.containers | map(select(.name == "consul")) | .[0].securityContext') + [ "$actual" == "null" ] } #-------------------------------------------------------------------- @@ -900,11 +911,6 @@ load _helpers local actual=$(echo "$manifest" | yq -r '.spec.template.spec.containers | map(select(.name == "consul")) | .[0].securityContext') local equal=$(jq -n --argjson a "$actual" --argjson b "$expected" '$a == $b') [ "$equal" == "true" ] - - # Check locality-init container - local actual=$(echo "$manifest" | yq -r '.spec.template.spec.initContainers | map(select(.name == "locality-init")) | .[0].securityContext') - local equal=$(jq -n --argjson a "$actual" --argjson b "$expected" '$a == $b') - [ "$equal" == "true" ] } #-------------------------------------------------------------------- @@ -2650,64 +2656,6 @@ MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ } -#-------------------------------------------------------------------- -# global.trustedCAs - -@test "server/StatefulSet: trustedCAs: if trustedCAs is set command is modified correctly" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/server-statefulset.yaml \ - --set 'global.trustedCAs[0]=-----BEGIN CERTIFICATE----- -MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].command[2] | contains("cat < /trusted-cas/custom-ca-0.pem")' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/StatefulSet: trustedCAs: if tustedCAs multiple are set" { - cd `chart_dir` - local object=$(helm template \ - -s templates/server-statefulset.yaml \ - --set 'global.trustedCAs[0]=-----BEGIN CERTIFICATE----- -MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ - --set 'global.trustedCAs[1]=-----BEGIN CERTIFICATE----- -MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0]' | tee /dev/stderr) - - - local actual=$(echo $object | jq '.command[2] | contains("cat < /trusted-cas/custom-ca-0.pem")' | tee /dev/stderr) - [ "${actual}" = "true" ] - local actual=$(echo $object | jq '.command[2] | contains("cat < /trusted-cas/custom-ca-1.pem")' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -# global.trustedCAs -@test "server/StatefulSet: trustedCAs: if trustedCAs is set /trusted-cas volumeMount is added" { - cd `chart_dir` - local object=$(helm template \ - -s templates/server-statefulset.yaml \ - --set 'global.trustedCAs[0]=-----BEGIN CERTIFICATE----- -MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ - . | tee /dev/stderr | yq -r '.spec.template.spec' | tee /dev/stderr) - local actual=$(echo $object | jq -r '.volumes[] | select(.name == "trusted-cas") | .name' | tee /dev/stderr) - [ "${actual}" = "trusted-cas" ] -} - -@test "server/StatefulSet: trustedCAs: if trustedCAs is set SSL_CERT_DIR env var is set" { - cd `chart_dir` - local object=$(helm template \ - -s templates/server-statefulset.yaml \ - --set 'global.trustedCAs[0]=-----BEGIN CERTIFICATE----- -MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ - . | tee /dev/stderr | yq -r '.spec.template.spec.containers[0].env[] | select(.name == "SSL_CERT_DIR")' | tee /dev/stderr) - - local actual=$(echo $object | jq -r '.name' | tee /dev/stderr) - [ "${actual}" = "SSL_CERT_DIR" ] - local actual=$(echo $object | jq -r '.value' | tee /dev/stderr) - [ "${actual}" = "/etc/ssl/certs:/trusted-cas" ] -} - #-------------------------------------------------------------------- # snapshotAgent license-autoload diff --git a/charts/consul/test/unit/telemetry-collector-configmap.bats b/charts/consul/test/unit/telemetry-collector-configmap.bats deleted file mode 100644 index c866f0d3e1..0000000000 --- a/charts/consul/test/unit/telemetry-collector-configmap.bats +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "telemetryCollector/ConfigMap: disabled by default" { - cd `chart_dir` - assert_empty helm template \ - -s templates/telemetry-collector-configmap.yaml \ - . -} - -@test "telemetryCollector/ConfigMap: enabled with telemetryCollector enabled and config has content" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-configmap.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.customExporterConfig="{}"' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "telemetryCollector/ConfigMap: disabled with telemetryCollector enabled and config is empty content" { - cd `chart_dir` - assert_empty helm template \ - -s templates/telemetry-collector-configmap.yaml \ - --set 'telemetryCollector.enabled=true' \ - . -} \ No newline at end of file diff --git a/charts/consul/test/unit/telemetry-collector-deployment.bats b/charts/consul/test/unit/telemetry-collector-deployment.bats deleted file mode 100755 index 7809039e11..0000000000 --- a/charts/consul/test/unit/telemetry-collector-deployment.bats +++ /dev/null @@ -1,1133 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "telemetryCollector/Deployment: disabled by default" { - cd `chart_dir` - assert_empty helm template \ - -s templates/telemetry-collector-deployment.yaml \ - . -} - -@test "telemetryCollector/Deployment: fails if no image is set" { - cd `chart_dir` - run helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=null' \ - . - [ "$status" -eq 1 ] - [[ "$output" =~ "telemetryCollector.image must be set to enable consul-telemetry-collector" ]] -} - -@test "telemetryCollector/Deployment: disable with telemetry-collector.enabled" { - cd `chart_dir` - assert_empty helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=false' \ - . -} - -@test "telemetryCollector/Deployment: disable with global.enabled" { - cd `chart_dir` - assert_empty helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'global.enabled=false' \ - . -} - -@test "telemetryCollector/Deployment: container image overrides" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - . | tee /dev/stderr | - yq '.spec.template.spec.containers[0].image' | tee /dev/stderr) - [ "${actual}" = "\"bar\"" ] -} - -#-------------------------------------------------------------------- -# nodeSelector - -@test "telemetryCollector/Deployment: nodeSelector is not set by default" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - . | tee /dev/stderr | - yq '.spec.template.spec.nodeSelector' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "telemetryCollector/Deployment: specified nodeSelector" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'telemetryCollector.nodeSelector=testing' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.nodeSelector' | tee /dev/stderr) - [ "${actual}" = "testing" ] -} - -#-------------------------------------------------------------------- -# consul.name - -@test "telemetryCollector/Deployment: name is constant regardless of consul name" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'consul.name=foobar' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].name' | tee /dev/stderr) - [ "${actual}" = "consul-telemetry-collector" ] -} - -#-------------------------------------------------------------------- -# global.tls.enabled - -@test "telemetryCollector/Deployment: Adds tls-ca-cert volume when global.tls.enabled is true" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=foo' \ - --set 'global.tls.enabled=true' \ - . | tee /dev/stderr | - yq '.spec.template.spec.volumes[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) - [ "${actual}" != "" ] -} - -@test "telemetryCollector/Deployment: Adds tls-ca-cert volumeMounts when global.tls.enabled is true" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=foo' \ - --set 'global.tls.enabled=true' \ - . | tee /dev/stderr | - yq '.spec.template.spec.containers[1].volumeMounts[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) - [ "${actual}" != "" ] -} - -@test "telemetryCollector/Deployment: can overwrite CA secret with the provided one" { - cd `chart_dir` - local ca_cert_volume=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=foo' \ - --set 'global.tls.enabled=true' \ - --set 'global.tls.caCert.secretName=foo-ca-cert' \ - --set 'global.tls.caCert.secretKey=key' \ - --set 'global.tls.caKey.secretName=foo-ca-key' \ - --set 'global.tls.caKey.secretKey=key' \ - . | tee /dev/stderr | - yq '.spec.template.spec.volumes[] | select(.name=="consul-ca-cert")' | tee /dev/stderr) - - # check that the provided ca cert secret is attached as a volume - local actual - actual=$(echo $ca_cert_volume | jq -r '.secret.secretName' | tee /dev/stderr) - [ "${actual}" = "foo-ca-cert" ] - - # check that the volume uses the provided secret key - actual=$(echo $ca_cert_volume | jq -r '.secret.items[0].key' | tee /dev/stderr) - [ "${actual}" = "key" ] -} - -#-------------------------------------------------------------------- -# global.tls.enableAutoEncrypt - -@test "telemetryCollector/Deployment: consul-ca-cert volumeMount is added when TLS with auto-encrypt is enabled without clients" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=foo' \ - --set 'global.tls.enabled=true' \ - --set 'global.tls.enableAutoEncrypt=true' \ - --set 'client.enabled=false' \ - . | tee /dev/stderr | - yq '.spec.template.spec.containers[1].volumeMounts[] | select(.name == "consul-ca-cert") | length > 0' | tee - /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "telemetryCollector/Deployment: consul-ca-cert volume is not added if externalServers.enabled=true and externalServers.useSystemRoots=true" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=foo' \ - --set 'global.tls.enabled=true' \ - --set 'global.tls.enableAutoEncrypt=true' \ - --set 'externalServers.enabled=true' \ - --set 'externalServers.hosts[0]=foo.com' \ - --set 'externalServers.useSystemRoots=true' \ - . | tee /dev/stderr | - yq '.spec.template.spec.volumes[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) - [ "${actual}" = "" ] -} - -#-------------------------------------------------------------------- -# resources - -@test "telemetryCollector/Deployment: resources has default" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=foo' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].resources' | tee /dev/stderr) - - [ $(echo "${actual}" | yq -r '.requests.memory') = "512Mi" ] - [ $(echo "${actual}" | yq -r '.requests.cpu') = "1000m" ] - [ $(echo "${actual}" | yq -r '.limits.memory') = "512Mi" ] - [ $(echo "${actual}" | yq -r '.limits.cpu') = "1000m" ] -} - -@test "telemetryCollector/Deployment: resources can be overridden" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=foo' \ - --set 'telemetryCollector.resources.foo=bar' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].resources.foo' | tee /dev/stderr) - [ "${actual}" = "bar" ] -} - -#-------------------------------------------------------------------- -# init container resources - -@test "telemetryCollector/Deployment: init container has default resources" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=foo' \ - --set 'global.acls.manageSystemACLs=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.initContainers[0].resources' | tee /dev/stderr) - - [ $(echo "${actual}" | yq -r '.requests.memory') = "25Mi" ] - [ $(echo "${actual}" | yq -r '.requests.cpu') = "50m" ] - [ $(echo "${actual}" | yq -r '.limits.memory') = "150Mi" ] - [ $(echo "${actual}" | yq -r '.limits.cpu') = "50m" ] -} - -@test "telemetryCollector/Deployment: init container resources can be set" { - cd `chart_dir` - local object=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=foo' \ - --set 'global.acls.manageSystemACLs=true' \ - --set 'telemetryCollector.initContainer.resources.requests.memory=memory' \ - --set 'telemetryCollector.initContainer.resources.requests.cpu=cpu' \ - --set 'telemetryCollector.initContainer.resources.limits.memory=memory2' \ - --set 'telemetryCollector.initContainer.resources.limits.cpu=cpu2' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.initContainers[0].resources' | tee /dev/stderr) - - local actual=$(echo $object | yq -r '.requests.memory' | tee /dev/stderr) - [ "${actual}" = "memory" ] - - local actual=$(echo $object | yq -r '.requests.cpu' | tee /dev/stderr) - [ "${actual}" = "cpu" ] - - local actual=$(echo $object | yq -r '.limits.memory' | tee /dev/stderr) - [ "${actual}" = "memory2" ] - - local actual=$(echo $object | yq -r '.limits.cpu' | tee /dev/stderr) - [ "${actual}" = "cpu2" ] -} - -#-------------------------------------------------------------------- -# priorityClassName - -@test "telemetryCollector/Deployment: no priorityClassName by default" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=foo' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.priorityClassName' | tee /dev/stderr) - - [ "${actual}" = "null" ] -} - -@test "telemetryCollector/Deployment: can set a priorityClassName" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=foo' \ - --set 'telemetryCollector.priorityClassName=name' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.priorityClassName' | tee /dev/stderr) - - [ "${actual}" = "name" ] -} - -#-------------------------------------------------------------------- -# replicas - -@test "telemetryCollector/Deployment: replicas defaults to 1" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=foo' \ - . | tee /dev/stderr | - yq '.spec.replicas' | tee /dev/stderr) - - [ "${actual}" = "1" ] -} - -@test "telemetryCollector/Deployment: replicas can be set" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=foo' \ - --set 'telemetryCollector.replicas=3' \ - . | tee /dev/stderr | - yq '.spec.replicas' | tee /dev/stderr) - - [ "${actual}" = "3" ] -} - -#-------------------------------------------------------------------- -# Vault - -@test "telemetryCollector/Deployment: vault CA is not configured by default" { - cd `chart_dir` - local object=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=foo' \ - --set 'global.tls.enabled=true' \ - --set 'global.tls.enableAutoEncrypt=true' \ - --set 'global.tls.caCert.secretName=foo' \ - --set 'global.secretsBackend.vault.enabled=true' \ - --set 'global.secretsBackend.vault.consulClientRole=foo' \ - --set 'global.secretsBackend.vault.consulServerRole=test' \ - --set 'global.secretsBackend.vault.consulCARole=test' \ - . | tee /dev/stderr | - yq -r '.spec.template' | tee /dev/stderr) - - local actual=$(echo $object | yq -r '.metadata.annotations | has("vault.hashicorp.com/agent-extra-secret")') - [ "${actual}" = "false" ] - local actual=$(echo $object | yq -r '.metadata.annotations | has("vault.hashicorp.com/ca-cert")') - [ "${actual}" = "false" ] -} - -@test "telemetryCollector/Deployment: vault CA is not configured when secretName is set but secretKey is not" { - cd `chart_dir` - local object=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=foo' \ - --set 'global.tls.enabled=true' \ - --set 'global.tls.enableAutoEncrypt=true' \ - --set 'global.tls.caCert.secretName=foo' \ - --set 'global.secretsBackend.vault.enabled=true' \ - --set 'global.secretsBackend.vault.consulClientRole=foo' \ - --set 'global.secretsBackend.vault.consulServerRole=test' \ - --set 'global.secretsBackend.vault.consulCARole=test' \ - --set 'global.secretsBackend.vault.ca.secretName=ca' \ - . | tee /dev/stderr | - yq -r '.spec.template' | tee /dev/stderr) - - local actual=$(echo $object | yq -r '.metadata.annotations | has("vault.hashicorp.com/agent-extra-secret")') - [ "${actual}" = "false" ] - local actual=$(echo $object | yq -r '.metadata.annotations | has("vault.hashicorp.com/ca-cert")') - [ "${actual}" = "false" ] -} - -@test "telemetryCollector/Deployment: vault CA is not configured when secretKey is set but secretName is not" { - cd `chart_dir` - local object=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=foo' \ - --set 'global.tls.enabled=true' \ - --set 'global.tls.enableAutoEncrypt=true' \ - --set 'global.tls.caCert.secretName=foo' \ - --set 'global.secretsBackend.vault.enabled=true' \ - --set 'global.secretsBackend.vault.consulClientRole=foo' \ - --set 'global.secretsBackend.vault.consulServerRole=test' \ - --set 'global.secretsBackend.vault.consulCARole=test' \ - --set 'global.secretsBackend.vault.ca.secretKey=tls.crt' \ - . | tee /dev/stderr | - yq -r '.spec.template' | tee /dev/stderr) - - local actual=$(echo $object | yq -r '.metadata.annotations | has("vault.hashicorp.com/agent-extra-secret")') - [ "${actual}" = "false" ] - local actual=$(echo $object | yq -r '.metadata.annotations | has("vault.hashicorp.com/ca-cert")') - [ "${actual}" = "false" ] -} - -@test "telemetryCollector/Deployment: vault CA is configured when both secretName and secretKey are set" { - cd `chart_dir` - local object=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=foo' \ - --set 'global.tls.enabled=true' \ - --set 'global.tls.enableAutoEncrypt=true' \ - --set 'global.tls.caCert.secretName=foo' \ - --set 'global.secretsBackend.vault.enabled=true' \ - --set 'global.secretsBackend.vault.consulClientRole=foo' \ - --set 'global.secretsBackend.vault.consulServerRole=test' \ - --set 'global.secretsBackend.vault.consulCARole=test' \ - --set 'global.secretsBackend.vault.ca.secretName=ca' \ - --set 'global.secretsBackend.vault.ca.secretKey=tls.crt' \ - . | tee /dev/stderr | - yq -r '.spec.template' | tee /dev/stderr) - - local actual=$(echo $object | yq -r '.metadata.annotations."vault.hashicorp.com/agent-extra-secret"') - [ "${actual}" = "ca" ] - local actual=$(echo $object | yq -r '.metadata.annotations."vault.hashicorp.com/ca-cert"') - [ "${actual}" = "/vault/custom/tls.crt" ] -} - -@test "telemetryCollector/Deployment: vault tls annotations are set when tls is enabled" { - cd `chart_dir` - local cmd=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=foo' \ - --set 'global.secretsBackend.vault.enabled=true' \ - --set 'global.secretsBackend.vault.consulClientRole=foo' \ - --set 'global.secretsBackend.vault.consulServerRole=bar' \ - --set 'global.secretsBackend.vault.consulCARole=test' \ - --set 'global.tls.enabled=true' \ - --set 'global.tls.enableAutoEncrypt=true' \ - --set 'server.serverCert.secretName=pki_int/issue/test' \ - --set 'global.tls.caCert.secretName=pki_int/cert/ca' \ - . | tee /dev/stderr | - yq -r '.spec.template.metadata' | tee /dev/stderr) - - local actual="$(echo $cmd | - yq -r '.annotations["vault.hashicorp.com/agent-inject-template-serverca.crt"]' | tee /dev/stderr)" - local expected=$'{{- with secret \"pki_int/cert/ca\" -}}\n{{- .Data.certificate -}}\n{{- end -}}' - [ "${actual}" = "${expected}" ] - - local actual="$(echo $cmd | - yq -r '.annotations["vault.hashicorp.com/agent-inject-secret-serverca.crt"]' | tee /dev/stderr)" - [ "${actual}" = "pki_int/cert/ca" ] - - local actual="$(echo $cmd | - yq -r '.annotations["vault.hashicorp.com/agent-init-first"]' | tee /dev/stderr)" - [ "${actual}" = "true" ] - - local actual="$(echo $cmd | - yq -r '.annotations["vault.hashicorp.com/agent-inject"]' | tee /dev/stderr)" - [ "${actual}" = "true" ] - - local actual="$(echo $cmd | - yq -r '.annotations["vault.hashicorp.com/role"]' | tee /dev/stderr)" - [ "${actual}" = "test" ] -} - -@test "telemetryCollector/Deployment: vault agent annotations can be set" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=foo' \ - --set 'global.tls.enabled=true' \ - --set 'global.tls.enableAutoEncrypt=true' \ - --set 'global.tls.caCert.secretName=foo' \ - --set 'global.secretsBackend.vault.enabled=true' \ - --set 'global.secretsBackend.vault.consulClientRole=test' \ - --set 'global.secretsBackend.vault.consulServerRole=foo' \ - --set 'global.secretsBackend.vault.consulCARole=test' \ - --set 'global.secretsBackend.vault.agentAnnotations=foo: bar' \ - . | tee /dev/stderr | - yq -r '.spec.template.metadata.annotations.foo' | tee /dev/stderr) - [ "${actual}" = "bar" ] -} - -#-------------------------------------------------------------------- -# telemetryCollector.cloud - -@test "telemetryCollector/Deployment: success with all cloud bits set" { - cd `chart_dir` - run helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'global.cloud.enabled=true' \ - --set 'global.cloud.clientSecret.secretName=client-secret-name' \ - --set 'global.cloud.clientSecret.secretKey=client-secret-key' \ - --set 'global.cloud.clientId.secretName=client-id-name' \ - --set 'global.cloud.clientId.secretKey=client-id-key' \ - --set 'global.cloud.resourceId.secretName=client-resource-id-name' \ - --set 'global.cloud.resourceId.secretKey=client-resource-id-key' \ - . -} - -@test "telemetryCollector/Deployment: fails when telemetryCollector.cloud.clientId is set and global.cloud.resourceId is not set or global.cloud.clientSecret.secretName is not set" { - cd `chart_dir` - run helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'global.tls.enabled=true' \ - --set 'global.tls.enableAutoEncrypt=true' \ - --set 'global.datacenter=dc-foo' \ - --set 'global.domain=bar' \ - --set 'global.cloud.enabled=true' \ - --set 'global.cloud.clientSecret.secretName=client-id-name' \ - --set 'global.cloud.clientSecret.secretKey=client-id-key' \ - --set 'global.cloud.resourceId.secretName=client-resource-id-name' \ - --set 'global.cloud.resourceId.secretKey=client-resource-id-key' \ - . - [ "$status" -eq 1 ] - [[ "$output" =~ "When global.cloud.enabled is true, global.cloud.resourceId.secretName, global.cloud.clientId.secretName, and global.cloud.clientSecret.secretName must also be set." ]] -} - -@test "telemetryCollector/Deployment: fails when global.cloud.enabled is true and global.cloud.clientSecret.secretName is not set but global.cloud.clientId.secretName and global.cloud.resourceId.secretName is set" { - cd `chart_dir` - run helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'global.tls.enabled=true' \ - --set 'global.tls.enableAutoEncrypt=true' \ - --set 'global.datacenter=dc-foo' \ - --set 'global.domain=bar' \ - --set 'global.cloud.enabled=true' \ - --set 'global.cloud.clientId.secretName=client-id-name' \ - --set 'global.cloud.clientId.secretKey=client-id-key' \ - --set 'global.cloud.resourceId.secretName=resource-id-name' \ - --set 'global.cloud.resourceId.secretKey=resource-id-key' \ - . - [ "$status" -eq 1 ] - [[ "$output" =~ "When global.cloud.enabled is true, global.cloud.resourceId.secretName, global.cloud.clientId.secretName, and global.cloud.clientSecret.secretName must also be set." ]] -} - -@test "telemetryCollector/Deployment: fails when global.cloud.enabled is true and global.cloud.resourceId.secretName is not set but global.cloud.clientId.secretName and global.cloud.clientSecret.secretName is set" { - cd `chart_dir` - run helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'global.tls.enabled=true' \ - --set 'global.tls.enableAutoEncrypt=true' \ - --set 'global.datacenter=dc-foo' \ - --set 'global.domain=bar' \ - --set 'global.cloud.enabled=true' \ - --set 'global.cloud.clientId.secretName=client-id-name' \ - --set 'global.cloud.clientId.secretKey=client-id-key' \ - --set 'global.cloud.clientSecret.secretName=client-secret-id-name' \ - --set 'global.cloud.clientSecret.secretKey=client-secret-id-key' \ - . - [ "$status" -eq 1 ] - [[ "$output" =~ "When global.cloud.enabled is true, global.cloud.resourceId.secretName, global.cloud.clientId.secretName, and global.cloud.clientSecret.secretName must also be set." ]] -} - -@test "telemetryCollector/Deployment: fails when global.cloud.resourceId.secretName is set but global.cloud.resourceId.secretKey is not set." { - cd `chart_dir` - run helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'global.tls.enabled=true' \ - --set 'global.tls.enableAutoEncrypt=true' \ - --set 'global.datacenter=dc-foo' \ - --set 'global.domain=bar' \ - --set 'global.cloud.enabled=true' \ - --set 'global.cloud.clientId.secretName=client-id-name' \ - --set 'global.cloud.clientId.secretKey=client-id-key' \ - --set 'global.cloud.clientSecret.secretName=client-secret-id-name' \ - --set 'global.cloud.clientSecret.secretKey=client-secret-id-key' \ - --set 'global.cloud.resourceId.secretName=resource-id-name' \ - . - [ "$status" -eq 1 ] - [[ "$output" =~ "When either global.cloud.resourceId.secretName or global.cloud.resourceId.secretKey is defined, both must be set." ]] -} - -@test "telemetryCollector/Deployment: fails when global.cloud.authURL.secretName is set but global.cloud.authURL.secretKey is not set." { - cd `chart_dir` - run helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'global.tls.enabled=true' \ - --set 'global.tls.enableAutoEncrypt=true' \ - --set 'global.datacenter=dc-foo' \ - --set 'global.domain=bar' \ - --set 'global.cloud.enabled=true' \ - --set 'global.cloud.clientId.secretName=client-id-name' \ - --set 'global.cloud.clientId.secretKey=client-id-key' \ - --set 'global.cloud.clientSecret.secretName=client-secret-id-name' \ - --set 'global.cloud.clientSecret.secretKey=client-secret-id-key' \ - --set 'global.cloud.resourceId.secretName=resource-id-name' \ - --set 'global.cloud.resourceId.secretKey=resource-id-key' \ - --set 'global.cloud.authUrl.secretName=auth-url-name' \ - . - [ "$status" -eq 1 ] - - [[ "$output" =~ "When either global.cloud.authUrl.secretName or global.cloud.authUrl.secretKey is defined, both must be set." ]] -} - -@test "telemetryCollector/Deployment: fails when global.cloud.authURL.secretKey is set but global.cloud.authURL.secretName is not set." { - cd `chart_dir` - run helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'global.tls.enabled=true' \ - --set 'global.tls.enableAutoEncrypt=true' \ - --set 'global.datacenter=dc-foo' \ - --set 'global.domain=bar' \ - --set 'global.cloud.enabled=true' \ - --set 'global.cloud.clientId.secretName=client-id-name' \ - --set 'global.cloud.clientId.secretKey=client-id-key' \ - --set 'global.cloud.clientSecret.secretName=client-secret-id-name' \ - --set 'global.cloud.clientSecret.secretKey=client-secret-id-key' \ - --set 'global.cloud.resourceId.secretName=resource-id-name' \ - --set 'global.cloud.resourceId.secretKey=resource-id-key' \ - --set 'global.cloud.authUrl.secretKey=auth-url-key' \ - . - [ "$status" -eq 1 ] - - [[ "$output" =~ "When either global.cloud.authUrl.secretName or global.cloud.authUrl.secretKey is defined, both must be set." ]] -} - -@test "telemetryCollector/Deployment: fails when global.cloud.apiHost.secretName is set but global.cloud.apiHost.secretKey is not set." { - cd `chart_dir` - run helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'global.tls.enabled=true' \ - --set 'global.tls.enableAutoEncrypt=true' \ - --set 'global.datacenter=dc-foo' \ - --set 'global.domain=bar' \ - --set 'global.cloud.enabled=true' \ - --set 'global.cloud.clientId.secretName=client-id-name' \ - --set 'global.cloud.clientId.secretKey=client-id-key' \ - --set 'global.cloud.clientSecret.secretName=client-secret-id-name' \ - --set 'global.cloud.clientSecret.secretKey=client-secret-id-key' \ - --set 'global.cloud.resourceId.secretName=resource-id-name' \ - --set 'global.cloud.resourceId.secretKey=resource-id-key' \ - --set 'global.cloud.apiHost.secretName=auth-url-name' \ - . - [ "$status" -eq 1 ] - - [[ "$output" =~ "When either global.cloud.apiHost.secretName or global.cloud.apiHost.secretKey is defined, both must be set." ]] -} - -@test "telemetryCollector/Deployment: fails when global.cloud.apiHost.secretKey is set but global.cloud.apiHost.secretName is not set." { - cd `chart_dir` - run helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'global.tls.enabled=true' \ - --set 'global.tls.enableAutoEncrypt=true' \ - --set 'global.datacenter=dc-foo' \ - --set 'global.domain=bar' \ - --set 'global.cloud.enabled=true' \ - --set 'global.cloud.clientId.secretName=client-id-name' \ - --set 'global.cloud.clientId.secretKey=client-id-key' \ - --set 'global.cloud.clientSecret.secretName=client-secret-id-name' \ - --set 'global.cloud.clientSecret.secretKey=client-secret-id-key' \ - --set 'global.cloud.resourceId.secretName=resource-id-name' \ - --set 'global.cloud.resourceId.secretKey=resource-id-key' \ - --set 'global.cloud.apiHost.secretKey=auth-url-key' \ - . - [ "$status" -eq 1 ] - - [[ "$output" =~ "When either global.cloud.apiHost.secretName or global.cloud.apiHost.secretKey is defined, both must be set." ]] -} - -@test "telemetryCollector/Deployment: fails when global.cloud.scadaAddress.secretName is set but global.cloud.scadaAddress.secretKey is not set." { - cd `chart_dir` - run helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'global.tls.enabled=true' \ - --set 'global.tls.enableAutoEncrypt=true' \ - --set 'global.datacenter=dc-foo' \ - --set 'global.domain=bar' \ - --set 'global.cloud.enabled=true' \ - --set 'global.cloud.clientId.secretName=client-id-name' \ - --set 'global.cloud.clientId.secretKey=client-id-key' \ - --set 'global.cloud.clientSecret.secretName=client-secret-id-name' \ - --set 'global.cloud.clientSecret.secretKey=client-secret-id-key' \ - --set 'global.cloud.resourceId.secretName=resource-id-name' \ - --set 'global.cloud.resourceId.secretKey=resource-id-key' \ - --set 'global.cloud.scadaAddress.secretName=scada-address-name' \ - . - [ "$status" -eq 1 ] - - [[ "$output" =~ "When either global.cloud.scadaAddress.secretName or global.cloud.scadaAddress.secretKey is defined, both must be set." ]] -} - -@test "telemetryCollector/Deployment: fails when global.cloud.scadaAddress.secretKey is set but global.cloud.scadaAddress.secretName is not set." { - cd `chart_dir` - run helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'global.tls.enabled=true' \ - --set 'global.tls.enableAutoEncrypt=true' \ - --set 'global.datacenter=dc-foo' \ - --set 'global.domain=bar' \ - --set 'global.cloud.enabled=true' \ - --set 'global.cloud.clientId.secretName=client-id-name' \ - --set 'global.cloud.clientId.secretKey=client-id-key' \ - --set 'global.cloud.clientSecret.secretName=client-secret-id-name' \ - --set 'global.cloud.clientSecret.secretKey=client-secret-id-key' \ - --set 'global.cloud.resourceId.secretName=resource-id-name' \ - --set 'global.cloud.resourceId.secretKey=resource-id-key' \ - --set 'global.cloud.scadaAddress.secretKey=scada-address-key' \ - . - [ "$status" -eq 1 ] - - [[ "$output" =~ "When either global.cloud.scadaAddress.secretName or global.cloud.scadaAddress.secretKey is defined, both must be set." ]] -} - -@test "telemetryCollector/Deployment: fails when telemetryCollector.cloud.clientId.secretName is set but telemetryCollector.cloud.clientId.secretKey is not set." { - cd `chart_dir` - run helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'telemetryCollector.cloud.clientId.secretName=client-id-name' \ - --set 'telemetryCollector.clientSecret.secretName=client-secret-id-name' \ - --set 'telemetryCollector.clientSecret.secretKey=client-secret-id-key' \ - --set 'global.resourceId.secretName=resource-id-name' \ - --set 'global.resourceId.secretKey=resource-id-key' \ - . - [ "$status" -eq 1 ] - - [[ "$output" =~ "When telemetryCollector.cloud.clientId.secretName is set, global.cloud.resourceId.secretName, telemetryCollector.cloud.clientSecret.secretName must also be set." ]] -} - -@test "telemetryCollector/Deployment: fails when telemetryCollector.cloud.clientId.secretKey is set but telemetryCollector.cloud.clientId.secretName is not set." { - cd `chart_dir` - run helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'telemetryCollector.cloud.clientId.secretName=client-id-name' \ - --set 'telemetryCollector.cloud.clientId.secretKey=client-id-key' \ - --set 'telemetryCollector.clientSecret.secretName=client-secret-id-name' \ - --set 'global.resourceId.secretName=resource-id-name' \ - --set 'global.resourceId.secretKey=resource-id-key' \ - . - [ "$status" -eq 1 ] - - [[ "$output" =~ "When telemetryCollector.cloud.clientId.secretName is set, global.cloud.resourceId.secretName, telemetryCollector.cloud.clientSecret.secretName must also be set." ]] -} - -@test "telemetryCollector/Deployment: fails when telemetryCollector.cloud.clientSecret.secretName is set but telemetryCollector.cloud.clientId.secretName is not set." { - cd `chart_dir` - run helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'telemetryCollector.cloud.clientId.secretName=client-id-name' \ - --set 'telemetryCollector.cloud.clientId.secretKey=client-id-key' \ - --set 'telemetryCollector.clientSecret.secretName=client-secret-id-name' \ - --set 'telemetryCollector.clientSecret.secretKey=client-secret-key-name' \ - . - [ "$status" -eq 1 ] - - [[ "$output" =~ "When telemetryCollector.cloud.clientId.secretName is set, global.cloud.resourceId.secretName, telemetryCollector.cloud.clientSecret.secretName must also be set." ]] -} - -@test "telemetryCollector/Deployment: fails when telemetryCollector.cloud.clientId.secretName is set but telemetry.cloud.clientId.secretKey is not set." { - cd `chart_dir` - run helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'telemetryCollector.cloud.clientId.secretName=client-id-name' \ - --set 'telemetryCollector.cloud.clientSecret.secretName=client-secret-name' \ - --set 'global.cloud.resourceId.secretName=resource-id-name' \ - . - [ "$status" -eq 1 ] - - [[ "$output" =~ "When either telemetryCollector.cloud.clientId.secretName or telemetryCollector.cloud.clientId.secretKey is defined, both must be set." ]] -} - -@test "telemetryCollector/Deployment: fails when telemetryCollector.cloud.clientSecret.secretName is set but telemetry.cloud.clientSecret.secretKey is not set." { - cd `chart_dir` - run helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'telemetryCollector.cloud.clientId.secretName=client-id-name' \ - --set 'telemetryCollector.cloud.clientId.secretKey=client-id-key' \ - --set 'telemetryCollector.cloud.clientSecret.secretName=client-secret-name' \ - --set 'global.cloud.resourceId.secretName=resource-id-name' \ - . - [ "$status" -eq 1 ] - - [[ "$output" =~ "When either telemetryCollector.cloud.clientSecret.secretName or telemetryCollector.cloud.clientSecret.secretKey is defined, both must be set." ]] -} - -@test "telemetryCollector/Deployment: fails when telemetryCollector.cloud.clientId and telemetryCollector.cloud.clientSecret is set but global.cloud.resourceId.secretKey is not set." { - cd `chart_dir` - run helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'telemetryCollector.cloud.clientId.secretName=client-id-name' \ - --set 'telemetryCollector.cloud.clientId.secretKey=client-id-key' \ - --set 'telemetryCollector.cloud.clientSecret.secretName=client-secret-name' \ - --set 'telemetryCollector.cloud.clientSecret.secretKey=client-secret-key' \ - --set 'global.cloud.resourceId.secretName=resource-id-name' \ - . - [ "$status" -eq 1 ] - - [[ "$output" =~ "When telemetryCollector has clientId and clientSecret .global.cloud.resourceId.secretKey must be set" ]] -} - -#-------------------------------------------------------------------- -# global.tls.enabled - -@test "telemetryCollector/Deployment: sets -tls-disabled args when when not using TLS." { - cd `chart_dir` - - local flags=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'global.tls.enabled=false' \ - . | yq -r .spec.template.spec.containers[1].args) - - local actual=$(echo $flags | yq -r '. | any(contains("-tls-disabled"))') - [ "${actual}" = 'true' ] - -} - -@test "telemetryCollector/Deployment: -ca-certs set correctly when using TLS." { - cd `chart_dir` - local flags=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'global.tls.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[1].args' | tee /dev/stderr) - - local actual=$(echo $flags | yq -r '. | any(contains("-ca-certs=/consul/tls/ca/tls.crt"))' | tee /dev/stderr) - [ "${actual}" = 'true' ] -} - -#-------------------------------------------------------------------- -# External Server - -@test "telemetryCollector/Deployment: sets external server args when global.tls.enabled and externalServers.enabled" { - cd `chart_dir` - local flags=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'global.tls.enabled=true' \ - --set 'externalServers.enabled=true' \ - --set 'externalServers.hosts[0]=external-consul.host' \ - --set 'externalServers.httpsPort=8501' \ - --set 'externalServers.tlsServerName=foo.tls.server' \ - --set 'externalServers.useSystemRoots=true' \ - --set 'server.enabled=false' \ - --set 'client.enabled=false' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[1].args' | tee /dev/stderr) - - local actual=$(echo $flags | yq -r '. | any(contains("-ca-certs=/consul/tls/ca/tls.crt"))' | tee /dev/stderr) - [ "${actual}" = 'false' ] - - local actual=$(echo $flags | yq -r '. | any(contains("-tls-server-name=foo.tls.server"))' | tee /dev/stderr) - [ "${actual}" = 'true' ] - - local actual=$(echo $flags | jq -r '. | any(contains("-addresses=external-consul.host"))' | tee /dev/stderr) - [ "${actual}" = 'true' ] -} - -#-------------------------------------------------------------------- -# Admin Partitions - -@test "telemetryCollector/Deployment: partition flags are set when using admin partitions" { - cd `chart_dir` - local flags=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'global.enableConsulNamespaces=true' \ - --set 'global.adminPartitions.enabled=true' \ - --set 'global.adminPartitions.name=hashi' \ - --set 'global.acls.manageSystemACLs=true' \ - . | tee /dev/stderr | - yq '.spec.template.spec.containers[1].args' | tee /dev/stderr) - - local actual=$(echo $flags | jq -r '. | any(contains("-login-partition=hashi"))' | tee /dev/stderr) - [ "${actual}" = 'true' ] - - local actual=$(echo $flags | jq -r '. | any(contains("-service-partition=hashi"))' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "telemetryCollector/Deployment: consul-ca-cert volume mount is not set when using externalServers and useSystemRoots" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'global.acls.manageSystemACLs=true' \ - --set 'global.tls.enabled=true' \ - --set 'server.enabled=false' \ - --set 'externalServers.hosts[0]=external-consul.host' \ - --set 'externalServers.enabled=true' \ - --set 'externalServers.useSystemRoots=true' \ - . | tee /dev/stderr | - yq '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) - [ "${actual}" = "" ] -} - -@test "telemetryCollector/Deployment: config volume mount is set when config exists" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'telemetryCollector.customExporterConfig="foo"' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "config") | .name' | tee /dev/stderr) - [ "${actual}" = "config" ] -} - -@test "telemetryCollector/Deployment: config flag is set when config exists" { - cd `chart_dir` - local flags=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'telemetryCollector.customExporterConfig="foo"' \ - . | tee /dev/stderr | - yq '.spec.template.spec.containers[0].command') - - local actual=$(echo $flags | yq -r '. | any(contains("-config-file-path /consul/config/config.json"))') - [ "${actual}" = "true" ] -} - -@test "telemetryCollector/Deployment: consul-ca-cert volume mount is not set on acl-init when using externalServers and useSystemRoots" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'global.acls.manageSystemACLs=true' \ - --set 'global.tls.enabled=true' \ - --set 'server.enabled=false' \ - --set 'externalServers.hosts[0]=external-consul.host' \ - --set 'externalServers.enabled=true' \ - --set 'externalServers.useSystemRoots=true' \ - . | tee /dev/stderr | - yq '.spec.template.spec.initContainers[1].volumeMounts[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) - [ "${actual}" = "" ] -} -#-------------------------------------------------------------------- -# trustedCAs - -@test "telemetryCollector/Deployment: trustedCAs: if trustedCAs is set command is modified correctly" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'global.trustedCAs[0]=-----BEGIN CERTIFICATE----- -MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].command[2] | contains("cat < /trusted-cas/custom-ca-0.pem")' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "telemetryCollector/Deployment: trustedCAs: if multiple Trusted cas were set" { - cd `chart_dir` - local object=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'global.trustedCAs[0]=-----BEGIN CERTIFICATE----- -MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ - --set 'global.trustedCAs[1]=-----BEGIN CERTIFICATE----- -MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0]' | tee /dev/stderr) - - - local actual=$(echo $object | jq '.command[2] | contains("cat < /trusted-cas/custom-ca-0.pem")' | tee /dev/stderr) - [ "${actual}" = "true" ] - local actual=$(echo $object | jq '.command[2] | contains("cat < /trusted-cas/custom-ca-1.pem")' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "telemetryCollector/Deployment: trustedCAs: if trustedCAs is set /trusted-cas volumeMount is added" { - cd `chart_dir` - local object=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'global.trustedCAs[0]=-----BEGIN CERTIFICATE----- -MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ - . | tee /dev/stderr | yq -r '.spec.template.spec' | tee /dev/stderr) - local actual=$(echo $object | jq -r '.volumes[] | select(.name == "trusted-cas") | .name' | tee /dev/stderr) - [ "${actual}" = "trusted-cas" ] -} - - -@test "telemetryCollector/Deployment: trustedCAs: if trustedCAs is set SSL_CERT_DIR env var is set" { - cd `chart_dir` - local object=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'global.trustedCAs[0]=-----BEGIN CERTIFICATE----- -MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ - . | tee /dev/stderr | yq -r '.spec.template.spec.containers[0].env[] | select(.name == "SSL_CERT_DIR")' | tee /dev/stderr) - - local actual=$(echo $object | jq -r '.name' | tee /dev/stderr) - [ "${actual}" = "SSL_CERT_DIR" ] - local actual=$(echo $object | jq -r '.value' | tee /dev/stderr) - [ "${actual}" = "/etc/ssl/certs:/trusted-cas" ] -} - -#-------------------------------------------------------------------- -# extraLabels - -@test "telemetryCollector/Deployment: no extra labels defined by default" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - . | tee /dev/stderr | - yq -r '.spec.template.metadata.labels | del(."app") | del(."chart") | del(."release") | del(."component") | del(."consul.hashicorp.com/connect-inject-managed-by")' \ - | tee /dev/stderr) - [ "${actual}" = "{}" ] -} - -@test "telemetryCollector/Deployment: extra global labels can be set" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'global.extraLabels.foo=bar' \ - . | tee /dev/stderr) - local actualBar=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) - [ "${actualBar}" = "bar" ] - local actualTemplateBar=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) - [ "${actualTemplateBar}" = "bar" ] -} - -@test "telemetryCollector/Deployment: multiple global extra labels can be set" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.image=bar' \ - --set 'global.extraLabels.foo=bar' \ - --set 'global.extraLabels.baz=qux' \ - . | tee /dev/stderr) - local actualFoo=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) - local actualBaz=$(echo "${actual}" | yq -r '.metadata.labels.baz' | tee /dev/stderr) - [ "${actualFoo}" = "bar" ] - [ "${actualBaz}" = "qux" ] - local actualTemplateFoo=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) - local actualTemplateBaz=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.baz' | tee /dev/stderr) - [ "${actualTemplateFoo}" = "bar" ] - [ "${actualTemplateBaz}" = "qux" ] -} - -#-------------------------------------------------------------------- -# extraEnvironmentVariables - -@test "telemetryCollector/Deployment: extra environment variables" { - cd `chart_dir` - local object=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.extraEnvironmentVars.HCP_AUTH_TLS=insecure' \ - --set 'telemetryCollector.extraEnvironmentVars.foo=bar' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r 'map(select(.name == "HCP_AUTH_TLS")) | .[0].value' | tee /dev/stderr) - [ "${actual}" = "insecure" ] - - local actual=$(echo $object | - yq -r 'map(select(.name == "foo")) | .[0].value' | tee /dev/stderr) - [ "${actual}" = "bar" ] -} - -#-------------------------------------------------------------------- -# logLevel - -@test "telemetryCollector/Deployment: use global.logLevel by default" { - cd `chart_dir` - local cmd=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.initContainers[0].command' | tee /dev/stderr) - - local actual=$(echo "$cmd" | - yq 'any(contains("-log-level=info"))' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "telemetryCollector/Deployment: override global.logLevel when telemetryCollector.logLevel is set" { - cd `chart_dir` - local cmd=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.logLevel=warn' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.initContainers[0].command' | tee /dev/stderr) - - local actual=$(echo "$cmd" | - yq 'any(contains("-log-level=warn"))' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "telemetryCollector/Deployment: use global.logLevel by default for dataplane container" { - cd `chart_dir` - local cmd=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[1].args' | tee /dev/stderr) - - local actual=$(echo "$cmd" | - yq 'any(contains("-log-level=info"))' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "telemetryCollector/Deployment: override global.logLevel when telemetryCollector.logLevel is set for dataplane container" { - cd `chart_dir` - local cmd=$(helm template \ - -s templates/telemetry-collector-deployment.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'telemetryCollector.logLevel=debug' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[1].args' | tee /dev/stderr) - - local actual=$(echo "$cmd" | - yq 'any(contains("-log-level=debug"))' | tee /dev/stderr) - [ "${actual}" = "true" ] -} \ No newline at end of file diff --git a/charts/consul/test/unit/telemetry-collector-podsecuritypolicy.bats b/charts/consul/test/unit/telemetry-collector-podsecuritypolicy.bats deleted file mode 100644 index 90c494eca5..0000000000 --- a/charts/consul/test/unit/telemetry-collector-podsecuritypolicy.bats +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "telemetryCollector/PodSecurityPolicy: disabled by default" { - cd `chart_dir` - assert_empty helm template \ - -s templates/telemetry-collector-podsecuritypolicy.yaml \ - . -} - -@test "telemetryCollector/PodSecurityPolicy: enabled with telemetryCollector and global.enablePodSecurityPolicies=true" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-podsecuritypolicy.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'global.enablePodSecurityPolicies=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} \ No newline at end of file diff --git a/charts/consul/test/unit/telemetry-collector-role.bats b/charts/consul/test/unit/telemetry-collector-role.bats deleted file mode 100644 index 8fa209ef4b..0000000000 --- a/charts/consul/test/unit/telemetry-collector-role.bats +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "telemetryCollector/Role: disabled by default" { - cd `chart_dir` - assert_empty helm template \ - -s templates/telemetry-collector-role.yaml \ - . -} - -@test "telemetryCollector/Role: enabled with telemetryCollector and global.enablePodSecurityPolicies=true" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-role.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'global.enablePodSecurityPolicies=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} \ No newline at end of file diff --git a/charts/consul/test/unit/telemetry-collector-rolebinding.bats b/charts/consul/test/unit/telemetry-collector-rolebinding.bats deleted file mode 100644 index 454c2569fb..0000000000 --- a/charts/consul/test/unit/telemetry-collector-rolebinding.bats +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "telemetryCollector/RoleBinding: disabled by default" { - cd `chart_dir` - assert_empty helm template \ - -s templates/telemetry-collector-rolebinding.yaml \ - . -} - -@test "telemetryCollector/RoleBinding: enabled with telemetryCollector and global.enablePodSecurityPolicies=true" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-rolebinding.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'global.enablePodSecurityPolicies=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} \ No newline at end of file diff --git a/charts/consul/test/unit/telemetry-collector-service.bats b/charts/consul/test/unit/telemetry-collector-service.bats deleted file mode 100755 index c5406b8124..0000000000 --- a/charts/consul/test/unit/telemetry-collector-service.bats +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "telemetryCollector/Service: disabled by default" { - cd `chart_dir` - assert_empty helm template \ - -s templates/telemetry-collector-service.yaml \ - . -} - -@test "telemetryCollector/Service: enabled by default with telemetryCollector, connectInject enabled" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-service.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'connectInject.enabled=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "telemetryCollector/Service: enabled with telemetryCollector.enabled=true" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-service.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'connectInject.enabled=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -#-------------------------------------------------------------------- -# consul.name - -@test "telemetryCollector/Service: name is constant regardless of consul name" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-service.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'consul.name=foobar' \ - . | tee /dev/stderr | - yq -r '.metadata.name' | tee /dev/stderr) - [ "${actual}" = "consul-telemetry-collector" ] -} - -#-------------------------------------------------------------------- -# annotations - -@test "telemetryCollector/Service: no annotations by default" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-service.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'connectInject.enabled=true' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "telemetryCollector/Service: can set annotations" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-service.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'connectInject.enabled=true' \ - --set 'telemetryCollector.service.annotations=key: value' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations.key' | tee /dev/stderr) - [ "${actual}" = "value" ] -} - -#-------------------------------------------------------------------- -# port - -@test "telemetryCollector/Service: has default port" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-service.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'connectInject.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].port' | tee /dev/stderr) - [ "${actual}" = "9356" ] -} \ No newline at end of file diff --git a/charts/consul/test/unit/telemetry-collector-serviceaccount.bats b/charts/consul/test/unit/telemetry-collector-serviceaccount.bats deleted file mode 100644 index 589632d5b5..0000000000 --- a/charts/consul/test/unit/telemetry-collector-serviceaccount.bats +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "telemetryCollector/ServiceAccount: disabled by default" { - cd `chart_dir` - assert_empty helm template \ - -s templates/telemetry-collector-serviceaccount.yaml \ - . -} - -@test "telemetryCollector/ServiceAccount: enabled with telemetryCollector, connectInject enabled" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-serviceaccount.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'connectInject.enabled=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -#-------------------------------------------------------------------- -# global.imagePullSecrets - -@test "telemetryCollector/ServiceAccount: can set image pull secrets" { - cd `chart_dir` - local object=$(helm template \ - -s templates/telemetry-collector-serviceaccount.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'connectInject.enabled=true' \ - --set 'global.imagePullSecrets[0].name=my-secret' \ - --set 'global.imagePullSecrets[1].name=my-secret2' \ - . | tee /dev/stderr) - - local actual=$(echo "$object" | - yq -r '.imagePullSecrets[0].name' | tee /dev/stderr) - [ "${actual}" = "my-secret" ] - - local actual=$(echo "$object" | - yq -r '.imagePullSecrets[1].name' | tee /dev/stderr) - [ "${actual}" = "my-secret2" ] -} - -#-------------------------------------------------------------------- -# telemetryCollector.serviceAccount.annotations - -@test "telemetryCollector/ServiceAccount: no annotations by default" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-serviceaccount.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'connectInject.enabled=true' \ - . | tee /dev/stderr | - yq '.metadata.annotations | length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "telemetryCollector/ServiceAccount: annotations when enabled" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-serviceaccount.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'connectInject.enabled=true' \ - --set "telemetryCollector.serviceAccount.annotations=foo: bar" \ - . | tee /dev/stderr | - yq -r '.metadata.annotations.foo' | tee /dev/stderr) - [ "${actual}" = "bar" ] -} - - -#-------------------------------------------------------------------- -# consul.name - -@test "telemetryCollector/ServiceAccount: name is constant regardless of consul name" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/telemetry-collector-serviceaccount.yaml \ - --set 'telemetryCollector.enabled=true' \ - --set 'consul.name=foobar' \ - . | tee /dev/stderr | - yq -r '.metadata.name' | tee /dev/stderr) - [ "${actual}" = "consul-telemetry-collector" ] -} diff --git a/charts/consul/test/unit/webhook-cert-manager-clusterrole.bats b/charts/consul/test/unit/webhook-cert-manager-clusterrole.bats index 4d1a4abdd2..d58ce20928 100644 --- a/charts/consul/test/unit/webhook-cert-manager-clusterrole.bats +++ b/charts/consul/test/unit/webhook-cert-manager-clusterrole.bats @@ -130,7 +130,7 @@ load _helpers #-------------------------------------------------------------------- # Vault -@test "webhookCertManager/ClusterRole: disabled when the following are configured - global.secretsBackend.vault.enabled, global.secretsBackend.vault.enabled, global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, and global.secretsBackend.vault.connectInject.caCert.secretName" { +@test "webhookCertManager/ClusterRole: disabled when the following are configured - global.secretsBackend.vault.enabled, global.secretsBackend.vault.enabled, global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName, global.secretsBackend.vault.controllerRole, global.secretsBackend.vault.controller.tlsCert.secretName, and .global.secretsBackend.vault.controller.caCert.secretName" { cd `chart_dir` assert_empty helm template \ -s templates/webhook-cert-manager-clusterrole.yaml \ @@ -142,6 +142,9 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=inject-ca-role' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=pki/issue/connect-webhook-cert-dc1' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=pki/issue/connect-webhook-cert-dc1' \ + --set 'global.secretsBackend.vault.controllerRole=test' \ + --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ + --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ --set 'global.secretsBackend.vault.consulClientRole=foo' \ --set 'global.secretsBackend.vault.consulServerRole=bar' \ --set 'global.secretsBackend.vault.consulCARole=test2' \ diff --git a/charts/consul/test/unit/webhook-cert-manager-clusterrolebinding.bats b/charts/consul/test/unit/webhook-cert-manager-clusterrolebinding.bats index 2e507d279d..89a60a0ef3 100644 --- a/charts/consul/test/unit/webhook-cert-manager-clusterrolebinding.bats +++ b/charts/consul/test/unit/webhook-cert-manager-clusterrolebinding.bats @@ -24,7 +24,7 @@ load _helpers #-------------------------------------------------------------------- # Vault -@test "webhookCertManager/ClusterRoleBinding: disabled when the following are configured - global.secretsBackend.vault.enabled, global.secretsBackend.vault.enabled, global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, and global.secretsBackend.vault.connectInject.caCert.secretName" { +@test "webhookCertManager/ClusterRoleBinding: disabled when the following are configured - global.secretsBackend.vault.enabled, global.secretsBackend.vault.enabled, global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName, global.secretsBackend.vault.controllerRole, global.secretsBackend.vault.controller.tlsCert.secretName, and .global.secretsBackend.vault.controller.caCert.secretName" { cd `chart_dir` assert_empty helm template \ -s templates/webhook-cert-manager-clusterrolebinding.yaml \ @@ -36,6 +36,9 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=inject-ca-role' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=pki/issue/connect-webhook-cert-dc1' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=pki/issue/connect-webhook-cert-dc1' \ + --set 'global.secretsBackend.vault.controllerRole=test' \ + --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ + --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ --set 'global.secretsBackend.vault.consulClientRole=foo' \ --set 'global.secretsBackend.vault.consulServerRole=bar' \ --set 'global.secretsBackend.vault.consulCARole=test2' \ diff --git a/charts/consul/test/unit/webhook-cert-manager-configmap.bats b/charts/consul/test/unit/webhook-cert-manager-configmap.bats index 196da220d4..774ada21d1 100644 --- a/charts/consul/test/unit/webhook-cert-manager-configmap.bats +++ b/charts/consul/test/unit/webhook-cert-manager-configmap.bats @@ -24,7 +24,7 @@ load _helpers #-------------------------------------------------------------------- # Vault -@test "webhookCertManager/Configmap: disabled when the following are configured - global.secretsBackend.vault.enabled, global.secretsBackend.vault.enabled, global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, and global.secretsBackend.vault.connectInject.caCert.secretName" { +@test "webhookCertManager/Configmap: disabled when the following are configured - global.secretsBackend.vault.enabled, global.secretsBackend.vault.enabled, global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName, global.secretsBackend.vault.controllerRole, global.secretsBackend.vault.controller.tlsCert.secretName, and .global.secretsBackend.vault.controller.caCert.secretName" { cd `chart_dir` assert_empty helm template \ -s templates/webhook-cert-manager-configmap.yaml \ @@ -36,6 +36,9 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=inject-ca-role' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=pki/issue/connect-webhook-cert-dc1' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=pki/issue/connect-webhook-cert-dc1' \ + --set 'global.secretsBackend.vault.controllerRole=test' \ + --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ + --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ --set 'global.secretsBackend.vault.consulClientRole=foo' \ --set 'global.secretsBackend.vault.consulServerRole=bar' \ --set 'global.secretsBackend.vault.consulCARole=test2' \ diff --git a/charts/consul/test/unit/webhook-cert-manager-deployment.bats b/charts/consul/test/unit/webhook-cert-manager-deployment.bats index 7d1a028d20..c0e54ccd25 100644 --- a/charts/consul/test/unit/webhook-cert-manager-deployment.bats +++ b/charts/consul/test/unit/webhook-cert-manager-deployment.bats @@ -66,7 +66,7 @@ load _helpers #-------------------------------------------------------------------- # Vault -@test "webhookCertManager/Deployment: disabled when the following are configured - global.secretsBackend.vault.enabled, global.secretsBackend.vault.enabled, global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, and global.secretsBackend.vault.connectInject.caCert.secretName" { +@test "webhookCertManager/Deployment: disabled when the following are configured - global.secretsBackend.vault.enabled, global.secretsBackend.vault.enabled, global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName, global.secretsBackend.vault.controllerRole, global.secretsBackend.vault.controller.tlsCert.secretName, and .global.secretsBackend.vault.controller.caCert.secretName" { cd `chart_dir` assert_empty helm template \ -s templates/webhook-cert-manager-deployment.yaml \ @@ -78,6 +78,9 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=inject-ca-role' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=pki/issue/connect-webhook-cert-dc1' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=pki/issue/connect-webhook-cert-dc1' \ + --set 'global.secretsBackend.vault.controllerRole=test' \ + --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ + --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ --set 'global.secretsBackend.vault.consulClientRole=foo' \ --set 'global.secretsBackend.vault.consulServerRole=bar' \ --set 'global.secretsBackend.vault.consulCARole=test2' \ diff --git a/charts/consul/test/unit/webhook-cert-manager-podsecuritypolicy.bats b/charts/consul/test/unit/webhook-cert-manager-podsecuritypolicy.bats index d8e16f867c..820be91404 100644 --- a/charts/consul/test/unit/webhook-cert-manager-podsecuritypolicy.bats +++ b/charts/consul/test/unit/webhook-cert-manager-podsecuritypolicy.bats @@ -35,7 +35,7 @@ load _helpers #-------------------------------------------------------------------- # Vault -@test "webhookCertManager/PodSecurityPolicy: disabled when the following are configured - global.secretsBackend.vault.enabled, global.secretsBackend.vault.enabled, global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, and global.secretsBackend.vault.connectInject.caCert.secretName" { +@test "webhookCertManager/PodSecurityPolicy: disabled when the following are configured - global.secretsBackend.vault.enabled, global.secretsBackend.vault.enabled, global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName, global.secretsBackend.vault.controllerRole, global.secretsBackend.vault.controller.tlsCert.secretName, and .global.secretsBackend.vault.controller.caCert.secretName" { cd `chart_dir` assert_empty helm template \ -s templates/webhook-cert-manager-podsecuritypolicy.yaml \ @@ -48,6 +48,9 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=inject-ca-role' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=pki/issue/connect-webhook-cert-dc1' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=pki/issue/connect-webhook-cert-dc1' \ + --set 'global.secretsBackend.vault.controllerRole=test' \ + --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ + --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ --set 'global.secretsBackend.vault.consulClientRole=foo' \ --set 'global.secretsBackend.vault.consulServerRole=bar' \ --set 'global.secretsBackend.vault.consulCARole=test2' \ diff --git a/charts/consul/test/unit/webhook-cert-manager-serviceaccount.bats b/charts/consul/test/unit/webhook-cert-manager-serviceaccount.bats index f420e7319c..766d20fb66 100644 --- a/charts/consul/test/unit/webhook-cert-manager-serviceaccount.bats +++ b/charts/consul/test/unit/webhook-cert-manager-serviceaccount.bats @@ -45,7 +45,7 @@ load _helpers #-------------------------------------------------------------------- # Vault -@test "webhookCertManager/ServiceAccount: disabled when the following are configured - global.secretsBackend.vault.enabled, global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, and global.secretsBackend.vault.connectInject.caCert.secretName" { +@test "webhookCertManager/ServiceAccount: disabled when the following are configured - global.secretsBackend.vault.enabled, global.secretsBackend.vault.connectInjectRole, global.secretsBackend.vault.connectInject.tlsCert.secretName, global.secretsBackend.vault.connectInject.caCert.secretName, global.secretsBackend.vault.controllerRole, global.secretsBackend.vault.controller.tlsCert.secretName, and .global.secretsBackend.vault.controller.caCert.secretName" { cd `chart_dir` assert_empty helm template \ -s templates/webhook-cert-manager-serviceaccount.yaml \ @@ -57,6 +57,9 @@ load _helpers --set 'global.secretsBackend.vault.connectInjectRole=inject-ca-role' \ --set 'global.secretsBackend.vault.connectInject.tlsCert.secretName=pki/issue/connect-webhook-cert-dc1' \ --set 'global.secretsBackend.vault.connectInject.caCert.secretName=pki/issue/connect-webhook-cert-dc1' \ + --set 'global.secretsBackend.vault.controllerRole=test' \ + --set 'global.secretsBackend.vault.controller.caCert.secretName=foo/ca' \ + --set 'global.secretsBackend.vault.controller.tlsCert.secretName=foo/tls' \ --set 'global.secretsBackend.vault.consulClientRole=foo' \ --set 'global.secretsBackend.vault.consulServerRole=bar' \ --set 'global.secretsBackend.vault.consulCARole=test2' \ diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 954680262a..7d0379d6f4 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -66,7 +66,7 @@ global: # image: "hashicorp/consul-enterprise:1.10.0-ent" # ``` # @default: hashicorp/consul: - image: docker.mirror.hashicorp.services/hashicorppreview/consul:1.17-dev + image: docker.mirror.hashicorp.services/hashicorppreview/consul:1.14-dev # Array of objects containing image pull secret names that will be applied to each service account. # This can be used to reference image pull secrets if using a custom consul or consul-k8s-control-plane Docker image. @@ -86,7 +86,7 @@ global: # image that is used for functionality such as catalog sync. # This can be overridden per component. # @default: hashicorp/consul-k8s-control-plane: - imageK8S: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.3.0-dev + imageK8S: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.0.10-dev # The name of the datacenter that the agents should # register as. This can't be changed once the Consul cluster is up and running @@ -164,6 +164,12 @@ global: # and check the name of `metadata.name`. adminPartitionsRole: "" + # The Vault role to read Consul controller's webhook's + # CA and issue a certificate and private key. + # A Vault policy must be created which grants issue capabilities to + # `global.secretsBackend.vault.controller.tlsCert.secretName`. + controllerRole: "" + # The Vault role to read Consul connect-injector webhook's CA # and issue a certificate and private key. # A Vault policy must be created which grants issue capabilities to @@ -239,6 +245,25 @@ global: additionalConfig: | {} + controller: + # Configuration to the Vault Secret that Kubernetes will use on + # Kubernetes CRD creation, deletion, and update, to get TLS certificates + # used issued from vault to send webhooks to the controller. + tlsCert: + # The Vault secret path that issues TLS certificates for controller + # webhooks. + # @type: string + secretName: null + + # Configuration to the Vault Secret that Kubernetes will use on + # Kubernetes CRD creation, deletion, and update, to get CA certificates + # used issued from vault to send webhooks to the controller. + caCert: + # The Vault secret path that contains the CA certificate for controller + # webhooks. + # @type: string + secretName: null + connectInject: # Configuration to the Vault Secret that Kubernetes uses on # Kubernetes pod creation, deletion, and update, to get CA certificates @@ -289,7 +314,7 @@ global: # The key within the Kubernetes secret or Vault secret key that holds the gossip # encryption key. secretKey: "" - # Override global log verbosity level for gossip-encryption-autogenerate-job pods. One of "trace", "debug", "info", "warn", or "error". + # Override global log verbosity level for `gossip-encryption-autogenerate-job` pods. One of "trace", "debug", "info", "warn", or "error". # @type: string logLevel: "" @@ -387,7 +412,7 @@ global: secretKey: null # This value defines additional annotations for - # tls init jobs. This should be formatted as a multi-line string. + # tls init jobs. Format this value as a multi-line string. # # ```yaml # annotations: | @@ -416,21 +441,15 @@ global: # Override global log verbosity level. One of "trace", "debug", "info", "warn", or "error". # @type: string logLevel: "" - - # A Kubernetes or Vault secret containing the bootstrap token to use for creating policies and - # tokens for all Consul and consul-k8s-control-plane components. If `secretName` and `secretKey` - # are unset, a default secret name and secret key are used. If the secret is populated, then - # we will skip ACL bootstrapping of the servers and will only initialize ACLs for the Consul - # clients and consul-k8s-control-plane system components. - # If the secret is empty, then we will bootstrap ACLs on the Consul servers, and write the - # bootstrap token to this secret. If ACLs are already bootstrapped on the servers, then the - # secret must contain the bootstrap token. + + # A Kubernetes or Vault secret containing the bootstrap token to use for + # creating policies and tokens for all Consul and consul-k8s-control-plane components. + # If set, we will skip ACL bootstrapping of the servers and will only + # initialize ACLs for the Consul clients and consul-k8s-control-plane system components. bootstrapToken: # The name of the Kubernetes or Vault secret that holds the bootstrap token. - # If unset, this defaults to `{{ global.name }}-bootstrap-acl-token`. secretName: null # The key within the Kubernetes or Vault secret that holds the bootstrap token. - # If unset, this defaults to `token`. secretKey: null # If true, an ACL token will be created that can be used in secondary @@ -453,7 +472,7 @@ global: # @type: string secretKey: null - # The resource requests (CPU, memory, etc.) for the server-acl-init and server-acl-init-cleanup pods. + # The resource requests (CPU, memory, etc.) for the server-acl-init and server-acl-init-cleanup pods. # This should be a YAML map corresponding to a Kubernetes # [`ResourceRequirements``](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#resourcerequirements-v1-core) # object. @@ -513,7 +532,7 @@ global: nodeSelector: null # This value defines additional annotations for - # acl init jobs. This should be formatted as a multi-line string. + # acl init jobs. Format this value as a multi-line string. # # ```yaml # annotations: | @@ -589,7 +608,7 @@ global: # @type: string k8sAuthMethodHost: null - # Override global log verbosity level for the create-federation-secret-job pods. One of "trace", "debug", "info", "warn", or "error". + # Override global log verbosity level for the `create-federation-secret-job` pods. One of "trace", "debug", "info", "warn", or "error". # @type: string logLevel: "" @@ -619,15 +638,10 @@ global: # @type: boolean enableGatewayMetrics: true - # Configures the Helm chart’s components to forward envoy metrics for the Consul service mesh to the - # consul-telemetry-collector. This includes gateway metrics and sidecar metrics. - # @type: boolean - enableTelemetryCollector: false - # The name (and tag) of the consul-dataplane Docker image used for the # connect-injected sidecar proxies and mesh, terminating, and ingress gateways. # @default: hashicorp/consul-dataplane: - imageConsulDataplane: docker.mirror.hashicorp.services/hashicorppreview/consul-dataplane:1.3-dev + imageConsulDataplane: docker.mirror.hashicorp.services/hashicorppreview/consul-dataplane:1.1-dev # Configuration for running this Helm chart on the Red Hat OpenShift platform. # This Helm chart currently supports OpenShift v4.x+. @@ -720,21 +734,6 @@ global: # @type: map extraLabels: {} - # Optional PEM-encoded CA certificates that will be added to trusted system CAs. - # - # Example: - # - # ```yaml - # trustedCAs: [ - # | - # -----BEGIN CERTIFICATE----- - # MIIC7jCCApSgAwIBAgIRAIq2zQEVexqxvtxP6J0bXAwwCgYIKoZIzj0EAwIwgbkx - # ... - # ] - # ``` - # @type: array - trustedCAs: [ ] - # Server, when enabled, configures a server cluster to run. This should # be disabled if you plan on connecting to a Consul cluster external to # the Kube cluster. @@ -1226,7 +1225,7 @@ server: # @type: string caCert: null - # [Enterprise Only] Added in Consul 1.8, the audit object allow users to enable auditing + # [Enterprise Only] Added in Consul 1.8, the audit object allow users to enable auditing # and configure a sink and filters for their audit logs. Please refer to # [audit logs](https://developer.hashicorp.com/consul/docs/enterprise/audit-logging) documentation # for further information. @@ -1235,7 +1234,7 @@ server: # global.acls.manageSystemACLs must be enabled to use this feature. enabled: false - # A single entry of the sink object provides configuration for the destination to which Consul + # A single entry of the sink object provides configuration for the destination to which Consul # will log auditing events. # # Example: @@ -1250,7 +1249,7 @@ server: # rotate_duration: 24h # rotate_max_files: 15 # rotate_bytes: 25165824 - # + # # ``` # # The sink object supports the following keys: @@ -1280,43 +1279,6 @@ server: # @type: array sinks: [] - # Settings for potentially limiting timeouts, rate limiting on clients as well - # as servers, and other settings to limit exposure too many requests, requests - # waiting for too long, and other runtime considerations. - limits: - # This object specifies configurations that limit the rate of RPC and gRPC - # requests on the Consul server. Limiting the rate of gRPC and RPC requests - # also limits HTTP requests to the Consul server. - # https://developer.hashicorp.com/consul/docs/agent/config/config-files#request_limits - requestLimits: - # Setting for disabling or enabling rate limiting. If not disabled, it - # enforces the action that will occur when RequestLimitsReadRate - # or RequestLimitsWriteRate is exceeded. The default value of "disabled" will - # prevent any rate limiting from occuring. A value of "enforce" will block - # the request from processings by returning an error. A value of - # "permissive" will not block the request and will allow the request to - # continue processing. - # @type: string - mode: "disabled" - - # Setting that controls how frequently RPC, gRPC, and HTTP - # queries are allowed to happen. In any large enough time interval, rate - # limiter limits the rate to RequestLimitsReadRate tokens per second. - # - # See https://en.wikipedia.org/wiki/Token_bucket for more about token - # buckets. - # @type: integer - readRate: -1 - - # Setting that controls how frequently RPC, gRPC, and HTTP - # writes are allowed to happen. In any large enough time interval, rate - # limiter limits the rate to RequestLimitsWriteRate tokens per second. - # - # See https://en.wikipedia.org/wiki/Token_bucket for more about token - # buckets. - # @type: integer - writeRate: -1 - # Configuration for Consul servers when the servers are running outside of Kubernetes. # When running external servers, configuring these values is recommended # if setting `global.tls.enableAutoEncrypt` to true @@ -2147,96 +2109,6 @@ connectInject: # @type: integer minAvailable: null - # Configuration settings for the Consul API Gateway integration. - apiGateway: - # Enables Consul on Kubernetes to manage the CRDs used for Gateway API. - # Setting this to true will install the CRDs used for the Gateway API when Consul on Kubernetes is installed. - # These CRDs can clash with existing Gateway API CRDs if they are already installed in your cluster. - # If this setting is false, you will need to install the Gateway API CRDs manually. - manageExternalCRDs: true - - # Configuration settings for the GatewayClass installed by Consul on Kubernetes. - managedGatewayClass: - # This value defines [`nodeSelector`](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) - # labels for gateway pod assignment, formatted as a multi-line string. - # - # Example: - # - # ```yaml - # nodeSelector: | - # beta.kubernetes.io/arch: amd64 - # ``` - # - # @type: string - nodeSelector: null - - # Toleration settings for gateway pods created with the managed gateway class. - # This should be a multi-line string matching the - # [Tolerations](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) array in a Pod spec. - # - # @type: string - tolerations: null - - # This value defines the type of Service created for gateways (e.g. LoadBalancer, ClusterIP) - serviceType: LoadBalancer - - # Configuration settings for annotations to be copied from the Gateway to other child resources. - copyAnnotations: - # This value defines a list of annotations to be copied from the Gateway to the Service created, formatted as a multi-line string. - # - # Example: - # - # ```yaml - # service: - # annotations: | - # - external-dns.alpha.kubernetes.io/hostname - # ``` - # - # @type: string - service: null - - # This value defines the number of pods to deploy for each Gateway as well as a min and max number of pods for all Gateways - deployment: - defaultInstances: 1 - maxInstances: 1 - minInstances: 1 - - # The name of the OpenShift SecurityContextConstraints resource to use for Gateways. - # Only applicable if `global.openshift.enabled` is true. - # @type: string - openshiftSCCName: "restricted-v2" - - # This value defines the amount we will add to privileged container ports on gateways that use this class. - # This is useful if you don't want to give your containers extra permissions to run privileged ports. - # Example: The gateway listener is defined on port 80, but the underlying value of the port on the container - # will be the 80 + the number defined below. - mapPrivilegedContainerPorts: 0 - - # Configuration for the ServiceAccount created for the api-gateway component - serviceAccount: - # This value defines additional annotations for the client service account. This should be formatted as a multi-line - # string. - # - # ```yaml - # annotations: | - # "sample/annotation1": "foo" - # "sample/annotation2": "bar" - # ``` - # - # @type: string - annotations: null - - # The resource settings for Pods handling traffic for Gateway API. - # @recurse: false - # @type: map - resources: - requests: - memory: "100Mi" - cpu: "100m" - limits: - memory: "100Mi" - cpu: "100m" - # Configures consul-cni plugin for Consul Service mesh services cni: # If true, then all traffic redirection setup uses the consul-cni plugin. @@ -2637,16 +2509,16 @@ connectInject: # - `consul.hashicorp.com/sidecar-proxy-lifecycle-graceful-shutdown-path` # @type: map lifecycle: - # @type: boolean - defaultEnabled: true - # @type: boolean - defaultEnableShutdownDrainListeners: true - # @type: integer - defaultShutdownGracePeriodSeconds: 30 - # @type: integer - defaultGracefulPort: 20600 - # @type: string - defaultGracefulShutdownPath: "/graceful_shutdown" + # @type: boolean + defaultEnabled: true + # @type: boolean + defaultEnableShutdownDrainListeners: true + # @type: integer + defaultShutdownGracePeriodSeconds: 30 + # @type: integer + defaultGracefulPort: 20600 + # @type: string + defaultGracefulShutdownPath: "/graceful_shutdown" # The resource settings for the Connect injected init container. If null, the resources # won't be set for the initContainer. The defaults are optimized for developer instances of @@ -2677,7 +2549,7 @@ meshGateway: # Requirements: consul 1.6.0+ if using `global.acls.manageSystemACLs``. enabled: false - # Override global log verbosity level for mesh-gateway-deployment pods. One of "trace", "debug", "info", "warn", or "error". + # Override global log verbosity level for `mesh-gateway-deployment` pods. One of "trace", "debug", "info", "warn", or "error". # @type: string logLevel: "" @@ -2890,10 +2762,11 @@ meshGateway: # for a specific gateway. # Requirements: consul >= 1.8.0 ingressGateways: - # Enable ingress gateway deployment. Requires `connectInject.enabled=true`. + # Enable ingress gateway deployment. Requires `connectInject.enabled=true` + # and `client.enabled=true`. enabled: false - # Override global log verbosity level for ingress-gateways-deployment pods. One of "trace", "debug", "info", "warn", or "error". + # Override global log verbosity level for `ingress-gateways-deployment` pods. One of "trace", "debug", "info", "warn", or "error". # @type: string logLevel: "" @@ -3060,7 +2933,8 @@ ingressGateways: # for a specific gateway. # Requirements: consul >= 1.8.0 terminatingGateways: - # Enable terminating gateway deployment. Requires `connectInject.enabled=true`. + # Enable terminating gateway deployment. Requires `connectInject.enabled=true` + # and `client.enabled=true`. enabled: false # Override global log verbosity level. One of "trace", "debug", "info", "warn", or "error". @@ -3197,7 +3071,6 @@ terminatingGateways: gateways: - name: terminating-gateway -# [DEPRECATED] Use connectInject.apiGateway instead. This stanza will be removed with the release of Consul 1.17 # Configuration settings for the Consul API Gateway integration apiGateway: # When true the helm chart will install the Consul API Gateway controller @@ -3212,7 +3085,7 @@ apiGateway: # The name (and tag) of the Envoy Docker image used for the # apiGateway. For other Consul compoenents, imageEnvoy has been replaced with Consul Dataplane. # @default: envoyproxy/envoy: - imageEnvoy: "envoyproxy/envoy:v1.25.1" + imageEnvoy: "envoyproxy/envoy:v1.24.10" # Override global log verbosity level for api-gateway-controller pods. One of "debug", "info", "warn", or "error". # @type: string @@ -3399,97 +3272,3 @@ prometheus: # is only useful when running helm template. tests: enabled: true - -telemetryCollector: - # Enables the consul-telemetry-collector deployment - # @type: boolean - enabled: false - - # Override global log verbosity level. One of "trace", "debug", "info", "warn", or "error". - # @type: string - logLevel: "" - - # The name of the Docker image (including any tag) for the containers running - # the consul-telemetry-collector - # @type: string - image: "hashicorp/consul-telemetry-collector:0.0.1" - - # The resource settings for consul-telemetry-collector pods. - # @recurse: false - # @type: map - resources: - requests: - memory: "512Mi" - cpu: "1000m" - limits: - memory: "512Mi" - cpu: "1000m" - - # This value sets the number of consul-telemetry-collector replicas to deploy. - replicas: 1 - - # This value defines additional configuration for the telemetry collector. It should be formatted as a multi-line - # json blob string - # - # ```yaml - # customExporterConfig: | - # {"http_collector_endpoint": "other-otel-collector"} - # ``` - # - # @type: string - customExporterConfig: null - - service: - # This value defines additional annotations for the server service account. This should be formatted as a multi-line - # string. - # - # ```yaml - # annotations: | - # "sample/annotation1": "foo" - # "sample/annotation2": "bar" - # ``` - # - # @type: string - annotations: null - - serviceAccount: - # This value defines additional annotations for the telemetry-collector's service account. This should be formatted - # as a multi-line string. - # - # ```yaml - # annotations: | - # "sample/annotation1": "foo" - # "sample/annotation2": "bar" - # ``` - # - # @type: string - annotations: null - - cloud: - clientId: - secretName: null - secretKey: null - clientSecret: - secretName: null - secretKey: null - - initContainer: - # The resource settings for consul-telemetry-collector initContainer. - # @recurse: false - # @type: map - resources: {} - - # Optional YAML string to specify a nodeSelector config. - # @type: string - nodeSelector: null - - # Optional priorityClassName. - # @type: string - priorityClassName: "" - - # A list of extra environment variables to set within the stateful set. - # These could be used to include proxy settings required for cloud auto-join - # feature, in case kubernetes cluster is behind egress http proxies. Additionally, - # it could be used to configure custom consul parameters. - # @type: map - extraEnvironmentVars: { } diff --git a/charts/demo/Chart.yaml b/charts/demo/Chart.yaml index ffd518082f..82fc51d2df 100644 --- a/charts/demo/Chart.yaml +++ b/charts/demo/Chart.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: v2 name: consul-demo description: A Helm chart for Consul demo app diff --git a/charts/demo/templates/frontend.yaml b/charts/demo/templates/frontend.yaml index f64aaa24a8..38d466e87e 100644 --- a/charts/demo/templates/frontend.yaml +++ b/charts/demo/templates/frontend.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: v1 kind: Service metadata: diff --git a/charts/demo/templates/intentions.yaml b/charts/demo/templates/intentions.yaml index ef36025b16..e0a0a0a5b1 100644 --- a/charts/demo/templates/intentions.yaml +++ b/charts/demo/templates/intentions.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceIntentions metadata: @@ -58,17 +55,6 @@ spec: --- apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceIntentions -metadata: - name: consul-telemetry-collector -spec: - destination: - name: 'consul-telemetry-collector' - sources: - - name: '*' - action: allow ---- -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceIntentions metadata: name: deny-all spec: diff --git a/charts/demo/templates/nginx.yaml b/charts/demo/templates/nginx.yaml index 3d37535733..ebca16f2a0 100644 --- a/charts/demo/templates/nginx.yaml +++ b/charts/demo/templates/nginx.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - --- apiVersion: v1 kind: Service diff --git a/charts/demo/templates/payments.yaml b/charts/demo/templates/payments.yaml index af36c62fb4..362a7ec1e1 100644 --- a/charts/demo/templates/payments.yaml +++ b/charts/demo/templates/payments.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - --- apiVersion: v1 kind: Service diff --git a/charts/demo/templates/postgres.yaml b/charts/demo/templates/postgres.yaml index 033a6724d8..5c7c903b7c 100644 --- a/charts/demo/templates/postgres.yaml +++ b/charts/demo/templates/postgres.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - --- apiVersion: v1 kind: Service diff --git a/charts/demo/templates/products-api.yaml b/charts/demo/templates/products-api.yaml index 22119f86d5..4e2fc4bea8 100644 --- a/charts/demo/templates/products-api.yaml +++ b/charts/demo/templates/products-api.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - --- apiVersion: v1 kind: Service diff --git a/charts/demo/templates/public-api.yaml b/charts/demo/templates/public-api.yaml index a397ddad35..14d4369ff8 100644 --- a/charts/demo/templates/public-api.yaml +++ b/charts/demo/templates/public-api.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - apiVersion: v1 kind: Service metadata: diff --git a/charts/demo/values.yaml b/charts/demo/values.yaml index 4509c555cb..2dd99602c7 100644 --- a/charts/demo/values.yaml +++ b/charts/demo/values.yaml @@ -1,4 +1 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - # Default values for demo. diff --git a/charts/embed_chart.go b/charts/embed_chart.go index 8e36abba23..29e7e9635e 100644 --- a/charts/embed_chart.go +++ b/charts/embed_chart.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package charts import "embed" @@ -15,7 +12,6 @@ import "embed" // // The embed directive does not include files with underscores unless explicitly listed, which is why _helpers.tpl is // explicitly embedded. - //go:embed consul/Chart.yaml consul/values.yaml consul/templates consul/templates/_helpers.tpl var ConsulHelmChart embed.FS diff --git a/charts/go.mod b/charts/go.mod index f76282d756..cdb23e46b0 100644 --- a/charts/go.mod +++ b/charts/go.mod @@ -1,3 +1,3 @@ module github.com/hashicorp/consul-k8s/charts -go 1.20 +go 1.19 diff --git a/cli/cmd/config/command.go b/cli/cmd/config/command.go index 302b054bd9..5e44677ff6 100644 --- a/cli/cmd/config/command.go +++ b/cli/cmd/config/command.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package config import ( diff --git a/cli/cmd/config/read/command.go b/cli/cmd/config/read/command.go index 0565294bd0..e2258bd013 100644 --- a/cli/cmd/config/read/command.go +++ b/cli/cmd/config/read/command.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package read import ( diff --git a/cli/cmd/config/read/command_test.go b/cli/cmd/config/read/command_test.go index 07275f872a..a3716cf3c1 100644 --- a/cli/cmd/config/read/command_test.go +++ b/cli/cmd/config/read/command_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package read import ( diff --git a/cli/cmd/install/install.go b/cli/cmd/install/install.go index f3d4671c6e..7b5d5bb31c 100644 --- a/cli/cmd/install/install.go +++ b/cli/cmd/install/install.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package install import ( @@ -592,7 +589,7 @@ func (c *Command) validateFlags(args []string) error { return fmt.Errorf("cannot set both -%s and -%s", flagNameConfigFile, flagNamePreset) } if ok := slices.Contains(preset.Presets, c.flagPreset); c.flagPreset != defaultPreset && !ok { - return fmt.Errorf("'%s' is not a valid preset (valid presets: %s)", c.flagPreset, strings.Join(preset.Presets, ", ")) + return fmt.Errorf("'%s' is not a valid preset", c.flagPreset) } if !common.IsValidLabel(c.flagNamespace) { return fmt.Errorf("'%s' is an invalid namespace. Namespaces follow the RFC 1123 label convention and must "+ diff --git a/cli/cmd/install/install_test.go b/cli/cmd/install/install_test.go index c34eac9ac3..04c250b1e4 100644 --- a/cli/cmd/install/install_test.go +++ b/cli/cmd/install/install_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package install import ( @@ -165,45 +162,39 @@ func TestValidateFlags(t *testing.T) { testCases := []struct { description string input []string - expErr string }{ { "Should disallow non-flag arguments.", []string{"foo", "-auto-approve"}, - "should have no non-flag arguments", }, { "Should disallow specifying both values file AND presets.", []string{"-f='f.txt'", "-preset=demo"}, - "cannot set both -config-file and -preset", }, { "Should error on invalid presets.", []string{"-preset=foo"}, - "'foo' is not a valid preset (valid presets: cloud, quickstart, secure)", }, { "Should error on invalid timeout.", []string{"-timeout=invalid-timeout"}, - "unable to parse -timeout: time: invalid duration \"invalid-timeout\"", }, { "Should error on an invalid namespace. If this failed, TestValidLabel() probably did too.", []string{"-namespace=\" nsWithSpace\""}, - "'\" nsWithSpace\"' is an invalid namespace. Namespaces follow the RFC 1123 label convention and must consist of a lower case alphanumeric character or '-' and must start/end with an alphanumeric character", }, { - "Should have errored on a non-existent file.", + "Should have errored on a non-existant file.", []string{"-f=\"does_not_exist.txt\""}, - "file '\"does_not_exist.txt\"' does not exist", }, } for _, testCase := range testCases { c := getInitializedCommand(t, nil) t.Run(testCase.description, func(t *testing.T) { - err := c.validateFlags(testCase.input) - require.EqualError(t, err, testCase.expErr) + if err := c.validateFlags(testCase.input); err == nil { + t.Errorf("Test case should have failed.") + } }) } } @@ -567,7 +558,7 @@ func TestInstall(t *testing.T) { }, messages: []string{ "\n==> Checking if Consul can be installed\n ✓ No existing Consul installations found.\n ✓ No existing Consul persistent volume claims found\n ✓ No existing Consul secrets found.\n", - "\n==> Consul Installation Summary\n Name: consul\n Namespace: consul\n \n Helm value overrides\n --------------------\n connectInject:\n enabled: true\n metrics:\n defaultEnableMerging: true\n defaultEnabled: true\n enableGatewayMetrics: true\n global:\n metrics:\n enableAgentMetrics: true\n enabled: true\n name: consul\n prometheus:\n enabled: true\n server:\n replicas: 1\n ui:\n enabled: true\n service:\n enabled: true\n \n", + "\n==> Consul Installation Summary\n Name: consul\n Namespace: consul\n \n Helm value overrides\n --------------------\n connectInject:\n enabled: true\n metrics:\n defaultEnableMerging: true\n defaultEnabled: true\n enableGatewayMetrics: true\n controller:\n enabled: true\n global:\n metrics:\n enableAgentMetrics: true\n enabled: true\n name: consul\n prometheus:\n enabled: true\n server:\n replicas: 1\n ui:\n enabled: true\n service:\n enabled: true\n \n", "\n==> Installing Consul\n ✓ Downloaded charts.\n ✓ Consul installed in namespace \"consul\".\n", }, helmActionsRunner: &helm.MockActionRunner{}, @@ -583,7 +574,7 @@ func TestInstall(t *testing.T) { }, messages: []string{ "\n==> Checking if Consul can be installed\n ✓ No existing Consul installations found.\n ✓ No existing Consul persistent volume claims found\n ✓ No existing Consul secrets found.\n", - "\n==> Consul Installation Summary\n Name: consul\n Namespace: consul\n \n Helm value overrides\n --------------------\n connectInject:\n enabled: true\n global:\n acls:\n manageSystemACLs: true\n gossipEncryption:\n autoGenerate: true\n name: consul\n tls:\n enableAutoEncrypt: true\n enabled: true\n server:\n replicas: 1\n \n", + "\n==> Consul Installation Summary\n Name: consul\n Namespace: consul\n \n Helm value overrides\n --------------------\n connectInject:\n enabled: true\n controller:\n enabled: true\n global:\n acls:\n manageSystemACLs: true\n gossipEncryption:\n autoGenerate: true\n name: consul\n tls:\n enableAutoEncrypt: true\n enabled: true\n server:\n replicas: 1\n \n", "\n==> Installing Consul\n ✓ Downloaded charts.\n ✓ Consul installed in namespace \"consul\".\n", }, helmActionsRunner: &helm.MockActionRunner{}, diff --git a/cli/cmd/proxy/command.go b/cli/cmd/proxy/command.go index e15469fb37..bc55ee0312 100644 --- a/cli/cmd/proxy/command.go +++ b/cli/cmd/proxy/command.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package proxy import ( diff --git a/cli/cmd/proxy/list/command.go b/cli/cmd/proxy/list/command.go index f427049de6..cbecda79a1 100644 --- a/cli/cmd/proxy/list/command.go +++ b/cli/cmd/proxy/list/command.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package list import ( diff --git a/cli/cmd/proxy/list/command_test.go b/cli/cmd/proxy/list/command_test.go index 3df205d3ca..b2c2cb6043 100644 --- a/cli/cmd/proxy/list/command_test.go +++ b/cli/cmd/proxy/list/command_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package list import ( diff --git a/cli/cmd/proxy/loglevel/command.go b/cli/cmd/proxy/loglevel/command.go deleted file mode 100644 index caf67d61de..0000000000 --- a/cli/cmd/proxy/loglevel/command.go +++ /dev/null @@ -1,349 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package loglevel - -import ( - "context" - "errors" - "fmt" - "strings" - "sync" - - "github.com/posener/complete" - helmCLI "helm.sh/helm/v3/pkg/cli" - "k8s.io/apimachinery/pkg/api/validation" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - - "github.com/hashicorp/consul-k8s/cli/common" - "github.com/hashicorp/consul-k8s/cli/common/envoy" - "github.com/hashicorp/consul-k8s/cli/common/flag" - "github.com/hashicorp/consul-k8s/cli/common/terminal" -) - -const ( - defaultAdminPort = 19000 - flagNameNamespace = "namespace" - flagNameUpdateLevel = "update-level" - flagNameReset = "reset" - flagNameKubeConfig = "kubeconfig" - flagNameKubeContext = "context" -) - -var ErrIncorrectArgFormat = errors.New("Exactly one positional argument is required: ") - -type LoggerConfig map[string]string - -var levelToColor = map[string]string{ - "trace": terminal.Green, - "debug": terminal.HiWhite, - "info": terminal.Blue, - "warning": terminal.Yellow, - "error": terminal.Red, - "critical": terminal.Magenta, - "off": "", -} - -type LogLevelCommand struct { - *common.BaseCommand - - kubernetes kubernetes.Interface - set *flag.Sets - - // Command Flags - podName string - namespace string - level string - reset bool - kubeConfig string - kubeContext string - - once sync.Once - help string - restConfig *rest.Config - envoyLoggingCaller func(context.Context, common.PortForwarder, *envoy.LoggerParams) (map[string]string, error) -} - -func (l *LogLevelCommand) init() { - l.Log.ResetNamed("loglevel") - l.set = flag.NewSets() - f := l.set.NewSet("Command Options") - f.StringVar(&flag.StringVar{ - Name: flagNameNamespace, - Target: &l.namespace, - Usage: "The namespace where the target Pod can be found.", - Aliases: []string{"n"}, - }) - - f.StringVar(&flag.StringVar{ - Name: flagNameUpdateLevel, - Target: &l.level, - Usage: "Update the level for the logger. Can be either `-update-level warning` to change all loggers to warning, or a comma delineated list of loggers with level can be passed like `-update-level grpc:warning,http:info` to only modify specific loggers.", - Aliases: []string{"u"}, - }) - - f.BoolVar(&flag.BoolVar{ - Name: flagNameReset, - Target: &l.reset, - Usage: "Reset the log level for all loggers in a pod to the Envoy default (info).", - Aliases: []string{"r"}, - }) - - f = l.set.NewSet("Global Options") - f.StringVar(&flag.StringVar{ - Name: flagNameKubeConfig, - Aliases: []string{"c"}, - Target: &l.kubeConfig, - Usage: "Set the path to kubeconfig file.", - }) - f.StringVar(&flag.StringVar{ - Name: flagNameKubeContext, - Target: &l.kubeContext, - Usage: "Set the Kubernetes context to use.", - }) - - l.help = l.set.Help() -} - -func (l *LogLevelCommand) Run(args []string) int { - l.once.Do(l.init) - defer common.CloseWithError(l.BaseCommand) - - err := l.parseFlags(args) - if err != nil { - return l.logOutputAndDie(err) - } - err = l.validateFlags() - if err != nil { - return l.logOutputAndDie(err) - } - - // if we're resetting the default log level for envoy is info: https://www.envoyproxy.io/docs/envoy/latest/start/quick-start/run-envoy#debugging-envoy - if l.reset { - l.level = "info" - } - - if l.envoyLoggingCaller == nil { - l.envoyLoggingCaller = envoy.CallLoggingEndpoint - } - - err = l.initKubernetes() - if err != nil { - return l.logOutputAndDie(err) - } - - adminPorts, err := l.fetchAdminPorts() - if err != nil { - return l.logOutputAndDie(err) - } - - err = l.fetchOrSetLogLevels(adminPorts) - if err != nil { - return l.logOutputAndDie(err) - } - - return 0 -} - -func (l *LogLevelCommand) parseFlags(args []string) error { - if len(args) == 0 { - return ErrIncorrectArgFormat - } - - positional := []string{} - // Separate positional args from keyed args - for _, arg := range args { - if strings.HasPrefix(arg, "-") { - break - } - positional = append(positional, arg) - } - keyed := args[len(positional):] - - if len(positional) != 1 { - return ErrIncorrectArgFormat - } - - l.podName = positional[0] - - err := l.set.Parse(keyed) - if err != nil { - return err - } - - return nil -} - -func (l *LogLevelCommand) validateFlags() error { - if l.level != "" && l.reset { - return fmt.Errorf("cannot set log level to %q and reset to 'info' at the same time", l.level) - } - if l.namespace == "" { - return nil - } - - errs := validation.ValidateNamespaceName(l.namespace, false) - if len(errs) > 0 { - return fmt.Errorf("invalid namespace name passed for -namespace/-n: %v", strings.Join(errs, "; ")) - } - - return nil -} - -func (l *LogLevelCommand) initKubernetes() error { - settings := helmCLI.New() - var err error - - if l.kubeConfig != "" { - settings.KubeConfig = l.kubeConfig - } - - if l.kubeContext != "" { - settings.KubeContext = l.kubeContext - } - - if l.restConfig == nil { - l.restConfig, err = settings.RESTClientGetter().ToRESTConfig() - if err != nil { - return fmt.Errorf("error creating Kubernetes REST config %v", err) - } - - } - - if l.kubernetes == nil { - l.kubernetes, err = kubernetes.NewForConfig(l.restConfig) - if err != nil { - return fmt.Errorf("error creating Kubernetes client %v", err) - } - } - if l.namespace == "" { - l.namespace = settings.Namespace() - } - - return nil -} - -// fetchAdminPorts retrieves all admin ports for Envoy Proxies running in a pod given namespace. -func (l *LogLevelCommand) fetchAdminPorts() (map[string]int, error) { - adminPorts := make(map[string]int, 0) - pod, err := l.kubernetes.CoreV1().Pods(l.namespace).Get(l.Ctx, l.podName, metav1.GetOptions{}) - if err != nil { - return adminPorts, err - } - - connectService, isMultiport := pod.Annotations["consul.hashicorp.com/connect-service"] - - if !isMultiport { - // Return the default port configuration. - adminPorts[l.podName] = defaultAdminPort - return adminPorts, nil - } - - for idx, svc := range strings.Split(connectService, ",") { - adminPorts[svc] = defaultAdminPort + idx - } - - return adminPorts, nil -} - -func (l *LogLevelCommand) fetchOrSetLogLevels(adminPorts map[string]int) error { - loggers := make(map[string]LoggerConfig, 0) - - for name, port := range adminPorts { - pf := common.PortForward{ - Namespace: l.namespace, - PodName: l.podName, - RemotePort: port, - KubeClient: l.kubernetes, - RestConfig: l.restConfig, - } - params, err := parseParams(l.level) - if err != nil { - return err - } - logLevels, err := l.envoyLoggingCaller(l.Ctx, &pf, params) - if err != nil { - return err - } - loggers[name] = logLevels - } - - l.outputLevels(loggers) - return nil -} - -func parseParams(params string) (*envoy.LoggerParams, error) { - loggerParams := envoy.NewLoggerParams() - if len(params) == 0 { - return loggerParams, nil - } - - // contains global log level change - if !strings.Contains(params, ":") { - err := loggerParams.SetGlobalLoggerLevel(params) - if err != nil { - return nil, err - } - return loggerParams, nil - } - - // contains changes to at least 1 specific log level - loggerChanges := strings.Split(params, ",") - - for _, logger := range loggerChanges { - levelValues := strings.Split(logger, ":") - err := loggerParams.SetLoggerLevel(levelValues[0], levelValues[1]) - if err != nil { - return nil, err - } - } - return loggerParams, nil -} - -func (l *LogLevelCommand) outputLevels(logLevels map[string]LoggerConfig) { - l.UI.Output(fmt.Sprintf("Envoy log configuration for %s in namespace default:", l.podName)) - for n, levels := range logLevels { - l.UI.Output(fmt.Sprintf("Log Levels for %s", n), terminal.WithHeaderStyle()) - table := terminal.NewTable("Name", "Level") - for name, level := range levels { - table.AddRow([]string{name, level}, []string{"", levelToColor[level]}) - } - l.UI.Table(table) - l.UI.Output("") - } -} - -func (l *LogLevelCommand) Help() string { - l.once.Do(l.init) - return fmt.Sprintf("%s\n\nUsage: consul-k8s proxy log [flags]\n\n%s", l.Synopsis(), l.help) -} - -func (l *LogLevelCommand) Synopsis() string { - return "Inspect and Modify the Envoy Log configuration for a given Pod." -} - -// AutocompleteFlags returns a mapping of supported flags and autocomplete -// options for this command. The map key for the Flags map should be the -// complete flag such as "-foo" or "--foo". -func (l *LogLevelCommand) AutocompleteFlags() complete.Flags { - return complete.Flags{ - fmt.Sprintf("-%s", flagNameNamespace): complete.PredictNothing, - fmt.Sprintf("-%s", flagNameKubeConfig): complete.PredictFiles("*"), - fmt.Sprintf("-%s", flagNameKubeContext): complete.PredictNothing, - } -} - -// AutocompleteArgs returns the argument predictor for this command. -// Since argument completion is not supported, this will return -// complete.PredictNothing. -func (l *LogLevelCommand) AutocompleteArgs() complete.Predictor { - return complete.PredictNothing -} - -func (l *LogLevelCommand) logOutputAndDie(err error) int { - l.UI.Output(err.Error(), terminal.WithErrorStyle()) - l.UI.Output(fmt.Sprintf("\n%s", l.Help())) - return 1 -} diff --git a/cli/cmd/proxy/loglevel/command_test.go b/cli/cmd/proxy/loglevel/command_test.go deleted file mode 100644 index 87e0355c1e..0000000000 --- a/cli/cmd/proxy/loglevel/command_test.go +++ /dev/null @@ -1,296 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package loglevel - -import ( - "bytes" - "context" - "fmt" - "io" - "os" - "regexp" - "testing" - - "github.com/stretchr/testify/require" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes/fake" - - "github.com/hashicorp/consul-k8s/cli/common" - "github.com/hashicorp/consul-k8s/cli/common/envoy" - "github.com/hashicorp/consul-k8s/cli/common/terminal" - "github.com/hashicorp/go-hclog" -) - -func TestFlagParsingFails(t *testing.T) { - t.Parallel() - testCases := map[string]struct { - args []string - out int - }{ - "No args": { - args: []string{}, - out: 1, - }, - "Multiple podnames passed": { - args: []string{"podname", "podname2"}, - out: 1, - }, - "Nonexistent flag passed, -foo bar": { - args: []string{"podName", "-foo", "bar"}, - out: 1, - }, - "Invalid argument passed, -namespace YOLO": { - args: []string{"podName", "-namespace", "YOLO"}, - out: 1, - }, - } - podName := "now-this-is-pod-racing" - fakePod := v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: podName, - Namespace: "default", - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - c := setupCommand(bytes.NewBuffer([]byte{})) - c.kubernetes = fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{fakePod}}) - c.envoyLoggingCaller = func(context.Context, common.PortForwarder, *envoy.LoggerParams) (map[string]string, error) { - return testLogConfig, nil - } - - out := c.Run(tc.args) - require.Equal(t, tc.out, out) - }) - } -} - -func TestFlagParsingSucceeds(t *testing.T) { - t.Parallel() - podName := "now-this-is-pod-racing" - testCases := map[string]struct { - args []string - podNamespace string - out int - }{ - "With single pod name": { - args: []string{podName}, - podNamespace: "default", - out: 0, - }, - "With single pod name and namespace": { - args: []string{podName, "-n", "another"}, - podNamespace: "another", - out: 0, - }, - "With single pod name and blanket level": { - args: []string{podName, "-u", "warning"}, - podNamespace: "default", - out: 0, - }, - "With single pod name and single level": { - args: []string{podName, "-u", "grpc:warning"}, - podNamespace: "default", - out: 0, - }, - "With single pod name and multiple levels": { - args: []string{podName, "-u", "grpc:warning,http:info"}, - podNamespace: "default", - out: 0, - }, - "With single pod name and blanket level full flag": { - args: []string{podName, "-update-level", "warning"}, - podNamespace: "default", - out: 0, - }, - "With single pod name and single level full flag": { - args: []string{podName, "-update-level", "grpc:warning"}, - podNamespace: "default", - out: 0, - }, - "With single pod name and multiple levels full flag": { - args: []string{podName, "-update-level", "grpc:warning,http:info"}, - podNamespace: "default", - out: 0, - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - fakePod := v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: podName, - Namespace: tc.podNamespace, - }, - } - - c := setupCommand(bytes.NewBuffer([]byte{})) - c.kubernetes = fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{fakePod}}) - c.envoyLoggingCaller = func(context.Context, common.PortForwarder, *envoy.LoggerParams) (map[string]string, error) { - return testLogConfig, nil - } - - out := c.Run(tc.args) - require.Equal(t, tc.out, out) - }) - } -} - -func TestOutputForGettingLogLevels(t *testing.T) { - t.Parallel() - podName := "now-this-is-pod-racing" - expectedHeader := fmt.Sprintf("Envoy log configuration for %s in namespace default:", podName) - fakePod := v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: podName, - Namespace: "default", - }, - } - - buf := bytes.NewBuffer([]byte{}) - c := setupCommand(buf) - newLogLevel := "warning" - config := make(map[string]string, len(testLogConfig)) - for logger := range testLogConfig { - config[logger] = newLogLevel - } - - c.envoyLoggingCaller = func(context.Context, common.PortForwarder, *envoy.LoggerParams) (map[string]string, error) { - return config, nil - } - c.kubernetes = fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{fakePod}}) - - args := []string{podName, "-u", newLogLevel} - out := c.Run(args) - require.Equal(t, 0, out) - - actual := buf.String() - - require.Regexp(t, expectedHeader, actual) - require.Regexp(t, "Log Levels for now-this-is-pod-racing", actual) - for logger, level := range config { - require.Regexp(t, regexp.MustCompile(logger+`.*`+level), actual) - } -} - -func TestOutputForSettingLogLevels(t *testing.T) { - t.Parallel() - podName := "now-this-is-pod-racing" - expectedHeader := fmt.Sprintf("Envoy log configuration for %s in namespace default:", podName) - fakePod := v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: podName, - Namespace: "default", - }, - } - - buf := bytes.NewBuffer([]byte{}) - c := setupCommand(buf) - c.envoyLoggingCaller = func(context.Context, common.PortForwarder, *envoy.LoggerParams) (map[string]string, error) { - return testLogConfig, nil - } - c.kubernetes = fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{fakePod}}) - - args := []string{podName, "-u", "warning"} - out := c.Run(args) - require.Equal(t, 0, out) - - actual := buf.String() - - require.Regexp(t, expectedHeader, actual) - require.Regexp(t, "Log Levels for now-this-is-pod-racing", actual) - for logger, level := range testLogConfig { - require.Regexp(t, regexp.MustCompile(logger+`.*`+level), actual) - } -} - -func TestHelp(t *testing.T) { - t.Parallel() - buf := bytes.NewBuffer([]byte{}) - c := setupCommand(buf) - expectedSynposis := "Inspect and Modify the Envoy Log configuration for a given Pod." - expectedUsage := `Usage: consul-k8s proxy log \[flags\]` - actual := c.Help() - require.Regexp(t, expectedSynposis, actual) - require.Regexp(t, expectedUsage, actual) -} - -func setupCommand(buf io.Writer) *LogLevelCommand { - log := hclog.New(&hclog.LoggerOptions{ - Name: "test", - Level: hclog.Debug, - Output: os.Stdout, - }) - - command := &LogLevelCommand{ - BaseCommand: &common.BaseCommand{ - Log: log, - UI: terminal.NewUI(context.Background(), buf), - }, - } - command.init() - return command -} - -var testLogConfig = map[string]string{ - "admin": "debug", - "alternate_protocols_cache": "debug", - "aws": "debug", - "assert": "debug", - "backtrace": "debug", - "cache_filter": "debug", - "client": "debug", - "config": "debug", - "connection": "debug", - "conn_handler": "debug", - "decompression": "debug", - "dns": "debug", - "dubbo": "debug", - "envoy_bug": "debug", - "ext_authz": "debug", - "ext_proc": "debug", - "rocketmq": "debug", - "file": "debug", - "filter": "debug", - "forward_proxy": "debug", - "grpc": "debug", - "happy_eyeballs": "debug", - "hc": "debug", - "health_checker": "debug", - "http": "debug", - "http2": "debug", - "hystrix": "debug", - "init": "debug", - "io": "debug", - "jwt": "debug", - "kafka": "debug", - "key_value_store": "debug", - "lua": "debug", - "main": "debug", - "matcher": "debug", - "misc": "debug", - "mongo": "debug", - "multi_connection": "debug", - "oauth2": "debug", - "quic": "debug", - "quic_stream": "debug", - "pool": "debug", - "rbac": "debug", - "rds": "debug", - "redis": "debug", - "router": "debug", - "runtime": "debug", - "stats": "debug", - "secret": "debug", - "tap": "debug", - "testing": "debug", - "thrift": "debug", - "tracing": "debug", - "upstream": "debug", - "udp": "debug", - "wasm": "debug", - "websocket": "debug", -} diff --git a/cli/cmd/proxy/read/command.go b/cli/cmd/proxy/read/command.go index 26ca33b045..ad2bb96303 100644 --- a/cli/cmd/proxy/read/command.go +++ b/cli/cmd/proxy/read/command.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package read import ( @@ -10,6 +7,9 @@ import ( "strings" "sync" + "github.com/hashicorp/consul-k8s/cli/common" + "github.com/hashicorp/consul-k8s/cli/common/flag" + "github.com/hashicorp/consul-k8s/cli/common/terminal" "github.com/posener/complete" helmCLI "helm.sh/helm/v3/pkg/cli" "k8s.io/apimachinery/pkg/api/validation" @@ -17,11 +17,6 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/utils/strings/slices" - - "github.com/hashicorp/consul-k8s/cli/common" - "github.com/hashicorp/consul-k8s/cli/common/envoy" - "github.com/hashicorp/consul-k8s/cli/common/flag" - "github.com/hashicorp/consul-k8s/cli/common/terminal" ) // defaultAdminPort is the port where the Envoy admin API is exposed. @@ -72,7 +67,7 @@ type ReadCommand struct { flagKubeConfig string flagKubeContext string - fetchConfig func(context.Context, common.PortForwarder) (*envoy.EnvoyConfig, error) + fetchConfig func(context.Context, common.PortForwarder) (*EnvoyConfig, error) restConfig *rest.Config @@ -82,7 +77,7 @@ type ReadCommand struct { func (c *ReadCommand) init() { if c.fetchConfig == nil { - c.fetchConfig = envoy.FetchConfig + c.fetchConfig = FetchConfig } c.set = flag.NewSets() @@ -325,8 +320,8 @@ func (c *ReadCommand) fetchAdminPorts() (map[string]int, error) { return adminPorts, nil } -func (c *ReadCommand) fetchConfigs(adminPorts map[string]int) (map[string]*envoy.EnvoyConfig, error) { - configs := make(map[string]*envoy.EnvoyConfig, 0) +func (c *ReadCommand) fetchConfigs(adminPorts map[string]int) (map[string]*EnvoyConfig, error) { + configs := make(map[string]*EnvoyConfig, 0) for name, adminPort := range adminPorts { pf := common.PortForward{ @@ -348,7 +343,7 @@ func (c *ReadCommand) fetchConfigs(adminPorts map[string]int) (map[string]*envoy return configs, nil } -func (c *ReadCommand) outputConfigs(configs map[string]*envoy.EnvoyConfig) error { +func (c *ReadCommand) outputConfigs(configs map[string]*EnvoyConfig) error { switch c.flagOutput { case Table: return c.outputTables(configs) @@ -401,7 +396,7 @@ func (c *ReadCommand) filterWarnings() []string { return warnings } -func (c *ReadCommand) outputTables(configs map[string]*envoy.EnvoyConfig) error { +func (c *ReadCommand) outputTables(configs map[string]*EnvoyConfig) error { if c.flagFQDN != "" || c.flagAddress != "" || c.flagPort != -1 { c.UI.Output("Filters applied", terminal.WithHeaderStyle()) @@ -436,7 +431,7 @@ func (c *ReadCommand) outputTables(configs map[string]*envoy.EnvoyConfig) error return nil } -func (c *ReadCommand) outputJSON(configs map[string]*envoy.EnvoyConfig) error { +func (c *ReadCommand) outputJSON(configs map[string]*EnvoyConfig) error { cfgs := make(map[string]interface{}) for name, config := range configs { cfg := make(map[string]interface{}) @@ -472,11 +467,11 @@ func (c *ReadCommand) outputJSON(configs map[string]*envoy.EnvoyConfig) error { return nil } -func (c *ReadCommand) outputRaw(configs map[string]*envoy.EnvoyConfig) error { +func (c *ReadCommand) outputRaw(configs map[string]*EnvoyConfig) error { cfgs := make(map[string]interface{}, 0) for name, config := range configs { var cfg interface{} - if err := json.Unmarshal(config.RawCfg, &cfg); err != nil { + if err := json.Unmarshal(config.rawCfg, &cfg); err != nil { return err } @@ -493,7 +488,7 @@ func (c *ReadCommand) outputRaw(configs map[string]*envoy.EnvoyConfig) error { return nil } -func (c *ReadCommand) outputClustersTable(clusters []envoy.Cluster) { +func (c *ReadCommand) outputClustersTable(clusters []Cluster) { if !c.shouldPrintTable(c.flagClusters) { return } @@ -501,16 +496,14 @@ func (c *ReadCommand) outputClustersTable(clusters []envoy.Cluster) { c.UI.Output(fmt.Sprintf("Clusters (%d)", len(clusters)), terminal.WithHeaderStyle()) table := terminal.NewTable("Name", "FQDN", "Endpoints", "Type", "Last Updated") for _, cluster := range clusters { - table.AddRow([]string{ - cluster.Name, cluster.FullyQualifiedDomainName, strings.Join(cluster.Endpoints, ", "), - cluster.Type, cluster.LastUpdated, - }, []string{}) + table.AddRow([]string{cluster.Name, cluster.FullyQualifiedDomainName, strings.Join(cluster.Endpoints, ", "), + cluster.Type, cluster.LastUpdated}, []string{}) } c.UI.Table(table) c.UI.Output("") } -func (c *ReadCommand) outputEndpointsTable(endpoints []envoy.Endpoint) { +func (c *ReadCommand) outputEndpointsTable(endpoints []Endpoint) { if !c.shouldPrintTable(c.flagEndpoints) { return } @@ -519,7 +512,7 @@ func (c *ReadCommand) outputEndpointsTable(endpoints []envoy.Endpoint) { c.UI.Table(formatEndpoints(endpoints)) } -func (c *ReadCommand) outputListenersTable(listeners []envoy.Listener) { +func (c *ReadCommand) outputListenersTable(listeners []Listener) { if !c.shouldPrintTable(c.flagListeners) { return } @@ -528,7 +521,7 @@ func (c *ReadCommand) outputListenersTable(listeners []envoy.Listener) { c.UI.Table(formatListeners(listeners)) } -func (c *ReadCommand) outputRoutesTable(routes []envoy.Route) { +func (c *ReadCommand) outputRoutesTable(routes []Route) { if !c.shouldPrintTable(c.flagRoutes) { return } @@ -537,7 +530,7 @@ func (c *ReadCommand) outputRoutesTable(routes []envoy.Route) { c.UI.Table(formatRoutes(routes)) } -func (c *ReadCommand) outputSecretsTable(secrets []envoy.Secret) { +func (c *ReadCommand) outputSecretsTable(secrets []Secret) { if !c.shouldPrintTable(c.flagSecrets) { return } diff --git a/cli/cmd/proxy/read/command_test.go b/cli/cmd/proxy/read/command_test.go index 992a9a3909..27f19e7370 100644 --- a/cli/cmd/proxy/read/command_test.go +++ b/cli/cmd/proxy/read/command_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package read import ( @@ -12,18 +9,16 @@ import ( "os" "testing" + "github.com/hashicorp/consul-k8s/cli/common" + cmnFlag "github.com/hashicorp/consul-k8s/cli/common/flag" + "github.com/hashicorp/consul-k8s/cli/common/terminal" + "github.com/hashicorp/go-hclog" "github.com/posener/complete" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/fake" - - "github.com/hashicorp/consul-k8s/cli/common" - "github.com/hashicorp/consul-k8s/cli/common/envoy" - cmnFlag "github.com/hashicorp/consul-k8s/cli/common/flag" - "github.com/hashicorp/consul-k8s/cli/common/terminal" - "github.com/hashicorp/go-hclog" ) func TestFlagParsing(t *testing.T) { @@ -70,48 +65,38 @@ func TestReadCommandOutput(t *testing.T) { // These regular expressions must be present in the output. expectedHeader := fmt.Sprintf("Envoy configuration for %s in namespace default:", podName) expected := map[string][]string{ - "-clusters": { - "==> Clusters \\(5\\)", + "-clusters": {"==> Clusters \\(5\\)", "Name.*FQDN.*Endpoints.*Type.*Last Updated", "local_agent.*192\\.168\\.79\\.187:8502.*STATIC.*2022-05-13T04:22:39\\.553Z", "local_app.*127\\.0\\.0\\.1:8080.*STATIC.*2022-05-13T04:22:39\\.655Z", "client.*client\\.default\\.dc1\\.internal\\.bc3815c2-1a0f-f3ff-a2e9-20d791f08d00\\.consul.*EDS", "frontend.*frontend\\.default\\.dc1\\.internal\\.bc3815c2-1a0f-f3ff-a2e9-20d791f08d00\\.consul", - "original-destination.*ORIGINAL_DST", - }, + "original-destination.*ORIGINAL_DST"}, - "-endpoints": { - "==> Endpoints \\(6\\)", + "-endpoints": {"==> Endpoints \\(6\\)", "Address:Port.*Cluster.*Weight.*Status", "192.168.79.187:8502.*local_agent.*1.00.*HEALTHY", "127.0.0.1:8080.*local_app.*1.00.*HEALTHY", "192.168.18.110:20000.*client.*1.00.*HEALTHY", "192.168.52.101:20000.*client.*1.00.*HEALTHY", "192.168.65.131:20000.*client.*1.00.*HEALTHY", - "192.168.63.120:20000.*frontend.*1.00.*HEALTHY", - }, + "192.168.63.120:20000.*frontend.*1.00.*HEALTHY"}, - "-listeners": { - "==> Listeners \\(2\\)", + "-listeners": {"==> Listeners \\(2\\)", "Name.*Address:Port.*Direction.*Filter Chain Match.*Filters.*Last Updated", "public_listener.*192\\.168\\.69\\.179:20000.*INBOUND.*Any.*\\* -> local_app/", "outbound_listener.*127.0.0.1:15001.*OUTBOUND.*10\\.100\\.134\\.173/32, 240\\.0\\.0\\.3/32.*TCP: -> client", "10\\.100\\.31\\.2/32, 240\\.0\\.0\\.5/32.*TCP: -> frontend", - "Any.*TCP: -> original-destination", - }, + "Any.*TCP: -> original-destination"}, - "-routes": { - "==> Routes \\(1\\)", + "-routes": {"==> Routes \\(1\\)", "Name.*Destination Cluster.*Last Updated", - "public_listener.*local_app/", - }, + "public_listener.*local_app/"}, - "-secrets": { - "==> Secrets \\(2\\)", + "-secrets": {"==> Secrets \\(2\\)", "Name.*Type.*Last Updated", "default.*Dynamic Active", - "ROOTCA.*Dynamic Warming", - }, + "ROOTCA.*Dynamic Warming"}, } cases := map[string][]string{ @@ -137,7 +122,7 @@ func TestReadCommandOutput(t *testing.T) { c.kubernetes = fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{fakePod}}) // A fetchConfig function that just returns the test Envoy config. - c.fetchConfig = func(context.Context, common.PortForwarder) (*envoy.EnvoyConfig, error) { + c.fetchConfig = func(context.Context, common.PortForwarder) (*EnvoyConfig, error) { return testEnvoyConfig, nil } @@ -245,7 +230,7 @@ func TestFilterWarnings(t *testing.T) { buf := new(bytes.Buffer) c := setupCommand(buf) c.kubernetes = fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{fakePod}}) - c.fetchConfig = func(context.Context, common.PortForwarder) (*envoy.EnvoyConfig, error) { + c.fetchConfig = func(context.Context, common.PortForwarder) (*EnvoyConfig, error) { return testEnvoyConfig, nil } @@ -310,73 +295,3 @@ func TestTaskCreateCommand_AutocompleteArgs(t *testing.T) { c := cmd.AutocompleteArgs() assert.Equal(t, complete.PredictNothing, c) } - -// testEnvoyConfig is what we expect the config at `test_config_dump.json` to be. - -var testEnvoyConfig = &envoy.EnvoyConfig{ - Clusters: []envoy.Cluster{ - {Name: "local_agent", FullyQualifiedDomainName: "local_agent", Endpoints: []string{"192.168.79.187:8502"}, Type: "STATIC", LastUpdated: "2022-05-13T04:22:39.553Z"}, - - {Name: "client", FullyQualifiedDomainName: "client.default.dc1.internal.bc3815c2-1a0f-f3ff-a2e9-20d791f08d00.consul", Endpoints: []string{"192.168.18.110:20000", "192.168.52.101:20000", "192.168.65.131:20000"}, Type: "EDS", LastUpdated: "2022-08-10T12:30:32.326Z"}, - - {Name: "frontend", FullyQualifiedDomainName: "frontend.default.dc1.internal.bc3815c2-1a0f-f3ff-a2e9-20d791f08d00.consul", Endpoints: []string{"192.168.63.120:20000"}, Type: "EDS", LastUpdated: "2022-08-10T12:30:32.233Z"}, - - {Name: "local_app", FullyQualifiedDomainName: "local_app", Endpoints: []string{"127.0.0.1:8080"}, Type: "STATIC", LastUpdated: "2022-05-13T04:22:39.655Z"}, - - {Name: "original-destination", FullyQualifiedDomainName: "original-destination", Endpoints: []string{}, Type: "ORIGINAL_DST", LastUpdated: "2022-05-13T04:22:39.743Z"}, - }, - - Endpoints: []envoy.Endpoint{ - {Address: "192.168.79.187:8502", Cluster: "local_agent", Weight: 1, Status: "HEALTHY"}, - - {Address: "192.168.18.110:20000", Cluster: "client", Weight: 1, Status: "HEALTHY"}, - - {Address: "192.168.52.101:20000", Cluster: "client", Weight: 1, Status: "HEALTHY"}, - - {Address: "192.168.65.131:20000", Cluster: "client", Weight: 1, Status: "HEALTHY"}, - - {Address: "192.168.63.120:20000", Cluster: "frontend", Weight: 1, Status: "HEALTHY"}, - - {Address: "127.0.0.1:8080", Cluster: "local_app", Weight: 1, Status: "HEALTHY"}, - }, - - Listeners: []envoy.Listener{ - {Name: "public_listener", Address: "192.168.69.179:20000", FilterChain: []envoy.FilterChain{{Filters: []string{"HTTP: * -> local_app/"}, FilterChainMatch: "Any"}}, Direction: "INBOUND", LastUpdated: "2022-08-10T12:30:47.142Z"}, - - {Name: "outbound_listener", Address: "127.0.0.1:15001", FilterChain: []envoy.FilterChain{ - {Filters: []string{"TCP: -> client"}, FilterChainMatch: "10.100.134.173/32, 240.0.0.3/32"}, - - {Filters: []string{"TCP: -> frontend"}, FilterChainMatch: "10.100.31.2/32, 240.0.0.5/32"}, - - {Filters: []string{"TCP: -> original-destination"}, FilterChainMatch: "Any"}, - }, Direction: "OUTBOUND", LastUpdated: "2022-07-18T15:31:03.246Z"}, - }, - - Routes: []envoy.Route{ - { - Name: "public_listener", - - DestinationCluster: "local_app/", - - LastUpdated: "2022-08-10T12:30:47.141Z", - }, - }, - - Secrets: []envoy.Secret{ - { - Name: "default", - - Type: "Dynamic Active", - - LastUpdated: "2022-05-24T17:41:59.078Z", - }, - - { - Name: "ROOTCA", - - Type: "Dynamic Warming", - - LastUpdated: "2022-03-15T05:14:22.868Z", - }, - }, -} diff --git a/cli/common/envoy/http.go b/cli/cmd/proxy/read/config.go similarity index 90% rename from cli/common/envoy/http.go rename to cli/cmd/proxy/read/config.go index 39c58df530..e7e6bcad34 100644 --- a/cli/common/envoy/http.go +++ b/cli/cmd/proxy/read/config.go @@ -1,13 +1,8 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package envoy +package read import ( - "bytes" "context" "encoding/json" - "errors" "fmt" "io" "net" @@ -17,13 +12,11 @@ import ( "github.com/hashicorp/consul-k8s/cli/common" ) -var ErrNoLoggersReturned = errors.New("No loggers were returned from Envoy") - // EnvoyConfig represents the configuration retrieved from a config dump at the // admin endpoint. It wraps the Envoy ConfigDump struct to give us convenient // access to the different sections of the config. type EnvoyConfig struct { - RawCfg []byte + rawCfg []byte Clusters []Cluster Endpoints []Endpoint Listeners []Listener @@ -76,54 +69,6 @@ type Secret struct { LastUpdated string } -// CallLoggingEndpoint requests the logging endpoint from Envoy Admin Interface for a given port -// This is used to both read and update the logging levels (the envoy admin interface uses the same endpoint for both) -// more can be read about that endpoint https://www.envoyproxy.io/docs/envoy/latest/operations/admin#post--logging -func CallLoggingEndpoint(ctx context.Context, portForward common.PortForwarder, params *LoggerParams) (map[string]string, error) { - endpoint, err := portForward.Open(ctx) - if err != nil { - return nil, err - } - - defer portForward.Close() - - // this endpoint does not support returning json, so we've gotta parse the plain text - response, err := http.Post(fmt.Sprintf("http://%s/logging%s", endpoint, params), "text/plain", bytes.NewBuffer([]byte{})) - if err != nil { - return nil, err - } - - body, err := io.ReadAll(response.Body) - if err != nil { - return nil, fmt.Errorf("failed to reach envoy: %v", err) - } - - if response.StatusCode >= 400 { - return nil, fmt.Errorf("call to envoy failed with status code: %d, and message: %s", response.StatusCode, body) - } - - loggers := strings.Split(string(body), "\n") - if len(loggers) == 0 { - return nil, ErrNoLoggersReturned - } - - logLevels := make(map[string]string) - var name string - var level string - - // the first line here is just a header - for _, logger := range loggers[1:] { - if len(logger) == 0 { - continue - } - fmt.Sscanf(logger, "%s %s", &name, &level) - name = strings.TrimRight(name, ":") - logLevels[name] = level - } - - return logLevels, nil -} - // FetchConfig opens a port forward to the Envoy admin API and fetches the // configuration from the config dump endpoint. func FetchConfig(ctx context.Context, portForward common.PortForwarder) (*EnvoyConfig, error) { @@ -172,7 +117,7 @@ func FetchConfig(ctx context.Context, portForward common.PortForwarder) (*EnvoyC // JSON returns the original JSON Envoy config dump data which was used to create // the Config object. func (c *EnvoyConfig) JSON() []byte { - return c.RawCfg + return c.rawCfg } // UnmarshalJSON implements the json.Unmarshaler interface to unmarshal the raw @@ -181,7 +126,7 @@ func (c *EnvoyConfig) JSON() []byte { func (c *EnvoyConfig) UnmarshalJSON(b []byte) error { // Save the original config dump bytes for marshalling. We should treat this // struct as immutable so this should be safe. - c.RawCfg = b + c.rawCfg = b var root root err := json.Unmarshal(b, &root) diff --git a/cli/common/envoy/http_test.go b/cli/cmd/proxy/read/config_test.go similarity index 92% rename from cli/common/envoy/http_test.go rename to cli/cmd/proxy/read/config_test.go index 961046100e..6b0e425794 100644 --- a/cli/common/envoy/http_test.go +++ b/cli/cmd/proxy/read/config_test.go @@ -1,41 +1,21 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package envoy +package read import ( "bytes" "context" + "embed" "encoding/json" "fmt" "net/http" "net/http/httptest" - "os" "strings" "testing" "github.com/stretchr/testify/require" ) -func TestCallLoggingEndpoint(t *testing.T) { - t.Parallel() - rawLogLevels, err := os.ReadFile("testdata/fetch_debug_levels.txt") - require.NoError(t, err) - mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write(rawLogLevels) - })) - - defer mockServer.Close() - - mpf := &mockPortForwarder{ - openBehavior: func(ctx context.Context) (string, error) { - return strings.Replace(mockServer.URL, "http://", "", 1), nil - }, - } - logLevels, err := CallLoggingEndpoint(context.Background(), mpf, NewLoggerParams()) - require.NoError(t, err) - require.Equal(t, testLogConfig(), logLevels) -} +//go:embed test_config_dump.json test_clusters.json +var fs embed.FS const ( testConfigDump = "test_config_dump.json" @@ -55,7 +35,7 @@ func TestUnmarshaling(t *testing.T) { } func TestJSON(t *testing.T) { - raw, err := os.ReadFile(fmt.Sprintf("testdata/%s", testConfigDump)) + raw, err := fs.ReadFile(testConfigDump) require.NoError(t, err) expected := bytes.TrimSpace(raw) @@ -69,10 +49,10 @@ func TestJSON(t *testing.T) { } func TestFetchConfig(t *testing.T) { - configDump, err := os.ReadFile(fmt.Sprintf("testdata/%s", testConfigDump)) + configDump, err := fs.ReadFile(testConfigDump) require.NoError(t, err) - clusters, err := os.ReadFile(fmt.Sprintf("testdata/%s", testClusters)) + clusters, err := fs.ReadFile(testClusters) require.NoError(t, err) mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -466,11 +446,18 @@ func TestClusterParsingEndpoints(t *testing.T) { require.Equal(t, expected, actual) } +type mockPortForwarder struct { + openBehavior func(context.Context) (string, error) +} + +func (m *mockPortForwarder) Open(ctx context.Context) (string, error) { return m.openBehavior(ctx) } +func (m *mockPortForwarder) Close() {} + func rawEnvoyConfig(t *testing.T) []byte { - configDump, err := os.ReadFile(fmt.Sprintf("testdata/%s", testConfigDump)) + configDump, err := fs.ReadFile(testConfigDump) require.NoError(t, err) - clusters, err := os.ReadFile(fmt.Sprintf("testdata/%s", testClusters)) + clusters, err := fs.ReadFile(testClusters) require.NoError(t, err) return []byte(fmt.Sprintf("{\n\"config_dump\":%s,\n\"clusters\":%s}", string(configDump), string(clusters))) @@ -521,18 +508,3 @@ var testEnvoyConfig = &EnvoyConfig{ }, }, } - -type mockPortForwarder struct { - openBehavior func(context.Context) (string, error) -} - -func (m *mockPortForwarder) Open(ctx context.Context) (string, error) { return m.openBehavior(ctx) } -func (m *mockPortForwarder) Close() {} - -func testLogConfig() map[string]string { - cfg := make(map[string]string, len(EnvoyLoggers)) - for k := range EnvoyLoggers { - cfg[k] = "debug" - } - return cfg -} diff --git a/cli/common/envoy/types.go b/cli/cmd/proxy/read/envoy_types.go similarity index 99% rename from cli/common/envoy/types.go rename to cli/cmd/proxy/read/envoy_types.go index 8923eee271..cc1ffcf7e2 100644 --- a/cli/common/envoy/types.go +++ b/cli/cmd/proxy/read/envoy_types.go @@ -1,7 +1,4 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package envoy +package read /* Envoy Types These types are based on the JSON returned from the Envoy Config Dump API on the diff --git a/cli/cmd/proxy/read/filters.go b/cli/cmd/proxy/read/filters.go index 3c02102df8..dc65172f32 100644 --- a/cli/cmd/proxy/read/filters.go +++ b/cli/cmd/proxy/read/filters.go @@ -1,13 +1,8 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package read import ( "strconv" "strings" - - "github.com/hashicorp/consul-k8s/cli/common/envoy" ) // FilterClusters takes a slice of clusters along with parameters for filtering @@ -22,7 +17,7 @@ import ( // // The filters are applied in combination such that a cluster must adhere to // all of the filtering values which are passed in. -func FilterClusters(clusters []envoy.Cluster, fqdn, address string, port int) []envoy.Cluster { +func FilterClusters(clusters []Cluster, fqdn, address string, port int) []Cluster { // No filtering no-op. if fqdn == "" && address == "" && port == -1 { return clusters @@ -30,7 +25,7 @@ func FilterClusters(clusters []envoy.Cluster, fqdn, address string, port int) [] portStr := ":" + strconv.Itoa(port) - filtered := make([]envoy.Cluster, 0) + filtered := make([]Cluster, 0) for _, cluster := range clusters { if !strings.Contains(cluster.FullyQualifiedDomainName, fqdn) { continue @@ -63,14 +58,14 @@ func FilterClusters(clusters []envoy.Cluster, fqdn, address string, port int) [] // // The filters are applied in combination such that an endpoint must adhere to // all of the filtering values which are passed in. -func FilterEndpoints(endpoints []envoy.Endpoint, address string, port int) []envoy.Endpoint { +func FilterEndpoints(endpoints []Endpoint, address string, port int) []Endpoint { if address == "" && port == -1 { return endpoints } portStr := ":" + strconv.Itoa(port) - filtered := make([]envoy.Endpoint, 0) + filtered := make([]Endpoint, 0) for _, endpoint := range endpoints { if strings.Contains(endpoint.Address, address) && (port == -1 || strings.Contains(endpoint.Address, portStr)) { filtered = append(filtered, endpoint) @@ -90,14 +85,14 @@ func FilterEndpoints(endpoints []envoy.Endpoint, address string, port int) []env // // The filters are applied in combination such that an listener must adhere to // all of the filtering values which are passed in. -func FilterListeners(listeners []envoy.Listener, address string, port int) []envoy.Listener { +func FilterListeners(listeners []Listener, address string, port int) []Listener { if address == "" && port == -1 { return listeners } portStr := ":" + strconv.Itoa(port) - filtered := make([]envoy.Listener, 0) + filtered := make([]Listener, 0) for _, listener := range listeners { if strings.Contains(listener.Address, address) && (port == -1 || strings.Contains(listener.Address, portStr)) { filtered = append(filtered, listener) diff --git a/cli/cmd/proxy/read/filters_test.go b/cli/cmd/proxy/read/filters_test.go index 5d998e6a7c..48ff3a97da 100644 --- a/cli/cmd/proxy/read/filters_test.go +++ b/cli/cmd/proxy/read/filters_test.go @@ -1,18 +1,13 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package read import ( "testing" "github.com/stretchr/testify/require" - - "github.com/hashicorp/consul-k8s/cli/common/envoy" ) func TestFilterClusters(t *testing.T) { - given := []envoy.Cluster{ + given := []Cluster{ { FullyQualifiedDomainName: "local_agent", Endpoints: []string{"192.168.79.187:8502"}, @@ -47,13 +42,13 @@ func TestFilterClusters(t *testing.T) { fqdn string address string port int - expected []envoy.Cluster + expected []Cluster }{ "No filter": { fqdn: "", address: "", port: -1, - expected: []envoy.Cluster{ + expected: []Cluster{ { FullyQualifiedDomainName: "local_agent", Endpoints: []string{"192.168.79.187:8502"}, @@ -88,7 +83,7 @@ func TestFilterClusters(t *testing.T) { fqdn: "default", address: "", port: -1, - expected: []envoy.Cluster{ + expected: []Cluster{ { FullyQualifiedDomainName: "client.default.dc1.internal.bc3815c2-1a0f-f3ff-a2e9-20d791f08d00.consul", Endpoints: []string{}, @@ -107,7 +102,7 @@ func TestFilterClusters(t *testing.T) { fqdn: "", address: "127.0.", port: -1, - expected: []envoy.Cluster{ + expected: []Cluster{ { FullyQualifiedDomainName: "local_app", Endpoints: []string{"127.0.0.1:8080"}, @@ -122,7 +117,7 @@ func TestFilterClusters(t *testing.T) { fqdn: "", address: "", port: 8080, - expected: []envoy.Cluster{ + expected: []Cluster{ { FullyQualifiedDomainName: "local_app", Endpoints: []string{"127.0.0.1:8080"}, @@ -137,7 +132,7 @@ func TestFilterClusters(t *testing.T) { fqdn: "local", address: "127.0.0.1", port: -1, - expected: []envoy.Cluster{ + expected: []Cluster{ { FullyQualifiedDomainName: "local_app", Endpoints: []string{"127.0.0.1:8080"}, @@ -152,7 +147,7 @@ func TestFilterClusters(t *testing.T) { fqdn: "local", address: "", port: 8080, - expected: []envoy.Cluster{ + expected: []Cluster{ { FullyQualifiedDomainName: "local_app", Endpoints: []string{"127.0.0.1:8080"}, @@ -163,7 +158,7 @@ func TestFilterClusters(t *testing.T) { fqdn: "", address: "127.0.0.1", port: 8080, - expected: []envoy.Cluster{ + expected: []Cluster{ { FullyQualifiedDomainName: "local_app", Endpoints: []string{"127.0.0.1:8080"}, @@ -174,7 +169,7 @@ func TestFilterClusters(t *testing.T) { fqdn: "local", address: "192.168.79.187", port: 8502, - expected: []envoy.Cluster{ + expected: []Cluster{ { FullyQualifiedDomainName: "local_agent", Endpoints: []string{"192.168.79.187:8502"}, @@ -192,7 +187,7 @@ func TestFilterClusters(t *testing.T) { } func TestFilterEndpoints(t *testing.T) { - given := []envoy.Endpoint{ + given := []Endpoint{ { Address: "192.168.79.187:8502", }, @@ -213,12 +208,12 @@ func TestFilterEndpoints(t *testing.T) { cases := map[string]struct { address string port int - expected []envoy.Endpoint + expected []Endpoint }{ "No filter": { address: "", port: -1, - expected: []envoy.Endpoint{ + expected: []Endpoint{ { Address: "192.168.79.187:8502", }, @@ -239,7 +234,7 @@ func TestFilterEndpoints(t *testing.T) { "Filter address": { address: "127.0.0.1", port: -1, - expected: []envoy.Endpoint{ + expected: []Endpoint{ { Address: "127.0.0.1:8080", }, @@ -248,7 +243,7 @@ func TestFilterEndpoints(t *testing.T) { "Filter port": { address: "", port: 20000, - expected: []envoy.Endpoint{ + expected: []Endpoint{ { Address: "192.168.31.201:20000", }, @@ -263,7 +258,7 @@ func TestFilterEndpoints(t *testing.T) { "Filter address and port": { address: "235", port: 20000, - expected: []envoy.Endpoint{ + expected: []Endpoint{ { Address: "192.168.47.235:20000", }, @@ -280,7 +275,7 @@ func TestFilterEndpoints(t *testing.T) { } func TestFilterListeners(t *testing.T) { - given := []envoy.Listener{ + given := []Listener{ { Address: "192.168.69.179:20000", }, @@ -292,12 +287,12 @@ func TestFilterListeners(t *testing.T) { cases := map[string]struct { address string port int - expected []envoy.Listener + expected []Listener }{ "No filter": { address: "", port: -1, - expected: []envoy.Listener{ + expected: []Listener{ { Address: "192.168.69.179:20000", }, @@ -309,7 +304,7 @@ func TestFilterListeners(t *testing.T) { "Filter address": { address: "127.0.0.1", port: -1, - expected: []envoy.Listener{ + expected: []Listener{ { Address: "127.0.0.1:15001", }, @@ -318,7 +313,7 @@ func TestFilterListeners(t *testing.T) { "Filter port": { address: "", port: 20000, - expected: []envoy.Listener{ + expected: []Listener{ { Address: "192.168.69.179:20000", }, @@ -327,7 +322,7 @@ func TestFilterListeners(t *testing.T) { "Filter address and port": { address: "192.168.69.179", port: 20000, - expected: []envoy.Listener{ + expected: []Listener{ { Address: "192.168.69.179:20000", }, diff --git a/cli/cmd/proxy/read/format.go b/cli/cmd/proxy/read/format.go index 596a6254f4..97d5ada86a 100644 --- a/cli/cmd/proxy/read/format.go +++ b/cli/cmd/proxy/read/format.go @@ -1,29 +1,23 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package read import ( "fmt" "strings" - "github.com/hashicorp/consul-k8s/cli/common/envoy" "github.com/hashicorp/consul-k8s/cli/common/terminal" ) -func formatClusters(clusters []envoy.Cluster) *terminal.Table { +func formatClusters(clusters []Cluster) *terminal.Table { table := terminal.NewTable("Name", "FQDN", "Endpoints", "Type", "Last Updated") for _, cluster := range clusters { - table.AddRow([]string{ - cluster.Name, cluster.FullyQualifiedDomainName, strings.Join(cluster.Endpoints, ", "), - cluster.Type, cluster.LastUpdated, - }, []string{}) + table.AddRow([]string{cluster.Name, cluster.FullyQualifiedDomainName, strings.Join(cluster.Endpoints, ", "), + cluster.Type, cluster.LastUpdated}, []string{}) } return table } -func formatEndpoints(endpoints []envoy.Endpoint) *terminal.Table { +func formatEndpoints(endpoints []Endpoint) *terminal.Table { table := terminal.NewTable("Address:Port", "Cluster", "Weight", "Status") for _, endpoint := range endpoints { var statusColor string @@ -41,7 +35,7 @@ func formatEndpoints(endpoints []envoy.Endpoint) *terminal.Table { return table } -func formatListeners(listeners []envoy.Listener) *terminal.Table { +func formatListeners(listeners []Listener) *terminal.Table { table := terminal.NewTable("Name", "Address:Port", "Direction", "Filter Chain Match", "Filters", "Last Updated") for _, listener := range listeners { for index, filter := range listener.FilterChain { @@ -63,7 +57,7 @@ func formatListeners(listeners []envoy.Listener) *terminal.Table { return table } -func formatRoutes(routes []envoy.Route) *terminal.Table { +func formatRoutes(routes []Route) *terminal.Table { table := terminal.NewTable("Name", "Destination Cluster", "Last Updated") for _, route := range routes { table.AddRow([]string{route.Name, route.DestinationCluster, route.LastUpdated}, []string{}) @@ -72,7 +66,7 @@ func formatRoutes(routes []envoy.Route) *terminal.Table { return table } -func formatSecrets(secrets []envoy.Secret) *terminal.Table { +func formatSecrets(secrets []Secret) *terminal.Table { table := terminal.NewTable("Name", "Type", "Last Updated") for _, secret := range secrets { table.AddRow([]string{secret.Name, secret.Type, secret.LastUpdated}, []string{}) diff --git a/cli/cmd/proxy/read/format_test.go b/cli/cmd/proxy/read/format_test.go index bac72df862..7d6f975d39 100644 --- a/cli/cmd/proxy/read/format_test.go +++ b/cli/cmd/proxy/read/format_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package read import ( @@ -8,10 +5,8 @@ import ( "context" "testing" - "github.com/stretchr/testify/require" - - "github.com/hashicorp/consul-k8s/cli/common/envoy" "github.com/hashicorp/consul-k8s/cli/common/terminal" + "github.com/stretchr/testify/require" ) func TestFormatClusters(t *testing.T) { @@ -26,7 +21,7 @@ func TestFormatClusters(t *testing.T) { "server.*server.default.dc1.internal.bc3815c2-1a0f-f3ff-a2e9-20d791f08d00.consul.*EDS.*2022-06-09T00:39:12\\.754Z", } - given := []envoy.Cluster{ + given := []Cluster{ { Name: "local_agent", FullyQualifiedDomainName: "local_agent", @@ -102,7 +97,7 @@ func TestFormatEndpoints(t *testing.T) { "192.168.65.131:20000.*1.00.*HEALTHY", } - given := []envoy.Endpoint{ + given := []Endpoint{ { Address: "192.168.79.187:8502", Cluster: "local_agent", @@ -179,11 +174,11 @@ func TestFormatListeners(t *testing.T) { "Any.*-> original-destination", } - given := []envoy.Listener{ + given := []Listener{ { Name: "public_listener", Address: "192.168.69.179:20000", - FilterChain: []envoy.FilterChain{ + FilterChain: []FilterChain{ { FilterChainMatch: "Any", Filters: []string{"* -> local_app/"}, @@ -195,7 +190,7 @@ func TestFormatListeners(t *testing.T) { { Name: "outbound_listener", Address: "127.0.0.1:15001", - FilterChain: []envoy.FilterChain{ + FilterChain: []FilterChain{ { FilterChainMatch: "10.100.134.173/32, 240.0.0.3/32", Filters: []string{"-> client.default.dc1.internal.bc3815c2-1a0f-f3ff-a2e9-20d791f08d00.consul"}, @@ -250,7 +245,7 @@ func TestFormatRoutes(t *testing.T) { "server.*server\\.default\\.dc1\\.internal\\.bc3815c2-1a0f-f3ff-a2e9-20d791f08d00\\.consul/.*2022-05-24T17:41:59\\.078Z", } - given := []envoy.Route{ + given := []Route{ { Name: "public_listener", DestinationCluster: "local_app/", @@ -287,7 +282,7 @@ func TestFormatSecrets(t *testing.T) { "ROOTCA.*Dynamic Warming.*2022-03-15T05:14:22.868Z", } - given := []envoy.Secret{ + given := []Secret{ { Name: "default", Type: "Dynamic Active", diff --git a/cli/common/envoy/testdata/test_clusters.json b/cli/cmd/proxy/read/test_clusters.json similarity index 100% rename from cli/common/envoy/testdata/test_clusters.json rename to cli/cmd/proxy/read/test_clusters.json diff --git a/cli/common/envoy/testdata/test_config_dump.json b/cli/cmd/proxy/read/test_config_dump.json similarity index 100% rename from cli/common/envoy/testdata/test_config_dump.json rename to cli/cmd/proxy/read/test_config_dump.json diff --git a/cli/cmd/status/status.go b/cli/cmd/status/status.go index ebe60528f2..c2108cc631 100644 --- a/cli/cmd/status/status.go +++ b/cli/cmd/status/status.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package status import ( diff --git a/cli/cmd/status/status_test.go b/cli/cmd/status/status_test.go index 7984415c43..8666fd8493 100644 --- a/cli/cmd/status/status_test.go +++ b/cli/cmd/status/status_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package status import ( diff --git a/cli/cmd/troubleshoot/command.go b/cli/cmd/troubleshoot/command.go deleted file mode 100644 index 723088a134..0000000000 --- a/cli/cmd/troubleshoot/command.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package troubleshoot - -import ( - "fmt" - - "github.com/hashicorp/consul-k8s/cli/common" - "github.com/mitchellh/cli" -) - -// TroubleshootCommand provides a synopsis for the troubleshoot subcommands (e.g. proxy, upstreams). -type TroubleshootCommand struct { - *common.BaseCommand -} - -// Run prints out information about the subcommands. -func (c *TroubleshootCommand) Run([]string) int { - return cli.RunResultHelp -} - -func (c *TroubleshootCommand) Help() string { - return fmt.Sprintf("%s\n\nUsage: consul-k8s troubleshoot ", c.Synopsis()) -} - -func (c *TroubleshootCommand) Synopsis() string { - return "Troubleshoot network and security configurations." -} diff --git a/cli/cmd/troubleshoot/proxy/proxy.go b/cli/cmd/troubleshoot/proxy/proxy.go deleted file mode 100644 index cd8362bf28..0000000000 --- a/cli/cmd/troubleshoot/proxy/proxy.go +++ /dev/null @@ -1,285 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package proxy - -import ( - "fmt" - "net" - "strings" - "sync" - - "github.com/hashicorp/consul-k8s/cli/common" - "github.com/hashicorp/consul-k8s/cli/common/flag" - "github.com/hashicorp/consul-k8s/cli/common/terminal" - troubleshoot "github.com/hashicorp/consul/troubleshoot/proxy" - "github.com/posener/complete" - helmCLI "helm.sh/helm/v3/pkg/cli" - "k8s.io/apimachinery/pkg/api/validation" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" -) - -const ( - defaultAdminPort int = 19000 - flagNameKubeConfig = "kubeconfig" - flagNameKubeContext = "context" - flagNameNamespace = "namespace" - flagNamePod = "pod" - flagNameUpstreamEnvoyID = "upstream-envoy-id" - flagNameUpstreamIP = "upstream-ip" - DebugColor = "\033[0;36m%s\033[0m" -) - -type ProxyCommand struct { - *common.BaseCommand - - kubernetes kubernetes.Interface - - set *flag.Sets - - flagKubeConfig string - flagKubeContext string - flagNamespace string - - flagPod string - flagUpstreamEnvoyID string - flagUpstreamIP string - - restConfig *rest.Config - - once sync.Once - help string -} - -// init sets up flags and help text for the command. -func (c *ProxyCommand) init() { - c.set = flag.NewSets() - f := c.set.NewSet("Command Options") - - f.StringVar(&flag.StringVar{ - Name: flagNamePod, - Target: &c.flagPod, - Usage: "The pod to port-forward to.", - Aliases: []string{"p"}, - }) - - f.StringVar(&flag.StringVar{ - Name: flagNameUpstreamEnvoyID, - Target: &c.flagUpstreamEnvoyID, - Usage: "The envoy identifier of the upstream service that receives the communication. (explicit upstreams only)", - Aliases: []string{"id"}, - }) - - f.StringVar(&flag.StringVar{ - Name: flagNameUpstreamIP, - Target: &c.flagUpstreamIP, - Usage: "The IP address of the upstream service that receives the communication. (transparent proxy only)", - Aliases: []string{"ip"}, - }) - - f = c.set.NewSet("Global Options") - f.StringVar(&flag.StringVar{ - Name: flagNameKubeConfig, - Aliases: []string{"c"}, - Target: &c.flagKubeConfig, - Default: "", - Usage: "Set the path to kubeconfig file.", - }) - f.StringVar(&flag.StringVar{ - Name: flagNameKubeContext, - Target: &c.flagKubeContext, - Default: "", - Usage: "Set the Kubernetes context to use.", - }) - - f.StringVar(&flag.StringVar{ - Name: flagNameNamespace, - Target: &c.flagNamespace, - Usage: "The namespace the pod is in.", - Aliases: []string{"n"}, - }) - - c.help = c.set.Help() -} - -// Run executes the list command. -func (c *ProxyCommand) Run(args []string) int { - c.once.Do(c.init) - c.Log.ResetNamed("list") - defer common.CloseWithError(c.BaseCommand) - - // Parse the command line flags. - if err := c.set.Parse(args); err != nil { - c.UI.Output("Error parsing arguments: %v", err.Error(), terminal.WithErrorStyle()) - return 1 - } - - // Validate the command line flags. - if err := c.validateFlags(); err != nil { - c.UI.Output("Invalid argument: %v", err.Error(), terminal.WithErrorStyle()) - return 1 - } - - if c.kubernetes == nil { - if err := c.initKubernetes(); err != nil { - c.UI.Output("Error initializing Kubernetes client: %v", err.Error(), terminal.WithErrorStyle()) - return 1 - } - } - - if err := c.Troubleshoot(); err != nil { - c.UI.Output("Error running troubleshoot: %v", err.Error(), terminal.WithErrorStyle()) - return 1 - } - - return 0 -} - -// validateFlags ensures that the flags passed in by the can be used. -func (c *ProxyCommand) validateFlags() error { - - if (c.flagUpstreamEnvoyID == "" && c.flagUpstreamIP == "") || (c.flagUpstreamEnvoyID != "" && c.flagUpstreamIP != "") { - return fmt.Errorf("-upstream-envoy-id OR -upstream-ip is required.\n Please run `consul troubleshoot upstreams` to find the corresponding upstream.") - } - - if c.flagPod == "" { - return fmt.Errorf("-pod flag is required") - } - - if errs := validation.ValidateNamespaceName(c.flagNamespace, false); c.flagNamespace != "" && len(errs) > 0 { - return fmt.Errorf("invalid namespace name passed for -namespace/-n: %v", strings.Join(errs, "; ")) - } - - return nil -} - -// initKubernetes initializes the Kubernetes client. -func (c *ProxyCommand) initKubernetes() (err error) { - settings := helmCLI.New() - - if c.flagKubeConfig != "" { - settings.KubeConfig = c.flagKubeConfig - } - - if c.flagKubeContext != "" { - settings.KubeContext = c.flagKubeContext - } - - if c.restConfig == nil { - if c.restConfig, err = settings.RESTClientGetter().ToRESTConfig(); err != nil { - return fmt.Errorf("error creating Kubernetes REST config %v", err) - } - } - - if c.kubernetes == nil { - if c.kubernetes, err = kubernetes.NewForConfig(c.restConfig); err != nil { - return fmt.Errorf("error creating Kubernetes client %v", err) - } - } - - if c.flagNamespace == "" { - c.flagNamespace = settings.Namespace() - } - - return nil -} - -func (c *ProxyCommand) Troubleshoot() error { - pf := common.PortForward{ - Namespace: c.flagNamespace, - PodName: c.flagPod, - RemotePort: defaultAdminPort, - KubeClient: c.kubernetes, - RestConfig: c.restConfig, - } - - endpoint, err := pf.Open(c.Ctx) - if err != nil { - return err - } - defer pf.Close() - - adminAddr, adminPort, err := net.SplitHostPort(endpoint) - if err != nil { - return err - } - - adminAddrIP, err := net.ResolveIPAddr("ip", adminAddr) - if err != nil { - return err - } - - t, err := troubleshoot.NewTroubleshoot(adminAddrIP, adminPort) - if err != nil { - return err - } - - // err = t.GetEnvoyConfigDump() - // if err != nil { - // return err - // } - - messages, err := t.RunAllTests(c.flagUpstreamEnvoyID, c.flagUpstreamIP) - if err != nil { - return err - } - - c.UI.Output("Validation", terminal.WithHeaderStyle()) - for _, o := range messages { - if o.Success { - c.UI.Output(o.Message, terminal.WithSuccessStyle()) - } else { - c.UI.Output(o.Message, terminal.WithErrorStyle()) - for _, action := range o.PossibleActions { - c.UI.Output(fmt.Sprintf("-> %s", action), terminal.WithInfoStyle()) - } - } - } - - return nil -} - -// AutocompleteFlags returns a mapping of supported flags and autocomplete -// options for this command. The map key for the Flags map should be the -// complete flag such as "-foo" or "--foo". -func (c *ProxyCommand) AutocompleteFlags() complete.Flags { - return complete.Flags{ - fmt.Sprintf("-%s", flagNameNamespace): complete.PredictNothing, - fmt.Sprintf("-%s", flagNameKubeConfig): complete.PredictFiles("*"), - fmt.Sprintf("-%s", flagNameKubeContext): complete.PredictNothing, - } -} - -// AutocompleteArgs returns the argument predictor for this command. -// Since argument completion is not supported, this will return -// complete.PredictNothing. -func (c *ProxyCommand) AutocompleteArgs() complete.Predictor { - return complete.PredictNothing -} - -func (c *ProxyCommand) Synopsis() string { - return synopsis -} - -func (c *ProxyCommand) Help() string { - return help -} - -const ( - synopsis = "Troubleshoots service mesh issues." - help = ` -Usage: consul-k8s troubleshoot proxy [options] - - Connect to a pod with a proxy and troubleshoots service mesh communication issues. - - Requires a pod and upstream service SNI. - - Examples: - $ consul-k8s troubleshoot proxy -pod pod1 -upstream foo - - where 'pod1' is the pod running a consul proxy and 'foo' is the upstream envoy ID which - can be obtained by running: - $ consul-k8s troubleshoot upstreams [options] -` -) diff --git a/cli/cmd/troubleshoot/proxy/proxy_test.go b/cli/cmd/troubleshoot/proxy/proxy_test.go deleted file mode 100644 index e018878bf5..0000000000 --- a/cli/cmd/troubleshoot/proxy/proxy_test.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package proxy - -import ( - "bytes" - "context" - "io" - "os" - "testing" - - "github.com/hashicorp/consul-k8s/cli/common" - "github.com/hashicorp/consul-k8s/cli/common/terminal" - "github.com/hashicorp/go-hclog" - "github.com/stretchr/testify/require" - "k8s.io/client-go/kubernetes/fake" -) - -func TestFlagParsing(t *testing.T) { - cases := map[string]struct { - args []string - out int - }{ - "No args, should fail": { - args: []string{}, - out: 1, - }, - "Nonexistent flag passed, -foo bar, should fail": { - args: []string{"-foo", "bar"}, - out: 1, - }, - "Invalid argument passed, -namespace notaname, should fail": { - args: []string{"-namespace", "notaname"}, - out: 1, - }, - "Cannot pass both -upstream-envoy-id and -upstream-ip flags, should fail": { - args: []string{"-upstream-envoy-id", "1234", "-upstream-ip", "127.0.0.1"}, - out: 1, - }, - "Cannot pass empty -upstream-envoy-id and -upstream-ip flags, should fail": { - args: []string{"-upstream-envoy-id", "-upstream-ip"}, - out: 1, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - c := setupCommand(new(bytes.Buffer)) - c.kubernetes = fake.NewSimpleClientset() - out := c.Run(tc.args) - require.Equal(t, tc.out, out) - }) - } -} - -func setupCommand(buf io.Writer) *ProxyCommand { - // Log at a test level to standard out. - log := hclog.New(&hclog.LoggerOptions{ - Name: "test", - Level: hclog.Debug, - Output: os.Stdout, - }) - - // Setup and initialize the command struct - command := &ProxyCommand{ - BaseCommand: &common.BaseCommand{ - Log: log, - UI: terminal.NewUI(context.Background(), buf), - }, - } - command.init() - - return command -} diff --git a/cli/cmd/troubleshoot/upstreams/upstreams.go b/cli/cmd/troubleshoot/upstreams/upstreams.go deleted file mode 100644 index abc3235cd5..0000000000 --- a/cli/cmd/troubleshoot/upstreams/upstreams.go +++ /dev/null @@ -1,277 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package upstreams - -import ( - "fmt" - "net" - "sort" - "strconv" - "strings" - "sync" - - "github.com/hashicorp/consul-k8s/cli/common" - "github.com/hashicorp/consul-k8s/cli/common/flag" - "github.com/hashicorp/consul-k8s/cli/common/terminal" - troubleshoot "github.com/hashicorp/consul/troubleshoot/proxy" - "github.com/posener/complete" - helmCLI "helm.sh/helm/v3/pkg/cli" - "k8s.io/apimachinery/pkg/api/validation" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" -) - -const ( - defaultAdminPort int = 19000 - flagNameKubeConfig = "kubeconfig" - flagNameKubeContext = "context" - flagNameNamespace = "namespace" - flagNamePod = "pod" -) - -type UpstreamsCommand struct { - *common.BaseCommand - - kubernetes kubernetes.Interface - - set *flag.Sets - - flagKubeConfig string - flagKubeContext string - flagNamespace string - - flagPod string - - restConfig *rest.Config - - once sync.Once - help string -} - -// init sets up flags and help text for the command. -func (c *UpstreamsCommand) init() { - c.set = flag.NewSets() - f := c.set.NewSet("Command Options") - - f.StringVar(&flag.StringVar{ - Name: flagNamePod, - Target: &c.flagPod, - Usage: "The pod to port-forward to.", - Aliases: []string{"p"}, - }) - - f = c.set.NewSet("Global Options") - f.StringVar(&flag.StringVar{ - Name: flagNameKubeConfig, - Aliases: []string{"c"}, - Target: &c.flagKubeConfig, - Default: "", - Usage: "Set the path to kubeconfig file.", - }) - f.StringVar(&flag.StringVar{ - Name: flagNameKubeContext, - Target: &c.flagKubeContext, - Default: "", - Usage: "Set the Kubernetes context to use.", - }) - - f.StringVar(&flag.StringVar{ - Name: flagNameNamespace, - Target: &c.flagNamespace, - Usage: "The namespace the pod is in.", - Aliases: []string{"n"}, - }) - - c.help = c.set.Help() -} - -// Run executes the list command. -func (c *UpstreamsCommand) Run(args []string) int { - c.once.Do(c.init) - c.Log.ResetNamed("list") - defer common.CloseWithError(c.BaseCommand) - - // Parse the command line flags. - if err := c.set.Parse(args); err != nil { - c.UI.Output("Error parsing arguments: %v", err.Error(), terminal.WithErrorStyle()) - return 1 - } - - // Validate the command line flags. - if err := c.validateFlags(); err != nil { - c.UI.Output("Invalid argument: %v", err.Error(), terminal.WithErrorStyle()) - return 1 - } - - if c.kubernetes == nil { - if err := c.initKubernetes(); err != nil { - c.UI.Output("Error initializing Kubernetes client: %v", err.Error(), terminal.WithErrorStyle()) - return 1 - } - } - - if err := c.Troubleshoot(); err != nil { - c.UI.Output("Error running troubleshoot: %v", err.Error(), terminal.WithErrorStyle()) - return 1 - } - - return 0 -} - -// validateFlags ensures that the flags passed in by the can be used. -func (c *UpstreamsCommand) validateFlags() error { - - if c.flagPod == "" { - return fmt.Errorf("-pod flag is required") - } - - if errs := validation.ValidateNamespaceName(c.flagNamespace, false); c.flagNamespace != "" && len(errs) > 0 { - return fmt.Errorf("invalid namespace name passed for -namespace/-n: %v", strings.Join(errs, "; ")) - } - - return nil -} - -// initKubernetes initializes the Kubernetes client. -func (c *UpstreamsCommand) initKubernetes() (err error) { - settings := helmCLI.New() - - if c.flagKubeConfig != "" { - settings.KubeConfig = c.flagKubeConfig - } - - if c.flagKubeContext != "" { - settings.KubeContext = c.flagKubeContext - } - - if c.restConfig == nil { - if c.restConfig, err = settings.RESTClientGetter().ToRESTConfig(); err != nil { - return fmt.Errorf("error creating Kubernetes REST config %v", err) - } - } - - if c.kubernetes == nil { - if c.kubernetes, err = kubernetes.NewForConfig(c.restConfig); err != nil { - return fmt.Errorf("error creating Kubernetes client %v", err) - } - } - - if c.flagNamespace == "" { - c.flagNamespace = settings.Namespace() - } - - return nil -} - -func (c *UpstreamsCommand) Troubleshoot() error { - pf := common.PortForward{ - Namespace: c.flagNamespace, - PodName: c.flagPod, - RemotePort: defaultAdminPort, - KubeClient: c.kubernetes, - RestConfig: c.restConfig, - } - - endpoint, err := pf.Open(c.Ctx) - if err != nil { - return fmt.Errorf("error opening endpoint: %v", err) - } - defer pf.Close() - - adminAddr, adminPort, err := net.SplitHostPort(endpoint) - if err != nil { - return fmt.Errorf("error splitting hostport: %v", err) - } - - adminAddrIP, err := net.ResolveIPAddr("ip", adminAddr) - if err != nil { - return fmt.Errorf("error resolving ip address: %v", err) - } - - t, err := troubleshoot.NewTroubleshoot(adminAddrIP, adminPort) - if err != nil { - return fmt.Errorf("error creating new troubleshoot: %v", err) - } - - envoyIDs, upstreamIPs, err := t.GetUpstreams() - if err != nil { - return fmt.Errorf("error getting upstreams: %v", err) - } - - c.UI.Output(fmt.Sprintf("Upstreams (explicit upstreams only) (%v)", len(envoyIDs)), terminal.WithHeaderStyle()) - for _, e := range envoyIDs { - c.UI.Output(e) - } - - c.UI.Output(fmt.Sprintf("Upstream IPs (transparent proxy only) (%v)", len(upstreamIPs)), terminal.WithHeaderStyle()) - table := terminal.NewTable("IPs ", "Virtual ", "Cluster Names") - for _, u := range upstreamIPs { - table.AddRow([]string{formatIPs(u.IPs), strconv.FormatBool(u.IsVirtual), formatClusterNames(u.ClusterNames)}, []string{}) - } - c.UI.Table(table) - - c.UI.Output("\nIf you cannot find the upstream address or cluster for a transparent proxy upstream:", terminal.WithInfoStyle()) - c.UI.Output("-> Check intentions: Transparent proxy upstreams are configured based on intentions. Make sure you "+ - "have configured intentions to allow traffic to your upstream.", terminal.WithInfoStyle()) - c.UI.Output("-> To check that the right cluster is being dialed, run a DNS lookup "+ - "for the upstream you are dialing. For example, run `dig backend.svc.consul` to return the IP address for the `backend` service. If the address you get from that is missing "+ - "from the upstream IPs, it means that your proxy may be misconfigured.", terminal.WithInfoStyle()) - - return nil -} - -// AutocompleteFlags returns a mapping of supported flags and autocomplete -// options for this command. The map key for the Flags map should be the -// complete flag such as "-foo" or "--foo". -func (c *UpstreamsCommand) AutocompleteFlags() complete.Flags { - return complete.Flags{ - fmt.Sprintf("-%s", flagNameNamespace): complete.PredictNothing, - fmt.Sprintf("-%s", flagNameKubeConfig): complete.PredictFiles("*"), - fmt.Sprintf("-%s", flagNameKubeContext): complete.PredictNothing, - } -} - -// AutocompleteArgs returns the argument predictor for this command. -// Since argument completion is not supported, this will return -// complete.PredictNothing. -func (c *UpstreamsCommand) AutocompleteArgs() complete.Predictor { - return complete.PredictNothing -} - -func (c *UpstreamsCommand) Synopsis() string { - return synopsis -} - -func (c *UpstreamsCommand) Help() string { - return help -} - -func formatIPs(ips []string) string { - return strings.Join(ips, ", ") -} - -func formatClusterNames(names map[string]struct{}) string { - var out []string - for k := range names { - out = append(out, k) - } - sort.Strings(out) - return strings.Join(out, ", ") -} - -const ( - synopsis = "Connect to a pod with a proxy and gather upstream services." - help = ` -Usage: consul-k8s troubleshoot upstreams [options] - - Connect to a pod with a proxy and gather upstream services. - - Requires a pod. - - Examples: - $ consul-k8s troubleshoot upstreams -pod pod1 - - where 'pod1' is the pod running a consul proxy -` -) diff --git a/cli/cmd/troubleshoot/upstreams/upstreams_test.go b/cli/cmd/troubleshoot/upstreams/upstreams_test.go deleted file mode 100644 index 595c480a35..0000000000 --- a/cli/cmd/troubleshoot/upstreams/upstreams_test.go +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package upstreams - -import ( - "bytes" - "context" - "io" - "os" - "testing" - - "github.com/hashicorp/consul-k8s/cli/common" - "github.com/hashicorp/consul-k8s/cli/common/terminal" - "github.com/hashicorp/go-hclog" - "github.com/stretchr/testify/require" - "k8s.io/client-go/kubernetes/fake" -) - -func TestFlagParsing(t *testing.T) { - cases := map[string]struct { - args []string - out int - }{ - "No args, should fail": { - args: []string{}, - out: 1, - }, - "Nonexistent flag passed, -foo bar, should fail": { - args: []string{"-foo", "bar"}, - out: 1, - }, - "Invalid argument passed, -namespace notaname, should fail": { - args: []string{"-namespace", "notaname"}, - out: 1, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - c := setupCommand(new(bytes.Buffer)) - c.kubernetes = fake.NewSimpleClientset() - out := c.Run(tc.args) - require.Equal(t, tc.out, out) - }) - } -} - -func TestFormatIPs(t *testing.T) { - t.Parallel() - - cases := []struct { - name string - actual []string - expected string - }{ - { - name: "single IPs", - actual: []string{"1.1.1.1"}, - expected: "1.1.1.1", - }, - - { - name: "several IPs", - actual: []string{"1.1.1.1", "2.2.2.2"}, - expected: "1.1.1.1, 2.2.2.2", - }, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - got := formatIPs(c.actual) - if c.expected != got { - t.Errorf("expected %v, got %v", c.expected, got) - } - }) - } -} - -func TestFormatClusterNames(t *testing.T) { - cases := []struct { - name string - actual map[string]struct{} - expected string - }{ - { - name: "single cluster", - actual: map[string]struct{}{ - "cluster1": {}, - }, - expected: "cluster1", - }, - { - name: "several clusters", - actual: map[string]struct{}{ - "cluster1": {}, - "cluster2": {}, - "cluster3": {}, - }, - expected: "cluster1, cluster2, cluster3", - }, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - got := formatClusterNames(c.actual) - if c.expected != got { - t.Errorf("expected %v, got %v", c.expected, got) - } - }) - } -} - -func setupCommand(buf io.Writer) *UpstreamsCommand { - // Log at a test level to standard out. - log := hclog.New(&hclog.LoggerOptions{ - Name: "test", - Level: hclog.Debug, - Output: os.Stdout, - }) - - // Setup and initialize the command struct - command := &UpstreamsCommand{ - BaseCommand: &common.BaseCommand{ - Log: log, - UI: terminal.NewUI(context.Background(), buf), - }, - } - command.init() - - return command -} diff --git a/cli/cmd/uninstall/uninstall.go b/cli/cmd/uninstall/uninstall.go index caaaa2f0fa..06bf4a19b3 100644 --- a/cli/cmd/uninstall/uninstall.go +++ b/cli/cmd/uninstall/uninstall.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package uninstall import ( diff --git a/cli/cmd/uninstall/uninstall_test.go b/cli/cmd/uninstall/uninstall_test.go index 1d3fffc2c1..2adc0960ab 100644 --- a/cli/cmd/uninstall/uninstall_test.go +++ b/cli/cmd/uninstall/uninstall_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package uninstall import ( diff --git a/cli/cmd/upgrade/upgrade.go b/cli/cmd/upgrade/upgrade.go index a7e79d2239..4c962c47b5 100644 --- a/cli/cmd/upgrade/upgrade.go +++ b/cli/cmd/upgrade/upgrade.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package upgrade import ( @@ -20,6 +17,7 @@ import ( "github.com/hashicorp/consul-k8s/cli/helm" "github.com/hashicorp/consul-k8s/cli/preset" "github.com/posener/complete" + helmCLI "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/cli/values" "helm.sh/helm/v3/pkg/getter" @@ -427,7 +425,7 @@ func (c *Command) validateFlags(args []string) error { return fmt.Errorf("cannot set both -%s and -%s", flagNameConfigFile, flagNamePreset) } if ok := slices.Contains(preset.Presets, c.flagPreset); c.flagPreset != defaultPreset && !ok { - return fmt.Errorf("'%s' is not a valid preset (valid presets: %s)", c.flagPreset, strings.Join(preset.Presets, ", ")) + return fmt.Errorf("'%s' is not a valid preset", c.flagPreset) } if _, err := time.ParseDuration(c.flagTimeout); err != nil { return fmt.Errorf("unable to parse -%s: %s", flagNameTimeout, err) diff --git a/cli/cmd/upgrade/upgrade_test.go b/cli/cmd/upgrade/upgrade_test.go index f8421296a7..809cac1034 100644 --- a/cli/cmd/upgrade/upgrade_test.go +++ b/cli/cmd/upgrade/upgrade_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package upgrade import ( @@ -455,7 +452,7 @@ func TestUpgrade(t *testing.T) { messages: []string{ "\n==> Checking if Consul can be upgraded\n ✓ Existing Consul installation found to be upgraded.\n Name: consul\n Namespace: consul\n", "\n==> Checking if Consul demo application can be upgraded\n No existing Consul demo application installation found.\n", - "\n==> Consul Upgrade Summary\n ✓ Downloaded charts.\n \n Difference between user overrides for current and upgraded charts\n -----------------------------------------------------------------\n + connectInject:\n + enabled: true\n + metrics:\n + defaultEnableMerging: true\n + defaultEnabled: true\n + enableGatewayMetrics: true\n + global:\n + metrics:\n + enableAgentMetrics: true\n + enabled: true\n + name: consul\n + prometheus:\n + enabled: true\n + server:\n + replicas: 1\n + ui:\n + enabled: true\n + service:\n + enabled: true\n \n", + "\n==> Consul Upgrade Summary\n ✓ Downloaded charts.\n \n Difference between user overrides for current and upgraded charts\n -----------------------------------------------------------------\n + connectInject:\n + enabled: true\n + metrics:\n + defaultEnableMerging: true\n + defaultEnabled: true\n + enableGatewayMetrics: true\n + controller:\n + enabled: true\n + global:\n + metrics:\n + enableAgentMetrics: true\n + enabled: true\n + name: consul\n + prometheus:\n + enabled: true\n + server:\n + replicas: 1\n + ui:\n + enabled: true\n + service:\n + enabled: true\n \n", "\n==> Upgrading Consul\n ✓ Consul upgraded in namespace \"consul\".\n", }, helmActionsRunner: &helm.MockActionRunner{ @@ -480,7 +477,7 @@ func TestUpgrade(t *testing.T) { messages: []string{ "\n==> Checking if Consul can be upgraded\n ✓ Existing Consul installation found to be upgraded.\n Name: consul\n Namespace: consul\n", "\n==> Checking if Consul demo application can be upgraded\n No existing Consul demo application installation found.\n", - "\n==> Consul Upgrade Summary\n ✓ Downloaded charts.\n \n Difference between user overrides for current and upgraded charts\n -----------------------------------------------------------------\n + connectInject:\n + enabled: true\n + global:\n + acls:\n + manageSystemACLs: true\n + gossipEncryption:\n + autoGenerate: true\n + name: consul\n + tls:\n + enableAutoEncrypt: true\n + enabled: true\n + server:\n + replicas: 1\n \n", + "\n==> Consul Upgrade Summary\n ✓ Downloaded charts.\n \n Difference between user overrides for current and upgraded charts\n -----------------------------------------------------------------\n + connectInject:\n + enabled: true\n + controller:\n + enabled: true\n + global:\n + acls:\n + manageSystemACLs: true\n + gossipEncryption:\n + autoGenerate: true\n + name: consul\n + tls:\n + enableAutoEncrypt: true\n + enabled: true\n + server:\n + replicas: 1\n \n", "\n==> Upgrading Consul\n ✓ Consul upgraded in namespace \"consul\".\n", }, helmActionsRunner: &helm.MockActionRunner{ diff --git a/cli/cmd/version/version.go b/cli/cmd/version/version.go index cab5cbc34d..f31fa711f1 100644 --- a/cli/cmd/version/version.go +++ b/cli/cmd/version/version.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package version import ( diff --git a/cli/commands.go b/cli/commands.go index 1c58e75cf8..fdb581decd 100644 --- a/cli/commands.go +++ b/cli/commands.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package main import ( @@ -11,12 +8,8 @@ import ( "github.com/hashicorp/consul-k8s/cli/cmd/install" "github.com/hashicorp/consul-k8s/cli/cmd/proxy" "github.com/hashicorp/consul-k8s/cli/cmd/proxy/list" - "github.com/hashicorp/consul-k8s/cli/cmd/proxy/loglevel" "github.com/hashicorp/consul-k8s/cli/cmd/proxy/read" "github.com/hashicorp/consul-k8s/cli/cmd/status" - "github.com/hashicorp/consul-k8s/cli/cmd/troubleshoot" - troubleshoot_proxy "github.com/hashicorp/consul-k8s/cli/cmd/troubleshoot/proxy" - "github.com/hashicorp/consul-k8s/cli/cmd/troubleshoot/upstreams" "github.com/hashicorp/consul-k8s/cli/cmd/uninstall" "github.com/hashicorp/consul-k8s/cli/cmd/upgrade" cmdversion "github.com/hashicorp/consul-k8s/cli/cmd/version" @@ -28,6 +21,7 @@ import ( ) func initializeCommands(ctx context.Context, log hclog.Logger) (*common.BaseCommand, map[string]cli.CommandFactory) { + baseCommand := &common.BaseCommand{ Ctx: ctx, Log: log, @@ -71,11 +65,6 @@ func initializeCommands(ctx context.Context, log hclog.Logger) (*common.BaseComm BaseCommand: baseCommand, }, nil }, - "proxy log": func() (cli.Command, error) { - return &loglevel.LogLevelCommand{ - BaseCommand: baseCommand, - }, nil - }, "proxy read": func() (cli.Command, error) { return &read.ReadCommand{ BaseCommand: baseCommand, @@ -91,21 +80,6 @@ func initializeCommands(ctx context.Context, log hclog.Logger) (*common.BaseComm BaseCommand: baseCommand, }, nil }, - "troubleshoot": func() (cli.Command, error) { - return &troubleshoot.TroubleshootCommand{ - BaseCommand: baseCommand, - }, nil - }, - "troubleshoot proxy": func() (cli.Command, error) { - return &troubleshoot_proxy.ProxyCommand{ - BaseCommand: baseCommand, - }, nil - }, - "troubleshoot upstreams": func() (cli.Command, error) { - return &upstreams.UpstreamsCommand{ - BaseCommand: baseCommand, - }, nil - }, } return baseCommand, commands diff --git a/cli/common/base.go b/cli/common/base.go index 126e52ab07..2237b66f84 100644 --- a/cli/common/base.go +++ b/cli/common/base.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package common import ( diff --git a/cli/common/diff.go b/cli/common/diff.go index c11f4f8c78..30d03e2968 100644 --- a/cli/common/diff.go +++ b/cli/common/diff.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package common import ( diff --git a/cli/common/diff_test.go b/cli/common/diff_test.go index 83bc4a59ab..31563e0b6b 100644 --- a/cli/common/diff_test.go +++ b/cli/common/diff_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package common import ( diff --git a/cli/common/envoy/logger_params.go b/cli/common/envoy/logger_params.go deleted file mode 100644 index ce18e727c3..0000000000 --- a/cli/common/envoy/logger_params.go +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package envoy - -import ( - "fmt" - "strings" -) - -type logLevel struct { - name string - level string -} - -type LoggerParams struct { - globalLevel string - individualLevels []logLevel -} - -func NewLoggerParams() *LoggerParams { - return &LoggerParams{ - individualLevels: make([]logLevel, 0), - } -} - -func (l *LoggerParams) SetLoggerLevel(name, level string) error { - err := validateLoggerName(name) - if err != nil { - return err - } - err = validateLogLevel(level) - if err != nil { - return err - } - - l.individualLevels = append(l.individualLevels, logLevel{name: name, level: level}) - return nil -} - -func (l *LoggerParams) SetGlobalLoggerLevel(level string) error { - err := validateLogLevel(level) - if err != nil { - return err - } - l.globalLevel = level - return nil -} - -func validateLogLevel(level string) error { - if _, ok := envoyLevels[level]; !ok { - logLevels := []string{} - for levelName := range envoyLevels { - logLevels = append(logLevels, levelName) - } - return fmt.Errorf("Unknown log level %s, available log levels are %q", level, strings.Join(logLevels, ", ")) - } - return nil -} - -func validateLoggerName(name string) error { - if _, ok := EnvoyLoggers[name]; !ok { - loggers := []string{} - for loggerName := range envoyLevels { - loggers = append(loggers, loggerName) - } - return fmt.Errorf("Unknown logger %s, available loggers are %q", name, strings.Join(loggers, ", ")) - - } - return nil -} - -func (l *LoggerParams) String() string { - switch { - // Global log level change is set - case l.globalLevel != "": - return fmt.Sprintf("?level=%s", l.globalLevel) - - // only one specific logger is changed - case len(l.individualLevels) == 1: - params := fmt.Sprintf("?%s=%s", l.individualLevels[0].name, l.individualLevels[0].level) - return params - - // multiple specific loggers are changed - case len(l.individualLevels) > 1: - logParams := make([]string, 0, len(l.individualLevels)) - for _, logger := range l.individualLevels { - logParams = append(logParams, fmt.Sprintf("%s:%s", logger.name, logger.level)) - } - - params := fmt.Sprintf("?paths=%s", strings.Join(logParams, ",")) - return params - default: - - // default path, this is hit if there are no params - return "" - } -} - -// trace debug info warning error critical off. -var envoyLevels = map[string]struct{}{ - "trace": {}, - "debug": {}, - "info": {}, - "warning": {}, - "error": {}, - "critical": {}, - "off": {}, -} - -var EnvoyLoggers = map[string]struct{}{ - "admin": {}, - "alternate_protocols_cache": {}, - "aws": {}, - "assert": {}, - "backtrace": {}, - "cache_filter": {}, - "client": {}, - "config": {}, - "connection": {}, - "conn_handler": {}, - "decompression": {}, - "dns": {}, - "dubbo": {}, - "envoy_bug": {}, - "ext_authz": {}, - "ext_proc": {}, - "rocketmq": {}, - "file": {}, - "filter": {}, - "forward_proxy": {}, - "grpc": {}, - "happy_eyeballs": {}, - "hc": {}, - "health_checker": {}, - "http": {}, - "http2": {}, - "hystrix": {}, - "init": {}, - "io": {}, - "jwt": {}, - "kafka": {}, - "key_value_store": {}, - "lua": {}, - "main": {}, - "matcher": {}, - "misc": {}, - "mongo": {}, - "multi_connection": {}, - "oauth2": {}, - "quic": {}, - "quic_stream": {}, - "pool": {}, - "rbac": {}, - "rds": {}, - "redis": {}, - "router": {}, - "runtime": {}, - "stats": {}, - "secret": {}, - "tap": {}, - "testing": {}, - "thrift": {}, - "tracing": {}, - "upstream": {}, - "udp": {}, - "wasm": {}, - "websocket": {}, -} diff --git a/cli/common/envoy/logger_params_test.go b/cli/common/envoy/logger_params_test.go deleted file mode 100644 index 1d1779e267..0000000000 --- a/cli/common/envoy/logger_params_test.go +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package envoy - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestSetLoggerLevelSucceeds(t *testing.T) { - t.Parallel() - testCases := map[string]struct { - levelsToSet [][]string - expectedIndividualLevels []logLevel - }{ - "single log level change trace": { - levelsToSet: [][]string{ - {"admin", "trace"}, - }, - expectedIndividualLevels: []logLevel{ - {name: "admin", level: "trace"}, - }, - }, - "single log level change debug": { - levelsToSet: [][]string{ - {"admin", "debug"}, - }, - expectedIndividualLevels: []logLevel{ - {name: "admin", level: "debug"}, - }, - }, - "single log level change info": { - levelsToSet: [][]string{ - {"admin", "info"}, - }, - expectedIndividualLevels: []logLevel{ - {name: "admin", level: "info"}, - }, - }, - "single log level change warning": { - levelsToSet: [][]string{ - {"admin", "warning"}, - }, - expectedIndividualLevels: []logLevel{ - {name: "admin", level: "warning"}, - }, - }, - "single log level change error": { - levelsToSet: [][]string{ - {"admin", "error"}, - }, - expectedIndividualLevels: []logLevel{ - {name: "admin", level: "error"}, - }, - }, - "single log level change critical": { - levelsToSet: [][]string{ - {"admin", "critical"}, - }, - expectedIndividualLevels: []logLevel{ - {name: "admin", level: "critical"}, - }, - }, - "single log level change off": { - levelsToSet: [][]string{ - {"admin", "off"}, - }, - expectedIndividualLevels: []logLevel{ - {name: "admin", level: "off"}, - }, - }, - "multiple log level change": { - levelsToSet: [][]string{ - {"admin", "info"}, - {"grpc", "debug"}, - }, - expectedIndividualLevels: []logLevel{ - {name: "admin", level: "info"}, - {name: "grpc", level: "debug"}, - }, - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - loggerParams := NewLoggerParams() - for _, loggerLevel := range tc.levelsToSet { - logger, level := loggerLevel[0], loggerLevel[1] - err := loggerParams.SetLoggerLevel(logger, level) - require.NoError(t, err) - } - require.Equal(t, loggerParams.individualLevels, tc.expectedIndividualLevels) - }) - } -} - -func TestSetLoggerLevelFails(t *testing.T) { - t.Parallel() - testCases := map[string]struct { - loggerName string - loggerLevel string - }{ - "invalid logger name": { - loggerName: "this is not the logger you're looking for", - loggerLevel: "info", - }, - "invalid logger level": { - loggerName: "grpc", - loggerLevel: "this is also incorrect", - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - loggerParams := NewLoggerParams() - err := loggerParams.SetLoggerLevel(tc.loggerName, tc.loggerLevel) - require.Error(t, err) - }) - } -} - -func TestSetGlobalLoggerLevel(t *testing.T) { - t.Parallel() - for level := range envoyLevels { - loggerParams := NewLoggerParams() - err := loggerParams.SetGlobalLoggerLevel(level) - require.NoError(t, err) - } -} - -func TestSetGlobalLoggerLevelFails(t *testing.T) { - t.Parallel() - loggerParams := NewLoggerParams() - err := loggerParams.SetGlobalLoggerLevel("not a valid level") - require.Error(t, err) -} - -func TestString(t *testing.T) { - t.Parallel() - testCases := map[string]struct { - subject *LoggerParams - expectedOutput string - }{ - "when global level is set": { - subject: &LoggerParams{globalLevel: "warn"}, - expectedOutput: "?level=warn", - }, - "when one specific log level is set": { - subject: &LoggerParams{ - individualLevels: []logLevel{ - {name: "grpc", level: "warn"}, - }, - }, - expectedOutput: "?grpc=warn", - }, - "when multiple specific log levels are set": { - subject: &LoggerParams{ - individualLevels: []logLevel{ - {name: "grpc", level: "warn"}, - {name: "http", level: "info"}, - }, - }, - expectedOutput: "?paths=grpc:warn,http:info", - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - actual := tc.subject.String() - require.Equal(t, actual, tc.expectedOutput) - }) - } -} diff --git a/cli/common/envoy/testdata/fetch_debug_levels.txt b/cli/common/envoy/testdata/fetch_debug_levels.txt deleted file mode 100644 index 6b059dc1aa..0000000000 --- a/cli/common/envoy/testdata/fetch_debug_levels.txt +++ /dev/null @@ -1,59 +0,0 @@ -active loggers: - admin: debug - alternate_protocols_cache: debug - aws: debug - assert: debug - backtrace: debug - cache_filter: debug - client: debug - config: debug - connection: debug - conn_handler: debug - decompression: debug - dns: debug - dubbo: debug - envoy_bug: debug - ext_authz: debug - ext_proc: debug - rocketmq: debug - file: debug - filter: debug - forward_proxy: debug - grpc: debug - happy_eyeballs: debug - hc: debug - health_checker: debug - http: debug - http2: debug - hystrix: debug - init: debug - io: debug - jwt: debug - kafka: debug - key_value_store: debug - lua: debug - main: debug - matcher: debug - misc: debug - mongo: debug - multi_connection: debug - oauth2: debug - quic: debug - quic_stream: debug - pool: debug - rbac: debug - rds: debug - redis: debug - router: debug - runtime: debug - stats: debug - secret: debug - tap: debug - testing: debug - thrift: debug - tracing: debug - upstream: debug - udp: debug - wasm: debug - websocket: debug - diff --git a/cli/common/error.go b/cli/common/error.go index bbdd4087ff..3d8e3deb51 100644 --- a/cli/common/error.go +++ b/cli/common/error.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package common // DanglingResourceError should be used when a request was made to remove diff --git a/cli/common/flag/doc.go b/cli/common/flag/doc.go index 90d2b46ffa..1e40f0742c 100644 --- a/cli/common/flag/doc.go +++ b/cli/common/flag/doc.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - // Package flag is a thin layer over the stdlib flag package that provides // some minimal features such as aliasing, autocompletion handling, improved // defaults, etc. It was created for mitchellh/cli but can work as a standalone diff --git a/cli/common/flag/flag.go b/cli/common/flag/flag.go index 471ff86dcf..a0ccf2b239 100644 --- a/cli/common/flag/flag.go +++ b/cli/common/flag/flag.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flag import ( diff --git a/cli/common/flag/flag_bool.go b/cli/common/flag/flag_bool.go index 2800c84495..2862c2fe40 100644 --- a/cli/common/flag/flag_bool.go +++ b/cli/common/flag/flag_bool.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flag import ( diff --git a/cli/common/flag/flag_enum.go b/cli/common/flag/flag_enum.go index a5612ccdb3..ac9ecaedc1 100644 --- a/cli/common/flag/flag_enum.go +++ b/cli/common/flag/flag_enum.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flag import ( diff --git a/cli/common/flag/flag_enum_single.go b/cli/common/flag/flag_enum_single.go index 0165689c76..ddef52796b 100644 --- a/cli/common/flag/flag_enum_single.go +++ b/cli/common/flag/flag_enum_single.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flag import ( diff --git a/cli/common/flag/flag_float.go b/cli/common/flag/flag_float.go index a27be881c8..1f7fcfad67 100644 --- a/cli/common/flag/flag_float.go +++ b/cli/common/flag/flag_float.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flag import ( diff --git a/cli/common/flag/flag_int.go b/cli/common/flag/flag_int.go index fc6b880ac8..41ce1c19ab 100644 --- a/cli/common/flag/flag_int.go +++ b/cli/common/flag/flag_int.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flag import ( diff --git a/cli/common/flag/flag_string.go b/cli/common/flag/flag_string.go index b79b44c6ed..b16a1be16d 100644 --- a/cli/common/flag/flag_string.go +++ b/cli/common/flag/flag_string.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flag import ( diff --git a/cli/common/flag/flag_string_map.go b/cli/common/flag/flag_string_map.go index dfa54da656..5314a66655 100644 --- a/cli/common/flag/flag_string_map.go +++ b/cli/common/flag/flag_string_map.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flag import ( diff --git a/cli/common/flag/flag_string_slice.go b/cli/common/flag/flag_string_slice.go index 93d3483f5b..b567c64936 100644 --- a/cli/common/flag/flag_string_slice.go +++ b/cli/common/flag/flag_string_slice.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flag import ( diff --git a/cli/common/flag/flag_string_slice_test.go b/cli/common/flag/flag_string_slice_test.go index 87a5790db0..5bea43386f 100644 --- a/cli/common/flag/flag_string_slice_test.go +++ b/cli/common/flag/flag_string_slice_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flag import ( diff --git a/cli/common/flag/flag_time.go b/cli/common/flag/flag_time.go index 062c558c1a..45dc297f3d 100644 --- a/cli/common/flag/flag_time.go +++ b/cli/common/flag/flag_time.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flag import ( diff --git a/cli/common/flag/flag_var.go b/cli/common/flag/flag_var.go index 69e2a46675..1b4114f16f 100644 --- a/cli/common/flag/flag_var.go +++ b/cli/common/flag/flag_var.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flag import ( diff --git a/cli/common/flag/set.go b/cli/common/flag/set.go index 8c97313f98..03ee353f80 100644 --- a/cli/common/flag/set.go +++ b/cli/common/flag/set.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flag import ( diff --git a/cli/common/flag/set_test.go b/cli/common/flag/set_test.go index 46e195430c..9bb4ef4a7a 100644 --- a/cli/common/flag/set_test.go +++ b/cli/common/flag/set_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flag import ( diff --git a/cli/common/portforward.go b/cli/common/portforward.go index 79536706d9..4cca979ac6 100644 --- a/cli/common/portforward.go +++ b/cli/common/portforward.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package common import ( diff --git a/cli/common/portforward_test.go b/cli/common/portforward_test.go index e4dad39e5a..9bf7cf8e9d 100644 --- a/cli/common/portforward_test.go +++ b/cli/common/portforward_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package common import ( diff --git a/cli/common/terminal/basic.go b/cli/common/terminal/basic.go index 4aba11f9f6..e8866415bc 100644 --- a/cli/common/terminal/basic.go +++ b/cli/common/terminal/basic.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package terminal import ( diff --git a/cli/common/terminal/doc.go b/cli/common/terminal/doc.go index 975bb416ec..2935fa9db5 100644 --- a/cli/common/terminal/doc.go +++ b/cli/common/terminal/doc.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - // Package terminal is a modified version of // https://github.com/hashicorp/waypoint-plugin-sdk/tree/74d9328929293551499078da388b8d057f3b2341/terminal. // diff --git a/cli/common/terminal/table.go b/cli/common/terminal/table.go index 7768a48bce..67278931a4 100644 --- a/cli/common/terminal/table.go +++ b/cli/common/terminal/table.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package terminal import ( @@ -8,21 +5,15 @@ import ( ) const ( - Yellow = "yellow" - Green = "green" - Red = "red" - Blue = "blue" - Magenta = "magenta" - HiWhite = "hiwhite" + Yellow = "yellow" + Green = "green" + Red = "red" ) var colorMapping = map[string]int{ - Green: tablewriter.FgGreenColor, - Yellow: tablewriter.FgYellowColor, - Red: tablewriter.FgRedColor, - Blue: tablewriter.FgBlueColor, - Magenta: tablewriter.FgMagentaColor, - HiWhite: tablewriter.FgHiWhiteColor, + Green: tablewriter.FgGreenColor, + Yellow: tablewriter.FgYellowColor, + Red: tablewriter.FgRedColor, } // Passed to UI.Table to provide a nicely formatted table. diff --git a/cli/common/terminal/ui.go b/cli/common/terminal/ui.go index b371e95654..dde5d532ba 100644 --- a/cli/common/terminal/ui.go +++ b/cli/common/terminal/ui.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package terminal import ( diff --git a/cli/common/usage.go b/cli/common/usage.go index 6b09950b3e..b4b4cdf780 100644 --- a/cli/common/usage.go +++ b/cli/common/usage.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package common import ( diff --git a/cli/common/utils.go b/cli/common/utils.go index 43a94a38ea..b2e9714a9d 100644 --- a/cli/common/utils.go +++ b/cli/common/utils.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package common import ( diff --git a/cli/common/utils_test.go b/cli/common/utils_test.go index 9001aba79e..c15397e4a3 100644 --- a/cli/common/utils_test.go +++ b/cli/common/utils_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package common import ( diff --git a/cli/config/config.go b/cli/config/config.go index 6bda5b702a..d964bc3b5c 100644 --- a/cli/config/config.go +++ b/cli/config/config.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package config import "sigs.k8s.io/yaml" diff --git a/cli/go.mod b/cli/go.mod index 3e92113d18..020192cf0a 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -1,22 +1,21 @@ module github.com/hashicorp/consul-k8s/cli -go 1.20 +go 1.19 require ( github.com/bgentry/speakeasy v0.1.0 github.com/cenkalti/backoff v2.2.1+incompatible - github.com/fatih/color v1.14.1 - github.com/google/go-cmp v0.5.9 + github.com/fatih/color v1.13.0 + github.com/google/go-cmp v0.5.8 github.com/hashicorp/consul-k8s/charts v0.0.0-00010101000000-000000000000 - github.com/hashicorp/consul/troubleshoot v0.3.0-rc1 - github.com/hashicorp/go-hclog v1.5.0 + github.com/hashicorp/go-hclog v0.16.2 github.com/hashicorp/hcp-sdk-go v0.23.1-0.20220921131124-49168300a7dc github.com/kr/text v0.2.0 - github.com/mattn/go-isatty v0.0.17 + github.com/mattn/go-isatty v0.0.14 github.com/mitchellh/cli v1.1.2 github.com/olekukonko/tablewriter v0.0.5 github.com/posener/complete v1.2.3 - github.com/stretchr/testify v1.8.3 + github.com/stretchr/testify v1.7.2 golang.org/x/text v0.11.0 helm.sh/helm/v3 v3.9.4 k8s.io/api v0.25.0 @@ -28,11 +27,8 @@ require ( sigs.k8s.io/yaml v1.3.0 ) -require go.opentelemetry.io/proto/otlp v0.19.0 // indirect - require ( - cloud.google.com/go/compute v1.19.0 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect + cloud.google.com/go v0.99.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.27 // indirect @@ -50,17 +46,14 @@ require ( github.com/Masterminds/squirrel v1.5.3 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect - github.com/armon/go-metrics v0.4.1 // indirect github.com/armon/go-radix v1.0.0 // indirect - github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect + github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect - github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195 // indirect github.com/containerd/containerd v1.6.6 // indirect github.com/cyphar/filepath-securejoin v0.2.3 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/cli v20.10.17+incompatible // indirect github.com/docker/distribution v2.8.1+incompatible // indirect github.com/docker/docker v20.10.17+incompatible // indirect @@ -69,50 +62,41 @@ require ( github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-units v0.4.0 // indirect github.com/emicklei/go-restful/v3 v3.8.0 // indirect - github.com/envoyproxy/go-control-plane v0.11.0 // indirect - github.com/envoyproxy/go-control-plane/xdsmatcher v0.0.0-20230524161521-aaaacbfbe53e // indirect - github.com/envoyproxy/protoc-gen-validate v0.10.0 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/go-errors/errors v1.0.1 // indirect - github.com/go-gorp/gorp/v3 v3.1.0 // indirect + github.com/go-gorp/gorp/v3 v3.0.2 // indirect github.com/go-logr/logr v1.2.3 // indirect - github.com/go-openapi/analysis v0.21.2 // indirect + github.com/go-openapi/analysis v0.20.0 // indirect github.com/go-openapi/errors v0.20.2 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.6 // indirect - github.com/go-openapi/loads v0.21.1 // indirect - github.com/go-openapi/runtime v0.24.1 // indirect - github.com/go-openapi/spec v0.20.4 // indirect - github.com/go-openapi/strfmt v0.21.3 // indirect - github.com/go-openapi/swag v0.21.1 // indirect - github.com/go-openapi/validate v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.19.5 // indirect + github.com/go-openapi/loads v0.20.2 // indirect + github.com/go-openapi/runtime v0.19.24 // indirect + github.com/go-openapi/spec v0.20.3 // indirect + github.com/go-openapi/strfmt v0.20.0 // indirect + github.com/go-openapi/swag v0.19.14 // indirect + github.com/go-openapi/validate v0.20.2 // indirect github.com/go-ozzo/ozzo-validation v3.6.0+incompatible // indirect + github.com/go-stack/stack v1.8.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.2.0 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.2 // indirect github.com/google/btree v1.0.1 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.2.0 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gosuri/uitable v0.0.4 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect - github.com/hashicorp/consul/api v1.22.0-rc1 // indirect - github.com/hashicorp/consul/envoyextensions v0.3.0-rc1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-cleanhttp v0.5.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/go-version v1.2.1 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/hashicorp/serf v0.10.1 // indirect github.com/huandu/xstrings v1.3.2 // indirect - github.com/imdario/mergo v0.3.13 // indirect + github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jmoiron/sqlx v1.3.5 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -120,17 +104,16 @@ require ( github.com/klauspost/compress v1.13.6 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect - github.com/lib/pq v1.10.7 // indirect + github.com/lib/pq v1.10.6 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mailru/easyjson v0.7.6 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/mapstructure v1.4.1 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/locker v1.0.1 // indirect github.com/moby/spdystream v0.2.0 // indirect @@ -140,16 +123,14 @@ require ( github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/oklog/ulid v1.3.1 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect - github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.12.2 // indirect - github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect github.com/rubenv/sql-migrate v1.1.1 // indirect @@ -164,20 +145,19 @@ require ( github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect - go.mongodb.org/mongo-driver v1.11.1 // indirect - go.starlark.net v0.0.0-20230128213706-3f75dec8e403 // indirect + go.mongodb.org/mongo-driver v1.4.6 // indirect + go.starlark.net v0.0.0-20200707032745-474f21a9602d // indirect golang.org/x/crypto v0.11.0 // indirect - golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect golang.org/x/net v0.13.0 // indirect - golang.org/x/oauth2 v0.6.0 // indirect - golang.org/x/sync v0.2.0 // indirect + golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect + golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect golang.org/x/sys v0.10.0 // indirect golang.org/x/term v0.10.0 // indirect golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect - google.golang.org/grpc v1.55.0 // indirect - google.golang.org/protobuf v1.30.0 // indirect + google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect + google.golang.org/grpc v1.47.0 // indirect + google.golang.org/protobuf v1.28.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/cli/go.sum b/cli/go.sum index 6b54e646b2..48969abfef 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -18,16 +18,21 @@ cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmW cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0 h1:y/cM2iqGgGi5D5DQZl6D9STN/3dR/Vx5Mp8s752oJTY= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.19.0 h1:+9zda3WGgW1ZSTlVppLCYFIr48Pa35q1uG2N1itbCEQ= -cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= @@ -66,7 +71,6 @@ github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= -github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= @@ -86,30 +90,34 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6 github.com/Microsoft/hcsshim v0.9.3 h1:k371PzBuRrz2b+ebGuI2nVgVhgsVX60jMfSw80NECxo= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= -github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef h1:46PFijGLmAjMPwCCCo7Jf0W6f9slllCkkv7vyc1yOSg= github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -127,31 +135,24 @@ github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXe github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= -github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 h1:7aWHqerlJ41y6FOsEUvknqgXnGmJyJSbjhAWq5pO4F8= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195 h1:58f1tJ1ra+zFINPlwLWvQsR9CzAKt2e+EWV2yX9oXQ4= -github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= github.com/containerd/containerd v1.6.6 h1:xJNPhbrmz8xAMDNoVjHy9YHtWwEQNS+CDkcIRh7t8Y0= github.com/containerd/containerd v1.6.6/go.mod h1:ZoP1geJldzCVY3Tonoz7b1IXk8rIX0Nltt5QE4OMNk0= @@ -166,9 +167,8 @@ github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= 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/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/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/distribution/distribution/v3 v3.0.0-20220526142353-ffbd94cbe269 h1:hbCT8ZPPMqefiAWD2ZKjn7ypokIGViTvBBg/ExLSdCk= @@ -185,6 +185,7 @@ github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5Xh github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= @@ -202,14 +203,8 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.11.0 h1:jtLewhRR2vMRNnq2ZZUoCjUlgut+Y0+sDDWPOfwOi1o= -github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI= -github.com/envoyproxy/go-control-plane/xdsmatcher v0.0.0-20230524161521-aaaacbfbe53e h1:g8euodkL4GdSpVAjfzhssb07KgVmOUqyF4QOmwFumTs= -github.com/envoyproxy/go-control-plane/xdsmatcher v0.0.0-20230524161521-aaaacbfbe53e/go.mod h1:/NGEcKqwNq3HAS2vCqHfsPx9sJZbkiNQ6dGx9gTE/NA= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.10.0 h1:oIfnZFdC0YhpNNEX+SuIqko4cqqVZeN9IGTrhZje83Y= -github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -217,10 +212,8 @@ github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwC github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= -github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -232,14 +225,15 @@ github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmV github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gorp/gorp/v3 v3.0.2 h1:ULqJXIekoqMx29FI5ekXXFoH1dT2Vc8UhnRzBg+Emz4= github.com/go-gorp/gorp/v3 v3.0.2/go.mod h1:BJ3q1ejpV8cVALtcXvXaXyTOlMmJhWDxTmncaR6rwBY= -github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= -github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= @@ -252,45 +246,104 @@ github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= -github.com/go-openapi/analysis v0.21.2 h1:hXFrOYFHUAMQdu6zwAiKKJHJQ8kqZs1ux/ru1P1wLJU= -github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.4/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= +github.com/go-openapi/analysis v0.19.10/go.mod h1:qmhS3VNFxBlquFJ0RGoDtylO9y4pgTAUNE9AEEMdlJQ= +github.com/go-openapi/analysis v0.19.16/go.mod h1:GLInF007N83Ad3m8a/CbQ5TPzdnGT7workfHwuVjNVk= +github.com/go-openapi/analysis v0.20.0 h1:UN09o0kNhleunxW7LR+KnltD0YrJ8FF03pSqvAN3Vro= +github.com/go-openapi/analysis v0.20.0/go.mod h1:BMchjvaHDykmRMsK40iPtvyOfFdMMxlOmQr9FBZk+Og= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/errors v0.19.3/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/errors v0.19.6/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.19.7/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/errors v0.20.2 h1:dxy7PGTqEh94zj2E3h1cUmQQWiM1+aeCROfAr02EmK8= github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= -github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= -github.com/go-openapi/loads v0.21.1 h1:Wb3nVZpdEzDTcly8S4HMkey6fjARRzb7iEaySimlDW0= -github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g= -github.com/go-openapi/runtime v0.24.1 h1:Sml5cgQKGYQHF+M7yYSHaH1eOjvTykrddTE/KtQVjqo= -github.com/go-openapi/runtime v0.24.1/go.mod h1:AKurw9fNre+h3ELZfk6ILsfvPN+bvvlaU/M9q/r9hpk= -github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= -github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= -github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= -github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= -github.com/go-openapi/strfmt v0.21.2/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= -github.com/go-openapi/strfmt v0.21.3 h1:xwhj5X6CjXEZZHMWy1zKJxvW9AfHC9pkyUjLvHtKG7o= -github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/loads v0.19.3/go.mod h1:YVfqhUCdahYwR3f3iiwQLhicVRvLlU/WO5WPaZvcvSI= +github.com/go-openapi/loads v0.19.5/go.mod h1:dswLCAdonkRufe/gSUC3gN8nTSaB9uaS2es0x5/IbjY= +github.com/go-openapi/loads v0.19.6/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc= +github.com/go-openapi/loads v0.19.7/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc= +github.com/go-openapi/loads v0.20.0/go.mod h1:2LhKquiE513rN5xC6Aan6lYOSddlL8Mp20AW9kpviM4= +github.com/go-openapi/loads v0.20.2 h1:z5p5Xf5wujMxS1y8aP+vxwW5qYT2zdJBbXKmQUG3lcc= +github.com/go-openapi/loads v0.20.2/go.mod h1:hTVUotJ+UonAMMZsvakEgmWKgtulweO9vYP2bQYKA/o= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= +github.com/go-openapi/runtime v0.19.15/go.mod h1:dhGWCTKRXlAfGnQG0ONViOZpjfg0m2gUt9nTQPQZuoo= +github.com/go-openapi/runtime v0.19.16/go.mod h1:5P9104EJgYcizotuXhEuUrzVc+j1RiSjahULvYmlv98= +github.com/go-openapi/runtime v0.19.24 h1:TqagMVlRAOTwllE/7hNKx6rQ10O6T8ZzeJdMjSTKaD4= +github.com/go-openapi/runtime v0.19.24/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/spec v0.19.6/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= +github.com/go-openapi/spec v0.19.8/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= +github.com/go-openapi/spec v0.19.15/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= +github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= +github.com/go-openapi/spec v0.20.1/go.mod h1:93x7oh+d+FQsmsieroS4cmR3u0p/ywH649a3qwC9OsQ= +github.com/go-openapi/spec v0.20.3 h1:uH9RQ6vdyPSs2pSy9fL8QPspDF2AMIMPtmK5coSSjtQ= +github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/strfmt v0.19.4/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= +github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= +github.com/go-openapi/strfmt v0.19.11/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc= +github.com/go-openapi/strfmt v0.20.0 h1:l2omNtmNbMc39IGptl9BuXBEKcZfS8zjrTsPKTiJiDM= +github.com/go-openapi/strfmt v0.20.0/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= +github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= +github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M= +github.com/go-openapi/swag v0.19.13/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= -github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/validate v0.21.0 h1:+Wqk39yKOhfpLqNLEC0/eViCkzM5FVXVqrvt526+wcI= -github.com/go-openapi/validate v0.21.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= +github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo= +github.com/go-openapi/validate v0.19.10/go.mod h1:RKEZTUWDkxKQxN2jDT7ZnZi2bhZlbNMAuKvKB+IaGx8= +github.com/go-openapi/validate v0.19.12/go.mod h1:Rzou8hA/CBw8donlS6WNEUQupNvUZ0waH08tGe6kAQ4= +github.com/go-openapi/validate v0.19.15/go.mod h1:tbn/fdOwYHgrhPBzidZfJC2MIVvs9GA7monOmWBbeCI= +github.com/go-openapi/validate v0.20.1/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE9E4k54HpKcJ0= +github.com/go-openapi/validate v0.20.2 h1:AhqDegYV3J3iQkMPJSXkvzymHKMTw0BST3RK3hTT4ts= +github.com/go-openapi/validate v0.20.2/go.mod h1:e7OJoKNgd0twXZwIn0A43tHbvIcr/rZIVCbJBpTUoY0= github.com/go-ozzo/ozzo-validation v3.6.0+incompatible h1:msy24VGS42fKO9K1vLz82/GeYW1cILu7Nuuj1N3BBkE= github.com/go-ozzo/ozzo-validation v3.6.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= @@ -334,7 +387,6 @@ github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8 github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -348,6 +400,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -364,10 +417,10 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -388,8 +441,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/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 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= @@ -397,6 +450,7 @@ github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -408,15 +462,21 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= @@ -428,63 +488,35 @@ github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16 github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.22.0-rc1 h1:ePmGqndeMgaI38KUbSA/CqTzeEAIogXyWnfNJzglo70= -github.com/hashicorp/consul/api v1.22.0-rc1/go.mod h1:wtduXtbAqSGtBdi3tyA5SSAYGAG51rBejV9SEUBciMY= -github.com/hashicorp/consul/envoyextensions v0.3.0-rc1 h1:weclrwjvLeX+vxPOyo4b4dCDxSpnDl60Z9K16nnCVnI= -github.com/hashicorp/consul/envoyextensions v0.3.0-rc1/go.mod h1:ckxoPHMiWXAe6dhyxmKsX1XqO4KTV64KWIyTu44z8UI= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.14.0-rc1 h1:PuETOfN0uxl28i0Pq6rK7TBCrIl7psMbL0YTSje4KvM= -github.com/hashicorp/consul/troubleshoot v0.3.0-rc1 h1:Z6ZUEKILsf85wA/zXK3XMop6IGtjui4ZZ0bAu+JIAz4= -github.com/hashicorp/consul/troubleshoot v0.3.0-rc1/go.mod h1:2WfcYZ8M4vpLtTv9M5Dp3egqSPZ16l5XsqMpO9QUYxc= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= -github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs= +github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= -github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= -github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= -github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcp-sdk-go v0.23.1-0.20220921131124-49168300a7dc h1:on26TCKYnX7JzZCtwkR/LWHSqMu40PoZ6h/0e6Pq8ug= github.com/hashicorp/hcp-sdk-go v0.23.1-0.20220921131124-49168300a7dc/go.mod h1:/9UoDY2FYYA8lFaKBb2HmM/jKYZGANmf65q9QRc/cVw= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= -github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= -github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= @@ -493,10 +525,12 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= -github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= @@ -506,7 +540,6 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -520,8 +553,10 @@ github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaR github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -532,9 +567,10 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= @@ -544,18 +580,20 @@ github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhR github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= -github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs= +github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= @@ -565,35 +603,27 @@ github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= -github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/cli v1.1.2 h1:PvH+lL2B7IQ101xQL63Of8yFS2y+aDlsFcsqNc+u/Kw= github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= @@ -602,8 +632,6 @@ github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFW github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= @@ -612,11 +640,11 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= @@ -649,8 +677,6 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= @@ -669,11 +695,9 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec= github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= @@ -685,18 +709,16 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1 h1:oL4IBbcqwhhNWh31bjOX8C/OCy0zs9906d/VUru+bqg= github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU= -github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 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.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= @@ -705,12 +727,10 @@ github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrb 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= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= 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.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= @@ -718,7 +738,6 @@ github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= @@ -727,7 +746,6 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rubenv/sql-migrate v1.1.1 h1:haR5Hn8hbW9/SpAICrXoZqXnywS7Q5WijwkQENPeNWY= github.com/rubenv/sql-migrate v1.1.1/go.mod h1:/7TZymwxN8VWumcIxw1jjHEcR1djpdkMHQPT4FWdnbQ= @@ -736,8 +754,8 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= @@ -773,29 +791,22 @@ github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= -github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= -github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= -github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= @@ -804,7 +815,6 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17 github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI= github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -815,24 +825,27 @@ github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= +github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= -go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= -go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= -go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= -go.mongodb.org/mongo-driver v1.11.1 h1:QP0znIRTuL0jf1oBQoAoM0C6ZJfBK4kx0Uumtv1A7w8= -go.mongodb.org/mongo-driver v1.11.1/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= +go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= +go.mongodb.org/mongo-driver v1.3.4/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= +go.mongodb.org/mongo-driver v1.4.3/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= +go.mongodb.org/mongo-driver v1.4.4/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= +go.mongodb.org/mongo-driver v1.4.6 h1:rh7GdYmDrb8AQSkF8yteAus8qYOgOASWDOv1BWqBXkU= +go.mongodb.org/mongo-driver v1.4.6/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= @@ -844,11 +857,9 @@ go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= -go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= -go.starlark.net v0.0.0-20230128213706-3f75dec8e403 h1:jPeC7Exc+m8OBJUlWbBLh0O5UZPM7yU5W4adnhhbG4U= -go.starlark.net v0.0.0-20230128213706-3f75dec8e403/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= +go.starlark.net v0.0.0-20200707032745-474f21a9602d h1:uFqwFYlX7d5ZSp+IqhXxct0SybXrTzEBDvb2CkEhPBs= +go.starlark.net v0.0.0-20200707032745-474f21a9602d/go.mod h1:f0znQkUKRrkk36XxWbGjMqQM8wGv/xHBVE2qc3B5oFU= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= @@ -857,23 +868,23 @@ go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -886,8 +897,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= -golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -917,12 +926,14 @@ golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -932,7 +943,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -945,19 +955,21 @@ golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= -golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -978,9 +990,11 @@ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= -golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= 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= @@ -993,8 +1007,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1004,6 +1018,7 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1012,15 +1027,15 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1052,36 +1067,34 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1102,7 +1115,9 @@ golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U= golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -1116,11 +1131,12 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1160,7 +1176,10 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= @@ -1190,6 +1209,15 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1239,11 +1267,27 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 h1:hrbNEivu7Zn1pxvHk6MBrq9iE22woVILTHqexqBxe6I= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1264,11 +1308,15 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= -google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 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= @@ -1282,8 +1330,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1311,7 +1359,6 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= diff --git a/cli/helm/action.go b/cli/helm/action.go index 4afe1ba9d8..d71014c762 100644 --- a/cli/helm/action.go +++ b/cli/helm/action.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package helm import ( diff --git a/cli/helm/chart.go b/cli/helm/chart.go index c57d9220bc..f679ca591d 100644 --- a/cli/helm/chart.go +++ b/cli/helm/chart.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package helm import ( @@ -65,7 +62,7 @@ func readChartFiles(chart embed.FS, chartDirName string) ([]*loader.BufferedFile // filepath.* functions, then Go on Windows will try to use `\` delimiters to access // the embedded filesystem, which will then fail. - // Load Chart.yaml and values.yaml. + // Load Chart.yaml and values.yaml first. for _, f := range []string{chartFileName, valuesFileName} { file, err := readFile(chart, path.Join(chartDirName, f), chartDirName) if err != nil { @@ -74,7 +71,7 @@ func readChartFiles(chart embed.FS, chartDirName string) ([]*loader.BufferedFile chartFiles = append(chartFiles, file) } - // Load everything under templates/. + // Now load everything under templates/. dirs, err := chart.ReadDir(path.Join(chartDirName, templatesDirName)) if err != nil { return nil, err diff --git a/cli/helm/chart_test.go b/cli/helm/chart_test.go index f0e33e092b..9ed48d50a5 100644 --- a/cli/helm/chart_test.go +++ b/cli/helm/chart_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package helm import ( @@ -38,10 +35,10 @@ func TestLoadChart(t *testing.T) { func TestReadChartFiles(t *testing.T) { directory := "test_fixtures/consul" expectedFiles := map[string]string{ - "Chart.yaml": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\n# This is a mock Helm Chart.yaml file used for testing.\napiVersion: v2\nname: Foo\nversion: 0.1.0\ndescription: Mock Helm Chart for testing.", - "values.yaml": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\n# This is a mock Helm values.yaml file used for testing.\nkey: value", + "Chart.yaml": "# This is a mock Helm Chart.yaml file used for testing.\napiVersion: v2\nname: Foo\nversion: 0.1.0\ndescription: Mock Helm Chart for testing.", + "values.yaml": "# This is a mock Helm values.yaml file used for testing.\nkey: value", "templates/_helpers.tpl": "helpers", - "templates/foo.yaml": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\nfoo: bar\n", + "templates/foo.yaml": "foo: bar\n", } files, err := readChartFiles(testChartFiles, directory) diff --git a/cli/helm/install.go b/cli/helm/install.go index c19dd9dff3..1e0a83b8b8 100644 --- a/cli/helm/install.go +++ b/cli/helm/install.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package helm import ( diff --git a/cli/helm/install_test.go b/cli/helm/install_test.go index 7b154f6975..6458a06e54 100644 --- a/cli/helm/install_test.go +++ b/cli/helm/install_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package helm import ( diff --git a/cli/helm/mock.go b/cli/helm/mock.go index ce0793ef01..05d3b6edb4 100644 --- a/cli/helm/mock.go +++ b/cli/helm/mock.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package helm import ( diff --git a/cli/helm/test_fixtures/consul/Chart.yaml b/cli/helm/test_fixtures/consul/Chart.yaml index 488e2da2a7..72cdfea793 100644 --- a/cli/helm/test_fixtures/consul/Chart.yaml +++ b/cli/helm/test_fixtures/consul/Chart.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - # This is a mock Helm Chart.yaml file used for testing. apiVersion: v2 name: Foo diff --git a/cli/helm/test_fixtures/consul/templates/foo.yaml b/cli/helm/test_fixtures/consul/templates/foo.yaml index b17972509f..20e9ff3fea 100644 --- a/cli/helm/test_fixtures/consul/templates/foo.yaml +++ b/cli/helm/test_fixtures/consul/templates/foo.yaml @@ -1,4 +1 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - foo: bar diff --git a/cli/helm/test_fixtures/consul/values.yaml b/cli/helm/test_fixtures/consul/values.yaml index a0b3ab57b1..e8062923bc 100644 --- a/cli/helm/test_fixtures/consul/values.yaml +++ b/cli/helm/test_fixtures/consul/values.yaml @@ -1,5 +1,2 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - # This is a mock Helm values.yaml file used for testing. key: value \ No newline at end of file diff --git a/cli/helm/upgrade.go b/cli/helm/upgrade.go index 8c0072c345..e9d4545652 100644 --- a/cli/helm/upgrade.go +++ b/cli/helm/upgrade.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package helm import ( diff --git a/cli/helm/upgrade_test.go b/cli/helm/upgrade_test.go index 750c903d33..4ae09bb36f 100644 --- a/cli/helm/upgrade_test.go +++ b/cli/helm/upgrade_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package helm import ( diff --git a/cli/helm/values.go b/cli/helm/values.go index 19e19d8d05..1f55ecfdf3 100644 --- a/cli/helm/values.go +++ b/cli/helm/values.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package helm // HACK this is a temporary hard-coded struct. We should actually generate this from our `values.yaml` file. @@ -576,13 +573,11 @@ type CopyAnnotations struct { } type ManagedGatewayClass struct { - Enabled bool `yaml:"enabled"` - NodeSelector interface{} `yaml:"nodeSelector"` - ServiceType string `yaml:"serviceType"` - UseHostPorts bool `yaml:"useHostPorts"` - CopyAnnotations CopyAnnotations `yaml:"copyAnnotations"` - OpenshiftSCCName string `yaml:"openshiftSCCName"` - MapPrivilegedContainerPorts int `yaml:"mapPrivilegedContainerPorts"` + Enabled bool `yaml:"enabled"` + NodeSelector interface{} `yaml:"nodeSelector"` + ServiceType string `yaml:"serviceType"` + UseHostPorts bool `yaml:"useHostPorts"` + CopyAnnotations CopyAnnotations `yaml:"copyAnnotations"` } type Service struct { diff --git a/cli/main.go b/cli/main.go index 133e2a28a8..0ffd03328c 100644 --- a/cli/main.go +++ b/cli/main.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package main import ( diff --git a/cli/preset/cloud_preset.go b/cli/preset/cloud_preset.go index cbc335ae17..95219cb378 100644 --- a/cli/preset/cloud_preset.go +++ b/cli/preset/cloud_preset.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package preset import ( @@ -8,7 +5,6 @@ import ( "encoding/json" "fmt" "net/http" - "strings" "github.com/hashicorp/consul-k8s/cli/common" "github.com/hashicorp/consul-k8s/cli/common/terminal" @@ -202,8 +198,6 @@ global: bootstrapToken: secretName: %s secretKey: %s - metrics: - enableTelemetryCollector: true cloud: enabled: true resourceId: @@ -218,15 +212,6 @@ global: %s %s %s -telemetryCollector: - enabled: true - cloud: - clientId: - secretName: %s - secretKey: %s - clientSecret: - secretName: %s - secretKey: %s server: replicas: %d affinity: null @@ -236,15 +221,13 @@ connectInject: enabled: true controller: enabled: true -`, strings.ToLower(cfg.BootstrapResponse.Cluster.ID), secretNameServerCA, corev1.TLSCertKey, +`, cfg.BootstrapResponse.Cluster.ID, secretNameServerCA, corev1.TLSCertKey, secretNameGossipKey, secretKeyGossipKey, secretNameBootstrapToken, secretKeyBootstrapToken, secretNameHCPResourceID, secretKeyHCPResourceID, secretNameHCPClientID, secretKeyHCPClientID, secretNameHCPClientSecret, secretKeyHCPClientSecret, apiHostCfg, authURLCfg, scadaAddressCfg, - secretNameHCPClientID, secretKeyHCPClientID, - secretNameHCPClientSecret, secretKeyHCPClientSecret, cfg.BootstrapResponse.Cluster.BootstrapExpect, secretNameServerCert) valuesMap := config.ConvertToMap(values) return valuesMap diff --git a/cli/preset/cloud_preset_test.go b/cli/preset/cloud_preset_test.go index d905cb4088..946e1ca158 100644 --- a/cli/preset/cloud_preset_test.go +++ b/cli/preset/cloud_preset_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package preset import ( @@ -43,7 +40,7 @@ const ( { "cluster": { - "id": "Dc1", + "id": "dc1", "bootstrap_expect" : 3 }, "bootstrap": @@ -63,7 +60,7 @@ const ( var validBootstrapReponse *models.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse = &models.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse{ Bootstrap: &models.HashicorpCloudGlobalNetworkManager20220215ClusterBootstrap{ - ID: "Dc1", + ID: "dc1", GossipKey: "Wa6/XFAnYy0f9iqVH2iiG+yore3CqHSemUy4AIVTa/w=", BootstrapExpect: 3, ServerTLS: &models.HashicorpCloudGlobalNetworkManager20220215ServerTLS{ @@ -483,8 +480,6 @@ global: gossipEncryption: secretKey: key secretName: consul-gossip-key - metrics: - enableTelemetryCollector: true tls: caCert: secretKey: tls.crt @@ -496,15 +491,6 @@ server: replicas: 3 serverCert: secretName: consul-server-cert -telemetryCollector: - cloud: - clientId: - secretKey: client-id - secretName: consul-hcp-client-id - clientSecret: - secretKey: client-secret - secretName: consul-hcp-client-secret - enabled: true ` const expectedWithoutOptional = `connectInject: @@ -532,8 +518,6 @@ global: gossipEncryption: secretKey: key secretName: consul-gossip-key - metrics: - enableTelemetryCollector: true tls: caCert: secretKey: tls.crt @@ -545,43 +529,16 @@ server: replicas: 3 serverCert: secretName: consul-server-cert -telemetryCollector: - cloud: - clientId: - secretKey: client-id - secretName: consul-hcp-client-id - clientSecret: - secretKey: client-secret - secretName: consul-hcp-client-secret - enabled: true ` cloudPreset := &CloudPreset{} - testCases := map[string]struct { + testCases := []struct { + description string config *CloudBootstrapConfig expectedYaml string }{ - "Config_including_optional_parameters_with_mixedcase_DC": { - &CloudBootstrapConfig{ - BootstrapResponse: &models.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse{ - Cluster: &models.HashicorpCloudGlobalNetworkManager20220215Cluster{ - BootstrapExpect: 3, - ID: "Dc1", - }, - }, - HCPConfig: HCPConfig{ - ResourceID: "consul-hcp-resource-id", - ClientID: "consul-hcp-client-id", - ClientSecret: "consul-hcp-client-secret", - AuthURL: "consul-hcp-auth-url", - APIHostname: "consul-hcp-api-host", - ScadaAddress: "consul-hcp-scada-address", - }, - }, - expectedFull, - }, - "Config_including_optional_parameters": { + {"Config including optional parameters", &CloudBootstrapConfig{ BootstrapResponse: &models.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse{ Cluster: &models.HashicorpCloudGlobalNetworkManager20220215Cluster{ @@ -600,7 +557,7 @@ telemetryCollector: }, expectedFull, }, - "Config_without_optional_parameters": { + {"Config without optional parameters", &CloudBootstrapConfig{ BootstrapResponse: &models.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse{ Cluster: &models.HashicorpCloudGlobalNetworkManager20220215Cluster{ @@ -617,8 +574,8 @@ telemetryCollector: expectedWithoutOptional, }, } - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { + for _, tc := range testCases { + t.Run(tc.description, func(t *testing.T) { cloudHelmValues := cloudPreset.getHelmConfigWithMapSecretNames(tc.config) require.NotNil(t, cloudHelmValues) valuesYaml, err := yaml.Marshal(cloudHelmValues) diff --git a/cli/preset/demo.go b/cli/preset/demo.go index 5e011904fa..bf6c0bb122 100644 --- a/cli/preset/demo.go +++ b/cli/preset/demo.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package preset import "github.com/hashicorp/consul-k8s/cli/config" @@ -32,6 +29,8 @@ connectInject: enableGatewayMetrics: true server: replicas: 1 +controller: + enabled: true ui: enabled: true service: diff --git a/cli/preset/preset.go b/cli/preset/preset.go index 1b53dd021f..2eb2c94bc4 100644 --- a/cli/preset/preset.go +++ b/cli/preset/preset.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package preset import ( diff --git a/cli/preset/preset_test.go b/cli/preset/preset_test.go index 53b95b56e6..c39c11e80f 100644 --- a/cli/preset/preset_test.go +++ b/cli/preset/preset_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package preset import ( diff --git a/cli/preset/quickstart.go b/cli/preset/quickstart.go index 1e16a1e9c3..52b3f000b1 100644 --- a/cli/preset/quickstart.go +++ b/cli/preset/quickstart.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package preset import "github.com/hashicorp/consul-k8s/cli/config" @@ -32,6 +29,8 @@ connectInject: enableGatewayMetrics: true server: replicas: 1 +controller: + enabled: true ui: enabled: true service: diff --git a/cli/preset/secure.go b/cli/preset/secure.go index 62727273be..ded436804c 100644 --- a/cli/preset/secure.go +++ b/cli/preset/secure.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package preset import "github.com/hashicorp/consul-k8s/cli/config" @@ -32,6 +29,8 @@ server: replicas: 1 connectInject: enabled: true +controller: + enabled: true ` return config.ConvertToMap(values), nil diff --git a/cli/release/release.go b/cli/release/release.go index 9b973f080f..e1590f9057 100644 --- a/cli/release/release.go +++ b/cli/release/release.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package release import ( diff --git a/cli/release/release_test.go b/cli/release/release_test.go index f65c67ac05..a6f1c28994 100644 --- a/cli/release/release_test.go +++ b/cli/release/release_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package release import ( diff --git a/cli/validation/kubernetes.go b/cli/validation/kubernetes.go index 8a7d77ed5b..99dc8af6f7 100644 --- a/cli/validation/kubernetes.go +++ b/cli/validation/kubernetes.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package validation import ( diff --git a/cli/validation/kubernetes_test.go b/cli/validation/kubernetes_test.go index 90a5c6c298..60d7ec243b 100644 --- a/cli/validation/kubernetes_test.go +++ b/cli/validation/kubernetes_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package validation import ( diff --git a/cli/version/fips_build.go b/cli/version/fips_build.go deleted file mode 100644 index 63e0e68883..0000000000 --- a/cli/version/fips_build.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -//go:build fips - -package version - -// This validates during compilation that we are being built with a FIPS enabled go toolchain -import ( - _ "crypto/tls/fipsonly" - "runtime" - "strings" -) - -// IsFIPS returns true if consul-k8s is operating in FIPS-140-2 mode. -func IsFIPS() bool { - return true -} - -func GetFIPSInfo() string { - str := "Enabled" - // Try to get the crypto module name - gover := strings.Split(runtime.Version(), "X:") - if len(gover) >= 2 { - gover_last := gover[len(gover)-1] - // Able to find crypto module name; add that to status string. - str = "FIPS 140-2 Enabled, crypto module " + gover_last - } - return str -} diff --git a/cli/version/non_fips_build.go b/cli/version/non_fips_build.go deleted file mode 100644 index ce99575d2c..0000000000 --- a/cli/version/non_fips_build.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -//go:build !fips - -package version - -// IsFIPS returns true if consul-k8s is operating in FIPS-140-2 mode. -func IsFIPS() bool { - return false -} - -func GetFIPSInfo() string { - return "" -} diff --git a/cli/version/version.go b/cli/version/version.go index 952cd9ca81..c17eb5d89e 100644 --- a/cli/version/version.go +++ b/cli/version/version.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package version import ( @@ -17,7 +14,7 @@ var ( // // Version must conform to the format expected by // github.com/hashicorp/go-version for tests to work. - Version = "1.3.0" + Version = "1.0.10" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release @@ -39,12 +36,8 @@ func GetHumanVersion() string { release = "dev" } - if IsFIPS() { - version += "+fips1402" - } - if release != "" { - if !strings.Contains(version, "-"+release) { + if !strings.HasSuffix(version, "-"+release) { // if we tagged a prerelease version then the release is in the version already version += fmt.Sprintf("-%s", release) } diff --git a/control-plane/Dockerfile b/control-plane/Dockerfile index c09f5ecf80..773db0b0e9 100644 --- a/control-plane/Dockerfile +++ b/control-plane/Dockerfile @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - # This Dockerfile contains multiple targets. # Use 'docker build --target= .' to build one. # @@ -16,7 +13,7 @@ # go-discover builds the discover binary (which we don't currently publish # either). -FROM golang:1.19.2-alpine as go-discover +FROM golang:1.19.9-alpine as go-discover RUN CGO_ENABLED=0 go install github.com/hashicorp/go-discover/cmd/discover@214571b6a5309addf3db7775f4ee8cf4d264fd5f # dev copies the binary from a local build @@ -92,11 +89,7 @@ LABEL name=${BIN_NAME} \ ENV BIN_NAME=${BIN_NAME} ENV VERSION=${PRODUCT_VERSION} -RUN apk add --no-cache ca-certificates libcap openssl su-exec iputils gcompat libc6-compat libstdc++ iptables - -# for FIPS CGO glibc compatibility in alpine -# see https://github.com/golang/go/issues/59305 -RUN ln -s /lib/libc.so.6 /usr/lib/libresolv.so.2 +RUN apk add --no-cache ca-certificates libcap openssl su-exec iputils libc6-compat iptables # TARGETOS and TARGETARCH are set automatically when --platform is provided. ARG TARGETOS @@ -113,9 +106,6 @@ COPY dist/cni/${TARGETOS}/${TARGETARCH}/${CNI_BIN_NAME} /bin/ USER 100 CMD /bin/${BIN_NAME} -# Duplicate target for FIPS builds -FROM release-default AS release-default-fips - # ----------------------------------- # Dockerfile target for consul-k8s with UBI as its base image. Used for running on # OpenShift. @@ -178,8 +168,6 @@ COPY dist/cni/${TARGETOS}/${TARGETARCH}/${CNI_BIN_NAME} /bin/ USER 100 CMD /bin/${BIN_NAME} -# Duplicate target for FIPS builds -FROM ubi AS ubi-fips # =================================== # # Set default target to 'dev'. diff --git a/control-plane/PROJECT b/control-plane/PROJECT index 5a4e24d0f8..c11e857849 100644 --- a/control-plane/PROJECT +++ b/control-plane/PROJECT @@ -1,7 +1,3 @@ -# Code generated by tool. DO NOT EDIT. -# This file is used to track the info used to scaffold your project -# and allow the plugins properly work. -# More info: https://book.kubebuilder.io/reference/project-config.html domain: hashicorp.com layout: - go.kubebuilder.io/v2 @@ -81,49 +77,4 @@ resources: kind: PeeringDialer path: github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1 version: v1alpha1 -- api: - crdVersion: v1beta1 - namespaced: true - controller: true - domain: hashicorp.com - group: consul - kind: SamenessGroup - path: github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1 - version: v1alpha1 -- api: - crdVersion: v1beta1 - namespaced: true - controller: true - domain: hashicorp.com - group: consul - kind: JWTProvider - path: github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1 - version: v1alpha1 -- api: - crdVersion: v1beta1 - namespaced: true - controller: true - domain: hashicorp.com - group: consul - kind: ControlPlaneRequestLimit - path: github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1 - version: v1alpha1 -- api: - crdVersion: v1beta1 - namespaced: true - controller: true - domain: hashicorp.com - group: consul - kind: RouteRetryFilter - path: github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1 - version: v1alpha1 -- api: - crdVersion: v1beta1 - namespaced: true - controller: true - domain: hashicorp.com - group: consul - kind: RouteTimeoutFilter - path: github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1 - version: v1alpha1 version: "3" diff --git a/control-plane/api-gateway/binding/annotations.go b/control-plane/api-gateway/binding/annotations.go deleted file mode 100644 index 2bd4d0db15..0000000000 --- a/control-plane/api-gateway/binding/annotations.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package binding - -import ( - "encoding/json" - - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" -) - -func serializeGatewayClassConfig(gw *gwv1beta1.Gateway, gwcc *v1alpha1.GatewayClassConfig) (*v1alpha1.GatewayClassConfig, bool) { - if gwcc == nil { - return nil, false - } - - if gw.Annotations == nil { - gw.Annotations = make(map[string]string) - } - - if annotatedConfig, ok := gw.Annotations[common.AnnotationGatewayClassConfig]; ok { - var config v1alpha1.GatewayClassConfig - if err := json.Unmarshal([]byte(annotatedConfig), &config.Spec); err == nil { - // if we can unmarshal the gateway, return it - return &config, false - } - } - - // otherwise if we failed to unmarshal or there was no annotation, marshal it onto - // the gateway - marshaled, _ := json.Marshal(gwcc.Spec) - gw.Annotations[common.AnnotationGatewayClassConfig] = string(marshaled) - return gwcc, true -} diff --git a/control-plane/api-gateway/binding/annotations_test.go b/control-plane/api-gateway/binding/annotations_test.go deleted file mode 100644 index edb44ccfb4..0000000000 --- a/control-plane/api-gateway/binding/annotations_test.go +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package binding - -import ( - "encoding/json" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" -) - -func TestSerializeGatewayClassConfig_HappyPath(t *testing.T) { - t.Parallel() - - type args struct { - gw *gwv1beta1.Gateway - gwcc *v1alpha1.GatewayClassConfig - } - tests := []struct { - name string - args args - expectedDidUpdate bool - }{ - { - name: "when gateway has not been annotated yet and annotations are nil", - args: args{ - gw: &gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-gw", - }, - Spec: gwv1beta1.GatewaySpec{}, - Status: gwv1beta1.GatewayStatus{}, - }, - gwcc: &v1alpha1.GatewayClassConfig{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "the config", - }, - Spec: v1alpha1.GatewayClassConfigSpec{ - ServiceType: common.PointerTo(corev1.ServiceType("serviceType")), - NodeSelector: map[string]string{ - "selector": "of node", - }, - Tolerations: []v1.Toleration{ - { - Key: "key", - Operator: "op", - Value: "120", - Effect: "to the moon", - TolerationSeconds: new(int64), - }, - }, - CopyAnnotations: v1alpha1.CopyAnnotationsSpec{ - Service: []string{"service"}, - }, - }, - }, - }, - expectedDidUpdate: true, - }, - { - name: "when gateway has not been annotated yet but annotations are empty", - args: args{ - gw: &gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-gw", - Annotations: make(map[string]string), - }, - Spec: gwv1beta1.GatewaySpec{}, - Status: gwv1beta1.GatewayStatus{}, - }, - gwcc: &v1alpha1.GatewayClassConfig{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "the config", - }, - Spec: v1alpha1.GatewayClassConfigSpec{ - ServiceType: common.PointerTo(corev1.ServiceType("serviceType")), - NodeSelector: map[string]string{ - "selector": "of node", - }, - Tolerations: []v1.Toleration{ - { - Key: "key", - Operator: "op", - Value: "120", - Effect: "to the moon", - TolerationSeconds: new(int64), - }, - }, - CopyAnnotations: v1alpha1.CopyAnnotationsSpec{ - Service: []string{"service"}, - }, - }, - }, - }, - expectedDidUpdate: true, - }, - { - name: "when gateway has been annotated", - args: args{ - gw: &gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-gw", - Annotations: map[string]string{ - common.AnnotationGatewayClassConfig: `{"serviceType":"serviceType","nodeSelector":{"selector":"of node"},"tolerations":[{"key":"key","operator":"op","value":"120","effect":"to the moon","tolerationSeconds":0}],"copyAnnotations":{"service":["service"]}}`, - }, - }, - Spec: gwv1beta1.GatewaySpec{}, - Status: gwv1beta1.GatewayStatus{}, - }, - gwcc: &v1alpha1.GatewayClassConfig{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "the config", - }, - Spec: v1alpha1.GatewayClassConfigSpec{ - ServiceType: common.PointerTo(corev1.ServiceType("serviceType")), - NodeSelector: map[string]string{ - "selector": "of node", - }, - Tolerations: []v1.Toleration{ - { - Key: "key", - Operator: "op", - Value: "120", - Effect: "to the moon", - TolerationSeconds: new(int64), - }, - }, - CopyAnnotations: v1alpha1.CopyAnnotationsSpec{ - Service: []string{"service"}, - }, - }, - }, - }, - expectedDidUpdate: false, - }, - { - name: "when gateway has been annotated but the serialization was invalid", - args: args{ - gw: &gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-gw", - Annotations: map[string]string{ - // we remove the opening brace to make unmarshalling fail - common.AnnotationGatewayClassConfig: `"serviceType":"serviceType","nodeSelector":{"selector":"of node"},"tolerations":[{"key":"key","operator":"op","value":"120","effect":"to the moon","tolerationSeconds":0}],"copyAnnotations":{"service":["service"]}}`, - }, - }, - Spec: gwv1beta1.GatewaySpec{}, - Status: gwv1beta1.GatewayStatus{}, - }, - gwcc: &v1alpha1.GatewayClassConfig{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "the config", - }, - Spec: v1alpha1.GatewayClassConfigSpec{ - ServiceType: common.PointerTo(corev1.ServiceType("serviceType")), - NodeSelector: map[string]string{ - "selector": "of node", - }, - Tolerations: []v1.Toleration{ - { - Key: "key", - Operator: "op", - Value: "120", - Effect: "to the moon", - TolerationSeconds: new(int64), - }, - }, - CopyAnnotations: v1alpha1.CopyAnnotationsSpec{ - Service: []string{"service"}, - }, - }, - }, - }, - expectedDidUpdate: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - _, actualDidUpdate := serializeGatewayClassConfig(tt.args.gw, tt.args.gwcc) - - if actualDidUpdate != tt.expectedDidUpdate { - t.Errorf("SerializeGatewayClassConfig() = %v, want %v", actualDidUpdate, tt.expectedDidUpdate) - } - - var config v1alpha1.GatewayClassConfig - err := json.Unmarshal([]byte(tt.args.gw.Annotations[common.AnnotationGatewayClassConfig]), &config.Spec) - require.NoError(t, err) - - if diff := cmp.Diff(config.Spec, tt.args.gwcc.Spec); diff != "" { - t.Errorf("Expected gwconfig spec to match serialized version (-want,+got):\n%s", diff) - } - }) - } -} diff --git a/control-plane/api-gateway/binding/binder.go b/control-plane/api-gateway/binding/binder.go deleted file mode 100644 index 7798a6b49c..0000000000 --- a/control-plane/api-gateway/binding/binder.go +++ /dev/null @@ -1,389 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package binding - -import ( - mapset "github.com/deckarep/golang-set" - "github.com/go-logr/logr" - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul/api" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -// BinderConfig configures a binder instance with all of the information -// that it needs to know to generate a snapshot of bound state. -type BinderConfig struct { - // Logger for any internal logs - Logger logr.Logger - // Translator instance initialized with proper name/namespace translation - // configuration from helm. - Translator common.ResourceTranslator - // ControllerName is the name of the controller used in determining which - // gateways we control, also leveraged for setting route statuses. - ControllerName string - - // Namespaces is a map of all namespaces in Kubernetes indexed by their names for looking up labels - // for AllowedRoutes matching purposes. - Namespaces map[string]corev1.Namespace - // GatewayClassConfig is the configuration corresponding to the given - // GatewayClass -- if it is nil we should treat the gateway as deleted - // since the gateway is now pointing to an invalid gateway class - GatewayClassConfig *v1alpha1.GatewayClassConfig - // GatewayClass is the GatewayClass corresponding to the Gateway we want to - // bind routes to. It is passed as a pointer because it could be nil. If no - // GatewayClass corresponds to a Gateway, we ought to clean up any sort of - // state that we may have set on the Gateway, its corresponding Routes or in - // Consul, because we should no longer be managing the Gateway (its association - // to our controller is through a parameter on the GatewayClass). - GatewayClass *gwv1beta1.GatewayClass - // Gateway is the Gateway being reconciled that we want to bind routes to. - Gateway gwv1beta1.Gateway - // HTTPRoutes is a list of HTTPRoute objects that ought to be bound to the Gateway. - HTTPRoutes []gwv1beta1.HTTPRoute - // TCPRoutes is a list of TCPRoute objects that ought to be bound to the Gateway. - TCPRoutes []gwv1alpha2.TCPRoute - // Pods are any pods that are part of the Gateway deployment. - Pods []corev1.Pod - // Service is the deployed service associated with the Gateway deployment. - Service *corev1.Service - - // ConsulGateway is the config entry we've created in Consul. - ConsulGateway *api.APIGatewayConfigEntry - // GatewayServices are the services associated with the Gateway - ConsulGatewayServices []api.CatalogService - - // Resources is a map containing all service targets to verify - // against the routing backends. - Resources *common.ResourceMap -} - -// Binder is used for generating a Snapshot of all operations that should occur both -// in Kubernetes and Consul as a result of binding routes to a Gateway. -type Binder struct { - statusSetter *setter - key types.NamespacedName - nonNormalizedConsulKey api.ResourceReference - normalizedConsulKey api.ResourceReference - config BinderConfig -} - -// NewBinder creates a Binder object with the given configuration. -func NewBinder(config BinderConfig) *Binder { - id := client.ObjectKeyFromObject(&config.Gateway) - - return &Binder{ - config: config, - statusSetter: newSetter(config.ControllerName), - key: id, - nonNormalizedConsulKey: config.Translator.NonNormalizedConfigEntryReference(api.APIGateway, id), - normalizedConsulKey: config.Translator.ConfigEntryReference(api.APIGateway, id), - } -} - -// isGatewayDeleted returns whether we should treat the given gateway as a deleted object. -// This is true if the gateway has a deleted timestamp, if its GatewayClass does not match -// our controller name, or if the GatewayClass it references doesn't exist. -func (b *Binder) isGatewayDeleted() bool { - gatewayClassMismatch := b.config.GatewayClass == nil || b.config.ControllerName != string(b.config.GatewayClass.Spec.ControllerName) - isGatewayDeleted := isDeleted(&b.config.Gateway) || gatewayClassMismatch || b.config.GatewayClassConfig == nil - return isGatewayDeleted -} - -// Snapshot generates a snapshot of operations that need to occur in Kubernetes and Consul -// in order for a Gateway to be reconciled. -func (b *Binder) Snapshot() *Snapshot { - // at this point we assume all tcp routes and http routes - // actually reference this gateway - snapshot := NewSnapshot() - - registrationPods := []corev1.Pod{} - // filter out any pod that is being deleted - for _, pod := range b.config.Pods { - if !isDeleted(&pod) { - registrationPods = append(registrationPods, pod) - } - } - - gatewayClassConfig := b.config.GatewayClassConfig - - isGatewayDeleted := b.isGatewayDeleted() - - var gatewayValidation gatewayValidationResult - var listenerValidation listenerValidationResults - - if !isGatewayDeleted { - var updated bool - - gatewayClassConfig, updated = serializeGatewayClassConfig(&b.config.Gateway, gatewayClassConfig) - - // we don't have a deletion but if we add a finalizer for the gateway, then just add it and return - // otherwise try and resolve as much as possible - if common.EnsureFinalizer(&b.config.Gateway) || updated { - // if we've added the finalizer or serialized the class config, then update - snapshot.Kubernetes.Updates.Add(&b.config.Gateway) - return snapshot - } - - // calculate the status for the gateway - gatewayValidation = validateGateway(b.config.Gateway, registrationPods, b.config.ConsulGateway) - listenerValidation = validateListeners(b.config.Gateway, b.config.Gateway.Spec.Listeners, b.config.Resources, b.config.GatewayClassConfig) - } - - // used for tracking how many routes have successfully bound to which listeners - // on a gateway for reporting the number of bound routes in a gateway listener's - // status - boundCounts := make(map[gwv1beta1.SectionName]int) - - // attempt to bind all routes - - for _, r := range b.config.HTTPRoutes { - b.bindRoute(common.PointerTo(r), boundCounts, snapshot) - } - - for _, r := range b.config.TCPRoutes { - b.bindRoute(common.PointerTo(r), boundCounts, snapshot) - } - - // process secrets - gatewaySecrets := secretsForGateway(b.config.Gateway, b.config.Resources) - if !isGatewayDeleted { - // we only do this if the gateway isn't going to be deleted so that the - // resources can get GC'd - for secret := range gatewaySecrets.Iter() { - // ignore the error if the certificate cannot be processed and just don't add it into the final - // sync set - if err := b.config.Resources.TranslateInlineCertificate(secret.(types.NamespacedName)); err != nil { - b.config.Logger.Error(err, "error parsing referenced secret, ignoring") - continue - } - } - } - - // now cleanup any routes or certificates that we haven't already processed - - snapshot.Consul.Deletions = b.config.Resources.ResourcesToGC(b.key) - snapshot.Consul.Updates = b.config.Resources.Mutations() - - // finally, handle the gateway itself - - // we only want to upsert the gateway into Consul or update its status - // if the gateway hasn't been marked for deletion - if !isGatewayDeleted { - snapshot.GatewayClassConfig = gatewayClassConfig - snapshot.UpsertGatewayDeployment = true - - var consulStatus api.ConfigEntryStatus - if b.config.ConsulGateway != nil { - consulStatus = b.config.ConsulGateway.Status - } - entry := b.config.Translator.ToAPIGateway(b.config.Gateway, b.config.Resources, gatewayClassConfig) - snapshot.Consul.Updates = append(snapshot.Consul.Updates, &common.ConsulUpdateOperation{ - Entry: entry, - OnUpdate: b.handleGatewaySyncStatus(snapshot, &b.config.Gateway, consulStatus), - }) - - registrations := registrationsForPods(entry.Namespace, b.config.Gateway, registrationPods) - snapshot.Consul.Registrations = registrations - - // deregister any not explicitly registered service - for _, service := range b.config.ConsulGatewayServices { - found := false - for _, registration := range registrations { - if service.ServiceID == registration.Service.ID { - found = true - break - } - } - if !found { - // we didn't register the service instance, so drop it - snapshot.Consul.Deregistrations = append(snapshot.Consul.Deregistrations, api.CatalogDeregistration{ - Node: service.Node, - ServiceID: service.ServiceID, - Namespace: service.Namespace, - }) - } - } - - // calculate the status for the gateway - var status gwv1beta1.GatewayStatus - for i, listener := range b.config.Gateway.Spec.Listeners { - status.Listeners = append(status.Listeners, gwv1beta1.ListenerStatus{ - Name: listener.Name, - SupportedKinds: supportedKinds(listener), - AttachedRoutes: int32(boundCounts[listener.Name]), - Conditions: listenerValidation.Conditions(b.config.Gateway.Generation, i), - }) - } - status.Conditions = b.config.Gateway.Status.Conditions - - // we do this loop to not accidentally override any additional statuses that - // have been set anywhere outside of validation. - for _, condition := range gatewayValidation.Conditions(b.config.Gateway.Generation, listenerValidation.Invalid()) { - status.Conditions, _ = setCondition(status.Conditions, condition) - } - status.Addresses = addressesForGateway(b.config.Service, registrationPods) - - // only mark the gateway as needing a status update if there's a diff with its old - // status, this keeps the controller from infinitely reconciling - if !common.GatewayStatusesEqual(status, b.config.Gateway.Status) { - b.config.Gateway.Status = status - snapshot.Kubernetes.StatusUpdates.Add(&b.config.Gateway) - } - } else { - // if the gateway has been deleted, unset whatever we've set on it - snapshot.Consul.Deletions = append(snapshot.Consul.Deletions, b.nonNormalizedConsulKey) - for _, service := range b.config.ConsulGatewayServices { - // deregister all gateways - snapshot.Consul.Deregistrations = append(snapshot.Consul.Deregistrations, api.CatalogDeregistration{ - Node: service.Node, - ServiceID: service.ServiceID, - Namespace: service.Namespace, - }) - } - if common.RemoveFinalizer(&b.config.Gateway) { - snapshot.Kubernetes.Updates.Add(&b.config.Gateway) - } - } - - return snapshot -} - -func secretsForGateway(gateway gwv1beta1.Gateway, resources *common.ResourceMap) mapset.Set { - set := mapset.NewSet() - - for _, listener := range gateway.Spec.Listeners { - if listener.TLS == nil { - continue - } - - for _, cert := range listener.TLS.CertificateRefs { - if resources.GatewayCanReferenceSecret(gateway, cert) { - if common.NilOrEqual(cert.Group, "") && common.NilOrEqual(cert.Kind, common.KindSecret) { - key := common.IndexedNamespacedNameWithDefault(cert.Name, cert.Namespace, gateway.Namespace) - set.Add(key) - } - } - } - } - - return set -} - -func addressesForGateway(service *corev1.Service, pods []corev1.Pod) []gwv1beta1.GatewayAddress { - if service == nil { - return addressesFromPods(pods) - } - - switch service.Spec.Type { - case corev1.ServiceTypeLoadBalancer: - return addressesFromLoadBalancer(service) - case corev1.ServiceTypeClusterIP: - return addressesFromClusterIP(service) - case corev1.ServiceTypeNodePort: - /* For serviceType: NodePort, there isn't a consistent way to guarantee access to the - * service from outside the k8s cluster. For now, we're putting the IP address of the - * nodes that the gateway pods are running on. - * The practitioner will have to understand that they may need to port forward into the - * cluster (in the case of Kind) or open firewall rules (in the case of GKE) in order to - * access the gateway from outside the cluster. - */ - return addressesFromPodHosts(pods) - } - - return []gwv1beta1.GatewayAddress{} -} - -func addressesFromLoadBalancer(service *corev1.Service) []gwv1beta1.GatewayAddress { - addresses := []gwv1beta1.GatewayAddress{} - - for _, ingress := range service.Status.LoadBalancer.Ingress { - if ingress.IP != "" { - addresses = append(addresses, gwv1beta1.GatewayAddress{ - Type: common.PointerTo(gwv1beta1.IPAddressType), - Value: ingress.IP, - }) - } - if ingress.Hostname != "" { - addresses = append(addresses, gwv1beta1.GatewayAddress{ - Type: common.PointerTo(gwv1beta1.HostnameAddressType), - Value: ingress.Hostname, - }) - } - } - - return addresses -} - -func addressesFromClusterIP(service *corev1.Service) []gwv1beta1.GatewayAddress { - addresses := []gwv1beta1.GatewayAddress{} - - if service.Spec.ClusterIP != "" { - addresses = append(addresses, gwv1beta1.GatewayAddress{ - Type: common.PointerTo(gwv1beta1.IPAddressType), - Value: service.Spec.ClusterIP, - }) - } - - return addresses -} - -func addressesFromPods(pods []corev1.Pod) []gwv1beta1.GatewayAddress { - addresses := []gwv1beta1.GatewayAddress{} - seenIPs := make(map[string]struct{}) - - for _, pod := range pods { - if pod.Status.PodIP != "" { - if _, found := seenIPs[pod.Status.PodIP]; !found { - addresses = append(addresses, gwv1beta1.GatewayAddress{ - Type: common.PointerTo(gwv1beta1.IPAddressType), - Value: pod.Status.PodIP, - }) - seenIPs[pod.Status.PodIP] = struct{}{} - } - } - } - - return addresses -} - -func addressesFromPodHosts(pods []corev1.Pod) []gwv1beta1.GatewayAddress { - addresses := []gwv1beta1.GatewayAddress{} - seenIPs := make(map[string]struct{}) - - for _, pod := range pods { - if pod.Status.HostIP != "" { - if _, found := seenIPs[pod.Status.HostIP]; !found { - addresses = append(addresses, gwv1beta1.GatewayAddress{ - Type: common.PointerTo(gwv1beta1.IPAddressType), - Value: pod.Status.HostIP, - }) - seenIPs[pod.Status.HostIP] = struct{}{} - } - } - } - - return addresses -} - -// isDeleted checks if the deletion timestamp is set for an object. -func isDeleted(object client.Object) bool { - return !object.GetDeletionTimestamp().IsZero() -} - -func supportedKinds(listener gwv1beta1.Listener) []gwv1beta1.RouteGroupKind { - if listener.AllowedRoutes != nil && listener.AllowedRoutes.Kinds != nil { - return common.Filter(listener.AllowedRoutes.Kinds, func(kind gwv1beta1.RouteGroupKind) bool { - if _, ok := allSupportedRouteKinds[kind.Kind]; !ok { - return true - } - return !common.NilOrEqual(kind.Group, gwv1beta1.GroupVersion.Group) - }) - } - return supportedKindsForProtocol[listener.Protocol] -} diff --git a/control-plane/api-gateway/binding/binder_test.go b/control-plane/api-gateway/binding/binder_test.go deleted file mode 100644 index 7366d1a164..0000000000 --- a/control-plane/api-gateway/binding/binder_test.go +++ /dev/null @@ -1,2384 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package binding - -import ( - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "math/big" - "testing" - "time" - - logrtest "github.com/go-logr/logr/testing" - "github.com/google/go-cmp/cmp" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul/api" -) - -func init() { - timeFunc = func() metav1.Time { - return metav1.Time{} - } -} - -const ( - testGatewayClassName = "gateway-class" - testControllerName = "test-controller" -) - -var ( - testGatewayClassObjectName = gwv1beta1.ObjectName(testGatewayClassName) - deletionTimestamp = common.PointerTo(metav1.Now()) - - testGatewayClass = &gwv1beta1.GatewayClass{ - ObjectMeta: metav1.ObjectMeta{ - Name: testGatewayClassName, - }, - Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: gwv1beta1.GatewayController(testControllerName), - }, - } -) - -type resourceMapResources struct { - grants []gwv1beta1.ReferenceGrant - secrets []corev1.Secret - gateways []gwv1beta1.Gateway - httpRoutes []gwv1beta1.HTTPRoute - tcpRoutes []gwv1alpha2.TCPRoute - meshServices []v1alpha1.MeshService - services []types.NamespacedName - consulInlineCertificates []api.InlineCertificateConfigEntry - consulHTTPRoutes []api.HTTPRouteConfigEntry - consulTCPRoutes []api.TCPRouteConfigEntry -} - -func newTestResourceMap(t *testing.T, resources resourceMapResources) *common.ResourceMap { - resourceMap := common.NewResourceMap(common.ResourceTranslator{}, NewReferenceValidator(resources.grants), logrtest.NewTestLogger(t)) - - for _, s := range resources.services { - resourceMap.AddService(s, s.Name) - } - for _, s := range resources.meshServices { - resourceMap.AddMeshService(s) - } - for _, s := range resources.secrets { - resourceMap.ReferenceCountCertificate(s) - } - for _, g := range resources.gateways { - resourceMap.ReferenceCountGateway(g) - } - for _, r := range resources.httpRoutes { - resourceMap.ReferenceCountHTTPRoute(r) - } - for _, r := range resources.tcpRoutes { - resourceMap.ReferenceCountTCPRoute(r) - } - for _, r := range resources.consulHTTPRoutes { - resourceMap.ReferenceCountConsulHTTPRoute(r) - } - for _, r := range resources.consulTCPRoutes { - resourceMap.ReferenceCountConsulTCPRoute(r) - } - return resourceMap -} - -func TestBinder_Lifecycle(t *testing.T) { - t.Parallel() - - certificateOne, secretOne := generateTestCertificate(t, "default", "secret-one") - certificateTwo, secretTwo := generateTestCertificate(t, "default", "secret-two") - - for name, tt := range map[string]struct { - resources resourceMapResources - config BinderConfig - expectedStatusUpdates []client.Object - expectedUpdates []client.Object - expectedConsulDeletions []api.ResourceReference - expectedConsulUpdates []api.ConfigEntry - }{ - "no gateway class and empty routes": { - config: BinderConfig{ - Gateway: gwv1beta1.Gateway{}, - }, - expectedConsulDeletions: []api.ResourceReference{{ - Kind: api.APIGateway, - }}, - }, - "no gateway class and empty routes remove finalizer": { - config: BinderConfig{ - Gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Finalizers: []string{common.GatewayFinalizer}, - }, - }, - }, - expectedUpdates: []client.Object{ - addClassConfig(gwv1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{}}}), - }, - expectedConsulDeletions: []api.ResourceReference{ - {Kind: api.APIGateway}, - }, - }, - "deleting gateway empty routes": { - config: BinderConfig{ - ControllerName: testControllerName, - GatewayClass: testGatewayClass, - Gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - DeletionTimestamp: deletionTimestamp, - Finalizers: []string{common.GatewayFinalizer}, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: testGatewayClassObjectName, - }, - }, - }, - expectedUpdates: []client.Object{ - addClassConfig(gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{DeletionTimestamp: deletionTimestamp, Finalizers: []string{}}, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: testGatewayClassObjectName, - }, - }), - }, - expectedConsulDeletions: []api.ResourceReference{ - {Kind: api.APIGateway}, - }, - }, - "basic gateway no finalizer": { - config: BinderConfig{ - ControllerName: testControllerName, - GatewayClass: testGatewayClass, - Gateway: gwv1beta1.Gateway{ - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: testGatewayClassObjectName, - }, - }, - }, - expectedUpdates: []client.Object{ - addClassConfig(gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{Finalizers: []string{common.GatewayFinalizer}}, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: testGatewayClassObjectName, - }, - }), - }, - }, - "basic gateway": { - config: controlledBinder(BinderConfig{ - Gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{ - Listeners: []gwv1beta1.Listener{{ - Protocol: gwv1beta1.HTTPSProtocolType, - TLS: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{ - {Name: "secret-one"}, - }, - Mode: common.PointerTo(gwv1beta1.TLSModeTerminate), - }, - }}, - }), - }), - resources: resourceMapResources{ - secrets: []corev1.Secret{ - secretOne, - }, - }, - expectedStatusUpdates: []client.Object{ - addClassConfig(gatewayWithFinalizerStatus( - gwv1beta1.GatewaySpec{ - Listeners: []gwv1beta1.Listener{{ - Protocol: gwv1beta1.HTTPSProtocolType, - TLS: &gwv1beta1.GatewayTLSConfig{ - Mode: common.PointerTo(gwv1beta1.TLSModeTerminate), - CertificateRefs: []gwv1beta1.SecretObjectReference{ - {Name: "secret-one"}, - }, - }, - }}, - }, - gwv1beta1.GatewayStatus{ - Addresses: []gwv1beta1.GatewayAddress{}, - Conditions: []metav1.Condition{ - { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "gateway accepted", - }, { - Type: "Programmed", - Status: metav1.ConditionFalse, - Reason: "Pending", - Message: "gateway pods are still being scheduled", - }, - }, - Listeners: []gwv1beta1.ListenerStatus{{ - SupportedKinds: supportedKindsForProtocol[gwv1beta1.HTTPSProtocolType], - Conditions: []metav1.Condition{ - { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "listener accepted", - }, { - Type: "Programmed", - Status: metav1.ConditionTrue, - Reason: "Programmed", - Message: "listener programmed", - }, { - Type: "Conflicted", - Status: metav1.ConditionFalse, - Reason: "NoConflicts", - Message: "listener has no conflicts", - }, { - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved certificate references", - }, - }, - }}, - }), - ), - }, - expectedConsulUpdates: []api.ConfigEntry{ - certificateOne, - &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "gateway", - Meta: map[string]string{ - "k8s-name": "gateway", - "k8s-namespace": "default", - }, - Listeners: []api.APIGatewayListener{{ - Protocol: "http", - TLS: api.APIGatewayTLSConfiguration{ - Certificates: []api.ResourceReference{{ - Kind: api.InlineCertificate, - Name: "secret-one", - }}, - }, - }}, - }, - }, - }, - "gateway http route no finalizer": { - config: controlledBinder(BinderConfig{ - Gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), - HTTPRoutes: []gwv1beta1.HTTPRoute{ - { - TypeMeta: metav1.TypeMeta{ - Kind: "HTTPRoute", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, - }, - }, - }, - }, - }), - expectedUpdates: []client.Object{ - common.PointerTo(testHTTPRoute("route", []string{"gateway"}, nil)), - }, - expectedStatusUpdates: []client.Object{ - addClassConfig(gatewayWithFinalizerStatus(gwv1beta1.GatewaySpec{}, gwv1beta1.GatewayStatus{ - Addresses: []gwv1beta1.GatewayAddress{}, - Conditions: []metav1.Condition{{ - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "gateway accepted", - }, { - Type: "Programmed", - Status: metav1.ConditionFalse, - Reason: "Pending", - Message: "gateway pods are still being scheduled", - }}, - })), - }, - expectedConsulUpdates: []api.ConfigEntry{ - &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "gateway", - Meta: map[string]string{ - "k8s-name": "gateway", - "k8s-namespace": "default", - }, - Listeners: []api.APIGatewayListener{}, - }, - }, - }, - "gateway http route deleting": { - config: controlledBinder(BinderConfig{ - Gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), - HTTPRoutes: []gwv1beta1.HTTPRoute{{ - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - DeletionTimestamp: deletionTimestamp, - Finalizers: []string{common.GatewayFinalizer}, - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, - }, - }, - }}, - }), - resources: resourceMapResources{ - consulHTTPRoutes: []api.HTTPRouteConfigEntry{{ - Kind: api.HTTPRoute, - Name: "route", - Parents: []api.ResourceReference{ - {Kind: api.APIGateway, Name: "gateway"}, - }, - }}, - }, - expectedUpdates: []client.Object{ - &gwv1beta1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - DeletionTimestamp: deletionTimestamp, - Finalizers: []string{}, - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, - }, - }, - }, - }, - expectedStatusUpdates: []client.Object{ - addClassConfig(gatewayWithFinalizerStatus(gwv1beta1.GatewaySpec{}, gwv1beta1.GatewayStatus{ - Addresses: []gwv1beta1.GatewayAddress{}, - Conditions: []metav1.Condition{{ - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "gateway accepted", - }, { - Type: "Programmed", - Status: metav1.ConditionFalse, - Reason: "Pending", - Message: "gateway pods are still being scheduled", - }}, - })), - }, - expectedConsulUpdates: []api.ConfigEntry{ - &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "gateway", - Meta: map[string]string{ - "k8s-name": "gateway", - "k8s-namespace": "default", - }, - Listeners: []api.APIGatewayListener{}, - }, - }, - expectedConsulDeletions: []api.ResourceReference{ - {Kind: api.HTTPRoute, Name: "route"}, - }, - }, - "gateway tcp route no finalizer": { - config: controlledBinder(BinderConfig{ - Gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), - TCPRoutes: []gwv1alpha2.TCPRoute{ - { - TypeMeta: metav1.TypeMeta{ - Kind: "TCPRoute", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, - }, - }, - }, - }, - }), - expectedUpdates: []client.Object{ - common.PointerTo(testTCPRoute("route", []string{"gateway"}, nil)), - }, - expectedStatusUpdates: []client.Object{ - addClassConfig(gatewayWithFinalizerStatus(gwv1beta1.GatewaySpec{}, gwv1beta1.GatewayStatus{ - Addresses: []gwv1beta1.GatewayAddress{}, - Conditions: []metav1.Condition{{ - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "gateway accepted", - }, { - Type: "Programmed", - Status: metav1.ConditionFalse, - Reason: "Pending", - Message: "gateway pods are still being scheduled", - }}, - })), - }, - expectedConsulUpdates: []api.ConfigEntry{ - &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "gateway", - Meta: map[string]string{ - "k8s-name": "gateway", - "k8s-namespace": "default", - }, - Listeners: []api.APIGatewayListener{}, - }, - }, - }, - "gateway tcp route deleting": { - config: controlledBinder(BinderConfig{ - Gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), - TCPRoutes: []gwv1alpha2.TCPRoute{{ - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - DeletionTimestamp: deletionTimestamp, - Finalizers: []string{common.GatewayFinalizer}, - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, - }, - }, - }}, - }), - resources: resourceMapResources{ - consulTCPRoutes: []api.TCPRouteConfigEntry{{ - Kind: api.TCPRoute, - Name: "route", - Parents: []api.ResourceReference{ - {Kind: api.APIGateway, Name: "gateway"}, - }, - }}, - }, - expectedUpdates: []client.Object{ - &gwv1alpha2.TCPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Name: "route", - DeletionTimestamp: deletionTimestamp, - Finalizers: []string{}, - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{{ - Name: "gateway", - }}, - }, - }, - }, - }, - expectedStatusUpdates: []client.Object{ - addClassConfig(gatewayWithFinalizerStatus(gwv1beta1.GatewaySpec{}, gwv1beta1.GatewayStatus{ - Addresses: []gwv1beta1.GatewayAddress{}, - Conditions: []metav1.Condition{{ - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "gateway accepted", - }, { - Type: "Programmed", - Status: metav1.ConditionFalse, - Reason: "Pending", - Message: "gateway pods are still being scheduled", - }}, - })), - }, - expectedConsulUpdates: []api.ConfigEntry{ - &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "gateway", - Meta: map[string]string{ - "k8s-name": "gateway", - "k8s-namespace": "default", - }, - Listeners: []api.APIGatewayListener{}, - }, - }, - expectedConsulDeletions: []api.ResourceReference{ - {Kind: api.TCPRoute, Name: "route"}, - }, - }, - "gateway deletion routes and secrets": { - config: controlledBinder(BinderConfig{ - Gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway-deleted", - DeletionTimestamp: deletionTimestamp, - Finalizers: []string{common.GatewayFinalizer}, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: testGatewayClassName, - Listeners: []gwv1beta1.Listener{{ - TLS: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{ - {Name: "secret-one"}, - {Name: "secret-two"}, - }, - }, - }}, - }, - }, - HTTPRoutes: []gwv1beta1.HTTPRoute{ - testHTTPRoute("http-route-one", []string{"gateway-deleted"}, nil), - testHTTPRouteStatus("http-route-two", nil, []gwv1alpha2.RouteParentStatus{ - {ParentRef: gwv1beta1.ParentReference{Name: "gateway-deleted"}, ControllerName: testControllerName, Conditions: []metav1.Condition{ - { - Type: "Accepted", - Status: metav1.ConditionTrue, - }, - }}, - {ParentRef: gwv1beta1.ParentReference{Name: "gateway"}, ControllerName: testControllerName, Conditions: []metav1.Condition{ - { - Type: "Accepted", - Status: metav1.ConditionTrue, - }, - }}, - }), - }, - TCPRoutes: []gwv1alpha2.TCPRoute{ - testTCPRoute("tcp-route-one", []string{"gateway-deleted"}, nil), - testTCPRouteStatus("tcp-route-two", nil, []gwv1alpha2.RouteParentStatus{ - {ParentRef: gwv1beta1.ParentReference{Name: "gateway-deleted"}, ControllerName: testControllerName, Conditions: []metav1.Condition{ - { - Type: "Accepted", - Status: metav1.ConditionTrue, - }, - }}, - {ParentRef: gwv1beta1.ParentReference{Name: "gateway"}, ControllerName: testControllerName, Conditions: []metav1.Condition{ - { - Type: "Accepted", - Status: metav1.ConditionTrue, - }, - }}, - }), - }, - }), - resources: resourceMapResources{ - consulHTTPRoutes: []api.HTTPRouteConfigEntry{ - { - Kind: api.HTTPRoute, Name: "http-route-two", Meta: map[string]string{ - "k8s-name": "http-route-two", - "k8s-namespace": "", - }, - Parents: []api.ResourceReference{ - {Kind: api.APIGateway, Name: "gateway-deleted"}, - {Kind: api.APIGateway, Name: "gateway"}, - }, - }, - { - Kind: api.HTTPRoute, Name: "http-route-one", Meta: map[string]string{ - "k8s-name": "http-route-one", - "k8s-namespace": "", - }, - Parents: []api.ResourceReference{ - {Kind: api.APIGateway, Name: "gateway-deleted"}, - }, - }, - }, - consulTCPRoutes: []api.TCPRouteConfigEntry{ - { - Kind: api.TCPRoute, Name: "tcp-route-two", - Meta: map[string]string{ - "k8s-name": "tcp-route-two", - "k8s-namespace": "", - }, - Parents: []api.ResourceReference{ - {Kind: api.APIGateway, Name: "gateway-deleted"}, - {Kind: api.APIGateway, Name: "gateway"}, - }, - }, - { - Kind: api.TCPRoute, Name: "tcp-route-one", - Meta: map[string]string{ - "k8s-name": "tcp-route-one", - "k8s-namespace": "", - }, - Parents: []api.ResourceReference{ - {Kind: api.APIGateway, Name: "gateway-deleted"}, - }, - }, - }, - consulInlineCertificates: []api.InlineCertificateConfigEntry{ - *certificateOne, - *certificateTwo, - }, - secrets: []corev1.Secret{ - secretOne, - secretTwo, - }, - gateways: []gwv1beta1.Gateway{ - gatewayWithFinalizer(gwv1beta1.GatewaySpec{ - Listeners: []gwv1beta1.Listener{{ - TLS: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{ - {Name: "secret-one"}, - {Name: "secret-three"}, - }, - }, - }}, - }), - }, - }, - expectedStatusUpdates: []client.Object{ - common.PointerTo(testHTTPRouteStatus("http-route-two", nil, []gwv1beta1.RouteParentStatus{ - {ParentRef: gwv1beta1.ParentReference{Name: "gateway"}, ControllerName: testControllerName, Conditions: []metav1.Condition{ - { - Type: "Accepted", - Status: metav1.ConditionTrue, - }, - }}, - }, "gateway-deleted")), - common.PointerTo(testTCPRouteStatus("tcp-route-two", nil, []gwv1beta1.RouteParentStatus{ - {ParentRef: gwv1beta1.ParentReference{Name: "gateway"}, ControllerName: testControllerName, Conditions: []metav1.Condition{ - { - Type: "Accepted", - Status: metav1.ConditionTrue, - }, - }}, - }, "gateway-deleted")), - }, - expectedUpdates: []client.Object{ - &gwv1beta1.HTTPRoute{ - TypeMeta: metav1.TypeMeta{ - Kind: "HTTPRoute", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "http-route-one", - // removing a finalizer - Finalizers: []string{}, - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - {Name: "gateway-deleted"}, - }, - }, - }, - Status: gwv1beta1.HTTPRouteStatus{RouteStatus: gwv1beta1.RouteStatus{Parents: []gwv1alpha2.RouteParentStatus{}}}, - }, - &gwv1alpha2.TCPRoute{ - TypeMeta: metav1.TypeMeta{ - Kind: "TCPRoute", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "tcp-route-one", - Finalizers: []string{}, - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - {Name: "gateway-deleted"}, - }, - }, - }, - Status: gwv1alpha2.TCPRouteStatus{RouteStatus: gwv1beta1.RouteStatus{Parents: []gwv1alpha2.RouteParentStatus{}}}, - }, - addClassConfig(gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway-deleted", - DeletionTimestamp: deletionTimestamp, - Finalizers: []string{}, - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: testGatewayClassName, - Listeners: []gwv1beta1.Listener{{ - TLS: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{ - {Name: "secret-one"}, - {Name: "secret-two"}, - }, - }, - }}, - }, - }), - }, - expectedConsulUpdates: []api.ConfigEntry{ - &api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "http-route-two", - Meta: map[string]string{ - "k8s-name": "http-route-two", - "k8s-namespace": "", - }, - // dropped ref to gateway - Parents: []api.ResourceReference{{ - Kind: api.APIGateway, - Name: "gateway", - }}, - }, - &api.TCPRouteConfigEntry{ - Kind: api.TCPRoute, - Name: "tcp-route-two", - Meta: map[string]string{ - "k8s-name": "tcp-route-two", - "k8s-namespace": "", - }, - // dropped ref to gateway - Parents: []api.ResourceReference{{ - Kind: api.APIGateway, - Name: "gateway", - }}, - }, - }, - expectedConsulDeletions: []api.ResourceReference{ - {Kind: api.HTTPRoute, Name: "http-route-one"}, - {Kind: api.TCPRoute, Name: "tcp-route-one"}, - {Kind: api.InlineCertificate, Name: "secret-two"}, - {Kind: api.APIGateway, Name: "gateway-deleted"}, - }, - }, - } { - t.Run(name, func(t *testing.T) { - tt.resources.gateways = append(tt.resources.gateways, tt.config.Gateway) - tt.resources.httpRoutes = append(tt.resources.httpRoutes, tt.config.HTTPRoutes...) - tt.resources.tcpRoutes = append(tt.resources.tcpRoutes, tt.config.TCPRoutes...) - - tt.config.Resources = newTestResourceMap(t, tt.resources) - tt.config.ControllerName = testControllerName - tt.config.Logger = logrtest.NewTestLogger(t) - tt.config.GatewayClassConfig = &v1alpha1.GatewayClassConfig{} - serializeGatewayClassConfig(&tt.config.Gateway, tt.config.GatewayClassConfig) - - binder := NewBinder(tt.config) - actual := binder.Snapshot() - - actualConsulUpdates := common.ConvertSliceFunc(actual.Consul.Updates, func(op *common.ConsulUpdateOperation) api.ConfigEntry { - return op.Entry - }) - - require.ElementsMatch(t, tt.expectedConsulUpdates, actualConsulUpdates, "consul updates differ", cmp.Diff(tt.expectedConsulUpdates, actualConsulUpdates)) - require.ElementsMatch(t, tt.expectedConsulDeletions, actual.Consul.Deletions, "consul deletions differ") - require.ElementsMatch(t, tt.expectedStatusUpdates, actual.Kubernetes.StatusUpdates.Operations(), "kubernetes statuses differ", cmp.Diff(tt.expectedStatusUpdates, actual.Kubernetes.StatusUpdates.Operations())) - require.ElementsMatch(t, tt.expectedUpdates, actual.Kubernetes.Updates.Operations(), "kubernetes updates differ", cmp.Diff(tt.expectedUpdates, actual.Kubernetes.Updates.Operations())) - }) - } -} - -func TestBinder_Registrations(t *testing.T) { - t.Parallel() - - setDeleted := func(gateway gwv1beta1.Gateway) gwv1beta1.Gateway { - gateway.DeletionTimestamp = deletionTimestamp - return gateway - } - - for name, tt := range map[string]struct { - config BinderConfig - resources resourceMapResources - expectedRegistrations []string - expectedDeregistrations []api.CatalogDeregistration - }{ - "deleting gateway with consul services": { - config: controlledBinder(BinderConfig{ - Gateway: setDeleted(gatewayWithFinalizer(gwv1beta1.GatewaySpec{})), - ConsulGatewayServices: []api.CatalogService{ - {Node: "test", ServiceID: "pod1", Namespace: "namespace1"}, - {Node: "test", ServiceID: "pod2", Namespace: "namespace1"}, - {Node: "test", ServiceID: "pod3", Namespace: "namespace1"}, - }, - Pods: []corev1.Pod{ - { - ObjectMeta: metav1.ObjectMeta{Name: "pod1"}, - Status: corev1.PodStatus{ - Phase: corev1.PodRunning, - Conditions: []corev1.PodCondition{{Type: corev1.PodReady, Status: corev1.ConditionTrue}}, - }, - }, - { - ObjectMeta: metav1.ObjectMeta{Name: "pod2"}, - Status: corev1.PodStatus{ - Phase: corev1.PodRunning, - Conditions: []corev1.PodCondition{{Type: corev1.PodReady, Status: corev1.ConditionTrue}}, - }, - }, - { - ObjectMeta: metav1.ObjectMeta{Name: "pod3"}, - Status: corev1.PodStatus{ - Phase: corev1.PodRunning, - Conditions: []corev1.PodCondition{{Type: corev1.PodReady, Status: corev1.ConditionTrue}}, - }, - }, - }, - }), - expectedDeregistrations: []api.CatalogDeregistration{ - {Node: "test", ServiceID: "pod1", Namespace: "namespace1"}, - {Node: "test", ServiceID: "pod2", Namespace: "namespace1"}, - {Node: "test", ServiceID: "pod3", Namespace: "namespace1"}, - }, - }, - "gateway with consul services and mixed pods": { - config: controlledBinder(BinderConfig{ - Gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), - Pods: []corev1.Pod{ - { - ObjectMeta: metav1.ObjectMeta{Name: "pod1", Namespace: "namespace1"}, - Status: corev1.PodStatus{ - Phase: corev1.PodRunning, - Conditions: []corev1.PodCondition{{Type: corev1.PodReady, Status: corev1.ConditionTrue}}, - }, - }, - { - ObjectMeta: metav1.ObjectMeta{Name: "pod3", Namespace: "namespace1"}, - Status: corev1.PodStatus{ - Phase: corev1.PodFailed, - }, - }, - { - ObjectMeta: metav1.ObjectMeta{Name: "pod4", Namespace: "namespace1"}, - Status: corev1.PodStatus{ - Phase: corev1.PodRunning, - Conditions: []corev1.PodCondition{{Type: corev1.PodReady, Status: corev1.ConditionTrue}}, - }, - }, - }, - ConsulGatewayServices: []api.CatalogService{ - {Node: "test", ServiceID: "pod1", Namespace: "namespace1"}, - {Node: "test", ServiceID: "pod2", Namespace: "namespace1"}, - {Node: "test", ServiceID: "pod3", Namespace: "namespace1"}, - }, - }), - expectedRegistrations: []string{"pod1", "pod3", "pod4"}, - expectedDeregistrations: []api.CatalogDeregistration{ - {Node: "test", ServiceID: "pod2", Namespace: "namespace1"}, - }, - }, - } { - t.Run(name, func(t *testing.T) { - tt.resources.gateways = append(tt.resources.gateways, tt.config.Gateway) - tt.resources.httpRoutes = append(tt.resources.httpRoutes, tt.config.HTTPRoutes...) - tt.resources.tcpRoutes = append(tt.resources.tcpRoutes, tt.config.TCPRoutes...) - - tt.config.Resources = newTestResourceMap(t, tt.resources) - tt.config.ControllerName = testControllerName - tt.config.Logger = logrtest.NewTestLogger(t) - tt.config.GatewayClassConfig = &v1alpha1.GatewayClassConfig{} - serializeGatewayClassConfig(&tt.config.Gateway, tt.config.GatewayClassConfig) - - binder := NewBinder(tt.config) - actual := binder.Snapshot() - - require.Len(t, actual.Consul.Registrations, len(tt.expectedRegistrations)) - for i := range actual.Consul.Registrations { - registration := actual.Consul.Registrations[i] - expected := tt.expectedRegistrations[i] - - require.EqualValues(t, expected, registration.Service.ID) - require.EqualValues(t, "gateway", registration.Service.Service) - } - - require.EqualValues(t, tt.expectedDeregistrations, actual.Consul.Deregistrations) - }) - } -} - -func TestBinder_BindingRulesKitchenSink(t *testing.T) { - t.Parallel() - - gateway := gatewayWithFinalizer(gwv1beta1.GatewaySpec{ - Listeners: []gwv1beta1.Listener{{ - Name: "http-listener-default-same", - Protocol: gwv1beta1.HTTPProtocolType, - }, { - Name: "http-listener-hostname", - Protocol: gwv1beta1.HTTPProtocolType, - Hostname: common.PointerTo[gwv1beta1.Hostname]("host.name"), - }, { - Name: "http-listener-mismatched-kind-allowed", - Protocol: gwv1beta1.HTTPProtocolType, - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Kinds: []gwv1beta1.RouteGroupKind{{ - Kind: "Foo", - }}, - }, - }, { - Name: "http-listener-explicit-all-allowed", - Protocol: gwv1beta1.HTTPProtocolType, - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.NamespacesFromAll), - }, - }, - }, { - Name: "http-listener-explicit-allowed-same", - Protocol: gwv1beta1.HTTPProtocolType, - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.NamespacesFromSame), - }, - }, - }, { - Name: "http-listener-allowed-selector", - Protocol: gwv1beta1.HTTPProtocolType, - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.NamespacesFromSelector), - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "test": "foo", - }, - }, - }, - }, - }, { - Name: "http-listener-tls", - Protocol: gwv1beta1.HTTPSProtocolType, - TLS: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{{ - Name: "secret-one", - }}, - }, - }, { - Name: "tcp-listener-default-same", - Protocol: gwv1beta1.TCPProtocolType, - }, { - Name: "tcp-listener-mismatched-kind-allowed", - Protocol: gwv1beta1.TCPProtocolType, - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Kinds: []gwv1beta1.RouteGroupKind{{ - Kind: "Foo", - }}, - }, - }, { - Name: "tcp-listener-explicit-all-allowed", - Protocol: gwv1beta1.TCPProtocolType, - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.NamespacesFromAll), - }, - }, - }, { - Name: "tcp-listener-explicit-allowed-same", - Protocol: gwv1beta1.TCPProtocolType, - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.NamespacesFromSame), - }, - }, - }, { - Name: "tcp-listener-allowed-selector", - Protocol: gwv1beta1.TCPProtocolType, - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.NamespacesFromSelector), - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "test": "foo", - }, - }, - }, - }, - }, { - Name: "tcp-listener-tls", - Protocol: gwv1beta1.TCPProtocolType, - TLS: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{{ - Name: "secret-one", - }}, - }, - }}, - }) - - namespaces := map[string]corev1.Namespace{ - "default": { - ObjectMeta: metav1.ObjectMeta{ - Name: "default", - }, - }, - "test": { - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Labels: map[string]string{ - "test": "foo", - }, - }, - }, - } - - _, secretOne := generateTestCertificate(t, "", "secret-one") - - gateway.Namespace = "default" - defaultNamespacePointer := common.PointerTo[gwv1beta1.Namespace]("default") - - for name, tt := range map[string]struct { - httpRoute *gwv1beta1.HTTPRoute - tcpRoute *gwv1alpha2.TCPRoute - referenceGrants []gwv1beta1.ReferenceGrant - expectedStatusUpdates []client.Object - }{ - "untargeted http route same namespace": { - httpRoute: testHTTPRouteBackends("route", "default", nil, []gwv1beta1.ParentReference{ - {Name: "gateway"}, - }), - expectedStatusUpdates: []client.Object{ - testHTTPRouteStatusBackends("route", "default", nil, []gwv1beta1.RouteParentStatus{ - {ControllerName: testControllerName, ParentRef: gwv1beta1.ParentReference{Name: "gateway"}, Conditions: []metav1.Condition{ - { - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, - { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }, - }}, - }), - }, - }, - "untargeted http route same namespace missing backend": { - httpRoute: testHTTPRouteBackends("route", "default", []gwv1beta1.BackendObjectReference{ - {Name: gwv1beta1.ObjectName("backend")}, - }, []gwv1beta1.ParentReference{ - {Name: "gateway"}, - }), - expectedStatusUpdates: []client.Object{ - testHTTPRouteStatusBackends("route", "default", []gwv1beta1.BackendObjectReference{ - {Name: gwv1beta1.ObjectName("backend")}, - }, []gwv1beta1.RouteParentStatus{ - {ControllerName: testControllerName, ParentRef: gwv1beta1.ParentReference{Name: "gateway"}, Conditions: []metav1.Condition{ - { - Type: "ResolvedRefs", - Status: metav1.ConditionFalse, - Reason: "BackendNotFound", - Message: "default/backend: backend not found", - }, - { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }, - }}, - }), - }, - }, - "untargeted http route same namespace invalid backend type": { - httpRoute: testHTTPRouteBackends("route", "default", []gwv1beta1.BackendObjectReference{ - { - Name: gwv1beta1.ObjectName("backend"), - Group: common.PointerTo[gwv1beta1.Group]("invalid.foo.com"), - }, - }, []gwv1beta1.ParentReference{ - {Name: "gateway"}, - }), - expectedStatusUpdates: []client.Object{ - testHTTPRouteStatusBackends("route", "default", []gwv1beta1.BackendObjectReference{ - { - Name: gwv1beta1.ObjectName("backend"), - Group: common.PointerTo[gwv1beta1.Group]("invalid.foo.com"), - }, - }, []gwv1beta1.RouteParentStatus{ - {ControllerName: testControllerName, ParentRef: gwv1beta1.ParentReference{Name: "gateway"}, Conditions: []metav1.Condition{ - { - Type: "ResolvedRefs", - Status: metav1.ConditionFalse, - Reason: "InvalidKind", - Message: "default/backend [Service.invalid.foo.com]: invalid backend kind", - }, - { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }, - }}, - }), - }, - }, - "untargeted http route different namespace": { - httpRoute: testHTTPRouteBackends("route", "other", nil, []gwv1beta1.ParentReference{ - { - Name: "gateway", - Namespace: defaultNamespacePointer, - }, - }), - expectedStatusUpdates: []client.Object{ - testHTTPRouteStatusBackends("route", "other", nil, []gwv1beta1.RouteParentStatus{ - {ControllerName: testControllerName, ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - }, Conditions: []metav1.Condition{ - { - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }, - }}, - }), - }, - }, - "untargeted http route different namespace and reference grants": { - httpRoute: testHTTPRouteBackends("route", "other", nil, []gwv1beta1.ParentReference{ - { - Name: "gateway", - Namespace: defaultNamespacePointer, - }, - }), - referenceGrants: []gwv1beta1.ReferenceGrant{ - {ObjectMeta: metav1.ObjectMeta{Namespace: "default", Name: "grant"}, Spec: gwv1beta1.ReferenceGrantSpec{ - From: []gwv1beta1.ReferenceGrantFrom{ - {Group: gwv1beta1.GroupName, Kind: "HTTPRoute", Namespace: gwv1beta1.Namespace("other")}, - }, - To: []gwv1beta1.ReferenceGrantTo{ - {Group: gwv1beta1.GroupName, Kind: "Gateway"}, - }, - }}, - }, - expectedStatusUpdates: []client.Object{ - testHTTPRouteStatusBackends("route", "other", nil, []gwv1beta1.RouteParentStatus{ - {ControllerName: testControllerName, ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - }, Conditions: []metav1.Condition{ - { - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }, - }}, - }), - }, - }, - "targeted http route same namespace": { - httpRoute: testHTTPRouteBackends("route", "default", nil, []gwv1beta1.ParentReference{ - { - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-default-same"), - }, { - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-hostname"), - }, { - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), - }, { - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), - }, { - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), - }, { - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), - }, { - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-tls"), - }, { - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), - }, - }), - expectedStatusUpdates: []client.Object{ - testHTTPRouteStatusBackends("route", "default", nil, []gwv1beta1.RouteParentStatus{ - { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-default-same"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-hostname"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-mismatched-kind-allowed: listener does not support route protocol", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-allowed-selector: listener does not allow binding routes from the given namespace", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-tls"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "tcp-listener-explicit-all-allowed: listener does not support route protocol", - }}, - }, - }), - }, - }, - "targeted http route different namespace": { - referenceGrants: []gwv1beta1.ReferenceGrant{ - {ObjectMeta: metav1.ObjectMeta{Namespace: "default", Name: "grant"}, Spec: gwv1beta1.ReferenceGrantSpec{ - From: []gwv1beta1.ReferenceGrantFrom{ - {Group: gwv1beta1.GroupName, Kind: "HTTPRoute", Namespace: gwv1beta1.Namespace("test")}, - }, - To: []gwv1beta1.ReferenceGrantTo{ - {Group: gwv1beta1.GroupName, Kind: "Gateway"}, - }, - }}, - }, - httpRoute: testHTTPRouteBackends("route", "test", nil, []gwv1beta1.ParentReference{ - { - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-default-same"), - }, { - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-hostname"), - }, { - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), - }, { - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), - }, { - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), - }, { - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), - }, { - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-tls"), - }, { - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), - }, - }), - expectedStatusUpdates: []client.Object{ - testHTTPRouteStatusBackends("route", "test", nil, []gwv1beta1.RouteParentStatus{ - { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-default-same"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-default-same: listener does not allow binding routes from the given namespace", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-hostname"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-hostname: listener does not allow binding routes from the given namespace", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-mismatched-kind-allowed: listener does not support route protocol", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-explicit-allowed-same: listener does not allow binding routes from the given namespace", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-tls"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-tls: listener does not allow binding routes from the given namespace", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "tcp-listener-explicit-all-allowed: listener does not support route protocol", - }}, - }, - }), - }, - }, - "untargeted tcp route same namespace": { - tcpRoute: testTCPRouteBackends("route", "default", nil, []gwv1beta1.ParentReference{ - {Name: "gateway"}, - }), - expectedStatusUpdates: []client.Object{ - testTCPRouteStatusBackends("route", "default", nil, []gwv1beta1.RouteParentStatus{ - {ControllerName: testControllerName, ParentRef: gwv1beta1.ParentReference{Name: "gateway"}, Conditions: []metav1.Condition{ - { - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, - { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }, - }}, - }), - }, - }, - "untargeted tcp route same namespace missing backend": { - tcpRoute: testTCPRouteBackends("route", "default", []gwv1beta1.BackendObjectReference{ - {Name: gwv1beta1.ObjectName("backend")}, - }, []gwv1beta1.ParentReference{ - {Name: "gateway"}, - }), - expectedStatusUpdates: []client.Object{ - testTCPRouteStatusBackends("route", "default", []gwv1beta1.BackendObjectReference{ - {Name: gwv1beta1.ObjectName("backend")}, - }, []gwv1beta1.RouteParentStatus{ - {ControllerName: testControllerName, ParentRef: gwv1beta1.ParentReference{Name: "gateway"}, Conditions: []metav1.Condition{ - { - Type: "ResolvedRefs", - Status: metav1.ConditionFalse, - Reason: "BackendNotFound", - Message: "default/backend: backend not found", - }, - { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }, - }}, - }), - }, - }, - "untargeted tcp route same namespace invalid backend type": { - tcpRoute: testTCPRouteBackends("route", "default", []gwv1beta1.BackendObjectReference{ - { - Name: gwv1beta1.ObjectName("backend"), - Group: common.PointerTo[gwv1beta1.Group]("invalid.foo.com"), - }, - }, []gwv1beta1.ParentReference{ - {Name: "gateway"}, - }), - expectedStatusUpdates: []client.Object{ - testTCPRouteStatusBackends("route", "default", []gwv1beta1.BackendObjectReference{ - { - Name: gwv1beta1.ObjectName("backend"), - Group: common.PointerTo[gwv1beta1.Group]("invalid.foo.com"), - }, - }, []gwv1beta1.RouteParentStatus{ - {ControllerName: testControllerName, ParentRef: gwv1beta1.ParentReference{Name: "gateway"}, Conditions: []metav1.Condition{ - { - Type: "ResolvedRefs", - Status: metav1.ConditionFalse, - Reason: "InvalidKind", - Message: "default/backend [Service.invalid.foo.com]: invalid backend kind", - }, - { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }, - }}, - }), - }, - }, - "untargeted tcp route different namespace": { - tcpRoute: testTCPRouteBackends("route", "other", nil, []gwv1beta1.ParentReference{ - { - Name: "gateway", - Namespace: defaultNamespacePointer, - }, - }), - expectedStatusUpdates: []client.Object{ - testTCPRouteStatusBackends("route", "other", nil, []gwv1beta1.RouteParentStatus{ - {ControllerName: testControllerName, ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - }, Conditions: []metav1.Condition{ - { - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }, - }}, - }), - }, - }, - "untargeted tcp route different namespace and reference grants": { - tcpRoute: testTCPRouteBackends("route", "other", nil, []gwv1beta1.ParentReference{ - { - Name: "gateway", - Namespace: defaultNamespacePointer, - }, - }), - referenceGrants: []gwv1beta1.ReferenceGrant{ - {ObjectMeta: metav1.ObjectMeta{Namespace: "default", Name: "grant"}, Spec: gwv1beta1.ReferenceGrantSpec{ - From: []gwv1beta1.ReferenceGrantFrom{ - {Group: gwv1beta1.GroupName, Kind: "TCPRoute", Namespace: gwv1beta1.Namespace("other")}, - }, - To: []gwv1beta1.ReferenceGrantTo{ - {Group: gwv1beta1.GroupName, Kind: "Gateway"}, - }, - }}, - }, - expectedStatusUpdates: []client.Object{ - testTCPRouteStatusBackends("route", "other", nil, []gwv1beta1.RouteParentStatus{ - {ControllerName: testControllerName, ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - }, Conditions: []metav1.Condition{ - { - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }, - }}, - }), - }, - }, - "targeted tcp route same namespace": { - tcpRoute: testTCPRouteBackends("route", "default", nil, []gwv1beta1.ParentReference{ - { - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-default-same"), - }, { - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-hostname"), - }, { - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), - }, { - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), - }, { - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), - }, { - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), - }, { - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-tls"), - }, { - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), - }, - }), - expectedStatusUpdates: []client.Object{ - testTCPRouteStatusBackends("route", "default", nil, []gwv1beta1.RouteParentStatus{ - { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-default-same"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-default-same: listener does not support route protocol", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-hostname"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-hostname: listener does not support route protocol", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-mismatched-kind-allowed: listener does not support route protocol", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-explicit-all-allowed: listener does not support route protocol", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-explicit-allowed-same: listener does not support route protocol", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-allowed-selector: listener does not support route protocol", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-tls"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-tls: listener does not support route protocol", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - SectionName: common.PointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }, - }), - }, - }, - "targeted tcp route different namespace": { - referenceGrants: []gwv1beta1.ReferenceGrant{ - {ObjectMeta: metav1.ObjectMeta{Namespace: "default", Name: "grant"}, Spec: gwv1beta1.ReferenceGrantSpec{ - From: []gwv1beta1.ReferenceGrantFrom{ - {Group: gwv1beta1.GroupName, Kind: "TCPRoute", Namespace: gwv1beta1.Namespace("test")}, - }, - To: []gwv1beta1.ReferenceGrantTo{ - {Group: gwv1beta1.GroupName, Kind: "Gateway"}, - }, - }}, - }, - tcpRoute: testTCPRouteBackends("route", "test", nil, []gwv1beta1.ParentReference{ - { - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-default-same"), - }, { - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-hostname"), - }, { - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), - }, { - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), - }, { - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), - }, { - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), - }, { - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-tls"), - }, { - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), - }, - }), - expectedStatusUpdates: []client.Object{ - testTCPRouteStatusBackends("route", "test", nil, []gwv1beta1.RouteParentStatus{ - { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-default-same"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-default-same: listener does not support route protocol", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-hostname"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-hostname: listener does not support route protocol", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-mismatched-kind-allowed"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-mismatched-kind-allowed: listener does not support route protocol", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-all-allowed"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-explicit-all-allowed: listener does not support route protocol", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-explicit-allowed-same"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-explicit-allowed-same: listener does not support route protocol", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-allowed-selector"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-allowed-selector: listener does not support route protocol", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("http-listener-tls"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "NotAllowedByListeners", - Message: "http-listener-tls: listener does not support route protocol", - }}, - }, { - ControllerName: testControllerName, - ParentRef: gwv1beta1.ParentReference{ - Name: "gateway", - Namespace: defaultNamespacePointer, - SectionName: common.PointerTo[gwv1beta1.SectionName]("tcp-listener-explicit-all-allowed"), - }, - Conditions: []metav1.Condition{{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - }, { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - }}, - }, - }), - }, - }, - } { - t.Run(name, func(t *testing.T) { - g := *addClassConfig(gateway) - - resources := resourceMapResources{ - gateways: []gwv1beta1.Gateway{g}, - secrets: []corev1.Secret{ - secretOne, - }, - grants: tt.referenceGrants, - } - - if tt.httpRoute != nil { - resources.httpRoutes = append(resources.httpRoutes, *tt.httpRoute) - } - if tt.tcpRoute != nil { - resources.tcpRoutes = append(resources.tcpRoutes, *tt.tcpRoute) - } - - config := controlledBinder(BinderConfig{ - Gateway: g, - GatewayClassConfig: &v1alpha1.GatewayClassConfig{}, - Namespaces: namespaces, - Resources: newTestResourceMap(t, resources), - HTTPRoutes: resources.httpRoutes, - TCPRoutes: resources.tcpRoutes, - }) - - binder := NewBinder(config) - actual := binder.Snapshot() - - compareUpdates(t, tt.expectedStatusUpdates, actual.Kubernetes.StatusUpdates.Operations()) - }) - } -} - -func compareUpdates(t *testing.T, expected []client.Object, actual []client.Object) { - t.Helper() - - filtered := common.Filter(actual, func(o client.Object) bool { - if _, ok := o.(*gwv1beta1.HTTPRoute); ok { - return false - } - if _, ok := o.(*gwv1alpha2.TCPRoute); ok { - return false - } - return true - }) - - require.ElementsMatch(t, expected, filtered, "statuses don't match", cmp.Diff(expected, filtered)) -} - -func addClassConfig(g gwv1beta1.Gateway) *gwv1beta1.Gateway { - serializeGatewayClassConfig(&g, &v1alpha1.GatewayClassConfig{}) - return &g -} - -func gatewayWithFinalizer(spec gwv1beta1.GatewaySpec) gwv1beta1.Gateway { - spec.GatewayClassName = testGatewayClassObjectName - - typeMeta := metav1.TypeMeta{} - typeMeta.SetGroupVersionKind(gwv1beta1.SchemeGroupVersion.WithKind("Gateway")) - - return gwv1beta1.Gateway{ - TypeMeta: typeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - Namespace: "default", - Finalizers: []string{common.GatewayFinalizer}, - }, - Spec: spec, - } -} - -func gatewayWithFinalizerStatus(spec gwv1beta1.GatewaySpec, status gwv1beta1.GatewayStatus) gwv1beta1.Gateway { - g := gatewayWithFinalizer(spec) - g.Status = status - return g -} - -func testHTTPRoute(name string, parents []string, services []string) gwv1beta1.HTTPRoute { - var parentRefs []gwv1beta1.ParentReference - var rules []gwv1beta1.HTTPRouteRule - - for _, parent := range parents { - parentRefs = append(parentRefs, gwv1beta1.ParentReference{Name: gwv1beta1.ObjectName(parent)}) - } - - for _, service := range services { - rules = append(rules, gwv1beta1.HTTPRouteRule{ - BackendRefs: []gwv1beta1.HTTPBackendRef{ - { - BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: gwv1beta1.ObjectName(service), - }, - }, - }, - }, - }) - } - - httpTypeMeta := metav1.TypeMeta{} - httpTypeMeta.SetGroupVersionKind(gwv1beta1.SchemeGroupVersion.WithKind("HTTPRoute")) - - return gwv1beta1.HTTPRoute{ - TypeMeta: httpTypeMeta, - ObjectMeta: metav1.ObjectMeta{Name: name, Finalizers: []string{common.GatewayFinalizer}}, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: parentRefs, - }, - Rules: rules, - }, - } -} - -func testHTTPRouteBackends(name, namespace string, services []gwv1beta1.BackendObjectReference, parents []gwv1beta1.ParentReference) *gwv1beta1.HTTPRoute { - var rules []gwv1beta1.HTTPRouteRule - for _, service := range services { - rules = append(rules, gwv1beta1.HTTPRouteRule{ - BackendRefs: []gwv1beta1.HTTPBackendRef{ - { - BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: service, - }, - }, - }, - }) - } - - httpTypeMeta := metav1.TypeMeta{} - httpTypeMeta.SetGroupVersionKind(gwv1beta1.SchemeGroupVersion.WithKind("HTTPRoute")) - - return &gwv1beta1.HTTPRoute{ - TypeMeta: httpTypeMeta, - ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace, Finalizers: []string{common.GatewayFinalizer}}, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: parents, - }, - Rules: rules, - }, - } -} - -func testHTTPRouteStatusBackends(name, namespace string, services []gwv1beta1.BackendObjectReference, parentStatuses []gwv1beta1.RouteParentStatus) *gwv1beta1.HTTPRoute { - var parentRefs []gwv1beta1.ParentReference - - for _, parent := range parentStatuses { - parentRefs = append(parentRefs, parent.ParentRef) - } - - route := testHTTPRouteBackends(name, namespace, services, parentRefs) - route.Status.RouteStatus.Parents = parentStatuses - return route -} - -func testHTTPRouteStatus(name string, services []string, parentStatuses []gwv1beta1.RouteParentStatus, extraParents ...string) gwv1beta1.HTTPRoute { - parentRefs := extraParents - - for _, parent := range parentStatuses { - parentRefs = append(parentRefs, string(parent.ParentRef.Name)) - } - - route := testHTTPRoute(name, parentRefs, services) - route.Status.RouteStatus.Parents = parentStatuses - - return route -} - -func testTCPRoute(name string, parents []string, services []string) gwv1alpha2.TCPRoute { - var parentRefs []gwv1beta1.ParentReference - var rules []gwv1alpha2.TCPRouteRule - - for _, parent := range parents { - parentRefs = append(parentRefs, gwv1beta1.ParentReference{Name: gwv1beta1.ObjectName(parent)}) - } - - for _, service := range services { - rules = append(rules, gwv1alpha2.TCPRouteRule{ - BackendRefs: []gwv1beta1.BackendRef{ - { - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: gwv1beta1.ObjectName(service), - }, - }, - }, - }) - } - - tcpTypeMeta := metav1.TypeMeta{} - tcpTypeMeta.SetGroupVersionKind(gwv1beta1.SchemeGroupVersion.WithKind("TCPRoute")) - - return gwv1alpha2.TCPRoute{ - TypeMeta: tcpTypeMeta, - ObjectMeta: metav1.ObjectMeta{Name: name, Finalizers: []string{common.GatewayFinalizer}}, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: parentRefs, - }, - Rules: rules, - }, - } -} - -func testTCPRouteBackends(name, namespace string, services []gwv1beta1.BackendObjectReference, parents []gwv1beta1.ParentReference) *gwv1alpha2.TCPRoute { - var rules []gwv1alpha2.TCPRouteRule - for _, service := range services { - rules = append(rules, gwv1alpha2.TCPRouteRule{ - BackendRefs: []gwv1beta1.BackendRef{ - {BackendObjectReference: service}, - }, - }) - } - - tcpTypeMeta := metav1.TypeMeta{} - tcpTypeMeta.SetGroupVersionKind(gwv1beta1.SchemeGroupVersion.WithKind("TCPRoute")) - - return &gwv1alpha2.TCPRoute{ - TypeMeta: tcpTypeMeta, - ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace, Finalizers: []string{common.GatewayFinalizer}}, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: parents, - }, - Rules: rules, - }, - } -} - -func testTCPRouteStatusBackends(name, namespace string, services []gwv1beta1.BackendObjectReference, parentStatuses []gwv1beta1.RouteParentStatus) *gwv1alpha2.TCPRoute { - var parentRefs []gwv1beta1.ParentReference - - for _, parent := range parentStatuses { - parentRefs = append(parentRefs, parent.ParentRef) - } - - route := testTCPRouteBackends(name, namespace, services, parentRefs) - route.Status.RouteStatus.Parents = parentStatuses - return route -} - -func testTCPRouteStatus(name string, services []string, parentStatuses []gwv1beta1.RouteParentStatus, extraParents ...string) gwv1alpha2.TCPRoute { - parentRefs := extraParents - - for _, parent := range parentStatuses { - parentRefs = append(parentRefs, string(parent.ParentRef.Name)) - } - - route := testTCPRoute(name, parentRefs, services) - route.Status.RouteStatus.Parents = parentStatuses - - return route -} - -func controlledBinder(config BinderConfig) BinderConfig { - config.ControllerName = testControllerName - config.GatewayClass = testGatewayClass - return config -} - -func generateTestCertificate(t *testing.T, namespace, name string) (*api.InlineCertificateConfigEntry, corev1.Secret) { - privateKey, err := rsa.GenerateKey(rand.Reader, common.MinKeyLength) - require.NoError(t, err) - - usage := x509.KeyUsageCertSign - expiration := time.Now().AddDate(10, 0, 0) - - cert := &x509.Certificate{ - SerialNumber: big.NewInt(1), - Subject: pkix.Name{ - CommonName: "consul.test", - }, - IsCA: true, - NotBefore: time.Now().Add(-10 * time.Minute), - NotAfter: expiration, - SubjectKeyId: []byte{1, 2, 3, 4, 6}, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, - KeyUsage: usage, - BasicConstraintsValid: true, - } - caCert := cert - caPrivateKey := privateKey - - data, err := x509.CreateCertificate(rand.Reader, cert, caCert, &privateKey.PublicKey, caPrivateKey) - require.NoError(t, err) - - certBytes := pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: data, - }) - - privateKeyBytes := pem.EncodeToMemory(&pem.Block{ - Type: "RSA PRIVATE KEY", - Bytes: x509.MarshalPKCS1PrivateKey(privateKey), - }) - - secret := corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - }, - Data: map[string][]byte{ - corev1.TLSCertKey: certBytes, - corev1.TLSPrivateKeyKey: privateKeyBytes, - }, - } - - certificate, err := (common.ResourceTranslator{}).ToInlineCertificate(secret) - require.NoError(t, err) - - return certificate, secret -} diff --git a/control-plane/api-gateway/binding/reference_grant.go b/control-plane/api-gateway/binding/reference_grant.go deleted file mode 100644 index c2cc421a30..0000000000 --- a/control-plane/api-gateway/binding/reference_grant.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package binding - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" -) - -type referenceValidator struct { - grants map[string]map[types.NamespacedName]gwv1beta1.ReferenceGrant -} - -func NewReferenceValidator(grants []gwv1beta1.ReferenceGrant) common.ReferenceValidator { - byNamespace := make(map[string]map[types.NamespacedName]gwv1beta1.ReferenceGrant) - for _, grant := range grants { - grantsForNamespace, ok := byNamespace[grant.Namespace] - if !ok { - grantsForNamespace = make(map[types.NamespacedName]gwv1beta1.ReferenceGrant) - } - grantsForNamespace[client.ObjectKeyFromObject(&grant)] = grant - byNamespace[grant.Namespace] = grantsForNamespace - } - return &referenceValidator{ - grants: byNamespace, - } -} - -func (rv *referenceValidator) GatewayCanReferenceSecret(gateway gwv1beta1.Gateway, secretRef gwv1beta1.SecretObjectReference) bool { - fromNS := gateway.GetNamespace() - fromGK := metav1.GroupKind{ - Group: gateway.GroupVersionKind().Group, - Kind: gateway.GroupVersionKind().Kind, - } - - // Kind should default to Secret if not set - // https://github.com/kubernetes-sigs/gateway-api/blob/v0.6.2/apis/v1beta1/object_reference_types.go#LL59C21-L59C21 - toNS, toGK := createValuesFromRef(secretRef.Namespace, secretRef.Group, secretRef.Kind, "", common.KindSecret) - - return rv.referenceAllowed(fromGK, fromNS, toGK, toNS, string(secretRef.Name)) -} - -func (rv *referenceValidator) HTTPRouteCanReferenceBackend(httproute gwv1beta1.HTTPRoute, backendRef gwv1beta1.BackendRef) bool { - fromNS := httproute.GetNamespace() - fromGK := metav1.GroupKind{ - Group: httproute.GroupVersionKind().Group, - Kind: httproute.GroupVersionKind().Kind, - } - - // Kind should default to Service if not set - // https://github.com/kubernetes-sigs/gateway-api/blob/v0.6.2/apis/v1beta1/object_reference_types.go#L106 - toNS, toGK := createValuesFromRef(backendRef.Namespace, backendRef.Group, backendRef.Kind, "", common.KindService) - - return rv.referenceAllowed(fromGK, fromNS, toGK, toNS, string(backendRef.Name)) -} - -func (rv *referenceValidator) TCPRouteCanReferenceBackend(tcpRoute gwv1alpha2.TCPRoute, backendRef gwv1beta1.BackendRef) bool { - fromNS := tcpRoute.GetNamespace() - fromGK := metav1.GroupKind{ - Group: tcpRoute.GroupVersionKind().Group, - Kind: tcpRoute.GroupVersionKind().Kind, - } - - // Kind should default to Service if not set - // https://github.com/kubernetes-sigs/gateway-api/blob/v0.6.2/apis/v1beta1/object_reference_types.go#L106 - toNS, toGK := createValuesFromRef(backendRef.Namespace, backendRef.Group, backendRef.Kind, common.BetaGroup, common.KindService) - - return rv.referenceAllowed(fromGK, fromNS, toGK, toNS, string(backendRef.Name)) -} - -func createValuesFromRef(ns *gwv1beta1.Namespace, group *gwv1beta1.Group, kind *gwv1beta1.Kind, defaultGroup, defaultKind string) (string, metav1.GroupKind) { - toNS := "" - if ns != nil { - toNS = string(*ns) - } - - gk := metav1.GroupKind{ - Kind: defaultKind, - Group: defaultGroup, - } - if group != nil { - gk.Group = string(*group) - } - if kind != nil { - gk.Kind = string(*kind) - } - - return toNS, gk -} - -// referenceAllowed checks to see if a reference between resources is allowed. -// In particular, references from one namespace to a resource in a different namespace -// require an applicable ReferenceGrant be found in the namespace containing the resource -// being referred to. -// -// For example, a Gateway in namespace "foo" may only reference a Secret in namespace "bar" -// if a ReferenceGrant in namespace "bar" allows references from namespace "foo". -func (rv *referenceValidator) referenceAllowed(fromGK metav1.GroupKind, fromNamespace string, toGK metav1.GroupKind, toNamespace, toName string) bool { - // Reference does not cross namespaces - if toNamespace == "" || toNamespace == fromNamespace { - return true - } - - // Fetch all ReferenceGrants in the referenced namespace - grants, ok := rv.grants[toNamespace] - if !ok { - return false - } - - for _, grant := range grants { - // Check for a From that applies - fromMatch := false - for _, from := range grant.Spec.From { - if fromGK.Group == string(from.Group) && fromGK.Kind == string(from.Kind) && fromNamespace == string(from.Namespace) { - fromMatch = true - break - } - } - - if !fromMatch { - continue - } - - // Check for a To that applies - for _, to := range grant.Spec.To { - if toGK.Group == string(to.Group) && toGK.Kind == string(to.Kind) { - if to.Name == nil || *to.Name == "" { - // No name specified is treated as a wildcard within the namespace - return true - } - - if gwv1beta1.ObjectName(toName) == *to.Name { - // The ReferenceGrant specifically targets this object - return true - } - } - } - } - - // No ReferenceGrant was found which allows this cross-namespace reference - return false -} diff --git a/control-plane/api-gateway/binding/reference_grant_test.go b/control-plane/api-gateway/binding/reference_grant_test.go deleted file mode 100644 index 12f01478fc..0000000000 --- a/control-plane/api-gateway/binding/reference_grant_test.go +++ /dev/null @@ -1,454 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package binding - -import ( - "context" - "testing" - - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -const ( - ToNamespace = "toNamespace" - FromNamespace = "fromNamespace" - InvalidNamespace = "invalidNamespace" - Group = "gateway.networking.k8s.io" - V1Beta1 = "/v1beta1" - V1Alpha2 = "/v1alpha2" - HTTPRouteKind = "HTTPRoute" - TCPRouteKind = "TCPRoute" - GatewayKind = "Gateway" - BackendRefKind = "Service" - SecretKind = "Secret" -) - -func TestGatewayCanReferenceSecret(t *testing.T) { - t.Parallel() - - objName := gwv1beta1.ObjectName("mysecret") - - basicValidReferenceGrant := gwv1beta1.ReferenceGrant{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: ToNamespace, - }, - Spec: gwv1beta1.ReferenceGrantSpec{ - From: []gwv1beta1.ReferenceGrantFrom{ - { - Group: Group, - Kind: GatewayKind, - Namespace: FromNamespace, - }, - }, - To: []gwv1beta1.ReferenceGrantTo{ - { - Group: Group, - Kind: SecretKind, - Name: &objName, - }, - }, - }, - } - - secretRefGroup := gwv1beta1.Group(Group) - secretRefKind := gwv1beta1.Kind(SecretKind) - secretRefNamespace := gwv1beta1.Namespace(ToNamespace) - - cases := map[string]struct { - canReference bool - err error - ctx context.Context - gateway gwv1beta1.Gateway - secret gwv1beta1.SecretObjectReference - k8sReferenceGrants []gwv1beta1.ReferenceGrant - }{ - "gateway allowed to secret": { - canReference: true, - err: nil, - ctx: context.TODO(), - gateway: gwv1beta1.Gateway{ - TypeMeta: metav1.TypeMeta{ - Kind: GatewayKind, - APIVersion: Group + V1Beta1, - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: FromNamespace, - }, - Spec: gwv1beta1.GatewaySpec{}, - Status: gwv1beta1.GatewayStatus{}, - }, - secret: gwv1beta1.SecretObjectReference{ - Group: &secretRefGroup, - Kind: &secretRefKind, - Namespace: &secretRefNamespace, - Name: objName, - }, - k8sReferenceGrants: []gwv1beta1.ReferenceGrant{ - basicValidReferenceGrant, - }, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - rv := NewReferenceValidator(tc.k8sReferenceGrants) - canReference := rv.GatewayCanReferenceSecret(tc.gateway, tc.secret) - - require.Equal(t, tc.canReference, canReference) - }) - } -} - -func TestHTTPRouteCanReferenceBackend(t *testing.T) { - t.Parallel() - - objName := gwv1beta1.ObjectName("myBackendRef") - - basicValidReferenceGrant := gwv1beta1.ReferenceGrant{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: ToNamespace, - }, - Spec: gwv1beta1.ReferenceGrantSpec{ - From: []gwv1beta1.ReferenceGrantFrom{ - { - Group: Group, - Kind: HTTPRouteKind, - Namespace: FromNamespace, - }, - }, - To: []gwv1beta1.ReferenceGrantTo{ - { - Group: Group, - Kind: BackendRefKind, - Name: &objName, - }, - }, - }, - } - - backendRefGroup := gwv1beta1.Group(Group) - backendRefKind := gwv1beta1.Kind(BackendRefKind) - backendRefNamespace := gwv1beta1.Namespace(ToNamespace) - - cases := map[string]struct { - canReference bool - err error - ctx context.Context - httpRoute gwv1beta1.HTTPRoute - backendRef gwv1beta1.BackendRef - k8sReferenceGrants []gwv1beta1.ReferenceGrant - }{ - "httproute allowed to gateway": { - canReference: true, - err: nil, - ctx: context.TODO(), - httpRoute: gwv1beta1.HTTPRoute{ - TypeMeta: metav1.TypeMeta{ - Kind: HTTPRouteKind, - APIVersion: Group + V1Beta1, - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: FromNamespace, - }, - Spec: gwv1beta1.HTTPRouteSpec{}, - Status: gwv1beta1.HTTPRouteStatus{}, - }, - backendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Group: &backendRefGroup, - Kind: &backendRefKind, - Name: objName, - Namespace: &backendRefNamespace, - Port: nil, - }, - Weight: nil, - }, - k8sReferenceGrants: []gwv1beta1.ReferenceGrant{ - basicValidReferenceGrant, - }, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - rv := NewReferenceValidator(tc.k8sReferenceGrants) - canReference := rv.HTTPRouteCanReferenceBackend(tc.httpRoute, tc.backendRef) - - require.Equal(t, tc.canReference, canReference) - }) - } -} - -func TestTCPRouteCanReferenceBackend(t *testing.T) { - t.Parallel() - - objName := gwv1beta1.ObjectName("myBackendRef") - - basicValidReferenceGrant := gwv1beta1.ReferenceGrant{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: ToNamespace, - }, - Spec: gwv1beta1.ReferenceGrantSpec{ - From: []gwv1beta1.ReferenceGrantFrom{ - { - Group: Group, - Kind: TCPRouteKind, - Namespace: FromNamespace, - }, - }, - To: []gwv1beta1.ReferenceGrantTo{ - { - Group: Group, - Kind: BackendRefKind, - Name: &objName, - }, - }, - }, - } - - backendRefGroup := gwv1beta1.Group(Group) - backendRefKind := gwv1beta1.Kind(BackendRefKind) - backendRefNamespace := gwv1beta1.Namespace(ToNamespace) - - cases := map[string]struct { - canReference bool - err error - ctx context.Context - tcpRoute gwv1alpha2.TCPRoute - backendRef gwv1beta1.BackendRef - k8sReferenceGrants []gwv1beta1.ReferenceGrant - }{ - "tcpRoute allowed to gateway": { - canReference: true, - err: nil, - ctx: context.TODO(), - tcpRoute: gwv1alpha2.TCPRoute{ - TypeMeta: metav1.TypeMeta{ - Kind: TCPRouteKind, - APIVersion: Group + V1Alpha2, - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: FromNamespace, - }, - Spec: gwv1alpha2.TCPRouteSpec{}, - Status: gwv1alpha2.TCPRouteStatus{}, - }, - backendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Group: &backendRefGroup, - Kind: &backendRefKind, - Name: objName, - Namespace: &backendRefNamespace, - Port: nil, - }, - Weight: nil, - }, - k8sReferenceGrants: []gwv1beta1.ReferenceGrant{ - basicValidReferenceGrant, - }, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - rv := NewReferenceValidator(tc.k8sReferenceGrants) - canReference := rv.TCPRouteCanReferenceBackend(tc.tcpRoute, tc.backendRef) - - require.Equal(t, tc.canReference, canReference) - }) - } -} - -func TestReferenceAllowed(t *testing.T) { - t.Parallel() - - objName := gwv1beta1.ObjectName("myObject") - - basicValidReferenceGrant := gwv1beta1.ReferenceGrant{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: ToNamespace, - }, - Spec: gwv1beta1.ReferenceGrantSpec{ - From: []gwv1beta1.ReferenceGrantFrom{ - { - Group: Group, - Kind: HTTPRouteKind, - Namespace: FromNamespace, - }, - }, - To: []gwv1beta1.ReferenceGrantTo{ - { - Group: Group, - Kind: GatewayKind, - Name: &objName, - }, - }, - }, - } - - cases := map[string]struct { - refAllowed bool - err error - ctx context.Context - fromGK metav1.GroupKind - fromNamespace string - toGK metav1.GroupKind - toNamespace string - toName string - k8sReferenceGrants []gwv1beta1.ReferenceGrant - }{ - "same namespace": { - refAllowed: true, - err: nil, - ctx: context.TODO(), - fromGK: metav1.GroupKind{ - Group: Group, - Kind: HTTPRouteKind, - }, - fromNamespace: FromNamespace, - toGK: metav1.GroupKind{ - Group: Group, - Kind: GatewayKind, - }, - toNamespace: FromNamespace, - toName: string(objName), - k8sReferenceGrants: []gwv1beta1.ReferenceGrant{ - { - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: FromNamespace, - }, - Spec: gwv1beta1.ReferenceGrantSpec{ - From: []gwv1beta1.ReferenceGrantFrom{ - { - Group: Group, - Kind: HTTPRouteKind, - Namespace: FromNamespace, - }, - }, - To: []gwv1beta1.ReferenceGrantTo{ - { - Group: Group, - Kind: GatewayKind, - Name: &objName, - }, - }, - }, - }, - }, - }, - "reference allowed": { - refAllowed: true, - err: nil, - ctx: context.TODO(), - fromGK: metav1.GroupKind{ - Group: Group, - Kind: HTTPRouteKind, - }, - fromNamespace: FromNamespace, - toGK: metav1.GroupKind{ - Group: Group, - Kind: GatewayKind, - }, - toNamespace: ToNamespace, - toName: string(objName), - k8sReferenceGrants: []gwv1beta1.ReferenceGrant{ - basicValidReferenceGrant, - }, - }, - "reference not allowed": { - refAllowed: false, - err: nil, - ctx: context.TODO(), - fromGK: metav1.GroupKind{ - Group: Group, - Kind: HTTPRouteKind, - }, - fromNamespace: InvalidNamespace, - toGK: metav1.GroupKind{ - Group: Group, - Kind: GatewayKind, - }, - toNamespace: ToNamespace, - toName: string(objName), - k8sReferenceGrants: []gwv1beta1.ReferenceGrant{ - basicValidReferenceGrant, - }, - }, - "no reference grant defined in namespace": { - refAllowed: false, - err: nil, - ctx: context.TODO(), - fromGK: metav1.GroupKind{ - Group: Group, - Kind: HTTPRouteKind, - }, - fromNamespace: FromNamespace, - toGK: metav1.GroupKind{ - Group: Group, - Kind: GatewayKind, - }, - toNamespace: ToNamespace, - toName: string(objName), - k8sReferenceGrants: nil, - }, - "reference allowed to all objects in namespace": { - refAllowed: true, - err: nil, - ctx: context.TODO(), - fromGK: metav1.GroupKind{ - Group: Group, - Kind: HTTPRouteKind, - }, - fromNamespace: FromNamespace, - toGK: metav1.GroupKind{ - Group: Group, - Kind: GatewayKind, - }, - toNamespace: ToNamespace, - toName: string(objName), - k8sReferenceGrants: []gwv1beta1.ReferenceGrant{ - { - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: ToNamespace, - }, - Spec: gwv1beta1.ReferenceGrantSpec{ - From: []gwv1beta1.ReferenceGrantFrom{ - { - Group: Group, - Kind: HTTPRouteKind, - Namespace: FromNamespace, - }, - }, - To: []gwv1beta1.ReferenceGrantTo{ - { - Group: Group, - Kind: GatewayKind, - Name: nil, - }, - }, - }, - }, - }, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - rv := NewReferenceValidator(tc.k8sReferenceGrants).(*referenceValidator) - refAllowed := rv.referenceAllowed(tc.fromGK, tc.fromNamespace, tc.toGK, tc.toNamespace, tc.toName) - - require.Equal(t, tc.refAllowed, refAllowed) - }) - } -} diff --git a/control-plane/api-gateway/binding/registration.go b/control-plane/api-gateway/binding/registration.go deleted file mode 100644 index ae26ab51f6..0000000000 --- a/control-plane/api-gateway/binding/registration.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package binding - -import ( - "fmt" - - "github.com/hashicorp/consul-k8s/control-plane/connect-inject/common" - "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" - "github.com/hashicorp/consul/api" - corev1 "k8s.io/api/core/v1" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -const ( - metaKeySyntheticNode = "synthetic-node" - kubernetesSuccessReasonMsg = "Kubernetes health checks passing" - - // consulKubernetesCheckType is the type of health check in Consul for Kubernetes readiness status. - consulKubernetesCheckType = "kubernetes-readiness" - - // consulKubernetesCheckName is the name of health check in Consul for Kubernetes readiness status. - consulKubernetesCheckName = "Kubernetes Readiness Check" -) - -func registrationsForPods(namespace string, gateway gwv1beta1.Gateway, pods []corev1.Pod) []api.CatalogRegistration { - registrations := []api.CatalogRegistration{} - for _, pod := range pods { - registrations = append(registrations, registrationForPod(namespace, gateway, pod)) - } - return registrations -} - -func registrationForPod(namespace string, gateway gwv1beta1.Gateway, pod corev1.Pod) api.CatalogRegistration { - healthStatus := api.HealthCritical - if isPodReady(pod) { - healthStatus = api.HealthPassing - } - - return api.CatalogRegistration{ - Node: common.ConsulNodeNameFromK8sNode(pod.Spec.NodeName), - Address: pod.Status.HostIP, - NodeMeta: map[string]string{ - metaKeySyntheticNode: "true", - }, - Service: &api.AgentService{ - Kind: api.ServiceKindAPIGateway, - ID: pod.Name, - Service: gateway.Name, - Address: pod.Status.PodIP, - Namespace: namespace, - Meta: map[string]string{ - constants.MetaKeyPodName: pod.Name, - constants.MetaKeyKubeNS: pod.Namespace, - constants.MetaKeyKubeServiceName: gateway.Name, - "external-source": "consul-api-gateway", - }, - }, - Check: &api.AgentCheck{ - CheckID: fmt.Sprintf("%s/%s", pod.Namespace, pod.Name), - Name: consulKubernetesCheckName, - Type: consulKubernetesCheckType, - Status: healthStatus, - ServiceID: pod.Name, - Output: getHealthCheckStatusReason(healthStatus, pod.Name, pod.Namespace), - Namespace: namespace, - }, - SkipNodeUpdate: true, - } -} - -func getHealthCheckStatusReason(healthCheckStatus, podName, podNamespace string) string { - if healthCheckStatus == api.HealthPassing { - return kubernetesSuccessReasonMsg - } - - return fmt.Sprintf("Pod \"%s/%s\" is not ready", podNamespace, podName) -} - -func isPodReady(pod corev1.Pod) bool { - if corev1.PodRunning != pod.Status.Phase { - return false - } - - for _, condition := range pod.Status.Conditions { - if condition.Type == corev1.PodReady && condition.Status == corev1.ConditionTrue { - return true - } - } - return false -} diff --git a/control-plane/api-gateway/binding/registration_test.go b/control-plane/api-gateway/binding/registration_test.go deleted file mode 100644 index 356915f9f7..0000000000 --- a/control-plane/api-gateway/binding/registration_test.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package binding - -import ( - "testing" - - "github.com/hashicorp/consul/api" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -func TestRegistrationsForPods_Health(t *testing.T) { - t.Parallel() - - for name, tt := range map[string]struct { - consulNamespace string - gateway gwv1beta1.Gateway - pods []corev1.Pod - expected []string - }{ - "empty": { - consulNamespace: "", - gateway: gwv1beta1.Gateway{}, - pods: []corev1.Pod{}, - expected: []string{}, - }, - "mix": { - consulNamespace: "", - gateway: gwv1beta1.Gateway{}, - pods: []corev1.Pod{ - // Pods without a running status - {Status: corev1.PodStatus{Phase: corev1.PodFailed}}, - {Status: corev1.PodStatus{Phase: corev1.PodPending}}, - {Status: corev1.PodStatus{Phase: corev1.PodSucceeded}}, - {Status: corev1.PodStatus{Phase: corev1.PodUnknown}}, - // Running statuses that don't show readiness - {Status: corev1.PodStatus{Phase: corev1.PodRunning, Conditions: []corev1.PodCondition{ - {Type: corev1.PodScheduled, Status: corev1.ConditionTrue}, - }}}, - {Status: corev1.PodStatus{Phase: corev1.PodRunning, Conditions: []corev1.PodCondition{ - {Type: corev1.PodInitialized, Status: corev1.ConditionTrue}, - }}}, - {Status: corev1.PodStatus{Phase: corev1.PodRunning, Conditions: []corev1.PodCondition{ - {Type: corev1.DisruptionTarget, Status: corev1.ConditionTrue}, - }}}, - {Status: corev1.PodStatus{Phase: corev1.PodRunning, Conditions: []corev1.PodCondition{ - {Type: corev1.ContainersReady, Status: corev1.ConditionTrue}, - }}}, - // And finally, the successful check - {Status: corev1.PodStatus{Phase: corev1.PodRunning, Conditions: []corev1.PodCondition{ - {Type: corev1.PodReady, Status: corev1.ConditionTrue}, - }}}, - }, - expected: []string{ - api.HealthCritical, - api.HealthCritical, - api.HealthCritical, - api.HealthCritical, - api.HealthCritical, - api.HealthCritical, - api.HealthCritical, - api.HealthCritical, - api.HealthPassing, - }, - }, - } { - t.Run(name, func(t *testing.T) { - registrations := registrationsForPods(tt.consulNamespace, tt.gateway, tt.pods) - require.Len(t, registrations, len(tt.expected)) - - for i := range registrations { - registration := registrations[i] - expected := tt.expected[i] - - require.EqualValues(t, "Kubernetes Readiness Check", registration.Check.Name) - require.EqualValues(t, expected, registration.Check.Status) - } - }) - } -} diff --git a/control-plane/api-gateway/binding/result.go b/control-plane/api-gateway/binding/result.go deleted file mode 100644 index 0ba4a257fd..0000000000 --- a/control-plane/api-gateway/binding/result.go +++ /dev/null @@ -1,564 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package binding - -import ( - "errors" - "fmt" - "sort" - "strings" - - mapset "github.com/deckarep/golang-set" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" -) - -// override function for tests. -var timeFunc = metav1.Now - -// This is used for any error related to a lack of proper reference grant creation. -var errRefNotPermitted = errors.New("reference not permitted due to lack of ReferenceGrant") - -var ( - // Each of the below are specified in the Gateway spec under RouteConditionReason - // to the RouteConditionReason given in the spec. If a reason is overloaded and can - // be used with two different types of things (i.e. something is not found or it's not supported) - // then we distinguish those two usages with errRoute*_Usage. - errRouteNotAllowedByListeners_Namespace = errors.New("listener does not allow binding routes from the given namespace") - errRouteNotAllowedByListeners_Protocol = errors.New("listener does not support route protocol") - errRouteNoMatchingListenerHostname = errors.New("listener cannot bind route with a non-aligned hostname") - errRouteInvalidKind = errors.New("invalid backend kind") - errRouteBackendNotFound = errors.New("backend not found") - errRouteNoMatchingParent = errors.New("no matching parent") - errInvalidExternalRefType = errors.New("invalid externalref filter kind") - errExternalRefNotFound = errors.New("ref not found") -) - -// routeValidationResult holds the result of validating a route globally, in other -// words, for a particular backend reference without consideration to its particular -// gateway. Unfortunately, due to the fact that the spec requires a route status be -// associated with a parent reference, what it means is that anything that is global -// in nature, like this status will need to be duplicated for every parent reference -// on a given route status. -type routeValidationResult struct { - namespace string - backend gwv1beta1.BackendRef - err error -} - -// Type is used for error printing a backend reference type that we don't support on -// a validation error. -func (v routeValidationResult) Type() string { - return (&metav1.GroupKind{ - Group: common.ValueOr(v.backend.Group, ""), - Kind: common.ValueOr(v.backend.Kind, common.KindService), - }).String() -} - -// String is the namespace/name of the reference that has an error. -func (v routeValidationResult) String() string { - return (types.NamespacedName{Namespace: v.namespace, Name: string(v.backend.Name)}).String() -} - -// routeValidationResults contains a list of validation results for the backend references -// on a route. -type routeValidationResults []routeValidationResult - -// Condition returns the ResolvedRefs condition that gets duplicated across every relevant -// parent on a route's status. -func (e routeValidationResults) Condition() metav1.Condition { - // we only use the first error due to the way the spec is structured - // where you can only have a single condition - for _, v := range e { - err := v.err - if err != nil { - switch err { - case errRouteInvalidKind: - return metav1.Condition{ - Type: "ResolvedRefs", - Status: metav1.ConditionFalse, - Reason: "InvalidKind", - Message: fmt.Sprintf("%s [%s]: %s", v.String(), v.Type(), err.Error()), - } - case errRouteBackendNotFound: - return metav1.Condition{ - Type: "ResolvedRefs", - Status: metav1.ConditionFalse, - Reason: "BackendNotFound", - Message: fmt.Sprintf("%s: %s", v.String(), err.Error()), - } - case errRefNotPermitted: - return metav1.Condition{ - Type: "ResolvedRefs", - Status: metav1.ConditionFalse, - Reason: "RefNotPermitted", - Message: fmt.Sprintf("%s: %s", v.String(), err.Error()), - } - default: - // this should never happen - return metav1.Condition{ - Type: "ResolvedRefs", - Status: metav1.ConditionFalse, - Reason: "UnhandledValidationError", - Message: err.Error(), - } - } - } - } - return metav1.Condition{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - Message: "resolved backend references", - } -} - -// bindResult holds the result of attempting to bind a route to a particular gateway listener -// an error value here means that the route did not bind successfully, no error means that -// the route should be considered bound. -type bindResult struct { - section gwv1beta1.SectionName - err error -} - -// bindResults holds the results of attempting to bind a route to a gateway, having a separate -// bindResult for each listener on the gateway. -type bindResults []bindResult - -// Error constructs a human readable error for bindResults, containing any errors that a route -// had in binding to a gateway. Note that this is only used if a route failed to bind to every -// listener it attempted to bind to. -func (b bindResults) Error() string { - messages := []string{} - for _, result := range b { - if result.err != nil { - message := result.err.Error() - if result.section != "" { - message = fmt.Sprintf("%s: %s", result.section, result.err.Error()) - } - messages = append(messages, message) - } - } - - sort.Strings(messages) - return strings.Join(messages, "; ") -} - -// DidBind returns whether a route successfully bound to any listener on a gateway. -func (b bindResults) DidBind() bool { - for _, result := range b { - if result.err == nil { - return true - } - } - return false -} - -// Condition constructs an Accepted condition for a route that will be scoped -// to the particular parent reference it's using to attempt binding. -func (b bindResults) Condition() metav1.Condition { - // if we bound to any listeners, say we're accepted - if b.DidBind() { - return metav1.Condition{ - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - } - } - - // default to the most generic reason in the spec "NotAllowedByListeners" - reason := "NotAllowedByListeners" - - // if we only have a single binding error, we can get more specific - if len(b) == 1 { - for _, result := range b { - switch { - case errors.Is(result.err, errRouteNoMatchingListenerHostname): - // if we have a hostname mismatch error, then use the more specific reason - reason = "NoMatchingListenerHostname" - case errors.Is(result.err, errRefNotPermitted): - // or if we have a ref not permitted, then use that - reason = "RefNotPermitted" - case errors.Is(result.err, errRouteNoMatchingParent): - // or if the route declares a parent that we can't find - reason = "NoMatchingParent" - case errors.Is(result.err, errExternalRefNotFound): - reason = "FilterNotFound" - case errors.Is(result.err, errInvalidExternalRefType): - reason = "UnsupportedValue" - } - } - } - - return metav1.Condition{ - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: reason, - Message: b.Error(), - } -} - -// parentBindResult associates a binding result with the given parent reference. -type parentBindResult struct { - parent gwv1beta1.ParentReference - results bindResults -} - -// parentBindResults contains the list of all results that occurred when this route -// attempted to bind to a gateway using its parent references. -type parentBindResults []parentBindResult - -func (p parentBindResults) boundSections() mapset.Set { - set := mapset.NewSet() - for _, result := range p { - for _, r := range result.results { - if r.err == nil { - set.Add(string(r.section)) - } - } - } - return set -} - -var ( - // Each of the below are specified in the Gateway spec under ListenerConditionReason. - // The general usage is that each error is specified as errListener* where * corresponds - // to the ListenerConditionReason given in the spec. If a reason is overloaded and can - // be used with two different types of things (i.e. something is not found or it's not supported) - // then we distinguish those two usages with errListener*_Usage. - errListenerUnsupportedProtocol = errors.New("listener protocol is unsupported") - errListenerPortUnavailable = errors.New("listener port is unavailable") - errListenerHostnameConflict = errors.New("listener hostname conflicts with another listener") - errListenerProtocolConflict = errors.New("listener protocol conflicts with another listener") - errListenerInvalidCertificateRef_NotFound = errors.New("certificate not found") - errListenerInvalidCertificateRef_NotSupported = errors.New("certificate type is not supported") - errListenerInvalidCertificateRef_InvalidData = errors.New("certificate is invalid or does not contain a supported server name") - errListenerInvalidCertificateRef_NonFIPSRSAKeyLen = errors.New("certificate has an invalid length: RSA Keys must be at least 2048-bit") - errListenerInvalidCertificateRef_FIPSRSAKeyLen = errors.New("certificate has an invalid length: RSA keys must be either 2048-bit, 3072-bit, or 4096-bit in FIPS mode") - errListenerInvalidRouteKinds = errors.New("allowed route kind is invalid") - errListenerProgrammed_Invalid = errors.New("listener cannot be programmed because it is invalid") - - // Below is where any custom generic listener validation errors should go. - // We map anything under here to a custom ListenerConditionReason of Invalid on - // an Accepted status type. - errListenerNoTLSPassthrough = errors.New("TLS passthrough is not supported") - errListenerTLSCipherSuiteNotConfigurable = errors.New("tls_min_version does not allow tls_cipher_suites configuration") - errListenerUnsupportedTLSCipherSuite = errors.New("unsupported cipher suite in tls_cipher_suites") - errListenerUnsupportedTLSMaxVersion = errors.New("unsupported tls_max_version") - errListenerUnsupportedTLSMinVersion = errors.New("unsupported tls_min_version") - - // This custom listener validation error is used to differentiate between an errListenerPortUnavailable because of - // direct port conflicts defined by the user (two listeners on the same port) vs a port conflict because we map - // privileged ports by adding the value passed into the gatewayClassConfig. - // (i.e. one listener on 80 with a privileged port mapping of 2000, and one listener on 2080 would conflict). - errListenerMappedToPrivilegedPortMapping = errors.New("listener conflicts with privileged port mapped by GatewayClassConfig privileged port mapping setting") -) - -// listenerValidationResult contains the result of internally validating a single listener -// as well as the result of validating it in relation to all its peers (via conflictedErr). -// an error set on any of its members corresponds to an error condition on the corresponding -// status type. -type listenerValidationResult struct { - // status type: Accepted - acceptedErr error - // status type: Conflicted - conflictedErr error - // status type: ResolvedRefs - refErr error - // status type: ResolvedRefs (but with internal validation) - routeKindErr error -} - -// programmedCondition constructs the condition for the Programmed status type. -// If there are no validation errors for the listener, we mark it as programmed. -// If there are validation errors for the listener, we mark it as invalid. -func (l listenerValidationResult) programmedCondition(generation int64) metav1.Condition { - now := timeFunc() - - switch { - case l.acceptedErr != nil, l.conflictedErr != nil, l.refErr != nil, l.routeKindErr != nil: - return metav1.Condition{ - Type: "Programmed", - Status: metav1.ConditionFalse, - Reason: "Invalid", - ObservedGeneration: generation, - Message: errListenerProgrammed_Invalid.Error(), - LastTransitionTime: now, - } - default: - return metav1.Condition{ - Type: "Programmed", - Status: metav1.ConditionTrue, - Reason: "Programmed", - ObservedGeneration: generation, - Message: "listener programmed", - LastTransitionTime: now, - } - } -} - -// acceptedCondition constructs the condition for the Accepted status type. -func (l listenerValidationResult) acceptedCondition(generation int64) metav1.Condition { - now := timeFunc() - switch l.acceptedErr { - case errListenerPortUnavailable, errListenerMappedToPrivilegedPortMapping: - return metav1.Condition{ - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "PortUnavailable", - ObservedGeneration: generation, - Message: l.acceptedErr.Error(), - LastTransitionTime: now, - } - case errListenerUnsupportedProtocol: - return metav1.Condition{ - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "UnsupportedProtocol", - ObservedGeneration: generation, - Message: l.acceptedErr.Error(), - LastTransitionTime: now, - } - case nil: - return metav1.Condition{ - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - ObservedGeneration: generation, - Message: "listener accepted", - LastTransitionTime: now, - } - default: - // falback to invalid - return metav1.Condition{ - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "Invalid", - ObservedGeneration: generation, - Message: l.acceptedErr.Error(), - LastTransitionTime: now, - } - } -} - -// conflictedCondition constructs the condition for the Conflicted status type. -func (l listenerValidationResult) conflictedCondition(generation int64) metav1.Condition { - now := timeFunc() - - switch l.conflictedErr { - case errListenerProtocolConflict: - return metav1.Condition{ - Type: "Conflicted", - Status: metav1.ConditionTrue, - Reason: "ProtocolConflict", - ObservedGeneration: generation, - Message: l.conflictedErr.Error(), - LastTransitionTime: now, - } - case errListenerHostnameConflict: - return metav1.Condition{ - Type: "Conflicted", - Status: metav1.ConditionTrue, - Reason: "HostnameConflict", - ObservedGeneration: generation, - Message: l.conflictedErr.Error(), - LastTransitionTime: now, - } - default: - return metav1.Condition{ - Type: "Conflicted", - Status: metav1.ConditionFalse, - Reason: "NoConflicts", - ObservedGeneration: generation, - Message: "listener has no conflicts", - LastTransitionTime: now, - } - } -} - -// acceptedCondition constructs the condition for the ResolvedRefs status type. -func (l listenerValidationResult) resolvedRefsCondition(generation int64) metav1.Condition { - now := timeFunc() - - if l.routeKindErr != nil { - return metav1.Condition{ - Type: "ResolvedRefs", - Status: metav1.ConditionFalse, - Reason: "InvalidRouteKinds", - ObservedGeneration: generation, - Message: l.routeKindErr.Error(), - LastTransitionTime: now, - } - } - - switch l.refErr { - case errListenerInvalidCertificateRef_NotFound, errListenerInvalidCertificateRef_NotSupported, errListenerInvalidCertificateRef_InvalidData, errListenerInvalidCertificateRef_NonFIPSRSAKeyLen, errListenerInvalidCertificateRef_FIPSRSAKeyLen: - return metav1.Condition{ - Type: "ResolvedRefs", - Status: metav1.ConditionFalse, - Reason: "InvalidCertificateRef", - ObservedGeneration: generation, - Message: l.refErr.Error(), - LastTransitionTime: now, - } - case errRefNotPermitted: - return metav1.Condition{ - Type: "ResolvedRefs", - Status: metav1.ConditionFalse, - Reason: "RefNotPermitted", - ObservedGeneration: generation, - Message: l.refErr.Error(), - LastTransitionTime: now, - } - default: - return metav1.Condition{ - Type: "ResolvedRefs", - Status: metav1.ConditionTrue, - Reason: "ResolvedRefs", - ObservedGeneration: generation, - Message: "resolved certificate references", - LastTransitionTime: now, - } - } -} - -// Conditions constructs the entire set of conditions for a given gateway listener. -func (l listenerValidationResult) Conditions(generation int64) []metav1.Condition { - return []metav1.Condition{ - l.acceptedCondition(generation), - l.programmedCondition(generation), - l.conflictedCondition(generation), - l.resolvedRefsCondition(generation), - } -} - -// listenerValidationResults holds all of the results for a gateway's listeners -// the index of each result needs to correspond exactly to the index of the listener -// on the gateway spec for which it is describing. -type listenerValidationResults []listenerValidationResult - -// Invalid returns whether or not there is any listener that is not "Accepted" -// this is used in constructing a gateway's status where the Accepted status -// at the top-level can have a GatewayConditionReason of ListenersNotValid. -func (l listenerValidationResults) Invalid() bool { - for _, r := range l { - if r.acceptedErr != nil { - return true - } - } - return false -} - -// Conditions returns the listener conditions at a given index. -func (l listenerValidationResults) Conditions(generation int64, index int) []metav1.Condition { - result := l[index] - return result.Conditions(generation) -} - -var ( - // Each of the below are specified in the Gateway spec under GatewayConditionReason - // the general usage is that each error is specified as errGateway* where * corresponds - // to the GatewayConditionReason given in the spec. - errGatewayUnsupportedAddress = errors.New("gateway does not support specifying addresses") - errGatewayListenersNotValid = errors.New("one or more listeners are invalid") - errGatewayPending_Pods = errors.New("gateway pods are still being scheduled") - errGatewayPending_Consul = errors.New("gateway configuration is not yet synced to Consul") -) - -// gatewayValidationResult contains the result of internally validating a gateway. -// An error set on any of its members corresponds to an error condition on the corresponding -// status type. -type gatewayValidationResult struct { - acceptedErr error - programmedErr error -} - -// programmedCondition returns a condition for the Programmed status type. -func (l gatewayValidationResult) programmedCondition(generation int64) metav1.Condition { - now := timeFunc() - - switch l.programmedErr { - case errGatewayPending_Pods, errGatewayPending_Consul: - return metav1.Condition{ - Type: "Programmed", - Status: metav1.ConditionFalse, - Reason: "Pending", - ObservedGeneration: generation, - Message: l.programmedErr.Error(), - LastTransitionTime: now, - } - default: - return metav1.Condition{ - Type: "Programmed", - Status: metav1.ConditionTrue, - Reason: "Programmed", - ObservedGeneration: generation, - Message: "gateway programmed", - LastTransitionTime: now, - } - } -} - -// acceptedCondition returns a condition for the Accepted status type. It takes a boolean argument -// for whether or not any of the gateway's listeners are invalid, if they are, it overrides whatever -// Reason is set as an error on the result and instead uses the ListenersNotValid reason. -func (l gatewayValidationResult) acceptedCondition(generation int64, listenersInvalid bool) metav1.Condition { - now := timeFunc() - - if l.acceptedErr == nil { - if listenersInvalid { - return metav1.Condition{ - Type: "Accepted", - // should one invalid listener cause the entire gateway to become invalid? - Status: metav1.ConditionFalse, - Reason: "ListenersNotValid", - ObservedGeneration: generation, - Message: errGatewayListenersNotValid.Error(), - LastTransitionTime: now, - } - } - - return metav1.Condition{ - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - ObservedGeneration: generation, - Message: "gateway accepted", - LastTransitionTime: now, - } - } - - if l.acceptedErr == errGatewayUnsupportedAddress { - return metav1.Condition{ - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "UnsupportedAddress", - ObservedGeneration: generation, - Message: l.acceptedErr.Error(), - LastTransitionTime: now, - } - } - - // fallback to Invalid reason - return metav1.Condition{ - Type: "Accepted", - Status: metav1.ConditionFalse, - Reason: "Invalid", - ObservedGeneration: generation, - Message: l.acceptedErr.Error(), - LastTransitionTime: now, - } -} - -// Conditions constructs the gateway conditions given whether its listeners are valid. -func (l gatewayValidationResult) Conditions(generation int64, listenersInvalid bool) []metav1.Condition { - return []metav1.Condition{ - l.acceptedCondition(generation, listenersInvalid), - l.programmedCondition(generation), - } -} diff --git a/control-plane/api-gateway/binding/result_test.go b/control-plane/api-gateway/binding/result_test.go deleted file mode 100644 index 6989bb09ab..0000000000 --- a/control-plane/api-gateway/binding/result_test.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package binding - -import ( - "errors" - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func TestBindResults_Condition(t *testing.T) { - testCases := []struct { - Name string - Results bindResults - Expected metav1.Condition - }{ - { - Name: "route successfully bound", - Results: bindResults{{section: "", err: nil}}, - Expected: metav1.Condition{Type: "Accepted", Status: "True", Reason: "Accepted", Message: "route accepted"}, - }, - { - Name: "multiple bind results", - Results: bindResults{ - {section: "abc", err: errRouteNoMatchingListenerHostname}, - {section: "def", err: errRouteNoMatchingParent}, - }, - Expected: metav1.Condition{Type: "Accepted", Status: "False", Reason: "NotAllowedByListeners", Message: "abc: listener cannot bind route with a non-aligned hostname; def: no matching parent"}, - }, - { - Name: "no matching listener hostname error", - Results: bindResults{{section: "abc", err: errRouteNoMatchingListenerHostname}}, - Expected: metav1.Condition{Type: "Accepted", Status: "False", Reason: "NoMatchingListenerHostname", Message: "abc: listener cannot bind route with a non-aligned hostname"}, - }, - { - Name: "ref not permitted error", - Results: bindResults{{section: "abc", err: errRefNotPermitted}}, - Expected: metav1.Condition{Type: "Accepted", Status: "False", Reason: "RefNotPermitted", Message: "abc: reference not permitted due to lack of ReferenceGrant"}, - }, - { - Name: "no matching parent error", - Results: bindResults{{section: "hello1", err: errRouteNoMatchingParent}}, - Expected: metav1.Condition{Type: "Accepted", Status: "False", Reason: "NoMatchingParent", Message: "hello1: no matching parent"}, - }, - { - Name: "bind result without section name", - Results: bindResults{{section: "", err: errRouteNoMatchingParent}}, - Expected: metav1.Condition{Type: "Accepted", Status: "False", Reason: "NoMatchingParent", Message: "no matching parent"}, - }, - { - Name: "unhandled error type", - Results: bindResults{{section: "abc", err: errors.New("you don't know me")}}, - Expected: metav1.Condition{Type: "Accepted", Status: "False", Reason: "NotAllowedByListeners", Message: "abc: you don't know me"}, - }, - } - - for _, tc := range testCases { - t.Run(fmt.Sprintf("%s_%s", t.Name(), tc.Name), func(t *testing.T) { - actual := tc.Results.Condition() - assert.Equalf(t, tc.Expected.Type, actual.Type, "expected condition with type %q but got %q", tc.Expected.Type, actual.Type) - assert.Equalf(t, tc.Expected.Status, actual.Status, "expected condition with status %q but got %q", tc.Expected.Status, actual.Status) - assert.Equalf(t, tc.Expected.Reason, actual.Reason, "expected condition with reason %q but got %q", tc.Expected.Reason, actual.Reason) - assert.Equalf(t, tc.Expected.Message, actual.Message, "expected condition with message %q but got %q", tc.Expected.Message, actual.Message) - }) - } -} diff --git a/control-plane/api-gateway/binding/route_binding.go b/control-plane/api-gateway/binding/route_binding.go deleted file mode 100644 index 75a70c2974..0000000000 --- a/control-plane/api-gateway/binding/route_binding.go +++ /dev/null @@ -1,513 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package binding - -import ( - mapset "github.com/deckarep/golang-set" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul/api" -) - -// bindRoute contains the main logic for binding a route to a given gateway. -func (r *Binder) bindRoute(route client.Object, boundCount map[gwv1beta1.SectionName]int, snapshot *Snapshot) { - // use the non-normalized key since we can't write back enterprise metadata - // on non-enterprise installations - routeConsulKey := r.config.Translator.NonNormalizedConfigEntryReference(entryKind(route), client.ObjectKeyFromObject(route)) - filteredParents := filterParentRefs(r.key, route.GetNamespace(), getRouteParents(route)) - filteredParentStatuses := filterParentRefs(r.key, route.GetNamespace(), - common.ConvertSliceFunc(getRouteParentsStatus(route), func(parentStatus gwv1beta1.RouteParentStatus) gwv1beta1.ParentReference { - return parentStatus.ParentRef - }), - ) - - // flags to mark that some operation needs to occur - kubernetesNeedsUpdate := false - kubernetesNeedsStatusUpdate := false - - // we do this in a closure at the end to make sure we don't accidentally - // add something multiple times into the list of update/delete operations - // instead we just set a flag indicating that an update is needed and then - // append to the snapshot right before returning - defer func() { - if kubernetesNeedsUpdate { - snapshot.Kubernetes.Updates.Add(route) - } - if kubernetesNeedsStatusUpdate { - snapshot.Kubernetes.StatusUpdates.Add(route) - } - }() - - if isDeleted(route) { - // mark the route as needing to get cleaned up if we detect that it's being deleted - if common.RemoveFinalizer(route) { - kubernetesNeedsUpdate = true - } - return - } - - if r.isGatewayDeleted() { - if canGCOnUnbind(routeConsulKey, r.config.Resources) && common.RemoveFinalizer(route) { - kubernetesNeedsUpdate = true - } else { - // Remove the condition since we no longer know if we should - // control the route and drop any references for the Consul route. - // This only gets run if we can't GC the route at the end of this - // loop. - r.dropConsulRouteParent(snapshot, route, r.nonNormalizedConsulKey, r.config.Resources) - } - - // drop the status conditions - if r.statusSetter.removeRouteReferences(route, filteredParentStatuses) { - kubernetesNeedsStatusUpdate = true - } - if r.statusSetter.removeRouteReferences(route, filteredParents) { - kubernetesNeedsStatusUpdate = true - } - return - } - - if common.EnsureFinalizer(route) { - kubernetesNeedsUpdate = true - return - } - - validation := validateRefs(route, getRouteBackends(route), r.config.Resources) - // the spec is dumb and makes you set a parent for any status, even when the - // status is not with respect to a parent, as is the case of resolved refs - // so we need to set the status on all parents - for _, parent := range filteredParents { - if r.statusSetter.setRouteCondition(route, &parent, validation.Condition()) { - kubernetesNeedsStatusUpdate = true - } - } - // if we're orphaned from this gateway we'll - // always need a status update. - if len(filteredParents) == 0 { - // we already checked that these refs existed, so no need to check - // the return value here. - _ = r.statusSetter.removeRouteReferences(route, filteredParentStatuses) - kubernetesNeedsStatusUpdate = true - } - - namespace := r.config.Namespaces[route.GetNamespace()] - groupKind := route.GetObjectKind().GroupVersionKind().GroupKind() - - var results parentBindResults - - for _, ref := range filteredParents { - var result bindResults - - listeners := listenersFor(&r.config.Gateway, ref.SectionName) - - // If there are no matching listeners, then we failed to find the parent - if len(listeners) == 0 { - var sectionName gwv1beta1.SectionName - if ref.SectionName != nil { - sectionName = *ref.SectionName - } - - result = append(result, bindResult{ - section: sectionName, - err: errRouteNoMatchingParent, - }) - } - - for _, listener := range listeners { - if !routeKindIsAllowedForListener(supportedKindsForProtocol[listener.Protocol], groupKind) { - result = append(result, bindResult{ - section: listener.Name, - err: errRouteNotAllowedByListeners_Protocol, - }) - continue - } - - if !routeKindIsAllowedForListenerExplicit(listener.AllowedRoutes, groupKind) { - result = append(result, bindResult{ - section: listener.Name, - err: errRouteNotAllowedByListeners_Protocol, - }) - continue - } - - if !routeAllowedForListenerNamespaces(r.config.Gateway.Namespace, listener.AllowedRoutes, namespace) { - result = append(result, bindResult{ - section: listener.Name, - err: errRouteNotAllowedByListeners_Namespace, - }) - continue - } - - if !routeAllowedForListenerHostname(listener.Hostname, getRouteHostnames(route)) { - result = append(result, bindResult{ - section: listener.Name, - err: errRouteNoMatchingListenerHostname, - }) - continue - } - - result = append(result, bindResult{ - section: listener.Name, - }) - - boundCount[listener.Name]++ - } - - results = append(results, parentBindResult{ - parent: ref, - results: result, - }) - - httproute, ok := route.(*gwv1beta1.HTTPRoute) - if ok { - if !externalRefsOnRouteAllExist(httproute, r.config.Resources) { - results = append(results, parentBindResult{ - parent: ref, - results: []bindResult{ - { - err: errExternalRefNotFound, - }, - }, - }) - } - - if !externalRefsKindAllowedOnRoute(httproute) { - results = append(results, parentBindResult{ - parent: ref, - results: []bindResult{ - { - err: errInvalidExternalRefType, - }, - }, - }) - } - } - } - - updated := false - for _, result := range results { - if r.statusSetter.setRouteCondition(route, &result.parent, result.results.Condition()) { - updated = true - } - } - - if updated { - kubernetesNeedsStatusUpdate = true - } - - r.mutateRouteWithBindingResults(snapshot, route, r.nonNormalizedConsulKey, r.config.Resources, results) -} - -// filterParentRefs returns the subset of parent references on a route that point to the given gateway. -func filterParentRefs(gateway types.NamespacedName, namespace string, refs []gwv1beta1.ParentReference) []gwv1beta1.ParentReference { - references := []gwv1beta1.ParentReference{} - for _, ref := range refs { - if common.NilOrEqual(ref.Group, common.BetaGroup) && - common.NilOrEqual(ref.Kind, common.KindGateway) && - gateway.Namespace == common.ValueOr(ref.Namespace, namespace) && - gateway.Name == string(ref.Name) { - references = append(references, ref) - } - } - - return references -} - -// listenersFor returns the listeners corresponding to the given section name. If the section -// name is actually specified, the returned set will only contain the named listener. If it is -// unspecified, then all gateway listeners will be returned. -func listenersFor(gateway *gwv1beta1.Gateway, name *gwv1beta1.SectionName) []gwv1beta1.Listener { - listeners := []gwv1beta1.Listener{} - for _, listener := range gateway.Spec.Listeners { - if name == nil { - listeners = append(listeners, listener) - continue - } - if listener.Name == *name { - listeners = append(listeners, listener) - } - } - return listeners -} - -func consulParentMatches(namespace string, gatewayKey api.ResourceReference, parent api.ResourceReference) bool { - gatewayKey = common.NormalizeMeta(gatewayKey) - - if parent.Namespace == "" { - parent.Namespace = namespace - } - if parent.Kind == "" { - parent.Kind = api.APIGateway - } - - parent = common.NormalizeMeta(parent) - - return parent.Kind == api.APIGateway && - parent.Name == gatewayKey.Name && - parent.Namespace == gatewayKey.Namespace && - parent.Partition == gatewayKey.Partition -} - -func (r *Binder) dropConsulRouteParent(snapshot *Snapshot, object client.Object, gateway api.ResourceReference, resources *common.ResourceMap) { - switch object.(type) { - case *gwv1beta1.HTTPRoute: - resources.MutateHTTPRoute(client.ObjectKeyFromObject(object), r.handleRouteSyncStatus(snapshot, object), func(entry api.HTTPRouteConfigEntry) api.HTTPRouteConfigEntry { - entry.Parents = common.Filter(entry.Parents, func(parent api.ResourceReference) bool { - return consulParentMatches(entry.Namespace, gateway, parent) - }) - return entry - }) - case *gwv1alpha2.TCPRoute: - resources.MutateTCPRoute(client.ObjectKeyFromObject(object), r.handleRouteSyncStatus(snapshot, object), func(entry api.TCPRouteConfigEntry) api.TCPRouteConfigEntry { - entry.Parents = common.Filter(entry.Parents, func(parent api.ResourceReference) bool { - return consulParentMatches(entry.Namespace, gateway, parent) - }) - return entry - }) - } -} - -func (r *Binder) mutateRouteWithBindingResults(snapshot *Snapshot, object client.Object, gatewayConsulKey api.ResourceReference, resources *common.ResourceMap, results parentBindResults) { - if results.boundSections().Cardinality() == 0 { - r.dropConsulRouteParent(snapshot, object, r.nonNormalizedConsulKey, r.config.Resources) - return - } - - key := client.ObjectKeyFromObject(object) - - parents := mapset.NewSet() - // the normalized set keeps us from accidentally adding the same thing - // twice due to the Consul server normalizing our refs. - normalized := make(map[api.ResourceReference]api.ResourceReference) - for section := range results.boundSections().Iter() { - ref := api.ResourceReference{ - Kind: api.APIGateway, - Name: gatewayConsulKey.Name, - SectionName: section.(string), - Namespace: gatewayConsulKey.Namespace, - Partition: gatewayConsulKey.Partition, - } - parents.Add(ref) - normalized[common.NormalizeMeta(ref)] = ref - } - - switch object.(type) { - case *gwv1beta1.HTTPRoute: - resources.TranslateAndMutateHTTPRoute(key, r.handleRouteSyncStatus(snapshot, object), func(old *api.HTTPRouteConfigEntry, new api.HTTPRouteConfigEntry) api.HTTPRouteConfigEntry { - if old != nil { - for _, parent := range old.Parents { - // drop any references that already exist - if parents.Contains(parent) { - parents.Remove(parent) - } - if id, ok := normalized[parent]; ok { - parents.Remove(id) - } - } - - // set the old parent states - new.Parents = old.Parents - new.Status = old.Status - } - // and now add what is left - for parent := range parents.Iter() { - new.Parents = append(new.Parents, parent.(api.ResourceReference)) - } - - return new - }) - case *gwv1alpha2.TCPRoute: - resources.TranslateAndMutateTCPRoute(key, r.handleRouteSyncStatus(snapshot, object), func(old *api.TCPRouteConfigEntry, new api.TCPRouteConfigEntry) api.TCPRouteConfigEntry { - if old != nil { - for _, parent := range old.Parents { - // drop any references that already exist - if parents.Contains(parent) { - parents.Remove(parent) - } - } - - // set the old parent states - new.Parents = old.Parents - new.Status = old.Status - } - // and now add what is left - for parent := range parents.Iter() { - new.Parents = append(new.Parents, parent.(api.ResourceReference)) - } - return new - }) - } -} - -func entryKind(object client.Object) string { - switch object.(type) { - case *gwv1beta1.HTTPRoute: - return api.HTTPRoute - case *gwv1alpha2.TCPRoute: - return api.TCPRoute - } - return "" -} - -func canGCOnUnbind(id api.ResourceReference, resources *common.ResourceMap) bool { - switch id.Kind { - case api.HTTPRoute: - return resources.CanGCHTTPRouteOnUnbind(id) - case api.TCPRoute: - return resources.CanGCTCPRouteOnUnbind(id) - } - return true -} - -func getRouteHostnames(object client.Object) []gwv1beta1.Hostname { - switch v := object.(type) { - case *gwv1beta1.HTTPRoute: - return v.Spec.Hostnames - } - return nil -} - -func getRouteParents(object client.Object) []gwv1beta1.ParentReference { - switch v := object.(type) { - case *gwv1beta1.HTTPRoute: - return v.Spec.ParentRefs - case *gwv1alpha2.TCPRoute: - return v.Spec.ParentRefs - } - return nil -} - -func getRouteParentsStatus(object client.Object) []gwv1beta1.RouteParentStatus { - switch v := object.(type) { - case *gwv1beta1.HTTPRoute: - return v.Status.RouteStatus.Parents - case *gwv1alpha2.TCPRoute: - return v.Status.RouteStatus.Parents - } - return nil -} - -func setRouteParentsStatus(object client.Object, parents []gwv1beta1.RouteParentStatus) { - switch v := object.(type) { - case *gwv1beta1.HTTPRoute: - v.Status.RouteStatus.Parents = parents - case *gwv1alpha2.TCPRoute: - v.Status.RouteStatus.Parents = parents - } -} - -func getRouteBackends(object client.Object) []gwv1beta1.BackendRef { - switch v := object.(type) { - case *gwv1beta1.HTTPRoute: - return common.Flatten(common.ConvertSliceFunc(v.Spec.Rules, func(rule gwv1beta1.HTTPRouteRule) []gwv1beta1.BackendRef { - return common.ConvertSliceFunc(rule.BackendRefs, func(rule gwv1beta1.HTTPBackendRef) gwv1beta1.BackendRef { - return rule.BackendRef - }) - })) - case *gwv1alpha2.TCPRoute: - return common.Flatten(common.ConvertSliceFunc(v.Spec.Rules, func(rule gwv1alpha2.TCPRouteRule) []gwv1beta1.BackendRef { - return rule.BackendRefs - })) - } - return nil -} - -func canReferenceBackend(object client.Object, ref gwv1beta1.BackendRef, resources *common.ResourceMap) bool { - switch v := object.(type) { - case *gwv1beta1.HTTPRoute: - return resources.HTTPRouteCanReferenceBackend(*v, ref) - case *gwv1alpha2.TCPRoute: - return resources.TCPRouteCanReferenceBackend(*v, ref) - } - return false -} - -func (r *Binder) handleRouteSyncStatus(snapshot *Snapshot, object client.Object) func(error, api.ConfigEntryStatus) { - return func(err error, status api.ConfigEntryStatus) { - condition := metav1.Condition{ - Type: "Synced", - Status: metav1.ConditionTrue, - ObservedGeneration: object.GetGeneration(), - LastTransitionTime: timeFunc(), - Reason: "Synced", - Message: "route synced to Consul", - } - if err != nil { - condition = metav1.Condition{ - Type: "Synced", - Status: metav1.ConditionFalse, - ObservedGeneration: object.GetGeneration(), - LastTransitionTime: timeFunc(), - Reason: "SyncError", - Message: err.Error(), - } - } - if r.statusSetter.setRouteConditionOnAllRefs(object, condition) { - snapshot.Kubernetes.StatusUpdates.Add(object) - } - if consulCondition := consulCondition(object.GetGeneration(), status); consulCondition != nil { - if r.statusSetter.setRouteConditionOnAllRefs(object, *consulCondition) { - snapshot.Kubernetes.StatusUpdates.Add(object) - } - } - } -} - -func (r *Binder) handleGatewaySyncStatus(snapshot *Snapshot, gateway *gwv1beta1.Gateway, status api.ConfigEntryStatus) func(error) { - return func(err error) { - condition := metav1.Condition{ - Type: "Synced", - Status: metav1.ConditionTrue, - ObservedGeneration: gateway.Generation, - LastTransitionTime: timeFunc(), - Reason: "Synced", - Message: "gateway synced to Consul", - } - if err != nil { - condition = metav1.Condition{ - Type: "Synced", - Status: metav1.ConditionFalse, - ObservedGeneration: gateway.Generation, - LastTransitionTime: timeFunc(), - Reason: "SyncError", - Message: err.Error(), - } - } - - if conditions, updated := setCondition(gateway.Status.Conditions, condition); updated { - gateway.Status.Conditions = conditions - snapshot.Kubernetes.StatusUpdates.Add(gateway) - } - - if consulCondition := consulCondition(gateway.Generation, status); consulCondition != nil { - if conditions, updated := setCondition(gateway.Status.Conditions, *consulCondition); updated { - gateway.Status.Conditions = conditions - snapshot.Kubernetes.StatusUpdates.Add(gateway) - } - } - } -} - -func consulCondition(generation int64, status api.ConfigEntryStatus) *metav1.Condition { - for _, c := range status.Conditions { - // we only care about the top-level status that isn't in reference - // to a resource. - if c.Type == "Accepted" && (c.Resource == nil || c.Resource.Name == "") { - return &metav1.Condition{ - Type: "ConsulAccepted", - Reason: c.Reason, - Status: metav1.ConditionStatus(c.Status), - Message: c.Message, - ObservedGeneration: generation, - LastTransitionTime: timeFunc(), - } - } - } - return nil -} diff --git a/control-plane/api-gateway/binding/setter.go b/control-plane/api-gateway/binding/setter.go deleted file mode 100644 index 5b3a9096d6..0000000000 --- a/control-plane/api-gateway/binding/setter.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package binding - -import ( - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -// setter wraps the status setting logic for routes. -type setter struct { - controllerName string -} - -// newSetter constructs a status setter with the given controller name. -func newSetter(controllerName string) *setter { - return &setter{controllerName: controllerName} -} - -// removeRouteReferences removes the given parent reference sections from a routes's status. -func (s *setter) removeRouteReferences(route client.Object, refs []gwv1beta1.ParentReference) bool { - modified := false - for _, parent := range refs { - parents, removed := s.removeParentStatus(getRouteParentsStatus(route), parent) - setRouteParentsStatus(route, parents) - if removed { - modified = true - } - } - return modified -} - -// setRouteCondition sets an route condition on its status with the given parent. -func (s *setter) setRouteCondition(route client.Object, parent *gwv1beta1.ParentReference, condition metav1.Condition) bool { - condition.LastTransitionTime = timeFunc() - condition.ObservedGeneration = route.GetGeneration() - - parents := getRouteParentsStatus(route) - status := s.getParentStatus(parents, parent) - conditions, modified := setCondition(status.Conditions, condition) - if modified { - status.Conditions = conditions - setRouteParentsStatus(route, s.setParentStatus(parents, status)) - } - return modified -} - -// setRouteConditionOnAllRefs sets an route condition and its status on all parents. -func (s *setter) setRouteConditionOnAllRefs(route client.Object, condition metav1.Condition) bool { - condition.LastTransitionTime = timeFunc() - condition.ObservedGeneration = route.GetGeneration() - - parents := getRouteParentsStatus(route) - statuses := common.Filter(getRouteParentsStatus(route), func(status gwv1beta1.RouteParentStatus) bool { - return string(status.ControllerName) != s.controllerName - }) - - updated := false - for _, status := range statuses { - conditions, modified := setCondition(status.Conditions, condition) - if modified { - updated = true - status.Conditions = conditions - setRouteParentsStatus(route, s.setParentStatus(parents, status)) - } - } - return updated -} - -// getParentStatus returns the section of a status referenced by the given parent reference. -func (s *setter) getParentStatus(statuses []gwv1beta1.RouteParentStatus, parent *gwv1beta1.ParentReference) gwv1beta1.RouteParentStatus { - var parentRef gwv1beta1.ParentReference - if parent != nil { - parentRef = *parent - } - - for _, status := range statuses { - if common.ParentsEqual(status.ParentRef, parentRef) && string(status.ControllerName) == s.controllerName { - return status - } - } - return gwv1beta1.RouteParentStatus{ - ParentRef: parentRef, - ControllerName: gwv1beta1.GatewayController(s.controllerName), - } -} - -// removeParentStatus removes the section of a status referenced by the given parent reference. -func (s *setter) removeParentStatus(statuses []gwv1beta1.RouteParentStatus, parent gwv1beta1.ParentReference) ([]gwv1beta1.RouteParentStatus, bool) { - found := false - filtered := []gwv1beta1.RouteParentStatus{} - for _, status := range statuses { - if common.ParentsEqual(status.ParentRef, parent) && string(status.ControllerName) == s.controllerName { - found = true - continue - } - filtered = append(filtered, status) - } - return filtered, found -} - -// setCondition overrides or appends a condition to the list of conditions, returning if a modification -// to the condition set was made or not. Modifications only occur if a field other than the observation -// timestamp is modified. -func setCondition(conditions []metav1.Condition, condition metav1.Condition) ([]metav1.Condition, bool) { - for i, existing := range conditions { - if existing.Type == condition.Type { - // no-op if we have the exact same thing - if condition.Reason == existing.Reason && condition.Message == existing.Message && condition.ObservedGeneration == existing.ObservedGeneration { - return conditions, false - } - - conditions[i] = condition - return conditions, true - } - } - return append(conditions, condition), true -} - -// setParentStatus updates or inserts the set of parent statuses with the newly modified parent. -func (s *setter) setParentStatus(statuses []gwv1beta1.RouteParentStatus, parent gwv1beta1.RouteParentStatus) []gwv1beta1.RouteParentStatus { - for i, status := range statuses { - if common.ParentsEqual(status.ParentRef, parent.ParentRef) && status.ControllerName == parent.ControllerName { - statuses[i] = parent - return statuses - } - } - return append(statuses, parent) -} diff --git a/control-plane/api-gateway/binding/setter_test.go b/control-plane/api-gateway/binding/setter_test.go deleted file mode 100644 index 84d3ecc7d5..0000000000 --- a/control-plane/api-gateway/binding/setter_test.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package binding - -import ( - "testing" - - "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -func TestSetter(t *testing.T) { - setter := newSetter("test") - parentRef := gwv1beta1.ParentReference{ - Name: "test", - } - parentRefDup := gwv1beta1.ParentReference{ - Name: "test", - } - condition := metav1.Condition{ - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: "Accepted", - Message: "route accepted", - } - route := &gwv1beta1.HTTPRoute{ - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{parentRef}, - }, - }, - } - require.True(t, setter.setRouteCondition(route, &parentRef, condition)) - require.False(t, setter.setRouteCondition(route, &parentRefDup, condition)) - require.False(t, setter.setRouteCondition(route, &parentRefDup, condition)) - require.False(t, setter.setRouteCondition(route, &parentRefDup, condition)) - - require.Len(t, route.Status.Parents, 1) - require.Len(t, route.Status.Parents[0].Conditions, 1) -} diff --git a/control-plane/api-gateway/binding/snapshot.go b/control-plane/api-gateway/binding/snapshot.go deleted file mode 100644 index 18888a6d46..0000000000 --- a/control-plane/api-gateway/binding/snapshot.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package binding - -import ( - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul/api" -) - -// KubernetesSnapshot contains all the operations -// required in Kubernetes to complete reconciliation. -type KubernetesSnapshot struct { - // Updates is the list of objects that need to have - // aspects of their metadata or spec updated in Kubernetes - // (i.e. for finalizers or annotations) - Updates *common.KubernetesUpdates - // StatusUpdates is the list of objects that need - // to have their statuses updated in Kubernetes - StatusUpdates *common.KubernetesUpdates -} - -// ConsulSnapshot contains all the operations required -// in Consul to complete reconciliation. -type ConsulSnapshot struct { - // Updates is the list of ConfigEntry objects that should - // either be updated or created in Consul - Updates []*common.ConsulUpdateOperation - // Deletions is a list of references that ought to be - // deleted in Consul - Deletions []api.ResourceReference - // Registrations is a list of Consul services to make sure - // are registered in Consul - Registrations []api.CatalogRegistration - // Deregistrations is a list of Consul services to make sure - // are no longer registered in Consul - Deregistrations []api.CatalogDeregistration -} - -// Snapshot contains all Kubernetes and Consul operations -// needed to complete reconciliation. -type Snapshot struct { - // Kubernetes holds the snapshot of required Kubernetes operations - Kubernetes *KubernetesSnapshot - // Consul holds the snapshot of required Consul operations - Consul *ConsulSnapshot - // GatewayClassConfig is the configuration to use for determining - // a Gateway deployment, if it is not set, a deployment should be - // deleted instead of updated - GatewayClassConfig *v1alpha1.GatewayClassConfig - - // UpsertGatewayDeployment determines whether the gateway deployment - // objects should be updated, i.e. deployments, roles, services - UpsertGatewayDeployment bool -} - -func NewSnapshot() *Snapshot { - return &Snapshot{ - Kubernetes: &KubernetesSnapshot{ - Updates: common.NewKubernetesUpdates(), - StatusUpdates: common.NewKubernetesUpdates(), - }, - Consul: &ConsulSnapshot{}, - } -} diff --git a/control-plane/api-gateway/binding/validation.go b/control-plane/api-gateway/binding/validation.go deleted file mode 100644 index a2726b8bb1..0000000000 --- a/control-plane/api-gateway/binding/validation.go +++ /dev/null @@ -1,559 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package binding - -import ( - "strings" - - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - klabels "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul-k8s/control-plane/version" - "github.com/hashicorp/consul/api" -) - -var ( - // the list of kinds we can support by listener protocol. - supportedKindsForProtocol = map[gwv1beta1.ProtocolType][]gwv1beta1.RouteGroupKind{ - gwv1beta1.HTTPProtocolType: {{ - Group: (*gwv1beta1.Group)(&gwv1beta1.GroupVersion.Group), - Kind: "HTTPRoute", - }}, - gwv1beta1.HTTPSProtocolType: {{ - Group: (*gwv1beta1.Group)(&gwv1beta1.GroupVersion.Group), - Kind: "HTTPRoute", - }}, - gwv1beta1.TCPProtocolType: {{ - Group: (*gwv1alpha2.Group)(&gwv1alpha2.GroupVersion.Group), - Kind: "TCPRoute", - }}, - } - allSupportedRouteKinds = map[gwv1beta1.Kind]struct{}{ - gwv1beta1.Kind("HTTPRoute"): {}, - gwv1beta1.Kind("TCPRoute"): {}, - } - - allSupportedTLSVersions = map[string]struct{}{ - "TLS_AUTO": {}, - "TLSv1_0": {}, - "TLSv1_1": {}, - "TLSv1_2": {}, - "TLSv1_3": {}, - } - - allTLSVersionsWithConfigurableCipherSuites = map[string]struct{}{ - // Remove "" and "TLS_AUTO" if Envoy ever sets TLS 1.3 as default minimum - "": {}, - "TLS_AUTO": {}, - "TLSv1_0": {}, - "TLSv1_1": {}, - "TLSv1_2": {}, - } - - allSupportedTLSCipherSuites = map[string]struct{}{ - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": {}, - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256": {}, - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": {}, - "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256": {}, - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": {}, - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": {}, - - // NOTE: the following cipher suites are currently supported by Envoy - // but have been identified as insecure and are pending removal - // https://github.com/envoyproxy/envoy/issues/5399 - "TLS_RSA_WITH_AES_128_GCM_SHA256": {}, - "TLS_RSA_WITH_AES_128_CBC_SHA": {}, - "TLS_RSA_WITH_AES_256_GCM_SHA384": {}, - "TLS_RSA_WITH_AES_256_CBC_SHA": {}, - // https://github.com/envoyproxy/envoy/issues/5400 - "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": {}, - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": {}, - "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": {}, - "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": {}, - } -) - -// validateRefs validates backend references for a route, determining whether or -// not they were found in the list of known connect-injected services. -func validateRefs(route client.Object, refs []gwv1beta1.BackendRef, resources *common.ResourceMap) routeValidationResults { - namespace := route.GetNamespace() - - var result routeValidationResults - for _, ref := range refs { - backendRef := ref.BackendObjectReference - - nsn := types.NamespacedName{ - Name: string(backendRef.Name), - Namespace: common.ValueOr(backendRef.Namespace, namespace), - } - - isServiceRef := common.NilOrEqual(backendRef.Group, "") && common.NilOrEqual(backendRef.Kind, common.KindService) - isMeshServiceRef := common.DerefEqual(backendRef.Group, v1alpha1.ConsulHashicorpGroup) && common.DerefEqual(backendRef.Kind, v1alpha1.MeshServiceKind) - - if !isServiceRef && !isMeshServiceRef { - result = append(result, routeValidationResult{ - namespace: nsn.Namespace, - backend: ref, - err: errRouteInvalidKind, - }) - continue - } - - if isServiceRef && !resources.HasService(nsn) { - result = append(result, routeValidationResult{ - namespace: nsn.Namespace, - backend: ref, - err: errRouteBackendNotFound, - }) - continue - } - - if isMeshServiceRef && !resources.HasMeshService(nsn) { - result = append(result, routeValidationResult{ - namespace: nsn.Namespace, - backend: ref, - err: errRouteBackendNotFound, - }) - continue - } - - if !canReferenceBackend(route, ref, resources) { - result = append(result, routeValidationResult{ - namespace: nsn.Namespace, - backend: ref, - err: errRefNotPermitted, - }) - continue - } - - result = append(result, routeValidationResult{ - namespace: nsn.Namespace, - backend: ref, - }) - } - return result -} - -// validateGateway validates that a gateway is semantically valid given -// the set of features that we support. -func validateGateway(gateway gwv1beta1.Gateway, pods []corev1.Pod, consulGateway *api.APIGatewayConfigEntry) gatewayValidationResult { - var result gatewayValidationResult - - if len(gateway.Spec.Addresses) > 0 { - result.acceptedErr = errGatewayUnsupportedAddress - } - - if len(pods) == 0 { - result.programmedErr = errGatewayPending_Pods - } else if consulGateway == nil { - result.programmedErr = errGatewayPending_Consul - } - - return result -} - -// mergedListener associates a listener with its indexed position -// in the gateway spec, it's used to re-associate a status with -// a listener after we merge compatible listeners together and then -// validate their conflicts. -type mergedListener struct { - index int - listener gwv1beta1.Listener -} - -// mergedListeners is a set of a listeners that are considered "merged" -// due to referencing the same listener port. -type mergedListeners []mergedListener - -// validateProtocol validates that the protocols used across all merged -// listeners are compatible. -func (m mergedListeners) validateProtocol() error { - var protocol *gwv1beta1.ProtocolType - for _, l := range m { - if protocol == nil { - protocol = common.PointerTo(l.listener.Protocol) - } - if *protocol != l.listener.Protocol { - return errListenerProtocolConflict - } - } - return nil -} - -// validateHostname validates that the merged listeners don't use the same -// hostnames as per the spec. -func (m mergedListeners) validateHostname(index int, listener gwv1beta1.Listener) error { - for _, l := range m { - if l.index == index { - continue - } - if common.BothNilOrEqual(listener.Hostname, l.listener.Hostname) { - return errListenerHostnameConflict - } - } - return nil -} - -// validateTLS validates that the TLS configuration for a given listener is valid and that -// the certificates that it references exist. -func validateTLS(gateway gwv1beta1.Gateway, tls *gwv1beta1.GatewayTLSConfig, resources *common.ResourceMap) (error, error) { - // If there's no TLS, there's nothing to validate - if tls == nil { - return nil, nil - } - - // Validate the certificate references and then return any error - // alongside any TLS configuration error that we find below. - refsErr := validateCertificateRefs(gateway, tls.CertificateRefs, resources) - - if tls.Mode != nil && *tls.Mode == gwv1beta1.TLSModePassthrough { - return errListenerNoTLSPassthrough, refsErr - } - - if err := validateTLSOptions(tls.Options); err != nil { - return err, refsErr - } - - return nil, refsErr -} - -func validateCertificateRefs(gateway gwv1beta1.Gateway, refs []gwv1beta1.SecretObjectReference, resources *common.ResourceMap) error { - for _, cert := range refs { - // Verify that the reference has a group and kind that we support - if !common.NilOrEqual(cert.Group, "") || !common.NilOrEqual(cert.Kind, common.KindSecret) { - return errListenerInvalidCertificateRef_NotSupported - } - - // Verify that the reference is within the namespace or, - // if cross-namespace, that it's allowed by a ReferenceGrant - if !resources.GatewayCanReferenceSecret(gateway, cert) { - return errRefNotPermitted - } - - // Verify that the referenced resource actually exists - key := common.IndexedNamespacedNameWithDefault(cert.Name, cert.Namespace, gateway.Namespace) - secret := resources.Certificate(key) - if secret == nil { - return errListenerInvalidCertificateRef_NotFound - } - - // Verify that the referenced resource contains the data shape that we expect - if err := validateCertificateData(*secret); err != nil { - return err - } - } - - return nil -} - -func validateTLSOptions(options map[gwv1beta1.AnnotationKey]gwv1beta1.AnnotationValue) error { - if options == nil { - return nil - } - - tlsMinVersionValue := string(options[common.TLSMinVersionAnnotationKey]) - if tlsMinVersionValue != "" { - if _, supported := allSupportedTLSVersions[tlsMinVersionValue]; !supported { - return errListenerUnsupportedTLSMinVersion - } - } - - tlsMaxVersionValue := string(options[common.TLSMaxVersionAnnotationKey]) - if tlsMaxVersionValue != "" { - if _, supported := allSupportedTLSVersions[tlsMaxVersionValue]; !supported { - return errListenerUnsupportedTLSMaxVersion - } - } - - tlsCipherSuitesValue := string(options[common.TLSCipherSuitesAnnotationKey]) - if tlsCipherSuitesValue != "" { - // If a minimum TLS version is configured, verify that it supports configuring cipher suites - if tlsMinVersionValue != "" { - if _, supported := allTLSVersionsWithConfigurableCipherSuites[tlsMinVersionValue]; !supported { - return errListenerTLSCipherSuiteNotConfigurable - } - } - - for _, tlsCipherSuiteValue := range strings.Split(tlsCipherSuitesValue, ",") { - tlsCipherSuite := strings.TrimSpace(tlsCipherSuiteValue) - if _, supported := allSupportedTLSCipherSuites[tlsCipherSuite]; !supported { - return errListenerUnsupportedTLSCipherSuite - } - } - } - - return nil -} - -func validateCertificateData(secret corev1.Secret) error { - _, privateKey, err := common.ParseCertificateData(secret) - if err != nil { - return errListenerInvalidCertificateRef_InvalidData - } - - err = common.ValidateKeyLength(privateKey) - if err != nil { - if version.IsFIPS() { - return errListenerInvalidCertificateRef_FIPSRSAKeyLen - } - - return errListenerInvalidCertificateRef_NonFIPSRSAKeyLen - } - - return nil -} - -// validateListeners validates the given listeners both internally and with respect to each -// other for purposes of setting "Conflicted" status conditions. -func validateListeners(gateway gwv1beta1.Gateway, listeners []gwv1beta1.Listener, resources *common.ResourceMap, gwcc *v1alpha1.GatewayClassConfig) listenerValidationResults { - var results listenerValidationResults - merged := make(map[gwv1beta1.PortNumber]mergedListeners) - for i, listener := range listeners { - merged[listener.Port] = append(merged[listener.Port], mergedListener{ - index: i, - listener: listener, - }) - } - // This list keeps track of port conflicts directly on gateways. i.e., two listeners on the same port as - // defined by the user. - seenListenerPorts := map[int]struct{}{} - // This list keeps track of port conflicts caused by privileged port mappings. - seenContainerPorts := map[int]struct{}{} - portMapping := int32(0) - if gwcc != nil { - portMapping = gwcc.Spec.MapPrivilegedContainerPorts - } - for i, listener := range listeners { - var result listenerValidationResult - - err, refErr := validateTLS(gateway, listener.TLS, resources) - result.refErr = refErr - if err != nil { - result.acceptedErr = err - } else { - _, supported := supportedKindsForProtocol[listener.Protocol] - if !supported { - result.acceptedErr = errListenerUnsupportedProtocol - } else if listener.Port == 20000 { // admin port - result.acceptedErr = errListenerPortUnavailable - } else if _, ok := seenListenerPorts[int(listener.Port)]; ok { - result.acceptedErr = errListenerPortUnavailable - } else if _, ok := seenContainerPorts[common.ToContainerPort(listener.Port, portMapping)]; ok { - result.acceptedErr = errListenerMappedToPrivilegedPortMapping - } - - result.routeKindErr = validateListenerAllowedRouteKinds(listener.AllowedRoutes) - } - - if err := merged[listener.Port].validateProtocol(); err != nil { - result.conflictedErr = err - } else { - result.conflictedErr = merged[listener.Port].validateHostname(i, listener) - } - - results = append(results, result) - - seenListenerPorts[int(listener.Port)] = struct{}{} - seenContainerPorts[common.ToContainerPort(listener.Port, portMapping)] = struct{}{} - } - return results -} - -func validateListenerAllowedRouteKinds(allowedRoutes *gwv1beta1.AllowedRoutes) error { - if allowedRoutes == nil { - return nil - } - for _, kind := range allowedRoutes.Kinds { - if _, ok := allSupportedRouteKinds[kind.Kind]; !ok { - return errListenerInvalidRouteKinds - } - if !common.NilOrEqual(kind.Group, gwv1beta1.GroupVersion.Group) { - return errListenerInvalidRouteKinds - } - } - return nil -} - -// routeAllowedForListenerNamespaces determines whether the route is allowed -// to bind to the Gateway based on the AllowedRoutes namespace selectors. -func routeAllowedForListenerNamespaces(gatewayNamespace string, allowedRoutes *gwv1beta1.AllowedRoutes, namespace corev1.Namespace) bool { - var namespaceSelector *gwv1beta1.RouteNamespaces - if allowedRoutes != nil { - // check gateway namespace - namespaceSelector = allowedRoutes.Namespaces - } - - // set default if namespace selector is nil - from := gwv1beta1.NamespacesFromSame - if namespaceSelector != nil && namespaceSelector.From != nil && *namespaceSelector.From != "" { - from = *namespaceSelector.From - } - - switch from { - case gwv1beta1.NamespacesFromAll: - return true - case gwv1beta1.NamespacesFromSame: - return gatewayNamespace == namespace.Name - case gwv1beta1.NamespacesFromSelector: - namespaceSelector, err := metav1.LabelSelectorAsSelector(namespaceSelector.Selector) - if err != nil { - // log the error here, the label selector is invalid - return false - } - - return namespaceSelector.Matches(toNamespaceSet(namespace.GetName(), namespace.GetLabels())) - default: - return false - } -} - -// routeAllowedForListenerHostname checks that a hostname specified on a route and the hostname specified -// on the gateway listener are compatible. -func routeAllowedForListenerHostname(hostname *gwv1beta1.Hostname, hostnames []gwv1beta1.Hostname) bool { - if hostname == nil || len(hostnames) == 0 { - return true - } - - for _, name := range hostnames { - if hostnamesMatch(name, *hostname) { - return true - } - } - return false -} - -// externalRefsOnRouteAllExist checks to make sure that all external filters referenced by the route exist in the resource map. -func externalRefsOnRouteAllExist(route *gwv1beta1.HTTPRoute, resources *common.ResourceMap) bool { - for _, rule := range route.Spec.Rules { - for _, filter := range rule.Filters { - if filter.Type != gwv1beta1.HTTPRouteFilterExtensionRef { - continue - } - - if !resources.ExternalFilterExists(*filter.ExtensionRef, route.Namespace) { - return false - } - - } - - for _, backendRef := range rule.BackendRefs { - for _, filter := range backendRef.Filters { - if filter.Type != gwv1beta1.HTTPRouteFilterExtensionRef { - continue - } - - if !resources.ExternalFilterExists(*filter.ExtensionRef, route.Namespace) { - return false - } - } - - } - } - - return true -} - -// externalRefsKindAllowedOnRoute makes sure that all externalRefs reference a kind supported by gatewaycontroller. -func externalRefsKindAllowedOnRoute(route *gwv1beta1.HTTPRoute) bool { - for _, rule := range route.Spec.Rules { - if !filtersAllAllowedType(rule.Filters) { - return false - } - - //same thing but for backendref - for _, backendRef := range rule.BackendRefs { - if !filtersAllAllowedType(backendRef.Filters) { - return false - } - } - } - return true -} - -func filtersAllAllowedType(filters []gwv1beta1.HTTPRouteFilter) bool { - for _, filter := range filters { - if filter.ExtensionRef == nil { - continue - } - - if !common.FilterIsExternalFilter(filter) { - return false - } - } - return true -} - -// hostnameMatch checks that an individual hostname matches another hostname for -// compatibility. -func hostnamesMatch(a gwv1alpha2.Hostname, b gwv1beta1.Hostname) bool { - if a == "" || a == "*" || b == "" || b == "*" { - // any wildcard always matches - return true - } - - if strings.HasPrefix(string(a), "*.") || strings.HasPrefix(string(b), "*.") { - aLabels, bLabels := strings.Split(string(a), "."), strings.Split(string(b), ".") - if len(aLabels) != len(bLabels) { - return false - } - - for i := 1; i < len(aLabels); i++ { - if !strings.EqualFold(aLabels[i], bLabels[i]) { - return false - } - } - return true - } - - return string(a) == string(b) -} - -// routeKindIsAllowedForListener checks that the given route kind is present in the allowed set. -func routeKindIsAllowedForListener(kinds []gwv1beta1.RouteGroupKind, gk schema.GroupKind) bool { - if kinds == nil { - return true - } - - for _, kind := range kinds { - if string(kind.Kind) == gk.Kind && common.NilOrEqual(kind.Group, gk.Group) { - return true - } - } - - return false -} - -// routeKindIsAllowedForListenerExplicit checks that a route is allowed by the kinds specified explicitly -// on the listener. -func routeKindIsAllowedForListenerExplicit(allowedRoutes *gwv1alpha2.AllowedRoutes, gk schema.GroupKind) bool { - if allowedRoutes == nil { - return true - } - - return routeKindIsAllowedForListener(allowedRoutes.Kinds, gk) -} - -// toNamespaceSet constructs a list of labels used to match a Namespace. -func toNamespaceSet(name string, labels map[string]string) klabels.Labels { - // If namespace label is not set, implicitly insert it to support older Kubernetes versions - if labels[common.NamespaceNameLabel] == name { - // Already set, avoid copies - return klabels.Set(labels) - } - // First we need a copy to not modify the underlying object - ret := make(map[string]string, len(labels)+1) - for k, v := range labels { - ret[k] = v - } - ret[common.NamespaceNameLabel] = name - return klabels.Set(ret) -} diff --git a/control-plane/api-gateway/binding/validation_test.go b/control-plane/api-gateway/binding/validation_test.go deleted file mode 100644 index 1f2b143387..0000000000 --- a/control-plane/api-gateway/binding/validation_test.go +++ /dev/null @@ -1,862 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package binding - -import ( - "testing" - - logrtest "github.com/go-logr/logr/testing" - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -func TestValidateRefs(t *testing.T) { - t.Parallel() - - for name, tt := range map[string]struct { - route client.Object - services map[types.NamespacedName]corev1.Service - referenceGrants []gwv1beta1.ReferenceGrant - meshServices []v1alpha1.MeshService - expectedErrors []error - }{ - "all pass no namespaces": { - route: testHTTPRouteBackends("route", "test", []gwv1beta1.BackendObjectReference{{Name: "1"}, {Name: "2"}}, nil), - services: map[types.NamespacedName]corev1.Service{ - {Name: "1", Namespace: "test"}: {}, - {Name: "2", Namespace: "test"}: {}, - {Name: "3", Namespace: "test"}: {}, - }, - meshServices: []v1alpha1.MeshService{}, - expectedErrors: []error{nil, nil}, - }, - "all fails namespaces no reference grants": { - route: testHTTPRouteBackends("route", "test", []gwv1beta1.BackendObjectReference{ - {Name: "1", Namespace: common.PointerTo[gwv1beta1.Namespace]("other")}, - {Name: "2", Namespace: common.PointerTo[gwv1beta1.Namespace]("other")}, - }, nil), - services: map[types.NamespacedName]corev1.Service{ - {Name: "1", Namespace: "other"}: {}, - {Name: "2", Namespace: "other"}: {}, - {Name: "3", Namespace: "other"}: {}, - }, - meshServices: []v1alpha1.MeshService{}, - expectedErrors: []error{errRefNotPermitted, errRefNotPermitted}, - }, - "all pass namespaces": { - referenceGrants: []gwv1beta1.ReferenceGrant{ - {ObjectMeta: metav1.ObjectMeta{Namespace: "other", Name: "grant"}, Spec: gwv1beta1.ReferenceGrantSpec{ - From: []gwv1beta1.ReferenceGrantFrom{ - {Group: gwv1beta1.GroupName, Kind: "HTTPRoute", Namespace: gwv1beta1.Namespace("test")}, - }, - To: []gwv1beta1.ReferenceGrantTo{ - {Kind: "Service"}, - }, - }}, - }, - route: testHTTPRouteBackends("route", "test", []gwv1beta1.BackendObjectReference{ - {Name: "1", Namespace: common.PointerTo[gwv1beta1.Namespace]("other")}, - {Name: "2", Namespace: common.PointerTo[gwv1beta1.Namespace]("other")}, - }, nil), - services: map[types.NamespacedName]corev1.Service{ - {Name: "1", Namespace: "other"}: {}, - {Name: "2", Namespace: "other"}: {}, - {Name: "3", Namespace: "other"}: {}, - }, - meshServices: []v1alpha1.MeshService{}, - expectedErrors: []error{nil, nil}, - }, - "some pass mixed missing reference grants": { - route: testHTTPRouteBackends("route", "test", []gwv1beta1.BackendObjectReference{ - {Name: "1", Namespace: common.PointerTo[gwv1beta1.Namespace]("other")}, - {Name: "2"}, - }, nil), - services: map[types.NamespacedName]corev1.Service{ - {Name: "1", Namespace: "other"}: {}, - {Name: "2", Namespace: "test"}: {}, - {Name: "3", Namespace: "other"}: {}, - }, - meshServices: []v1alpha1.MeshService{}, - expectedErrors: []error{errRefNotPermitted, nil}, - }, - "all pass mixed": { - referenceGrants: []gwv1beta1.ReferenceGrant{ - {ObjectMeta: metav1.ObjectMeta{Namespace: "other", Name: "grant"}, Spec: gwv1beta1.ReferenceGrantSpec{ - From: []gwv1beta1.ReferenceGrantFrom{ - {Group: gwv1beta1.GroupName, Kind: "HTTPRoute", Namespace: gwv1beta1.Namespace("test")}, - }, - To: []gwv1beta1.ReferenceGrantTo{ - {Kind: "Service"}, - }, - }}, - }, - route: testHTTPRouteBackends("route", "test", []gwv1beta1.BackendObjectReference{ - {Name: "1", Namespace: common.PointerTo[gwv1beta1.Namespace]("other")}, - {Name: "2"}, - }, nil), - services: map[types.NamespacedName]corev1.Service{ - {Name: "1", Namespace: "other"}: {}, - {Name: "2", Namespace: "test"}: {}, - {Name: "3", Namespace: "other"}: {}, - }, - meshServices: []v1alpha1.MeshService{}, - expectedErrors: []error{nil, nil}, - }, - "all fail mixed": { - referenceGrants: []gwv1beta1.ReferenceGrant{ - {ObjectMeta: metav1.ObjectMeta{Namespace: "other", Name: "grant"}, Spec: gwv1beta1.ReferenceGrantSpec{ - From: []gwv1beta1.ReferenceGrantFrom{ - {Group: gwv1beta1.GroupName, Kind: "HTTPRoute", Namespace: gwv1beta1.Namespace("test")}, - }, - To: []gwv1beta1.ReferenceGrantTo{ - {Kind: "Service"}, - }, - }}, - }, - route: testHTTPRouteBackends("route", "test", []gwv1beta1.BackendObjectReference{ - {Name: "1"}, - {Name: "2", Namespace: common.PointerTo[gwv1beta1.Namespace]("other")}, - }, nil), - services: map[types.NamespacedName]corev1.Service{ - {Name: "1", Namespace: "other"}: {}, - {Name: "2", Namespace: "test"}: {}, - {Name: "3", Namespace: "other"}: {}, - }, - meshServices: []v1alpha1.MeshService{}, - expectedErrors: []error{errRouteBackendNotFound, errRouteBackendNotFound}, - }, - "all fail no namespaces": { - route: testHTTPRouteBackends("route", "test", []gwv1beta1.BackendObjectReference{ - {Name: "1"}, - {Name: "2"}, - }, nil), - services: map[types.NamespacedName]corev1.Service{ - {Name: "1", Namespace: "other"}: {}, - {Name: "2", Namespace: "other"}: {}, - {Name: "3", Namespace: "other"}: {}, - }, - meshServices: []v1alpha1.MeshService{}, - expectedErrors: []error{errRouteBackendNotFound, errRouteBackendNotFound}, - }, - "all fail namespaces": { - route: testHTTPRouteBackends("route", "test", []gwv1beta1.BackendObjectReference{ - {Name: "1", Namespace: common.PointerTo[gwv1beta1.Namespace]("other")}, - {Name: "2", Namespace: common.PointerTo[gwv1beta1.Namespace]("other")}, - }, nil), - services: map[types.NamespacedName]corev1.Service{ - {Name: "1", Namespace: "test"}: {}, - {Name: "2", Namespace: "test"}: {}, - {Name: "3", Namespace: "test"}: {}, - }, - meshServices: []v1alpha1.MeshService{}, - expectedErrors: []error{errRouteBackendNotFound, errRouteBackendNotFound}, - }, - "type failures": { - route: testHTTPRouteBackends("route", "test", []gwv1beta1.BackendObjectReference{ - {Name: "1", Group: common.PointerTo[gwv1beta1.Group]("test")}, - {Name: "2"}, - }, nil), - services: map[types.NamespacedName]corev1.Service{ - {Name: "1", Namespace: "test"}: {}, - {Name: "2", Namespace: "test"}: {}, - {Name: "3", Namespace: "test"}: {}, - }, - meshServices: []v1alpha1.MeshService{}, - expectedErrors: []error{errRouteInvalidKind, nil}, - }, - "mesh services": { - route: testHTTPRouteBackends("route", "test", []gwv1beta1.BackendObjectReference{ - { - Name: "1", - Group: common.PointerTo(gwv1beta1.Group(v1alpha1.ConsulHashicorpGroup)), - Kind: common.PointerTo(gwv1beta1.Kind(v1alpha1.MeshServiceKind)), - }, - }, nil), - meshServices: []v1alpha1.MeshService{ - {ObjectMeta: metav1.ObjectMeta{Name: "1", Namespace: "test"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "2", Namespace: "test"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "3", Namespace: "test"}}, - }, - expectedErrors: []error{nil}, - }, - } { - t.Run(name, func(t *testing.T) { - refs := getRouteBackends(tt.route) - resources := common.NewResourceMap(common.ResourceTranslator{}, NewReferenceValidator(tt.referenceGrants), logrtest.NewTestLogger(t)) - for _, service := range tt.meshServices { - resources.AddMeshService(service) - } - for id := range tt.services { - resources.AddService(id, id.Name) - } - - actual := validateRefs(tt.route, refs, resources) - require.Equal(t, len(actual), len(tt.expectedErrors)) - for i, err := range tt.expectedErrors { - require.Equal(t, err, actual[i].err) - } - }) - } -} - -func TestValidateGateway(t *testing.T) { - t.Parallel() - - for name, tt := range map[string]struct { - object gwv1beta1.Gateway - expected error - }{ - "valid": { - object: gwv1beta1.Gateway{}, - expected: nil, - }, - "invalid": { - object: gwv1beta1.Gateway{Spec: gwv1beta1.GatewaySpec{Addresses: []gwv1beta1.GatewayAddress{ - {Value: "1"}, - }}}, - expected: errGatewayUnsupportedAddress, - }, - } { - t.Run(name, func(t *testing.T) { - require.Equal(t, tt.expected, validateGateway(tt.object, nil, nil).acceptedErr) - }) - } -} - -func TestMergedListeners_ValidateProtocol(t *testing.T) { - t.Parallel() - - for name, tt := range map[string]struct { - mergedListeners mergedListeners - expected error - }{ - "valid": { - mergedListeners: []mergedListener{ - {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, - {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, - {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, - {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, - }, - expected: nil, - }, - "invalid": { - mergedListeners: []mergedListener{ - {listener: gwv1beta1.Listener{Protocol: gwv1beta1.TCPProtocolType}}, - {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, - {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, - {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, - }, - expected: errListenerProtocolConflict, - }, - "big list": { - mergedListeners: []mergedListener{ - {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, - {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, - {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, - {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, - {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, - {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, - {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, - {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPSProtocolType}}, - {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, - {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, - {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, - {listener: gwv1beta1.Listener{Protocol: gwv1beta1.HTTPProtocolType}}, - }, - expected: errListenerProtocolConflict, - }, - } { - t.Run(name, func(t *testing.T) { - require.Equal(t, tt.expected, tt.mergedListeners.validateProtocol()) - }) - } -} - -func TestMergedListeners_ValidateHostname(t *testing.T) { - t.Parallel() - - for name, tt := range map[string]struct { - mergedListeners mergedListeners - expected error - }{ - "valid": { - mergedListeners: []mergedListener{ - {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("1")}}, - {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("2")}}, - {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("3")}}, - {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("4")}}, - {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("5")}}, - {}, - }, - expected: nil, - }, - "invalid nil": { - mergedListeners: []mergedListener{ - {}, - {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("1")}}, - {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("2")}}, - {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("3")}}, - {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("4")}}, - {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("5")}}, - {}, - }, - expected: errListenerHostnameConflict, - }, - "invalid set": { - mergedListeners: []mergedListener{ - {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("1")}}, - {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("2")}}, - {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("3")}}, - {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("4")}}, - {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("5")}}, - {}, - {listener: gwv1beta1.Listener{Hostname: common.PointerTo[gwv1beta1.Hostname]("1")}}, - }, - expected: errListenerHostnameConflict, - }, - } { - t.Run(name, func(t *testing.T) { - for i, l := range tt.mergedListeners { - l.index = i - tt.mergedListeners[i] = l - } - - require.Equal(t, tt.expected, tt.mergedListeners.validateHostname(0, tt.mergedListeners[0].listener)) - }) - } -} - -func TestValidateTLS(t *testing.T) { - t.Parallel() - - _, secret := generateTestCertificate(t, "", "") - - for name, tt := range map[string]struct { - gateway gwv1beta1.Gateway - grants []gwv1beta1.ReferenceGrant - tls *gwv1beta1.GatewayTLSConfig - certificates []corev1.Secret - expectedResolvedRefsErr error - expectedAcceptedErr error - }{ - "no tls": { - gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), - tls: nil, - certificates: nil, - expectedResolvedRefsErr: nil, - expectedAcceptedErr: nil, - }, - "not supported certificate": { - gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), - tls: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{ - {Name: "foo", Namespace: common.PointerTo[gwv1beta1.Namespace]("other"), Group: common.PointerTo[gwv1beta1.Group]("test")}, - }, - }, - certificates: []corev1.Secret{ - {ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "other"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "other"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "other"}}, - }, - expectedResolvedRefsErr: errListenerInvalidCertificateRef_NotSupported, - expectedAcceptedErr: nil, - }, - "not allowed certificate": { - gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), - tls: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{ - {Name: "foo", Namespace: common.PointerTo[gwv1beta1.Namespace]("other")}, - }, - }, - certificates: []corev1.Secret{ - {ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "other"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "other"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "other"}}, - }, - expectedResolvedRefsErr: errRefNotPermitted, - expectedAcceptedErr: nil, - }, - "not found certificate": { - grants: []gwv1beta1.ReferenceGrant{ - {ObjectMeta: metav1.ObjectMeta{Namespace: "other", Name: "grant"}, Spec: gwv1beta1.ReferenceGrantSpec{ - From: []gwv1beta1.ReferenceGrantFrom{ - {Group: gwv1beta1.GroupName, Kind: "Gateway", Namespace: gwv1beta1.Namespace("default")}, - }, - To: []gwv1beta1.ReferenceGrantTo{ - {Kind: "Secret"}, - }, - }}, - }, - gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), - tls: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{ - {Name: "zoiks", Namespace: common.PointerTo[gwv1beta1.Namespace]("other")}, - }, - }, - certificates: []corev1.Secret{ - {ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "other"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "other"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "other"}}, - }, - expectedResolvedRefsErr: errListenerInvalidCertificateRef_NotFound, - expectedAcceptedErr: nil, - }, - "not found certificate mismatched namespace": { - grants: []gwv1beta1.ReferenceGrant{ - {ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "grant"}, Spec: gwv1beta1.ReferenceGrantSpec{ - From: []gwv1beta1.ReferenceGrantFrom{ - {Group: gwv1beta1.GroupName, Kind: "Gateway", Namespace: gwv1beta1.Namespace("default")}, - }, - To: []gwv1beta1.ReferenceGrantTo{ - {Kind: "Secret"}, - }, - }}, - }, - gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), - tls: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{ - {Name: "foo", Namespace: common.PointerTo[gwv1beta1.Namespace]("foo")}, - }, - }, - certificates: []corev1.Secret{ - {ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "other"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "other"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "other"}}, - }, - expectedResolvedRefsErr: errListenerInvalidCertificateRef_NotFound, - expectedAcceptedErr: nil, - }, - "passthrough mode": { - gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), - tls: &gwv1beta1.GatewayTLSConfig{ - Mode: common.PointerTo(gwv1beta1.TLSModePassthrough), - }, - certificates: nil, - expectedResolvedRefsErr: nil, - expectedAcceptedErr: errListenerNoTLSPassthrough, - }, - "valid targeted namespace": { - grants: []gwv1beta1.ReferenceGrant{ - {ObjectMeta: metav1.ObjectMeta{Namespace: "1", Name: "grant"}, Spec: gwv1beta1.ReferenceGrantSpec{ - From: []gwv1beta1.ReferenceGrantFrom{ - {Group: gwv1beta1.GroupName, Kind: "Gateway", Namespace: gwv1beta1.Namespace("default")}, - }, - To: []gwv1beta1.ReferenceGrantTo{ - {Kind: "Secret"}, - }, - }}, - {ObjectMeta: metav1.ObjectMeta{Namespace: "2", Name: "grant"}, Spec: gwv1beta1.ReferenceGrantSpec{ - From: []gwv1beta1.ReferenceGrantFrom{ - {Group: gwv1beta1.GroupName, Kind: "Gateway", Namespace: gwv1beta1.Namespace("default")}, - }, - To: []gwv1beta1.ReferenceGrantTo{ - {Kind: "Secret"}, - }, - }}, - {ObjectMeta: metav1.ObjectMeta{Namespace: "3", Name: "grant"}, Spec: gwv1beta1.ReferenceGrantSpec{ - From: []gwv1beta1.ReferenceGrantFrom{ - {Group: gwv1beta1.GroupName, Kind: "Gateway", Namespace: gwv1beta1.Namespace("default")}, - }, - To: []gwv1beta1.ReferenceGrantTo{ - {Kind: "Secret"}, - }, - }}, - }, - gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), - tls: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{ - {Name: "foo", Namespace: common.PointerTo[gwv1beta1.Namespace]("1")}, - {Name: "bar", Namespace: common.PointerTo[gwv1beta1.Namespace]("2")}, - {Name: "baz", Namespace: common.PointerTo[gwv1beta1.Namespace]("3")}, - }, - }, - certificates: []corev1.Secret{ - {ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "1"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "2"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "3"}}, - }, - expectedResolvedRefsErr: nil, - expectedAcceptedErr: nil, - }, - "valid same namespace": { - gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), - tls: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{ - {Name: "foo"}, - {Name: "bar"}, - {Name: "baz"}, - }, - }, - certificates: []corev1.Secret{ - {ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "default"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "default"}}, - }, - expectedResolvedRefsErr: nil, - expectedAcceptedErr: nil, - }, - "valid empty certs": { - gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), - tls: &gwv1beta1.GatewayTLSConfig{}, - certificates: nil, - expectedResolvedRefsErr: nil, - expectedAcceptedErr: nil, - }, - "invalid cipher suite": { - gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), - tls: &gwv1beta1.GatewayTLSConfig{ - Options: map[gwv1beta1.AnnotationKey]gwv1beta1.AnnotationValue{ - common.TLSCipherSuitesAnnotationKey: "invalid", - }, - }, - certificates: nil, - expectedAcceptedErr: errListenerUnsupportedTLSCipherSuite, - }, - "cipher suite not configurable": { - gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), - tls: &gwv1beta1.GatewayTLSConfig{ - Options: map[gwv1beta1.AnnotationKey]gwv1beta1.AnnotationValue{ - common.TLSMinVersionAnnotationKey: "TLSv1_3", - common.TLSCipherSuitesAnnotationKey: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", - }, - }, - certificates: nil, - expectedAcceptedErr: errListenerTLSCipherSuiteNotConfigurable, - }, - "invalid max version": { - gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), - tls: &gwv1beta1.GatewayTLSConfig{ - Options: map[gwv1beta1.AnnotationKey]gwv1beta1.AnnotationValue{ - common.TLSMaxVersionAnnotationKey: "invalid", - }, - }, - certificates: nil, - expectedAcceptedErr: errListenerUnsupportedTLSMaxVersion, - }, - "invalid min version": { - gateway: gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), - tls: &gwv1beta1.GatewayTLSConfig{ - Options: map[gwv1beta1.AnnotationKey]gwv1beta1.AnnotationValue{ - common.TLSMinVersionAnnotationKey: "invalid", - }, - }, - certificates: nil, - expectedAcceptedErr: errListenerUnsupportedTLSMinVersion, - }, - } { - t.Run(name, func(t *testing.T) { - resources := common.NewResourceMap(common.ResourceTranslator{}, NewReferenceValidator(tt.grants), logrtest.NewTestLogger(t)) - for _, certificate := range tt.certificates { - // make the data valid - certificate.Data = secret.Data - resources.ReferenceCountCertificate(certificate) - } - - actualAcceptedError, actualResolvedRefsError := validateTLS(tt.gateway, tt.tls, resources) - require.Equal(t, tt.expectedResolvedRefsErr, actualResolvedRefsError) - require.Equal(t, tt.expectedAcceptedErr, actualAcceptedError) - }) - } -} - -func TestValidateListeners(t *testing.T) { - t.Parallel() - - for name, tt := range map[string]struct { - listeners []gwv1beta1.Listener - expectedAcceptedErr error - listenerIndexToTest int - mapPrivilegedContainerPorts int32 - }{ - "valid protocol HTTP": { - listeners: []gwv1beta1.Listener{ - {Protocol: gwv1beta1.HTTPProtocolType}, - }, - expectedAcceptedErr: nil, - }, - "valid protocol HTTPS": { - listeners: []gwv1beta1.Listener{ - {Protocol: gwv1beta1.HTTPSProtocolType}, - }, - expectedAcceptedErr: nil, - }, - "valid protocol TCP": { - listeners: []gwv1beta1.Listener{ - {Protocol: gwv1beta1.TCPProtocolType}, - }, - expectedAcceptedErr: nil, - }, - "invalid protocol UDP": { - listeners: []gwv1beta1.Listener{ - {Protocol: gwv1beta1.UDPProtocolType}, - }, - expectedAcceptedErr: errListenerUnsupportedProtocol, - }, - "invalid port": { - listeners: []gwv1beta1.Listener{ - {Protocol: gwv1beta1.TCPProtocolType, Port: 20000}, - }, - expectedAcceptedErr: errListenerPortUnavailable, - }, - "conflicted port": { - listeners: []gwv1beta1.Listener{ - {Protocol: gwv1beta1.TCPProtocolType, Port: 80}, - {Protocol: gwv1beta1.TCPProtocolType, Port: 80}, - }, - expectedAcceptedErr: errListenerPortUnavailable, - listenerIndexToTest: 1, - }, - "conflicted mapped port": { - listeners: []gwv1beta1.Listener{ - {Protocol: gwv1beta1.TCPProtocolType, Port: 80}, - {Protocol: gwv1beta1.TCPProtocolType, Port: 2080}, - }, - expectedAcceptedErr: errListenerMappedToPrivilegedPortMapping, - listenerIndexToTest: 1, - mapPrivilegedContainerPorts: 2000, - }, - } { - t.Run(name, func(t *testing.T) { - gwcc := &v1alpha1.GatewayClassConfig{ - Spec: v1alpha1.GatewayClassConfigSpec{ - MapPrivilegedContainerPorts: tt.mapPrivilegedContainerPorts, - }, - } - - require.Equal(t, tt.expectedAcceptedErr, validateListeners(gatewayWithFinalizer(gwv1beta1.GatewaySpec{}), tt.listeners, nil, gwcc)[tt.listenerIndexToTest].acceptedErr) - }) - } -} - -func TestRouteAllowedForListenerNamespaces(t *testing.T) { - t.Parallel() - - for name, tt := range map[string]struct { - allowedRoutes *gwv1beta1.AllowedRoutes - gatewayNamespace string - routeNamespace corev1.Namespace - expected bool - }{ - "default same namespace allowed": { - allowedRoutes: nil, - gatewayNamespace: "test", - routeNamespace: corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test"}}, - expected: true, - }, - "default same namespace not allowed": { - allowedRoutes: nil, - gatewayNamespace: "test", - routeNamespace: corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "other"}}, - expected: false, - }, - "explicit same namespace allowed": { - allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{From: common.PointerTo(gwv1beta1.NamespacesFromSame)}}, - gatewayNamespace: "test", - routeNamespace: corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test"}}, - expected: true, - }, - "explicit same namespace not allowed": { - allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{From: common.PointerTo(gwv1beta1.NamespacesFromSame)}}, - gatewayNamespace: "test", - routeNamespace: corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "other"}}, - expected: false, - }, - "all namespace allowed": { - allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{From: common.PointerTo(gwv1beta1.NamespacesFromAll)}}, - gatewayNamespace: "test", - routeNamespace: corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "other"}}, - expected: true, - }, - "invalid namespace from not allowed": { - allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{From: common.PointerTo[gwv1beta1.FromNamespaces]("other")}}, - gatewayNamespace: "test", - routeNamespace: corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test"}}, - expected: false, - }, - "labeled namespace allowed": { - allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.NamespacesFromSelector), - Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, - }}, - gatewayNamespace: "test", - routeNamespace: corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "other", Labels: map[string]string{ - "foo": "bar", - }}}, - expected: true, - }, - "labeled namespace not allowed": { - allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.NamespacesFromSelector), - Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, - }}, - gatewayNamespace: "test", - routeNamespace: corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "other", Labels: map[string]string{ - "foo": "baz", - }}}, - expected: false, - }, - "invalid labeled namespace": { - allowedRoutes: &gwv1beta1.AllowedRoutes{Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.NamespacesFromSelector), - Selector: &metav1.LabelSelector{MatchExpressions: []metav1.LabelSelectorRequirement{ - {Key: "foo", Operator: "junk", Values: []string{"1"}}, - }}, - }}, - gatewayNamespace: "test", - routeNamespace: corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "other", Labels: map[string]string{ - "foo": "bar", - }}}, - expected: false, - }, - } { - t.Run(name, func(t *testing.T) { - require.Equal(t, tt.expected, routeAllowedForListenerNamespaces(tt.gatewayNamespace, tt.allowedRoutes, tt.routeNamespace)) - }) - } -} - -func TestRouteAllowedForListenerHostname(t *testing.T) { - t.Parallel() - - for name, tt := range map[string]struct { - hostname *gwv1beta1.Hostname - hostnames []gwv1beta1.Hostname - expected bool - }{ - "empty hostnames": { - hostname: nil, - hostnames: []gwv1beta1.Hostname{"foo", "bar"}, - expected: true, - }, - "empty hostname": { - hostname: common.PointerTo[gwv1beta1.Hostname]("foo"), - hostnames: nil, - expected: true, - }, - "any hostname match": { - hostname: common.PointerTo[gwv1beta1.Hostname]("foo"), - hostnames: []gwv1beta1.Hostname{"foo", "bar"}, - expected: true, - }, - "no match": { - hostname: common.PointerTo[gwv1beta1.Hostname]("foo"), - hostnames: []gwv1beta1.Hostname{"bar"}, - expected: false, - }, - } { - t.Run(name, func(t *testing.T) { - require.Equal(t, tt.expected, routeAllowedForListenerHostname(tt.hostname, tt.hostnames)) - }) - } -} - -func TestHostnamesMatch(t *testing.T) { - t.Parallel() - - for name, tt := range map[string]struct { - one gwv1beta1.Hostname - two gwv1beta1.Hostname - expected bool - }{ - "wildcard one": { - one: "*", - two: "foo", - expected: true, - }, - "wildcard two": { - one: "foo", - two: "*", - expected: true, - }, - "empty one": { - one: "", - two: "foo", - expected: true, - }, - "empty two": { - one: "foo", - two: "", - expected: true, - }, - "subdomain one": { - one: "*.foo", - two: "sub.foo", - expected: true, - }, - "subdomain two": { - one: "sub.foo", - two: "*.foo", - expected: true, - }, - "exact match": { - one: "foo", - two: "foo", - expected: true, - }, - "no match": { - one: "foo", - two: "bar", - expected: false, - }, - } { - t.Run(name, func(t *testing.T) { - require.Equal(t, tt.expected, hostnamesMatch(tt.one, tt.two)) - }) - } -} - -func TestRouteKindIsAllowedForListener(t *testing.T) { - t.Parallel() - - for name, tt := range map[string]struct { - kinds []gwv1beta1.RouteGroupKind - gk schema.GroupKind - expected bool - }{ - "empty kinds": { - kinds: nil, - gk: schema.GroupKind{Group: "a", Kind: "b"}, - expected: true, - }, - "group specified": { - kinds: []gwv1beta1.RouteGroupKind{ - {Group: common.PointerTo[gwv1beta1.Group]("a"), Kind: "b"}, - }, - gk: schema.GroupKind{Group: "a", Kind: "b"}, - expected: true, - }, - "group unspecified": { - kinds: []gwv1beta1.RouteGroupKind{ - {Kind: "b"}, - }, - gk: schema.GroupKind{Group: "a", Kind: "b"}, - expected: true, - }, - "kind mismatch": { - kinds: []gwv1beta1.RouteGroupKind{ - {Kind: "b"}, - }, - gk: schema.GroupKind{Group: "a", Kind: "c"}, - expected: false, - }, - "group mismatch": { - kinds: []gwv1beta1.RouteGroupKind{ - {Group: common.PointerTo[gwv1beta1.Group]("a"), Kind: "b"}, - }, - gk: schema.GroupKind{Group: "d", Kind: "b"}, - expected: false, - }, - } { - t.Run(name, func(t *testing.T) { - require.Equal(t, tt.expected, routeKindIsAllowedForListener(tt.kinds, tt.gk)) - }) - } -} diff --git a/control-plane/api-gateway/cache/consul.go b/control-plane/api-gateway/cache/consul.go deleted file mode 100644 index 7737e80d57..0000000000 --- a/control-plane/api-gateway/cache/consul.go +++ /dev/null @@ -1,526 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package cache - -import ( - "bytes" - "context" - "fmt" - "strings" - "sync" - "text/template" - "time" - - "github.com/go-logr/logr" - "golang.org/x/exp/slices" - "sigs.k8s.io/controller-runtime/pkg/event" - - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" - "github.com/hashicorp/consul-k8s/control-plane/consul" - "github.com/hashicorp/consul-k8s/control-plane/namespaces" - "github.com/hashicorp/consul/api" -) - -func init() { - gatewayTpl = template.Must(template.New("root").Parse(strings.TrimSpace(gatewayRulesTpl))) -} - -type templateArgs struct { - EnableNamespaces bool -} - -var ( - gatewayTpl *template.Template - gatewayRulesTpl = ` -mesh = "read" -{{- if .EnableNamespaces }} - namespace_prefix "" { -{{- end }} - node_prefix "" { - policy = "read" - } - service_prefix "" { - policy = "write" - } -{{- if .EnableNamespaces }} - } -{{- end }} -` -) - -const ( - namespaceWildcard = "*" - apiTimeout = 5 * time.Minute -) - -var Kinds = []string{api.APIGateway, api.HTTPRoute, api.TCPRoute, api.InlineCertificate} - -type Config struct { - ConsulClientConfig *consul.Config - ConsulServerConnMgr consul.ServerConnectionManager - NamespacesEnabled bool - Datacenter string - CrossNamespaceACLPolicy string - Logger logr.Logger -} - -// Cache subscribes to and caches Consul objects, it also responsible for mainting subscriptions to -// resources that it caches. -type Cache struct { - config *consul.Config - serverMgr consul.ServerConnectionManager - logger logr.Logger - - cache map[string]*common.ReferenceMap - cacheMutex *sync.Mutex - - subscribers map[string][]*Subscription - subscriberMutex *sync.Mutex - - namespacesEnabled bool - crossNamespaceACLPolicy string - - synced chan struct{} - - kinds []string - - datacenter string -} - -func New(config Config) *Cache { - cache := make(map[string]*common.ReferenceMap, len(Kinds)) - for _, kind := range Kinds { - cache[kind] = common.NewReferenceMap() - } - config.ConsulClientConfig.APITimeout = apiTimeout - - return &Cache{ - config: config.ConsulClientConfig, - serverMgr: config.ConsulServerConnMgr, - namespacesEnabled: config.NamespacesEnabled, - cache: cache, - cacheMutex: &sync.Mutex{}, - subscribers: make(map[string][]*Subscription), - subscriberMutex: &sync.Mutex{}, - kinds: Kinds, - synced: make(chan struct{}, len(Kinds)), - logger: config.Logger, - crossNamespaceACLPolicy: config.CrossNamespaceACLPolicy, - datacenter: config.Datacenter, - } -} - -// WaitSynced is used to coordinate with the caller when the cache is initially filled. -func (c *Cache) WaitSynced(ctx context.Context) { - for range c.kinds { - select { - case <-c.synced: - case <-ctx.Done(): - return - } - } -} - -// Subscribe handles adding a new subscription for resources of a given kind. -func (c *Cache) Subscribe(ctx context.Context, kind string, translator TranslatorFn) *Subscription { - c.subscriberMutex.Lock() - defer c.subscriberMutex.Unlock() - - // check that kind is registered with cache - if !slices.Contains(c.kinds, kind) { - return &Subscription{} - } - - subscribers, ok := c.subscribers[kind] - if !ok { - subscribers = []*Subscription{} - } - - ctx, cancel := context.WithCancel(ctx) - events := make(chan event.GenericEvent) - sub := &Subscription{ - translator: translator, - ctx: ctx, - cancelCtx: cancel, - events: events, - } - - subscribers = append(subscribers, sub) - - c.subscribers[kind] = subscribers - - return sub -} - -// Run starts the cache watch cycle, on the first call it will fill the cache with existing resources. -func (c *Cache) Run(ctx context.Context) { - wg := &sync.WaitGroup{} - - for i := range c.kinds { - kind := c.kinds[i] - - wg.Add(1) - go func() { - defer wg.Done() - c.subscribeToConsul(ctx, kind) - }() - } - - wg.Wait() -} - -func (c *Cache) subscribeToConsul(ctx context.Context, kind string) { - once := &sync.Once{} - - opts := &api.QueryOptions{} - if c.namespacesEnabled { - opts.Namespace = namespaceWildcard - } - - for { - select { - case <-ctx.Done(): - return - default: - } - - client, err := consul.NewClientFromConnMgr(c.config, c.serverMgr) - if err != nil { - c.logger.Error(err, "error initializing consul client") - continue - } - - entries, meta, err := client.ConfigEntries().List(kind, opts.WithContext(ctx)) - if err != nil { - // if we timeout we don't care about the error message because it's expected to happen on long polls - // any other error we want to alert on - if !strings.Contains(strings.ToLower(err.Error()), "timeout") { - c.logger.Error(err, fmt.Sprintf("error fetching config entries for kind: %s", kind)) - } - continue - } - - opts.WaitIndex = meta.LastIndex - - c.updateAndNotify(ctx, once, kind, entries) - - select { - case <-ctx.Done(): - return - default: - continue - } - } -} - -func (c *Cache) updateAndNotify(ctx context.Context, once *sync.Once, kind string, entries []api.ConfigEntry) { - c.cacheMutex.Lock() - - cache := common.NewReferenceMap() - - for _, entry := range entries { - meta := entry.GetMeta() - if meta[constants.MetaKeyKubeName] == "" || meta[constants.MetaKeyDatacenter] != c.datacenter { - // Don't process things that don't belong to us. The main reason - // for this is so that we don't garbage collect config entries that - // are either user-created or that another controller running in a - // federated datacenter creates. While we still allow for competing controllers - // syncing/overriding each other due to conflicting Kubernetes objects in - // two federated clusters (which is what the rest of the controllers also allow - // for), we don't want to delete a config entry just because we don't have - // its corresponding Kubernetes object if we know it belongs to another datacenter. - continue - } - - cache.Set(common.EntryToReference(entry), entry) - } - - diffs := c.cache[kind].Diff(cache) - - c.cache[kind] = cache - - // we run this the first time the cache is filled to notify the waiter - once.Do(func() { - c.logger.Info("sync mark for " + kind) - c.synced <- struct{}{} - }) - - c.cacheMutex.Unlock() - - // now notify all subscribers - c.notifySubscribers(ctx, kind, diffs) -} - -// notifySubscribers notifies each subscriber for a given kind on changes to a config entry of that kind. It also -// handles removing any subscribers that have marked themselves as done. -func (c *Cache) notifySubscribers(ctx context.Context, kind string, entries []api.ConfigEntry) { - c.subscriberMutex.Lock() - defer c.subscriberMutex.Unlock() - - for _, entry := range entries { - // this will hold the new list of current subscribers after we finish notifying - subscribers := make([]*Subscription, 0, len(c.subscribers[kind])) - for _, subscriber := range c.subscribers[kind] { - addSubscriber := false - - for _, namespaceName := range subscriber.translator(entry) { - event := event.GenericEvent{ - Object: newConfigEntryObject(namespaceName), - } - - select { - case <-ctx.Done(): - return - case <-subscriber.ctx.Done(): - // don't add this subscriber to current list because it is done - addSubscriber = false - case subscriber.events <- event: - // keep this one since we can send events to it - addSubscriber = true - } - } - - if addSubscriber { - subscribers = append(subscribers, subscriber) - } - } - c.subscribers[kind] = subscribers - } -} - -// Write handles writing the config entry back to Consul, if the current reference of the -// config entry is stale then it returns an error. -func (c *Cache) Write(ctx context.Context, entry api.ConfigEntry) error { - c.cacheMutex.Lock() - defer c.cacheMutex.Unlock() - - entryMap, ok := c.cache[entry.GetKind()] - if !ok { - return nil - } - - ref := common.EntryToReference(entry) - - old := entryMap.Get(ref) - if old != nil && common.EntriesEqual(old, entry) { - return nil - } - - client, err := consul.NewClientFromConnMgr(c.config, c.serverMgr) - if err != nil { - return err - } - - if c.namespacesEnabled { - if _, err := namespaces.EnsureExists(client, entry.GetNamespace(), c.crossNamespaceACLPolicy); err != nil { - return err - } - } - - options := &api.WriteOptions{} - - _, _, err = client.ConfigEntries().Set(entry, options.WithContext(ctx)) - if err != nil { - return err - } - - return nil -} - -func (c *Cache) ensurePolicy(client *api.Client) (string, error) { - policy := c.gatewayPolicy() - - created, _, err := client.ACL().PolicyCreate(&policy, &api.WriteOptions{}) - - if isPolicyExistsErr(err, policy.Name) { - existing, _, err := client.ACL().PolicyReadByName(policy.Name, &api.QueryOptions{}) - if err != nil { - return "", err - } - return existing.ID, nil - } - if err != nil { - return "", err - } - return created.ID, nil -} - -func (c *Cache) ensureRole(client *api.Client) (string, error) { - policyID, err := c.ensurePolicy(client) - if err != nil { - return "", err - } - - aclRoleName := "managed-gateway-acl-role" - - aclRole, _, err := client.ACL().RoleReadByName(aclRoleName, &api.QueryOptions{}) - if err != nil { - return "", err - } - if aclRole != nil { - return aclRoleName, nil - } - - role := &api.ACLRole{ - Name: aclRoleName, - Description: "ACL Role for Managed API Gateways", - Policies: []*api.ACLLink{{ID: policyID}}, - } - - _, _, err = client.ACL().RoleCreate(role, &api.WriteOptions{}) - return aclRoleName, err -} - -func (c *Cache) gatewayPolicy() api.ACLPolicy { - var data bytes.Buffer - if err := gatewayTpl.Execute(&data, templateArgs{ - EnableNamespaces: c.namespacesEnabled, - }); err != nil { - // just panic if we can't compile the simple template - // as it means something else is going severly wrong. - panic(err) - } - - return api.ACLPolicy{ - Name: "api-gateway-token-policy", - Description: "API Gateway token Policy", - Rules: data.String(), - } -} - -// Get returns a config entry from the cache that corresponds to the given resource reference. -func (c *Cache) Get(ref api.ResourceReference) api.ConfigEntry { - c.cacheMutex.Lock() - defer c.cacheMutex.Unlock() - - entryMap, ok := c.cache[ref.Kind] - if !ok { - return nil - } - - return entryMap.Get(ref) -} - -// Delete handles deleting the config entry from consul, if the current reference of the config entry is stale then -// it returns an error. -func (c *Cache) Delete(ctx context.Context, ref api.ResourceReference) error { - c.cacheMutex.Lock() - defer c.cacheMutex.Unlock() - - entryMap, ok := c.cache[ref.Kind] - if !ok { - return nil - } - - if entryMap.Get(ref) == nil { - c.logger.Info("cached object not found, not deleting") - return nil - } - - client, err := consul.NewClientFromConnMgr(c.config, c.serverMgr) - if err != nil { - return err - } - - options := &api.WriteOptions{} - - _, err = client.ConfigEntries().Delete(ref.Kind, ref.Name, options.WithContext(ctx)) - return err -} - -// List returns a list of config entries from the cache that corresponds to the given kind. -func (c *Cache) List(kind string) []api.ConfigEntry { - c.cacheMutex.Lock() - defer c.cacheMutex.Unlock() - - refMap, ok := c.cache[kind] - if !ok { - return nil - } - - return refMap.Entries() -} - -func (c *Cache) EnsureRoleBinding(authMethod, service, namespace string) error { - client, err := consul.NewClientFromConnMgr(c.config, c.serverMgr) - if err != nil { - return err - } - - role, err := c.ensureRole(client) - if err != nil { - return ignoreACLsDisabled(err) - } - - bindingRule := &api.ACLBindingRule{ - Description: fmt.Sprintf("Binding Rule for %s/%s", namespace, service), - AuthMethod: authMethod, - Selector: fmt.Sprintf("serviceaccount.name==%q and serviceaccount.namespace==%q", service, namespace), - BindType: api.BindingRuleBindTypeRole, - BindName: role, - } - - existingRules, _, err := client.ACL().BindingRuleList(authMethod, &api.QueryOptions{}) - if err != nil { - return err - } - - for _, existingRule := range existingRules { - if existingRule.BindName == bindingRule.BindName && existingRule.Description == bindingRule.Description { - bindingRule.ID = existingRule.ID - } - } - - if bindingRule.ID == "" { - _, _, err := client.ACL().BindingRuleCreate(bindingRule, &api.WriteOptions{}) - return err - } - _, _, err = client.ACL().BindingRuleUpdate(bindingRule, &api.WriteOptions{}) - return err -} - -// Register registers a service in Consul. -func (c *Cache) Register(ctx context.Context, registration api.CatalogRegistration) error { - client, err := consul.NewClientFromConnMgr(c.config, c.serverMgr) - if err != nil { - return err - } - - options := &api.WriteOptions{} - - _, err = client.Catalog().Register(®istration, options.WithContext(ctx)) - return err -} - -// Deregister deregisters a service in Consul. -func (c *Cache) Deregister(ctx context.Context, deregistration api.CatalogDeregistration) error { - client, err := consul.NewClientFromConnMgr(c.config, c.serverMgr) - if err != nil { - return err - } - - options := &api.WriteOptions{} - - _, err = client.Catalog().Deregister(&deregistration, options.WithContext(ctx)) - return err -} - -func ignoreACLsDisabled(err error) error { - if err == nil { - return nil - } - if err.Error() == "Unexpected response code: 401 (ACL support disabled)" { - return nil - } - return err -} - -// isPolicyExistsErr returns true if err is due to trying to call the -// policy create API when the policy already exists. -func isPolicyExistsErr(err error, policyName string) bool { - return err != nil && - strings.Contains(err.Error(), "Unexpected response code: 500") && - strings.Contains(err.Error(), fmt.Sprintf("Invalid Policy: A Policy with Name %q already exists", policyName)) -} diff --git a/control-plane/api-gateway/cache/consul_test.go b/control-plane/api-gateway/cache/consul_test.go deleted file mode 100644 index 59570e532f..0000000000 --- a/control-plane/api-gateway/cache/consul_test.go +++ /dev/null @@ -1,2027 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package cache - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "net/http/httptest" - "net/url" - "strconv" - "testing" - - "github.com/go-logr/logr" - logrtest "github.com/go-logr/logr/testing" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/stretchr/testify/require" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/event" - - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" - "github.com/hashicorp/consul-k8s/control-plane/consul" - "github.com/hashicorp/consul-k8s/control-plane/helper/test" - "github.com/hashicorp/consul/api" -) - -func Test_resourceCache_diff(t *testing.T) { - t.Parallel() - type args struct { - newCache *common.ReferenceMap - } - tests := []struct { - name string - oldCache *common.ReferenceMap - args args - want []api.ConfigEntry - }{ - { - name: "no difference", - oldCache: loadedReferenceMaps([]api.ConfigEntry{ - &api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "my route", - Parents: []api.ResourceReference{ - { - Kind: api.APIGateway, - Name: "api-gw", - SectionName: "listener-1", - Namespace: "ns", - }, - }, - Rules: []api.HTTPRouteRule{ - { - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "add it on": "the value", - }, - Remove: []string{"time to go"}, - Set: map[string]string{ - "Magic": "v2", - "Another One": "dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{Path: "v1"}, - }, - Matches: []api.HTTPMatch{ - { - Headers: []api.HTTPHeaderMatch{ - { - Match: api.HTTPHeaderMatchExact, - Name: "my header match", - Value: "the value", - }, - }, - Method: api.HTTPMatchMethodGet, - Path: api.HTTPPathMatch{ - Match: api.HTTPPathMatchPrefix, - Value: "/v1", - }, - Query: []api.HTTPQueryMatch{ - { - Match: api.HTTPQueryMatchExact, - Name: "search", - Value: "term", - }, - }, - }, - }, - Services: []api.HTTPService{ - { - Name: "service one", - Weight: 45, - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "svc - add it on": "svc - the value", - }, - Remove: []string{"svc - time to go"}, - Set: map[string]string{ - "svc - Magic": "svc - v2", - "svc - Another One": "svc - dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{ - Path: "path", - }, - }, - Namespace: "some ns", - }, - }, - }, - }, - Hostnames: []string{"hostname.com"}, - Meta: map[string]string{ - constants.MetaKeyKubeName: "name", - }, - Status: api.ConfigEntryStatus{}, - }, - })[api.HTTPRoute], - args: args{ - newCache: loadedReferenceMaps([]api.ConfigEntry{ - &api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "my route", - Parents: []api.ResourceReference{ - { - Kind: api.APIGateway, - Name: "api-gw", - SectionName: "listener-1", - Namespace: "ns", - }, - }, - Rules: []api.HTTPRouteRule{ - { - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "add it on": "the value", - }, - Remove: []string{"time to go"}, - Set: map[string]string{ - "Magic": "v2", - "Another One": "dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{Path: "v1"}, - }, - Matches: []api.HTTPMatch{ - { - Headers: []api.HTTPHeaderMatch{ - { - Match: api.HTTPHeaderMatchExact, - Name: "my header match", - Value: "the value", - }, - }, - Method: api.HTTPMatchMethodGet, - Path: api.HTTPPathMatch{ - Match: api.HTTPPathMatchPrefix, - Value: "/v1", - }, - Query: []api.HTTPQueryMatch{ - { - Match: api.HTTPQueryMatchExact, - Name: "search", - Value: "term", - }, - }, - }, - }, - Services: []api.HTTPService{ - { - Name: "service one", - Weight: 45, - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "svc - add it on": "svc - the value", - }, - Remove: []string{"svc - time to go"}, - Set: map[string]string{ - "svc - Magic": "svc - v2", - "svc - Another One": "svc - dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{ - Path: "path", - }, - }, - Namespace: "some ns", - }, - }, - }, - }, - Hostnames: []string{"hostname.com"}, - Meta: map[string]string{ - constants.MetaKeyKubeName: "name", - }, - Status: api.ConfigEntryStatus{}, - }, - })[api.HTTPRoute], - }, - want: []api.ConfigEntry{}, - }, - { - name: "resource exists in old cache but not new one", - oldCache: loadedReferenceMaps([]api.ConfigEntry{ - &api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "my route", - Parents: []api.ResourceReference{ - { - Kind: api.APIGateway, - Name: "api-gw", - SectionName: "listener-1", - Namespace: "ns", - }, - }, - Rules: []api.HTTPRouteRule{ - { - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "add it on": "the value", - }, - Remove: []string{"time to go"}, - Set: map[string]string{ - "Magic": "v2", - "Another One": "dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{Path: "v1"}, - }, - Matches: []api.HTTPMatch{ - { - Headers: []api.HTTPHeaderMatch{ - { - Match: api.HTTPHeaderMatchExact, - Name: "my header match", - Value: "the value", - }, - }, - Method: api.HTTPMatchMethodGet, - Path: api.HTTPPathMatch{ - Match: api.HTTPPathMatchPrefix, - Value: "/v1", - }, - Query: []api.HTTPQueryMatch{ - { - Match: api.HTTPQueryMatchExact, - Name: "search", - Value: "term", - }, - }, - }, - }, - Services: []api.HTTPService{ - { - Name: "service one", - Weight: 45, - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "svc - add it on": "svc - the value", - }, - Remove: []string{"svc - time to go"}, - Set: map[string]string{ - "svc - Magic": "svc - v2", - "svc - Another One": "svc - dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{ - Path: "path", - }, - }, - Namespace: "some ns", - }, - }, - }, - }, - Hostnames: []string{"hostname.com"}, - Meta: map[string]string{ - constants.MetaKeyKubeName: "name", - }, - Status: api.ConfigEntryStatus{}, - }, - &api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "my route 2", - Parents: []api.ResourceReference{ - { - Kind: api.APIGateway, - Name: "api-gw", - SectionName: "listener-2", - Namespace: "ns", - }, - }, - Rules: []api.HTTPRouteRule{ - { - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "add it on": "the value", - }, - Remove: []string{"time to go"}, - Set: map[string]string{ - "Magic": "v2", - "Another One": "dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{Path: "v1"}, - }, - Matches: []api.HTTPMatch{ - { - Headers: []api.HTTPHeaderMatch{ - { - Match: api.HTTPHeaderMatchExact, - Name: "my header match", - Value: "the value", - }, - }, - Method: api.HTTPMatchMethodGet, - Path: api.HTTPPathMatch{ - Match: api.HTTPPathMatchPrefix, - Value: "/v1", - }, - Query: []api.HTTPQueryMatch{ - { - Match: api.HTTPQueryMatchExact, - Name: "search", - Value: "term", - }, - }, - }, - }, - Services: []api.HTTPService{ - { - Name: "service one", - Weight: 45, - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "svc - add it on": "svc - the value", - }, - Remove: []string{"svc - time to go"}, - Set: map[string]string{ - "svc - Magic": "svc - v2", - "svc - Another One": "svc - dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{ - Path: "path", - }, - }, - Namespace: "some ns", - }, - }, - }, - }, - Hostnames: []string{"hostname.com"}, - Meta: map[string]string{ - constants.MetaKeyKubeName: "name", - }, - Status: api.ConfigEntryStatus{}, - }, - })[api.HTTPRoute], - args: args{ - newCache: loadedReferenceMaps([]api.ConfigEntry{ - &api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "my route", - Parents: []api.ResourceReference{ - { - Kind: api.APIGateway, - Name: "api-gw", - SectionName: "listener-1", - Namespace: "ns", - }, - }, - Rules: []api.HTTPRouteRule{ - { - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "add it on": "the value", - }, - Remove: []string{"time to go"}, - Set: map[string]string{ - "Magic": "v2", - "Another One": "dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{Path: "v1"}, - }, - Matches: []api.HTTPMatch{ - { - Headers: []api.HTTPHeaderMatch{ - { - Match: api.HTTPHeaderMatchExact, - Name: "my header match", - Value: "the value", - }, - }, - Method: api.HTTPMatchMethodGet, - Path: api.HTTPPathMatch{ - Match: api.HTTPPathMatchPrefix, - Value: "/v1", - }, - Query: []api.HTTPQueryMatch{ - { - Match: api.HTTPQueryMatchExact, - Name: "search", - Value: "term", - }, - }, - }, - }, - Services: []api.HTTPService{ - { - Name: "service one", - Weight: 45, - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "svc - add it on": "svc - the value", - }, - Remove: []string{"svc - time to go"}, - Set: map[string]string{ - "svc - Magic": "svc - v2", - "svc - Another One": "svc - dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{ - Path: "path", - }, - }, - Namespace: "some ns", - }, - }, - }, - }, - Hostnames: []string{"hostname.com"}, - Meta: map[string]string{ - constants.MetaKeyKubeName: "name", - }, - Status: api.ConfigEntryStatus{}, - }, - })[api.HTTPRoute], - }, - want: []api.ConfigEntry{ - &api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "my route 2", - Parents: []api.ResourceReference{ - { - Kind: api.APIGateway, - Name: "api-gw", - SectionName: "listener-2", - Namespace: "ns", - }, - }, - Rules: []api.HTTPRouteRule{ - { - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "add it on": "the value", - }, - Remove: []string{"time to go"}, - Set: map[string]string{ - "Magic": "v2", - "Another One": "dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{Path: "v1"}, - }, - Matches: []api.HTTPMatch{ - { - Headers: []api.HTTPHeaderMatch{ - { - Match: api.HTTPHeaderMatchExact, - Name: "my header match", - Value: "the value", - }, - }, - Method: api.HTTPMatchMethodGet, - Path: api.HTTPPathMatch{ - Match: api.HTTPPathMatchPrefix, - Value: "/v1", - }, - Query: []api.HTTPQueryMatch{ - { - Match: api.HTTPQueryMatchExact, - Name: "search", - Value: "term", - }, - }, - }, - }, - Services: []api.HTTPService{ - { - Name: "service one", - Weight: 45, - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "svc - add it on": "svc - the value", - }, - Remove: []string{"svc - time to go"}, - Set: map[string]string{ - "svc - Magic": "svc - v2", - "svc - Another One": "svc - dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{ - Path: "path", - }, - }, - Namespace: "some ns", - }, - }, - }, - }, - Hostnames: []string{"hostname.com"}, - Meta: map[string]string{ - constants.MetaKeyKubeName: "name", - }, - Status: api.ConfigEntryStatus{}, - }, - }, - }, - { - name: "resource exists in new cache but not old one", - oldCache: loadedReferenceMaps([]api.ConfigEntry{ - &api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "my route", - Parents: []api.ResourceReference{ - { - Kind: api.APIGateway, - Name: "api-gw", - SectionName: "listener-1", - Namespace: "ns", - }, - }, - Rules: []api.HTTPRouteRule{ - { - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "add it on": "the value", - }, - Remove: []string{"time to go"}, - Set: map[string]string{ - "Magic": "v2", - "Another One": "dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{Path: "v1"}, - }, - Matches: []api.HTTPMatch{ - { - Headers: []api.HTTPHeaderMatch{ - { - Match: api.HTTPHeaderMatchExact, - Name: "my header match", - Value: "the value", - }, - }, - Method: api.HTTPMatchMethodGet, - Path: api.HTTPPathMatch{ - Match: api.HTTPPathMatchPrefix, - Value: "/v1", - }, - Query: []api.HTTPQueryMatch{ - { - Match: api.HTTPQueryMatchExact, - Name: "search", - Value: "term", - }, - }, - }, - }, - Services: []api.HTTPService{ - { - Name: "service one", - Weight: 45, - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "svc - add it on": "svc - the value", - }, - Remove: []string{"svc - time to go"}, - Set: map[string]string{ - "svc - Magic": "svc - v2", - "svc - Another One": "svc - dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{ - Path: "path", - }, - }, - Namespace: "some ns", - }, - }, - }, - }, - Hostnames: []string{"hostname.com"}, - Meta: map[string]string{ - constants.MetaKeyKubeName: "name", - }, - Status: api.ConfigEntryStatus{}, - }, - })[api.HTTPRoute], - args: args{ - newCache: loadedReferenceMaps([]api.ConfigEntry{ - &api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "my route", - Parents: []api.ResourceReference{ - { - Kind: api.APIGateway, - Name: "api-gw", - SectionName: "listener-1", - Namespace: "ns", - }, - }, - Rules: []api.HTTPRouteRule{ - { - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "add it on": "the value", - }, - Remove: []string{"time to go"}, - Set: map[string]string{ - "Magic": "v2", - "Another One": "dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{Path: "v1"}, - }, - Matches: []api.HTTPMatch{ - { - Headers: []api.HTTPHeaderMatch{ - { - Match: api.HTTPHeaderMatchExact, - Name: "my header match", - Value: "the value", - }, - }, - Method: api.HTTPMatchMethodGet, - Path: api.HTTPPathMatch{ - Match: api.HTTPPathMatchPrefix, - Value: "/v1", - }, - Query: []api.HTTPQueryMatch{ - { - Match: api.HTTPQueryMatchExact, - Name: "search", - Value: "term", - }, - }, - }, - }, - Services: []api.HTTPService{ - { - Name: "service one", - Weight: 45, - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "svc - add it on": "svc - the value", - }, - Remove: []string{"svc - time to go"}, - Set: map[string]string{ - "svc - Magic": "svc - v2", - "svc - Another One": "svc - dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{ - Path: "path", - }, - }, - Namespace: "some ns", - }, - }, - }, - }, - Hostnames: []string{"hostname.com"}, - Meta: map[string]string{ - constants.MetaKeyKubeName: "name", - }, - Status: api.ConfigEntryStatus{}, - }, - &api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "my route 2", - Parents: []api.ResourceReference{ - { - Kind: api.APIGateway, - Name: "api-gw", - SectionName: "listener-2", - Namespace: "ns", - }, - }, - Rules: []api.HTTPRouteRule{ - { - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "add it on": "the value", - }, - Remove: []string{"time to go"}, - Set: map[string]string{ - "Magic": "v2", - "Another One": "dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{Path: "v1"}, - }, - Matches: []api.HTTPMatch{ - { - Headers: []api.HTTPHeaderMatch{ - { - Match: api.HTTPHeaderMatchExact, - Name: "my header match", - Value: "the value", - }, - }, - Method: api.HTTPMatchMethodGet, - Path: api.HTTPPathMatch{ - Match: api.HTTPPathMatchPrefix, - Value: "/v1", - }, - Query: []api.HTTPQueryMatch{ - { - Match: api.HTTPQueryMatchExact, - Name: "search", - Value: "term", - }, - }, - }, - }, - Services: []api.HTTPService{ - { - Name: "service one", - Weight: 45, - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "svc - add it on": "svc - the value", - }, - Remove: []string{"svc - time to go"}, - Set: map[string]string{ - "svc - Magic": "svc - v2", - "svc - Another One": "svc - dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{ - Path: "path", - }, - }, - Namespace: "some ns", - }, - }, - }, - }, - Hostnames: []string{"hostname.com"}, - Meta: map[string]string{ - constants.MetaKeyKubeName: "name", - }, - Status: api.ConfigEntryStatus{}, - }, - })[api.HTTPRoute], - }, - want: []api.ConfigEntry{ - &api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "my route 2", - Parents: []api.ResourceReference{ - { - Kind: api.APIGateway, - Name: "api-gw", - SectionName: "listener-2", - Namespace: "ns", - }, - }, - Rules: []api.HTTPRouteRule{ - { - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "add it on": "the value", - }, - Remove: []string{"time to go"}, - Set: map[string]string{ - "Magic": "v2", - "Another One": "dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{Path: "v1"}, - }, - Matches: []api.HTTPMatch{ - { - Headers: []api.HTTPHeaderMatch{ - { - Match: api.HTTPHeaderMatchExact, - Name: "my header match", - Value: "the value", - }, - }, - Method: api.HTTPMatchMethodGet, - Path: api.HTTPPathMatch{ - Match: api.HTTPPathMatchPrefix, - Value: "/v1", - }, - Query: []api.HTTPQueryMatch{ - { - Match: api.HTTPQueryMatchExact, - Name: "search", - Value: "term", - }, - }, - }, - }, - Services: []api.HTTPService{ - { - Name: "service one", - Weight: 45, - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "svc - add it on": "svc - the value", - }, - Remove: []string{"svc - time to go"}, - Set: map[string]string{ - "svc - Magic": "svc - v2", - "svc - Another One": "svc - dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{ - Path: "path", - }, - }, - Namespace: "some ns", - }, - }, - }, - }, - Hostnames: []string{"hostname.com"}, - Meta: map[string]string{ - constants.MetaKeyKubeName: "name", - }, - Status: api.ConfigEntryStatus{}, - }, - }, - }, - { - name: "same ref new cache has a greater modify index", - oldCache: loadedReferenceMaps([]api.ConfigEntry{ - &api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "my route", - ModifyIndex: 1, - Parents: []api.ResourceReference{ - { - Kind: api.APIGateway, - Name: "api-gw", - SectionName: "listener-1", - Namespace: "ns", - }, - }, - Rules: []api.HTTPRouteRule{ - { - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "add it on": "the value", - }, - Remove: []string{"time to go"}, - Set: map[string]string{ - "Magic": "v2", - "Another One": "dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{Path: "v1"}, - }, - Matches: []api.HTTPMatch{ - { - Headers: []api.HTTPHeaderMatch{ - { - Match: api.HTTPHeaderMatchExact, - Name: "my header match", - Value: "the value", - }, - }, - Method: api.HTTPMatchMethodGet, - Path: api.HTTPPathMatch{ - Match: api.HTTPPathMatchPrefix, - Value: "/v1", - }, - Query: []api.HTTPQueryMatch{ - { - Match: api.HTTPQueryMatchExact, - Name: "search", - Value: "term", - }, - }, - }, - }, - Services: []api.HTTPService{ - { - Name: "service one", - Weight: 45, - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "svc - add it on": "svc - the value", - }, - Remove: []string{"svc - time to go"}, - Set: map[string]string{ - "svc - Magic": "svc - v2", - "svc - Another One": "svc - dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{ - Path: "path", - }, - }, - Namespace: "some ns", - }, - }, - }, - }, - Hostnames: []string{"hostname.com"}, - Meta: map[string]string{ - constants.MetaKeyKubeName: "name", - }, - Status: api.ConfigEntryStatus{}, - }, - })[api.HTTPRoute], - args: args{ - newCache: loadedReferenceMaps([]api.ConfigEntry{ - &api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "my route", - ModifyIndex: 10, - Parents: []api.ResourceReference{ - { - Kind: api.APIGateway, - Name: "api-gw", - SectionName: "listener-1", - Namespace: "ns", - }, - }, - Rules: []api.HTTPRouteRule{ - { - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "add it on": "the value", - }, - Remove: []string{"time to go"}, - Set: map[string]string{ - "Magic": "v2", - "Another One": "dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{Path: "v1"}, - }, - Matches: []api.HTTPMatch{ - { - Headers: []api.HTTPHeaderMatch{ - { - Match: api.HTTPHeaderMatchExact, - Name: "my header match", - Value: "the value", - }, - }, - Method: api.HTTPMatchMethodGet, - Path: api.HTTPPathMatch{ - Match: api.HTTPPathMatchPrefix, - Value: "/v1", - }, - Query: []api.HTTPQueryMatch{ - { - Match: api.HTTPQueryMatchExact, - Name: "search", - Value: "term", - }, - }, - }, - }, - Services: []api.HTTPService{ - { - Name: "service one", - Weight: 45, - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "svc - add it on": "svc - the value", - }, - Remove: []string{"svc - time to go"}, - Set: map[string]string{ - "svc - Magic": "svc - v2", - "svc - Another One": "svc - dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{ - Path: "path", - }, - }, - Namespace: "some ns", - }, - }, - }, - }, - Hostnames: []string{"hostname.com"}, - Meta: map[string]string{ - constants.MetaKeyKubeName: "name", - }, - Status: api.ConfigEntryStatus{}, - }, - })[api.HTTPRoute], - }, - want: []api.ConfigEntry{ - &api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "my route", - ModifyIndex: 10, - Parents: []api.ResourceReference{ - { - Kind: api.APIGateway, - Name: "api-gw", - SectionName: "listener-1", - Namespace: "ns", - }, - }, - Rules: []api.HTTPRouteRule{ - { - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "add it on": "the value", - }, - Remove: []string{"time to go"}, - Set: map[string]string{ - "Magic": "v2", - "Another One": "dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{Path: "v1"}, - }, - Matches: []api.HTTPMatch{ - { - Headers: []api.HTTPHeaderMatch{ - { - Match: api.HTTPHeaderMatchExact, - Name: "my header match", - Value: "the value", - }, - }, - Method: api.HTTPMatchMethodGet, - Path: api.HTTPPathMatch{ - Match: api.HTTPPathMatchPrefix, - Value: "/v1", - }, - Query: []api.HTTPQueryMatch{ - { - Match: api.HTTPQueryMatchExact, - Name: "search", - Value: "term", - }, - }, - }, - }, - Services: []api.HTTPService{ - { - Name: "service one", - Weight: 45, - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "svc - add it on": "svc - the value", - }, - Remove: []string{"svc - time to go"}, - Set: map[string]string{ - "svc - Magic": "svc - v2", - "svc - Another One": "svc - dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{ - Path: "path", - }, - }, - Namespace: "some ns", - }, - }, - }, - }, - Hostnames: []string{"hostname.com"}, - Meta: map[string]string{ - constants.MetaKeyKubeName: "name", - }, - Status: api.ConfigEntryStatus{}, - }, - }, - }, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - got := tt.oldCache.Diff(tt.args.newCache) - if diff := cmp.Diff(got, tt.want); diff != "" { - t.Errorf("resourceCache.diff mismatch (-want +got):\n%s", diff) - } - }) - } -} - -func TestCache_Subscribe(t *testing.T) { - t.Parallel() - type args struct { - ctx context.Context - kind string - translator TranslatorFn - } - tests := []struct { - name string - args args - subscribers map[string][]*Subscription - subscriberChange int - }{ - { - name: "new subscription added when there are no other subscribers of the same kind", - args: args{ - ctx: context.Background(), - kind: api.HTTPRoute, - translator: func(api.ConfigEntry) []types.NamespacedName { - return []types.NamespacedName{} - }, - }, - subscriberChange: 1, - }, - { - name: "new subscription added when there are existing subscribers of the same kind", - args: args{ - ctx: context.Background(), - kind: api.HTTPRoute, - translator: func(api.ConfigEntry) []types.NamespacedName { - return []types.NamespacedName{} - }, - }, - subscribers: map[string][]*Subscription{ - api.HTTPRoute: { - { - translator: func(api.ConfigEntry) []types.NamespacedName { - return []types.NamespacedName{} - }, - ctx: context.Background(), - cancelCtx: func() { - }, - events: make(chan event.GenericEvent), - }, - }, - }, - subscriberChange: 1, - }, - { - name: "subscription for kind that does not exist does not change any subscriber counts", - args: args{ - ctx: context.Background(), - kind: "UnknownKind", - translator: func(api.ConfigEntry) []types.NamespacedName { - return []types.NamespacedName{} - }, - }, - subscriberChange: 0, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := New(Config{ - ConsulClientConfig: &consul.Config{ - APIClientConfig: &api.Config{}, - HTTPPort: 0, - GRPCPort: 0, - APITimeout: 0, - }, - ConsulServerConnMgr: consul.NewMockServerConnectionManager(t), - NamespacesEnabled: false, - Logger: logr.Logger{}, - }) - - if len(tt.subscribers) > 0 { - c.subscribers = tt.subscribers - } - - kindSubscriberCounts := make(map[string]int) - for kind, subscribers := range c.subscribers { - kindSubscriberCounts[kind] = len(subscribers) - } - - c.Subscribe(tt.args.ctx, tt.args.kind, tt.args.translator) - - for kind, subscribers := range c.subscribers { - expectedSubscriberCount := kindSubscriberCounts[kind] - if kind == tt.args.kind { - expectedSubscriberCount += tt.subscriberChange - } - actualSubscriberCount := len(subscribers) - - if expectedSubscriberCount != actualSubscriberCount { - t.Errorf("Expected there to be %d subscribers, there were %d", expectedSubscriberCount, actualSubscriberCount) - } - } - }) - } -} - -func TestCache_Write(t *testing.T) { - t.Parallel() - testCases := []struct { - name string - responseFn func(w http.ResponseWriter) - expectedErr error - }{ - { - name: "write is successful", - responseFn: func(w http.ResponseWriter) { - w.WriteHeader(200) - fmt.Fprintln(w, `{updated: true}`) - }, - expectedErr: nil, - }, - } - - for _, tt := range testCases { - t.Run(tt.name, func(t *testing.T) { - consulServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case "/v1/config": - tt.responseFn(w) - case "/v1/catalog/services": - fmt.Fprintln(w, `{}`) - default: - w.WriteHeader(500) - fmt.Fprintln(w, "Mock Server not configured for this route: "+r.URL.Path) - } - })) - defer consulServer.Close() - - serverURL, err := url.Parse(consulServer.URL) - require.NoError(t, err) - - port, err := strconv.Atoi(serverURL.Port()) - require.NoError(t, err) - - c := New(Config{ - ConsulClientConfig: &consul.Config{ - APIClientConfig: &api.Config{}, - HTTPPort: port, - GRPCPort: port, - APITimeout: 0, - }, - ConsulServerConnMgr: test.MockConnMgrForIPAndPort(serverURL.Hostname(), port), - NamespacesEnabled: false, - Logger: logrtest.NewTestLogger(t), - }) - - entry := &api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "my route", - Parents: []api.ResourceReference{ - { - Kind: api.APIGateway, - Name: "api-gw", - SectionName: "listener-1", - Namespace: "ns", - }, - }, - Rules: []api.HTTPRouteRule{ - { - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "add it on": "the value", - }, - Remove: []string{"time to go"}, - Set: map[string]string{ - "Magic": "v2", - "Another One": "dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{Path: "v1"}, - }, - Matches: []api.HTTPMatch{ - { - Headers: []api.HTTPHeaderMatch{ - { - Match: api.HTTPHeaderMatchExact, - Name: "my header match", - Value: "the value", - }, - }, - Method: api.HTTPMatchMethodGet, - Path: api.HTTPPathMatch{ - Match: api.HTTPPathMatchPrefix, - Value: "/v1", - }, - Query: []api.HTTPQueryMatch{ - { - Match: api.HTTPQueryMatchExact, - Name: "search", - Value: "term", - }, - }, - }, - }, - Services: []api.HTTPService{ - { - Name: "service one", - Weight: 45, - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "svc - add it on": "svc - the value", - }, - Remove: []string{"svc - time to go"}, - Set: map[string]string{ - "svc - Magic": "svc - v2", - "svc - Another One": "svc - dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{ - Path: "path", - }, - }, - Namespace: "some ns", - }, - }, - }, - }, - Hostnames: []string{"hostname.com"}, - Meta: map[string]string{ - constants.MetaKeyKubeName: "name", - }, - Status: api.ConfigEntryStatus{}, - } - - err = c.Write(context.Background(), entry) - require.Equal(t, err, tt.expectedErr) - }) - } -} - -func TestCache_Get(t *testing.T) { - t.Parallel() - type args struct { - ref api.ResourceReference - } - tests := []struct { - name string - args args - want api.ConfigEntry - cache map[string]*common.ReferenceMap - }{ - { - name: "entry exists", - args: args{ - ref: api.ResourceReference{ - Kind: api.APIGateway, - Name: "api-gw", - }, - }, - want: &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "api-gw", - Meta: map[string]string{ - constants.MetaKeyKubeName: "name", - }, - }, - cache: loadedReferenceMaps([]api.ConfigEntry{ - &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "api-gw", - Meta: map[string]string{ - constants.MetaKeyKubeName: "name", - }, - }, - &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "api-gw-2", - Meta: map[string]string{ - constants.MetaKeyKubeName: "name", - }, - }, - }), - }, - { - name: "entry does not exist", - args: args{ - ref: api.ResourceReference{ - Kind: api.APIGateway, - Name: "api-gw-4", - }, - }, - want: nil, - cache: loadedReferenceMaps([]api.ConfigEntry{ - &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "api-gw", - Meta: map[string]string{ - constants.MetaKeyKubeName: "name", - }, - }, - &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "api-gw-2", - Meta: map[string]string{ - constants.MetaKeyKubeName: "name", - }, - }, - }), - }, - { - name: "kind key does not exist", - args: args{ - ref: api.ResourceReference{ - Kind: api.APIGateway, - Name: "api-gw-4", - }, - }, - want: nil, - cache: loadedReferenceMaps([]api.ConfigEntry{ - &api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "route", - Meta: map[string]string{ - constants.MetaKeyKubeName: "name", - }, - }, - }), - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := New(Config{ - ConsulClientConfig: &consul.Config{ - APIClientConfig: &api.Config{}, - }, - }) - c.cache = tt.cache - - got := c.Get(tt.args.ref) - - if diff := cmp.Diff(got, tt.want); diff != "" { - t.Errorf("Cache.Get mismatch (-want +got):\n%s", diff) - } - }) - } -} - -func Test_Run(t *testing.T) { - t.Parallel() - // setup httproutes - httpRouteOne, httpRouteTwo := setupHTTPRoutes() - httpRoutes := []*api.HTTPRouteConfigEntry{httpRouteOne, httpRouteTwo} - - // setup gateway - gw := setupGateway() - gateways := []*api.APIGatewayConfigEntry{gw} - - // setup TCPRoutes - tcpRoute := setupTCPRoute() - tcpRoutes := []*api.TCPRouteConfigEntry{tcpRoute} - - // setup inline certs - inlineCert := setupInlineCertificate() - certs := []*api.InlineCertificateConfigEntry{inlineCert} - - consulServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case "/v1/config/http-route": - val, err := json.Marshal(httpRoutes) - if err != nil { - w.WriteHeader(500) - fmt.Fprintln(w, err) - return - } - fmt.Fprintln(w, string(val)) - case "/v1/config/api-gateway": - val, err := json.Marshal(gateways) - if err != nil { - w.WriteHeader(500) - fmt.Fprintln(w, err) - return - } - fmt.Fprintln(w, string(val)) - case "/v1/config/tcp-route": - val, err := json.Marshal(tcpRoutes) - if err != nil { - w.WriteHeader(500) - fmt.Fprintln(w, err) - return - } - fmt.Fprintln(w, string(val)) - case "/v1/config/inline-certificate": - val, err := json.Marshal(certs) - if err != nil { - w.WriteHeader(500) - fmt.Fprintln(w, err) - return - } - fmt.Fprintln(w, string(val)) - case "/v1/catalog/services": - fmt.Fprintln(w, `{}`) - case "/v1/peerings": - fmt.Fprintln(w, `[]`) - default: - w.WriteHeader(500) - fmt.Fprintln(w, "Mock Server not configured for this route: "+r.URL.Path) - } - })) - defer consulServer.Close() - - serverURL, err := url.Parse(consulServer.URL) - require.NoError(t, err) - - port, err := strconv.Atoi(serverURL.Port()) - require.NoError(t, err) - - c := New(Config{ - ConsulClientConfig: &consul.Config{ - APIClientConfig: &api.Config{}, - HTTPPort: port, - GRPCPort: port, - APITimeout: 0, - }, - ConsulServerConnMgr: test.MockConnMgrForIPAndPort(serverURL.Hostname(), port), - NamespacesEnabled: false, - Logger: logrtest.NewTestLogger(t), - }) - prevCache := make(map[string]*common.ReferenceMap) - for kind, cache := range c.cache { - resCache := common.NewReferenceMap() - for _, entry := range cache.Entries() { - resCache.Set(common.EntryToReference(entry), entry) - } - prevCache[kind] = resCache - } - - expectedCache := loadedReferenceMaps([]api.ConfigEntry{ - gw, tcpRoute, httpRouteOne, httpRouteTwo, inlineCert, - }) - - ctx, cancelFn := context.WithCancel(context.Background()) - - httpRouteOneNsn := types.NamespacedName{ - Name: httpRouteOne.Name, - Namespace: httpRouteOne.Namespace, - } - - httpRouteTwoNsn := types.NamespacedName{ - Name: httpRouteTwo.Name, - Namespace: httpRouteTwo.Namespace, - } - - httpRouteSubscriber := c.Subscribe(ctx, api.HTTPRoute, func(cfe api.ConfigEntry) []types.NamespacedName { - return []types.NamespacedName{ - {Name: cfe.GetName(), Namespace: cfe.GetNamespace()}, - } - }) - - canceledSub := c.Subscribe(ctx, api.HTTPRoute, func(cfe api.ConfigEntry) []types.NamespacedName { - return []types.NamespacedName{ - {Name: cfe.GetName(), Namespace: cfe.GetNamespace()}, - } - }) - - gwNsn := types.NamespacedName{ - Name: gw.Name, - Namespace: gw.Namespace, - } - - gwSubscriber := c.Subscribe(ctx, api.APIGateway, func(cfe api.ConfigEntry) []types.NamespacedName { - return []types.NamespacedName{ - {Name: cfe.GetName(), Namespace: cfe.GetNamespace()}, - } - }) - - tcpRouteNsn := types.NamespacedName{ - Name: tcpRoute.Name, - Namespace: tcpRoute.Namespace, - } - - tcpRouteSubscriber := c.Subscribe(ctx, api.TCPRoute, func(cfe api.ConfigEntry) []types.NamespacedName { - return []types.NamespacedName{ - {Name: cfe.GetName(), Namespace: cfe.GetNamespace()}, - } - }) - - certNsn := types.NamespacedName{ - Name: inlineCert.Name, - Namespace: inlineCert.Namespace, - } - - certSubscriber := c.Subscribe(ctx, api.InlineCertificate, func(cfe api.ConfigEntry) []types.NamespacedName { - return []types.NamespacedName{ - {Name: cfe.GetName(), Namespace: cfe.GetNamespace()}, - } - }) - - // mark this subscription as ended - canceledSub.Cancel() - - go c.Run(ctx) - - // Check subscribers - httpRouteExpectedEvents := []event.GenericEvent{{Object: newConfigEntryObject(httpRouteOneNsn)}, {Object: newConfigEntryObject(httpRouteTwoNsn)}} - gwExpectedEvent := event.GenericEvent{Object: newConfigEntryObject(gwNsn)} - tcpExpectedEvent := event.GenericEvent{Object: newConfigEntryObject(tcpRouteNsn)} - certExpectedEvent := event.GenericEvent{Object: newConfigEntryObject(certNsn)} - - // 2 http routes + 1 gw + 1 tcp route + 1 cert = 5 - i := 5 - for { - if i == 0 { - break - } - select { - case actualHTTPRouteEvent := <-httpRouteSubscriber.Events(): - require.Contains(t, httpRouteExpectedEvents, actualHTTPRouteEvent) - case actualGWEvent := <-gwSubscriber.Events(): - require.Equal(t, gwExpectedEvent, actualGWEvent) - case actualTCPRouteEvent := <-tcpRouteSubscriber.Events(): - require.Equal(t, tcpExpectedEvent, actualTCPRouteEvent) - case actualCertExpectedEvent := <-certSubscriber.Events(): - require.Equal(t, certExpectedEvent, actualCertExpectedEvent) - } - i -= 1 - } - - // the canceled Subscription should not receive any events - require.Zero(t, len(canceledSub.Events())) - c.WaitSynced(ctx) - - // cancel the context so the Run function exits - cancelFn() - - sorter := func(x, y api.ConfigEntry) bool { - return x.GetName() < y.GetName() - } - // Check cache - // expect the cache to have changed - for _, kind := range Kinds { - if diff := cmp.Diff(prevCache[kind].Entries(), c.cache[kind].Entries(), cmpopts.SortSlices(sorter)); diff == "" { - t.Error("Expect cache to have changed but it did not") - } - - if diff := cmp.Diff(expectedCache[kind].Entries(), c.cache[kind].Entries(), cmpopts.SortSlices(sorter)); diff != "" { - t.Errorf("Cache.cache mismatch (-want +got):\n%s", diff) - } - } -} - -func setupHTTPRoutes() (*api.HTTPRouteConfigEntry, *api.HTTPRouteConfigEntry) { - routeOne := &api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "my route", - Parents: []api.ResourceReference{ - { - Kind: api.APIGateway, - Name: "api-gw", - SectionName: "listener-1", - Namespace: "ns", - }, - }, - Rules: []api.HTTPRouteRule{ - { - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "add it on": "the value", - }, - Remove: []string{"time to go"}, - Set: map[string]string{ - "Magic": "v2", - "Another One": "dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{Path: "v1"}, - }, - Matches: []api.HTTPMatch{ - { - Headers: []api.HTTPHeaderMatch{ - { - Match: api.HTTPHeaderMatchExact, - Name: "my header match", - Value: "the value", - }, - }, - Method: api.HTTPMatchMethodGet, - Path: api.HTTPPathMatch{ - Match: api.HTTPPathMatchPrefix, - Value: "/v1", - }, - Query: []api.HTTPQueryMatch{ - { - Match: api.HTTPQueryMatchExact, - Name: "search", - Value: "term", - }, - }, - }, - }, - Services: []api.HTTPService{ - { - Name: "service one", - Weight: 45, - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "svc - add it on": "svc - the value", - }, - Remove: []string{"svc - time to go"}, - Set: map[string]string{ - "svc - Magic": "svc - v2", - "svc - Another One": "svc - dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{ - Path: "path", - }, - }, - Namespace: "some ns", - }, - }, - }, - }, - Hostnames: []string{"hostname.com"}, - Meta: map[string]string{ - "metaKey": "metaVal", - constants.MetaKeyKubeName: "name", - }, - Status: api.ConfigEntryStatus{}, - } - routeTwo := &api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "my route 2", - Parents: []api.ResourceReference{ - { - Kind: api.APIGateway, - Name: "api-gw", - SectionName: "listener-2", - Namespace: "ns", - }, - }, - Rules: []api.HTTPRouteRule{ - { - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "add it on": "the value", - }, - Remove: []string{"time to go"}, - Set: map[string]string{ - "Magic": "v2", - "Another One": "dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{Path: "v1"}, - }, - Matches: []api.HTTPMatch{ - { - Headers: []api.HTTPHeaderMatch{ - { - Match: api.HTTPHeaderMatchExact, - Name: "my header match", - Value: "the value", - }, - }, - Method: api.HTTPMatchMethodGet, - Path: api.HTTPPathMatch{ - Match: api.HTTPPathMatchPrefix, - Value: "/v1", - }, - Query: []api.HTTPQueryMatch{ - { - Match: api.HTTPQueryMatchExact, - Name: "search", - Value: "term", - }, - }, - }, - }, - Services: []api.HTTPService{ - { - Name: "service one", - Weight: 45, - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "svc - add it on": "svc - the value", - }, - Remove: []string{"svc - time to go"}, - Set: map[string]string{ - "svc - Magic": "svc - v2", - "svc - Another One": "svc - dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{ - Path: "path", - }, - }, - Namespace: "some ns", - }, - }, - }, - }, - Hostnames: []string{"hostname.com"}, - Meta: map[string]string{ - "metakey": "meta val", - constants.MetaKeyKubeName: "name", - }, - } - return routeOne, routeTwo -} - -func setupGateway() *api.APIGatewayConfigEntry { - return &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: "api-gw", - Meta: map[string]string{ - "metakey": "meta val", - constants.MetaKeyKubeName: "name", - }, - Listeners: []api.APIGatewayListener{ - { - Name: "listener one", - Hostname: "hostname.com", - Port: 3350, - Protocol: "https", - TLS: api.APIGatewayTLSConfiguration{}, - }, - }, - } -} - -func setupTCPRoute() *api.TCPRouteConfigEntry { - return &api.TCPRouteConfigEntry{ - Kind: api.TCPRoute, - Name: "tcp route", - Parents: []api.ResourceReference{ - { - Kind: api.APIGateway, - Name: "api-gw", - SectionName: "listener two", - }, - }, - Services: []api.TCPService{ - { - Name: "tcp service", - }, - }, - Meta: map[string]string{ - "metakey": "meta val", - constants.MetaKeyKubeName: "name", - }, - Status: api.ConfigEntryStatus{}, - } -} - -func setupInlineCertificate() *api.InlineCertificateConfigEntry { - return &api.InlineCertificateConfigEntry{ - Kind: api.InlineCertificate, - Name: "inline-cert", - Certificate: "cert", - PrivateKey: "super secret", - Meta: map[string]string{ - "metaKey": "meta val", - constants.MetaKeyKubeName: "name", - }, - } -} - -func TestCache_Delete(t *testing.T) { - t.Parallel() - testCases := []struct { - name string - responseFn func(w http.ResponseWriter) - expectedErr error - }{ - { - name: "delete is successful", - responseFn: func(w http.ResponseWriter) { - w.WriteHeader(200) - fmt.Fprintln(w, `{deleted: true}`) - }, - expectedErr: nil, - }, - } - for _, tt := range testCases { - t.Run(tt.name, func(t *testing.T) { - ref := api.ResourceReference{ - Name: "my-route", - Kind: api.HTTPRoute, - } - consulServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case fmt.Sprintf("/v1/config/%s/%s", ref.Kind, ref.Name): - tt.responseFn(w) - default: - w.WriteHeader(500) - fmt.Fprintln(w, "Mock Server not configured for this route: "+r.URL.Path) - } - })) - defer consulServer.Close() - - serverURL, err := url.Parse(consulServer.URL) - require.NoError(t, err) - - port, err := strconv.Atoi(serverURL.Port()) - require.NoError(t, err) - - c := New(Config{ - ConsulClientConfig: &consul.Config{ - APIClientConfig: &api.Config{}, - HTTPPort: port, - GRPCPort: port, - APITimeout: 0, - }, - ConsulServerConnMgr: test.MockConnMgrForIPAndPort(serverURL.Hostname(), port), - NamespacesEnabled: false, - Logger: logrtest.NewTestLogger(t), - }) - - err = c.Delete(context.Background(), ref) - require.ErrorIs(t, err, tt.expectedErr) - }) - } -} - -func loadedReferenceMaps(entries []api.ConfigEntry) map[string]*common.ReferenceMap { - refs := make(map[string]*common.ReferenceMap) - - for _, entry := range entries { - refMap, ok := refs[entry.GetKind()] - if !ok { - refMap = common.NewReferenceMap() - } - refMap.Set(common.EntryToReference(entry), entry) - refs[entry.GetKind()] = refMap - } - return refs -} diff --git a/control-plane/api-gateway/cache/gateway.go b/control-plane/api-gateway/cache/gateway.go deleted file mode 100644 index 0d79542eec..0000000000 --- a/control-plane/api-gateway/cache/gateway.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package cache - -import ( - "context" - "fmt" - "sync" - - "github.com/cenkalti/backoff" - "github.com/go-logr/logr" - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/consul" - "github.com/hashicorp/consul/api" - "k8s.io/apimachinery/pkg/types" -) - -type GatewayCache struct { - config Config - serverMgr consul.ServerConnectionManager - logger logr.Logger - - data map[api.ResourceReference][]api.CatalogService - dataMutex sync.RWMutex - - subscribedGateways map[api.ResourceReference]context.CancelFunc - mutex sync.RWMutex - - ctx context.Context -} - -func NewGatewayCache(ctx context.Context, config Config) *GatewayCache { - return &GatewayCache{ - config: config, - serverMgr: config.ConsulServerConnMgr, - logger: config.Logger, - data: make(map[api.ResourceReference][]api.CatalogService), - subscribedGateways: make(map[api.ResourceReference]context.CancelFunc), - ctx: ctx, - } -} - -func (r *GatewayCache) ServicesFor(ref api.ResourceReference) []api.CatalogService { - r.dataMutex.RLock() - defer r.dataMutex.RUnlock() - - return r.data[common.NormalizeMeta(ref)] -} - -func (r *GatewayCache) FetchServicesFor(ctx context.Context, ref api.ResourceReference) ([]api.CatalogService, error) { - client, err := consul.NewClientFromConnMgr(r.config.ConsulClientConfig, r.serverMgr) - if err != nil { - return nil, err - } - - opts := &api.QueryOptions{} - if r.config.NamespacesEnabled && ref.Namespace != "" { - opts.Namespace = ref.Namespace - } - - services, _, err := client.Catalog().Service(ref.Name, "", opts.WithContext(ctx)) - if err != nil { - return nil, err - } - return common.DerefAll(services), nil -} - -func (r *GatewayCache) EnsureSubscribed(ref api.ResourceReference, resource types.NamespacedName) { - r.mutex.Lock() - defer r.mutex.Unlock() - - if _, exists := r.subscribedGateways[common.NormalizeMeta(ref)]; exists { - return - } - - ctx, cancel := context.WithCancel(r.ctx) - r.subscribedGateways[common.NormalizeMeta(ref)] = cancel - go r.subscribeToGateway(ctx, ref, resource) -} - -func (r *GatewayCache) RemoveSubscription(ref api.ResourceReference) { - r.mutex.Lock() - defer r.mutex.Unlock() - - if cancel, exists := r.subscribedGateways[common.NormalizeMeta(ref)]; exists { - cancel() - delete(r.subscribedGateways, common.NormalizeMeta(ref)) - } -} - -func (r *GatewayCache) subscribeToGateway(ctx context.Context, ref api.ResourceReference, resource types.NamespacedName) { - opts := &api.QueryOptions{} - if r.config.NamespacesEnabled && ref.Namespace != "" { - opts.Namespace = ref.Namespace - } - - var ( - services []*api.CatalogService - meta *api.QueryMeta - ) - - for { - select { - case <-ctx.Done(): - r.dataMutex.Lock() - delete(r.data, ref) - r.dataMutex.Unlock() - return - default: - } - - retryBackoff := backoff.WithMaxRetries(backoff.NewExponentialBackOff(), 10) - - if err := backoff.Retry(func() error { - client, err := consul.NewClientFromConnMgr(r.config.ConsulClientConfig, r.serverMgr) - if err != nil { - return err - } - - services, meta, err = client.Catalog().Service(ref.Name, "", opts.WithContext(ctx)) - if err != nil { - return err - } - - return nil - }, backoff.WithContext(retryBackoff, ctx)); err != nil { - r.logger.Error(err, fmt.Sprintf("unable to fetch config entry for gateway: %s/%s", ref.Namespace, ref.Name)) - continue - } - - opts.WaitIndex = meta.LastIndex - - derefed := common.DerefAll(services) - - r.dataMutex.Lock() - r.data[common.NormalizeMeta(ref)] = derefed - r.dataMutex.Unlock() - } -} diff --git a/control-plane/api-gateway/cache/kubernetes.go b/control-plane/api-gateway/cache/kubernetes.go deleted file mode 100644 index 642a6935fb..0000000000 --- a/control-plane/api-gateway/cache/kubernetes.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package cache - -import ( - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// configEntryObject is used for generic k8s events so we maintain the consul name/namespace. -type configEntryObject struct { - client.Object // embed so we fufill the object interface - - Namespace string - Name string -} - -func (c *configEntryObject) GetNamespace() string { - return c.Namespace -} - -func (c *configEntryObject) GetName() string { - return c.Name -} - -func newConfigEntryObject(namespacedName types.NamespacedName) *configEntryObject { - return &configEntryObject{ - Namespace: namespacedName.Namespace, - Name: namespacedName.Name, - } -} diff --git a/control-plane/api-gateway/cache/subscription.go b/control-plane/api-gateway/cache/subscription.go deleted file mode 100644 index 8605c95926..0000000000 --- a/control-plane/api-gateway/cache/subscription.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package cache - -import ( - "context" - - "github.com/hashicorp/consul/api" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/event" -) - -type TranslatorFn func(api.ConfigEntry) []types.NamespacedName - -// Subscription represents a watcher for events on a specific kind. -type Subscription struct { - translator TranslatorFn - ctx context.Context - cancelCtx context.CancelFunc - events chan event.GenericEvent -} - -func (s *Subscription) Cancel() { - s.cancelCtx() -} - -func (s *Subscription) Events() chan event.GenericEvent { - return s.events -} diff --git a/control-plane/api-gateway/common/constants.go b/control-plane/api-gateway/common/constants.go deleted file mode 100644 index 04701662b7..0000000000 --- a/control-plane/api-gateway/common/constants.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package common - -const ( - GatewayClassControllerName = "consul.hashicorp.com/gateway-controller" - - AnnotationGatewayClassConfig = "consul.hashicorp.com/gateway-class-config" - - // The following annotation keys are used in the v1beta1.GatewayTLSConfig's Options on a v1beta1.Listener. - TLSCipherSuitesAnnotationKey = "api-gateway.consul.hashicorp.com/tls_cipher_suites" - TLSMaxVersionAnnotationKey = "api-gateway.consul.hashicorp.com/tls_max_version" - TLSMinVersionAnnotationKey = "api-gateway.consul.hashicorp.com/tls_min_version" -) diff --git a/control-plane/api-gateway/common/diff.go b/control-plane/api-gateway/common/diff.go deleted file mode 100644 index a50581ca31..0000000000 --- a/control-plane/api-gateway/common/diff.go +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package common - -import ( - "strings" - - "github.com/hashicorp/consul/api" - "golang.org/x/exp/maps" - "golang.org/x/exp/slices" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -func GatewayStatusesEqual(a, b gwv1beta1.GatewayStatus) bool { - return slices.EqualFunc(a.Addresses, b.Addresses, gatewayStatusesAddressesEqual) && - slices.EqualFunc(a.Conditions, b.Conditions, conditionsEqual) && - slices.EqualFunc(a.Listeners, b.Listeners, gatewayStatusesListenersEqual) -} - -func gatewayStatusesAddressesEqual(a, b gwv1beta1.GatewayAddress) bool { - return BothNilOrEqual(a.Type, b.Type) && - a.Value == b.Value -} - -func gatewayStatusesListenersEqual(a, b gwv1beta1.ListenerStatus) bool { - return a.AttachedRoutes == b.AttachedRoutes && - a.Name == b.Name && - slices.EqualFunc(a.SupportedKinds, b.SupportedKinds, routeGroupKindsEqual) && - slices.EqualFunc(a.Conditions, b.Conditions, conditionsEqual) -} - -func routeGroupKindsEqual(a, b gwv1beta1.RouteGroupKind) bool { - return BothNilOrEqual(a.Group, b.Group) && - a.Kind == b.Kind -} - -// this intentionally ignores the last set time so we don't -// always fail a conditional check per-reconciliation. -func conditionsEqual(a, b metav1.Condition) bool { - return a.Type == b.Type && - a.Status == b.Status && - a.Reason == b.Reason && - a.Message == b.Message && - a.ObservedGeneration == b.ObservedGeneration -} - -func EntriesEqual(a, b api.ConfigEntry) bool { - switch aCast := a.(type) { - case *api.APIGatewayConfigEntry: - if bCast, ok := b.(*api.APIGatewayConfigEntry); ok { - return apiGatewaysEqual(aCast, bCast) - } - case *api.HTTPRouteConfigEntry: - if bCast, ok := b.(*api.HTTPRouteConfigEntry); ok { - return httpRoutesEqual(aCast, bCast) - } - case *api.TCPRouteConfigEntry: - if bCast, ok := b.(*api.TCPRouteConfigEntry); ok { - return tcpRoutesEqual(aCast, bCast) - } - case *api.InlineCertificateConfigEntry: - if bCast, ok := b.(*api.InlineCertificateConfigEntry); ok { - return certificatesEqual(aCast, bCast) - } - } - return false -} - -type entryComparator struct { - namespaceA string - partitionA string - namespaceB string - partitionB string -} - -func apiGatewaysEqual(a, b *api.APIGatewayConfigEntry) bool { - if a == nil || b == nil { - return false - } - - return (entryComparator{ - namespaceA: NormalizeEmptyMetadataString(a.Namespace), - partitionA: NormalizeEmptyMetadataString(a.Partition), - namespaceB: NormalizeEmptyMetadataString(b.Namespace), - partitionB: NormalizeEmptyMetadataString(b.Partition), - }).apiGatewaysEqual(*a, *b) -} - -func (e entryComparator) apiGatewaysEqual(a, b api.APIGatewayConfigEntry) bool { - return a.Kind == b.Kind && - a.Name == b.Name && - e.namespaceA == e.namespaceB && - e.partitionA == e.partitionB && - maps.Equal(a.Meta, b.Meta) && - slices.EqualFunc(a.Listeners, b.Listeners, e.apiGatewayListenersEqual) -} - -func (e entryComparator) apiGatewayListenersEqual(a, b api.APIGatewayListener) bool { - return a.Hostname == b.Hostname && - a.Name == b.Name && - a.Port == b.Port && - // normalize the protocol name - strings.EqualFold(a.Protocol, b.Protocol) && - e.apiGatewayListenerTLSConfigurationsEqual(a.TLS, b.TLS) -} - -func (e entryComparator) apiGatewayListenerTLSConfigurationsEqual(a, b api.APIGatewayTLSConfiguration) bool { - return a.MaxVersion == b.MaxVersion && - a.MinVersion == b.MinVersion && - slices.Equal(a.CipherSuites, b.CipherSuites) && - slices.EqualFunc(a.Certificates, b.Certificates, e.resourceReferencesEqual) -} - -func (e entryComparator) resourceReferencesEqual(a, b api.ResourceReference) bool { - return a.Kind == b.Kind && - a.Name == b.Name && - a.SectionName == b.SectionName && - orDefault(a.Namespace, e.namespaceA) == orDefault(b.Namespace, e.namespaceB) && - orDefault(a.Partition, e.partitionA) == orDefault(b.Partition, e.partitionB) -} - -func httpRoutesEqual(a, b *api.HTTPRouteConfigEntry) bool { - if a == nil || b == nil { - return false - } - - return (entryComparator{ - namespaceA: NormalizeEmptyMetadataString(a.Namespace), - partitionA: NormalizeEmptyMetadataString(a.Partition), - namespaceB: NormalizeEmptyMetadataString(b.Namespace), - partitionB: NormalizeEmptyMetadataString(b.Partition), - }).httpRoutesEqual(*a, *b) -} - -func (e entryComparator) httpRoutesEqual(a, b api.HTTPRouteConfigEntry) bool { - return a.Kind == b.Kind && - a.Name == b.Name && - e.namespaceA == e.namespaceB && - e.partitionA == e.partitionB && - maps.Equal(a.Meta, b.Meta) && - slices.Equal(a.Hostnames, b.Hostnames) && - slices.EqualFunc(a.Parents, b.Parents, e.resourceReferencesEqual) && - slices.EqualFunc(a.Rules, b.Rules, e.httpRouteRulesEqual) -} - -func (e entryComparator) httpRouteRulesEqual(a, b api.HTTPRouteRule) bool { - return slices.EqualFunc(a.Filters.Headers, b.Filters.Headers, e.httpHeaderFiltersEqual) && - bothNilOrEqualFunc(a.Filters.URLRewrite, b.Filters.URLRewrite, e.urlRewritesEqual) && - slices.EqualFunc(a.Matches, b.Matches, e.httpMatchesEqual) && - slices.EqualFunc(a.Services, b.Services, e.httpServicesEqual) && - bothNilOrEqualFunc(a.Filters.RetryFilter, b.Filters.RetryFilter, e.retryFiltersEqual) && - bothNilOrEqualFunc(a.Filters.TimeoutFilter, b.Filters.TimeoutFilter, e.timeoutFiltersEqual) -} - -func (e entryComparator) httpServicesEqual(a, b api.HTTPService) bool { - return a.Name == b.Name && - a.Weight == b.Weight && - orDefault(a.Namespace, e.namespaceA) == orDefault(b.Namespace, e.namespaceB) && - orDefault(a.Partition, e.partitionA) == orDefault(b.Partition, e.partitionB) && - slices.EqualFunc(a.Filters.Headers, b.Filters.Headers, e.httpHeaderFiltersEqual) && - bothNilOrEqualFunc(a.Filters.URLRewrite, b.Filters.URLRewrite, e.urlRewritesEqual) -} - -func (e entryComparator) httpMatchesEqual(a, b api.HTTPMatch) bool { - return a.Method == b.Method && - slices.EqualFunc(a.Headers, b.Headers, e.httpHeaderMatchesEqual) && - slices.EqualFunc(a.Query, b.Query, e.httpQueryMatchesEqual) && - e.httpPathMatchesEqual(a.Path, b.Path) -} - -func (e entryComparator) httpPathMatchesEqual(a, b api.HTTPPathMatch) bool { - return a.Match == b.Match && a.Value == b.Value -} - -func (e entryComparator) httpHeaderMatchesEqual(a, b api.HTTPHeaderMatch) bool { - return a.Match == b.Match && a.Name == b.Name && a.Value == b.Value -} - -func (e entryComparator) httpQueryMatchesEqual(a, b api.HTTPQueryMatch) bool { - return a.Match == b.Match && a.Name == b.Name && a.Value == b.Value -} - -func (e entryComparator) httpHeaderFiltersEqual(a, b api.HTTPHeaderFilter) bool { - return maps.Equal(a.Add, b.Add) && - maps.Equal(a.Set, b.Set) && - slices.Equal(a.Remove, b.Remove) -} - -func (e entryComparator) urlRewritesEqual(a, b api.URLRewrite) bool { - return a.Path == b.Path -} - -func (e entryComparator) retryFiltersEqual(a, b api.RetryFilter) bool { - return BothNilOrEqual(a.NumRetries, b.NumRetries) && BothNilOrEqual(a.RetryOnConnectFailure, b.RetryOnConnectFailure) && - slices.Equal(a.RetryOn, b.RetryOn) && slices.Equal(a.RetryOnStatusCodes, b.RetryOnStatusCodes) -} - -func (e entryComparator) timeoutFiltersEqual(a, b api.TimeoutFilter) bool { - return a.RequestTimeout == b.RequestTimeout && a.IdleTimeout == b.IdleTimeout -} - -func tcpRoutesEqual(a, b *api.TCPRouteConfigEntry) bool { - if a == nil || b == nil { - return false - } - - return (entryComparator{ - namespaceA: NormalizeEmptyMetadataString(a.Namespace), - partitionA: NormalizeEmptyMetadataString(a.Partition), - namespaceB: NormalizeEmptyMetadataString(b.Namespace), - partitionB: NormalizeEmptyMetadataString(b.Partition), - }).tcpRoutesEqual(*a, *b) -} - -func (e entryComparator) tcpRoutesEqual(a, b api.TCPRouteConfigEntry) bool { - return a.Kind == b.Kind && - a.Name == b.Name && - e.namespaceA == e.namespaceB && - e.partitionA == e.partitionB && - maps.Equal(a.Meta, b.Meta) && - slices.EqualFunc(a.Parents, b.Parents, e.resourceReferencesEqual) && - slices.EqualFunc(a.Services, b.Services, e.tcpRouteServicesEqual) -} - -func (e entryComparator) tcpRouteServicesEqual(a, b api.TCPService) bool { - return a.Name == b.Name && - orDefault(a.Namespace, e.namespaceA) == orDefault(b.Namespace, e.namespaceB) && - orDefault(a.Partition, e.partitionA) == orDefault(b.Partition, e.partitionB) -} - -func certificatesEqual(a, b *api.InlineCertificateConfigEntry) bool { - if a == nil || b == nil { - return false - } - - return (entryComparator{ - namespaceA: NormalizeEmptyMetadataString(a.Namespace), - partitionA: NormalizeEmptyMetadataString(a.Partition), - namespaceB: NormalizeEmptyMetadataString(b.Namespace), - partitionB: NormalizeEmptyMetadataString(b.Partition), - }).certificatesEqual(*a, *b) -} - -func (e entryComparator) certificatesEqual(a, b api.InlineCertificateConfigEntry) bool { - return a.Kind == b.Kind && - a.Name == b.Name && - e.namespaceA == e.namespaceB && - e.partitionA == e.partitionB && - maps.Equal(a.Meta, b.Meta) && - a.Certificate == b.Certificate && - a.PrivateKey == b.PrivateKey -} - -func bothNilOrEqualFunc[T any](one, two *T, fn func(T, T) bool) bool { - if one == nil && two == nil { - return true - } - if one == nil { - return false - } - if two == nil { - return false - } - return fn(*one, *two) -} - -func orDefault[T ~string](v T, fallback string) string { - if v == "" { - return fallback - } - return string(v) -} diff --git a/control-plane/api-gateway/common/finalizers.go b/control-plane/api-gateway/common/finalizers.go deleted file mode 100644 index e1fe84bdac..0000000000 --- a/control-plane/api-gateway/common/finalizers.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package common - -import ( - "sigs.k8s.io/controller-runtime/pkg/client" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -const ( - // GatewayFinalizer is the finalizer we add to any gateway object. - GatewayFinalizer = "gateway-finalizer.consul.hashicorp.com" - - // NamespaceNameLabel represents that label added automatically to namespaces in newer Kubernetes clusters. - NamespaceNameLabel = "kubernetes.io/metadata.name" -) - -var ( - // constants extracted for ease of use. - KindGateway = "Gateway" - KindSecret = "Secret" - KindService = "Service" - BetaGroup = gwv1beta1.GroupVersion.Group -) - -// EnsureFinalizer ensures that our finalizer is set on an object -// returning whether or not it modified the object. -func EnsureFinalizer(object client.Object) bool { - if !object.GetDeletionTimestamp().IsZero() { - return false - } - - finalizers := object.GetFinalizers() - for _, f := range finalizers { - if f == GatewayFinalizer { - return false - } - } - - object.SetFinalizers(append(finalizers, GatewayFinalizer)) - return true -} - -// RemoveFinalizer ensures that our finalizer is absent from an object -// returning whether or not it modified the object. -func RemoveFinalizer(object client.Object) bool { - found := false - filtered := []string{} - for _, f := range object.GetFinalizers() { - if f == GatewayFinalizer { - found = true - continue - } - filtered = append(filtered, f) - } - - object.SetFinalizers(filtered) - return found -} diff --git a/control-plane/api-gateway/common/helm_config.go b/control-plane/api-gateway/common/helm_config.go deleted file mode 100644 index ecd9d42c29..0000000000 --- a/control-plane/api-gateway/common/helm_config.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package common - -import ( - "strings" - "time" -) - -const componentAuthMethod = "k8s-component-auth-method" - -// HelmConfig is the configuration of gateways that comes in from the user's Helm values. -// This is a combination of the apiGateway stanza and other settings that impact api-gateways. -type HelmConfig struct { - // ImageDataplane is the Consul Dataplane image to use in gateway deployments. - ImageDataplane string - ImageConsulK8S string - ConsulDestinationNamespace string - NamespaceMirroringPrefix string - EnableNamespaces bool - EnableNamespaceMirroring bool - AuthMethod string - // LogLevel is the logging level of the deployed Consul Dataplanes. - LogLevel string - ConsulPartition string - LogJSON bool - TLSEnabled bool - PeeringEnabled bool - ConsulTLSServerName string - ConsulCACert string - ConsulConfig ConsulConfig - - // EnableOpenShift indicates whether we're deploying into an OpenShift environment - // and should create SecurityContextConstraints. - EnableOpenShift bool - - // MapPrivilegedServicePorts is the value which Consul will add to privileged container port values (ports < 1024) - // defined on a Gateway. - MapPrivilegedServicePorts int -} - -type ConsulConfig struct { - Address string - GRPCPort int - HTTPPort int - APITimeout time.Duration -} - -func (h HelmConfig) Normalize() HelmConfig { - if h.AuthMethod != "" { - // strip off any DC naming off the back in case we're - // in a secondary DC, in which case our auth method is - // going to be a globally scoped auth method, and we want - // to target the locally scoped one, which is the auth - // method without the DC-specific suffix. - tokens := strings.Split(h.AuthMethod, componentAuthMethod) - if len(tokens) != 2 { - // skip the normalization if we can't do it. - return h - } - h.AuthMethod = tokens[0] + componentAuthMethod - } - return h -} diff --git a/control-plane/api-gateway/common/helpers.go b/control-plane/api-gateway/common/helpers.go deleted file mode 100644 index f2ac883571..0000000000 --- a/control-plane/api-gateway/common/helpers.go +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package common - -import ( - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul/api" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -func DerefAll[T any](vs []*T) []T { - e := make([]T, 0, len(vs)) - for _, v := range vs { - e = append(e, *v) - } - return e -} - -func EmptyOrEqual(v, check string) bool { - return v == "" || v == check -} - -func NilOrEqual[T ~string](v *T, check string) bool { - return v == nil || string(*v) == check -} - -func FilterIsExternalFilter(filter gwv1beta1.HTTPRouteFilter) bool { - if filter.Type != gwv1beta1.HTTPRouteFilterExtensionRef { - return false - } - - if !DerefEqual(&filter.ExtensionRef.Group, v1alpha1.ConsulHashicorpGroup) { - return false - } - - switch filter.ExtensionRef.Kind { - case v1alpha1.RouteRetryFilterKind, v1alpha1.RouteTimeoutFilterKind: - return true - } - - return false - -} - -func IndexedNamespacedNameWithDefault[T ~string, U ~string, V ~string](t T, u *U, v V) types.NamespacedName { - return types.NamespacedName{ - Namespace: DerefStringOr(u, v), - Name: string(t), - } -} - -func ResourceReferenceWithDefault[T ~string, U ~string, V ~string](kind string, name T, section string, u *U, v V, partition string) api.ResourceReference { - return api.ResourceReference{ - Kind: kind, - Name: string(name), - SectionName: section, - Namespace: DerefStringOr(u, v), - Partition: partition, - } -} - -func DerefStringOr[T ~string, U ~string](v *T, val U) string { - if v == nil { - return string(val) - } - return string(*v) -} - -func DerefLookup[T comparable, U any](v *T, lookup map[T]U) U { - var zero U - if v == nil { - return zero - } - return lookup[*v] -} - -func DerefConvertFunc[T any, U any](v *T, fn func(T) U) U { - var zero U - if v == nil { - return zero - } - return fn(*v) -} - -func DerefEqual[T ~string](v *T, check string) bool { - if v == nil { - return false - } - return string(*v) == check -} - -func DerefIntOr[T ~int | ~int32, U ~int](v *T, val U) int { - if v == nil { - return int(val) - } - return int(*v) -} - -func StringLikeSlice[T ~string](vs []T) []string { - converted := []string{} - for _, v := range vs { - converted = append(converted, string(v)) - } - return converted -} - -func ConvertMapValuesToSlice[T comparable, U any](vs map[T]U) []U { - converted := []U{} - for _, v := range vs { - converted = append(converted, v) - } - return converted -} - -func ConvertSliceFunc[T any, U any](vs []T, fn func(T) U) []U { - converted := []U{} - for _, v := range vs { - converted = append(converted, fn(v)) - } - return converted -} - -func ConvertSliceFuncIf[T any, U any](vs []T, fn func(T) (U, bool)) []U { - converted := []U{} - for _, v := range vs { - if c, ok := fn(v); ok { - converted = append(converted, c) - } - } - return converted -} - -func Flatten[T any](vs [][]T) []T { - flattened := []T{} - for _, v := range vs { - flattened = append(flattened, v...) - } - return flattened -} - -func Filter[T any](vs []T, filterFn func(T) bool) []T { - filtered := []T{} - for _, v := range vs { - if !filterFn(v) { - filtered = append(filtered, v) - } - } - return filtered -} - -func DefaultOrEqual(v, fallback, check string) bool { - if v == "" { - return fallback == check - } - return v == check -} - -// ObjectsToReconcileRequests takes a list of objects and returns a list of -// reconcile Requests. -func ObjectsToReconcileRequests[T metav1.Object](objects []T) []reconcile.Request { - requests := make([]reconcile.Request, 0, len(objects)) - - for _, object := range objects { - requests = append(requests, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Namespace: object.GetNamespace(), - Name: object.GetName(), - }, - }) - } - return requests -} - -// ParentRefs takes a list of ParentReference objects and returns a list of NamespacedName objects. -func ParentRefs(group, kind, namespace string, refs []gwv1beta1.ParentReference) []types.NamespacedName { - indexed := make([]types.NamespacedName, 0, len(refs)) - for _, parent := range refs { - if NilOrEqual(parent.Group, group) && NilOrEqual(parent.Kind, kind) { - indexed = append(indexed, IndexedNamespacedNameWithDefault(parent.Name, parent.Namespace, namespace)) - } - } - return indexed -} - -// BothNilOrEqual is used to determine if two pointers to comparable -// object are either nil or both point to the same value. -func BothNilOrEqual[T comparable](one, two *T) bool { - if one == nil && two == nil { - return true - } - if one == nil { - return false - } - if two == nil { - return false - } - return *one == *two -} - -// ValueOr checks if a string-like pointer is nil, and if it is, -// returns the given value instead. -func ValueOr[T ~string](v *T, fallback string) string { - if v == nil { - return fallback - } - return string(*v) -} - -// PointerTo is a convenience method for taking a pointer -// of an object without having to declare an intermediate variable. -// It's also useful for making sure we don't accidentally take -// the pointer of a range variable directly. -func PointerTo[T any](v T) *T { - return &v -} - -// ParentsEqual checks for equality between two parent references. -func ParentsEqual(one, two gwv1beta1.ParentReference) bool { - return BothNilOrEqual(one.Group, two.Group) && - BothNilOrEqual(one.Kind, two.Kind) && - BothNilOrEqual(one.SectionName, two.SectionName) && - BothNilOrEqual(one.Port, two.Port) && - one.Name == two.Name -} - -func EntryToReference(entry api.ConfigEntry) api.ResourceReference { - return api.ResourceReference{ - Kind: entry.GetKind(), - Name: entry.GetName(), - Partition: entry.GetPartition(), - Namespace: entry.GetNamespace(), - } -} diff --git a/control-plane/api-gateway/common/helpers_test.go b/control-plane/api-gateway/common/helpers_test.go deleted file mode 100644 index 62070b434c..0000000000 --- a/control-plane/api-gateway/common/helpers_test.go +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package common - -import ( - "testing" - - "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -func TestBothNilOrEqual(t *testing.T) { - t.Parallel() - - for name, tt := range map[string]struct { - first *string - second *string - expected bool - }{ - "both nil": { - first: nil, - second: nil, - expected: true, - }, - "second nil": { - first: PointerTo(""), - second: nil, - expected: false, - }, - "first nil": { - first: nil, - second: PointerTo(""), - expected: false, - }, - "both equal": { - first: PointerTo(""), - second: PointerTo(""), - expected: true, - }, - "both not equal": { - first: PointerTo("1"), - second: PointerTo("2"), - expected: false, - }, - } { - t.Run(name, func(t *testing.T) { - require.Equal(t, tt.expected, BothNilOrEqual(tt.first, tt.second)) - }) - } -} - -func TestValueOr(t *testing.T) { - t.Parallel() - - for name, tt := range map[string]struct { - value *string - or string - expected string - }{ - "nil value": { - value: nil, - or: "test", - expected: "test", - }, - "set value": { - value: PointerTo("value"), - or: "test", - expected: "value", - }, - } { - t.Run(name, func(t *testing.T) { - require.Equal(t, tt.expected, ValueOr(tt.value, tt.or)) - }) - } -} - -func TestNilOrEqual(t *testing.T) { - t.Parallel() - - for name, tt := range map[string]struct { - value *string - check string - expected bool - }{ - "nil value": { - value: nil, - check: "test", - expected: true, - }, - "equal values": { - value: PointerTo("test"), - check: "test", - expected: true, - }, - "unequal values": { - value: PointerTo("value"), - check: "test", - expected: false, - }, - } { - t.Run(name, func(t *testing.T) { - require.Equal(t, tt.expected, NilOrEqual(tt.value, tt.check)) - }) - } -} - -func TestEnsureFinalizer(t *testing.T) { - t.Parallel() - - for name, tt := range map[string]struct { - object client.Object - expected bool - finalizers []string - }{ - "gateway no finalizer": { - object: &gwv1beta1.Gateway{}, - expected: true, - finalizers: []string{GatewayFinalizer}, - }, - "gateway other finalizer": { - object: &gwv1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"other"}}}, - expected: true, - finalizers: []string{"other", GatewayFinalizer}, - }, - "gateway already has finalizer": { - object: &gwv1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{GatewayFinalizer}}}, - expected: false, - finalizers: []string{GatewayFinalizer}, - }, - } { - t.Run(name, func(t *testing.T) { - require.Equal(t, tt.expected, EnsureFinalizer(tt.object)) - require.Equal(t, tt.finalizers, tt.object.GetFinalizers()) - }) - } -} - -func TestRemoveFinalizer(t *testing.T) { - t.Parallel() - - for name, tt := range map[string]struct { - object client.Object - expected bool - finalizers []string - }{ - "gateway no finalizer": { - object: &gwv1beta1.Gateway{}, - expected: false, - finalizers: []string{}, - }, - "gateway other finalizer": { - object: &gwv1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"other"}}}, - expected: false, - finalizers: []string{"other"}, - }, - "gateway multiple finalizers": { - object: &gwv1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{GatewayFinalizer, GatewayFinalizer}}}, - expected: true, - finalizers: []string{}, - }, - "gateway mixed finalizers": { - object: &gwv1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"other", GatewayFinalizer}}}, - expected: true, - finalizers: []string{"other"}, - }, - } { - t.Run(name, func(t *testing.T) { - require.Equal(t, tt.expected, RemoveFinalizer(tt.object)) - require.Equal(t, tt.finalizers, tt.object.GetFinalizers()) - }) - } -} diff --git a/control-plane/api-gateway/common/labels.go b/control-plane/api-gateway/common/labels.go deleted file mode 100644 index cba13a603e..0000000000 --- a/control-plane/api-gateway/common/labels.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package common - -import ( - "fmt" - - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -const ( - nameLabel = "gateway.consul.hashicorp.com/name" - namespaceLabel = "gateway.consul.hashicorp.com/namespace" - createdAtLabel = "gateway.consul.hashicorp.com/created" - ManagedLabel = "gateway.consul.hashicorp.com/managed" -) - -// LabelsForGateway formats the default labels that appear on objects managed by the controllers. -func LabelsForGateway(gateway *gwv1beta1.Gateway) map[string]string { - return map[string]string{ - nameLabel: gateway.Name, - namespaceLabel: gateway.Namespace, - createdAtLabel: fmt.Sprintf("%d", gateway.CreationTimestamp.Unix()), - ManagedLabel: "true", - } -} - -func GatewayFromPod(pod *corev1.Pod) (types.NamespacedName, bool) { - if pod.Labels[ManagedLabel] == "true" { - return types.NamespacedName{ - Name: pod.Labels[nameLabel], - Namespace: pod.Labels[namespaceLabel], - }, true - } - return types.NamespacedName{}, false -} diff --git a/control-plane/api-gateway/common/reference.go b/control-plane/api-gateway/common/reference.go deleted file mode 100644 index 78935c11e1..0000000000 --- a/control-plane/api-gateway/common/reference.go +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package common - -import ( - "sync" - - "github.com/hashicorp/consul/api" -) - -// ReferenceMap is contains a map of config entries stored -// by their normalized resource references (with empty string -// for namespaces and partitions stored as "default"). -type ReferenceMap struct { - data map[api.ResourceReference]api.ConfigEntry - ids map[api.ResourceReference]struct{} - mutex sync.RWMutex -} - -// NewReferenceMap constructs a reference map. -func NewReferenceMap() *ReferenceMap { - return &ReferenceMap{ - data: make(map[api.ResourceReference]api.ConfigEntry), - ids: make(map[api.ResourceReference]struct{}), - } -} - -func (r *ReferenceMap) IDs() []api.ResourceReference { - r.mutex.RLock() - defer r.mutex.RUnlock() - - var ids []api.ResourceReference - for id := range r.ids { - ids = append(ids, id) - } - return ids -} - -// Set adds an entry to the reference map. -func (r *ReferenceMap) Set(ref api.ResourceReference, v api.ConfigEntry) { - r.mutex.Lock() - defer r.mutex.Unlock() - - r.ids[ref] = struct{}{} - r.data[NormalizeMeta(ref)] = v -} - -// Get returns an entry from the reference map. -func (r *ReferenceMap) Get(ref api.ResourceReference) api.ConfigEntry { - r.mutex.RLock() - defer r.mutex.RUnlock() - - v, ok := r.data[NormalizeMeta(ref)] - if !ok { - return nil - } - return v -} - -// Entries returns a list of entries stored in the reference map. -func (r *ReferenceMap) Entries() []api.ConfigEntry { - r.mutex.RLock() - defer r.mutex.RUnlock() - - entries := make([]api.ConfigEntry, 0, len(r.data)) - for _, entry := range r.data { - entries = append(entries, entry) - } - return entries -} - -// Delete deletes an entry stored in the reference map. -func (r *ReferenceMap) Delete(ref api.ResourceReference) { - r.mutex.Lock() - defer r.mutex.Unlock() - - delete(r.ids, ref) - delete(r.data, NormalizeMeta(ref)) -} - -// Diff calculates the difference between the stored entries in two reference maps. -func (r *ReferenceMap) Diff(other *ReferenceMap) []api.ConfigEntry { - r.mutex.RLock() - defer r.mutex.RUnlock() - - other.mutex.RLock() - defer other.mutex.RUnlock() - - diffs := make([]api.ConfigEntry, 0) - - for ref, entry := range other.data { - oldRef := r.Get(ref) - // ref from the new cache doesn't exist in the old one - // this means a resource was added - if oldRef == nil { - diffs = append(diffs, entry) - continue - } - - // the entry in the old cache has an older modify index than the ref - // from the new cache - if oldRef.GetModifyIndex() < entry.GetModifyIndex() { - diffs = append(diffs, entry) - } - } - - // get all deleted entries, these are entries present in the old cache - // that are not present in the new - for ref, entry := range r.data { - if other.Get(ref) == nil { - diffs = append(diffs, entry) - } - } - - return diffs -} - -// ReferenceSet is a set of stored references. -type ReferenceSet struct { - data map[api.ResourceReference]struct{} - ids map[api.ResourceReference]struct{} - - mutex sync.RWMutex -} - -// NewReferenceSet constructs a new reference set. -func NewReferenceSet() *ReferenceSet { - return &ReferenceSet{ - data: make(map[api.ResourceReference]struct{}), - ids: make(map[api.ResourceReference]struct{}), - } -} - -// Mark adds a reference to the reference set. -func (r *ReferenceSet) Mark(ref api.ResourceReference) { - r.mutex.Lock() - defer r.mutex.Unlock() - - r.ids[ref] = struct{}{} - r.data[NormalizeMeta(ref)] = struct{}{} -} - -// Contains checks for the inclusion of a reference in the set. -func (r *ReferenceSet) Contains(ref api.ResourceReference) bool { - r.mutex.RLock() - defer r.mutex.RUnlock() - - _, ok := r.data[NormalizeMeta(ref)] - return ok -} - -// Remove drops a reference from the set. -func (r *ReferenceSet) Remove(ref api.ResourceReference) { - r.mutex.Lock() - defer r.mutex.Unlock() - - delete(r.ids, ref) - delete(r.data, NormalizeMeta(ref)) -} - -func (r *ReferenceSet) IDs() []api.ResourceReference { - r.mutex.RLock() - defer r.mutex.RUnlock() - - var ids []api.ResourceReference - for id := range r.ids { - ids = append(ids, id) - } - return ids -} - -func NormalizeMeta(ref api.ResourceReference) api.ResourceReference { - ref.Namespace = NormalizeEmptyMetadataString(ref.Namespace) - ref.Partition = NormalizeEmptyMetadataString(ref.Partition) - return ref -} - -func NormalizeEmptyMetadataString(metaString string) string { - if metaString == "" { - return "default" - } - return metaString -} diff --git a/control-plane/api-gateway/common/resources.go b/control-plane/api-gateway/common/resources.go deleted file mode 100644 index 40fe74bf8d..0000000000 --- a/control-plane/api-gateway/common/resources.go +++ /dev/null @@ -1,647 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package common - -import ( - mapset "github.com/deckarep/golang-set" - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul/api" -) - -// ConsulUpdateOperation is an operation representing an -// update in Consul. -type ConsulUpdateOperation struct { - // Entry is the ConfigEntry to write to Consul. - Entry api.ConfigEntry - // OnUpdate is an optional callback to fire after running - // the Consul update operation. If specified, then no more - // error handling occurs after the function is called, otherwise - // normal error handling logic applies. - OnUpdate func(err error) -} - -type gvkNamespacedName struct { - gvk string - nsn types.NamespacedName -} - -// KubernetesUpdates holds all update operations (including status) -// that need to be synced to Kubernetes. So long as you're -// modifying the same pointer object passed in to its Add -// function, this de-duplicates any calls to Add, in order -// for us to Add any previously unseen entires, but ignore -// them if they've already been added. -type KubernetesUpdates struct { - operations map[gvkNamespacedName]client.Object -} - -func NewKubernetesUpdates() *KubernetesUpdates { - return &KubernetesUpdates{ - operations: make(map[gvkNamespacedName]client.Object), - } -} - -func (k *KubernetesUpdates) Add(object client.Object) { - k.operations[gvkNamespacedName{ - gvk: object.GetObjectKind().GroupVersionKind().String(), - nsn: client.ObjectKeyFromObject(object), - }] = object -} - -func (k *KubernetesUpdates) Operations() []client.Object { - return ConvertMapValuesToSlice(k.operations) -} - -type ReferenceValidator interface { - GatewayCanReferenceSecret(gateway gwv1beta1.Gateway, secretRef gwv1beta1.SecretObjectReference) bool - HTTPRouteCanReferenceBackend(httproute gwv1beta1.HTTPRoute, backendRef gwv1beta1.BackendRef) bool - TCPRouteCanReferenceBackend(tcpRoute gwv1alpha2.TCPRoute, backendRef gwv1beta1.BackendRef) bool -} - -type certificate struct { - secret *corev1.Secret - gateways mapset.Set -} - -type httpRoute struct { - route gwv1beta1.HTTPRoute - gateways mapset.Set -} - -type tcpRoute struct { - route gwv1alpha2.TCPRoute - gateways mapset.Set -} - -type consulHTTPRoute struct { - route api.HTTPRouteConfigEntry - gateways mapset.Set -} - -type consulTCPRoute struct { - route api.TCPRouteConfigEntry - gateways mapset.Set -} - -type resourceSet struct { - httpRoutes mapset.Set - tcpRoutes mapset.Set - certificates mapset.Set - - consulObjects *ReferenceSet -} - -type ResourceMap struct { - translator ResourceTranslator - referenceValidator ReferenceValidator - logger logr.Logger - - services map[types.NamespacedName]api.ResourceReference - meshServices map[types.NamespacedName]api.ResourceReference - certificates mapset.Set - - // this acts a a secondary store of what has not yet - // been processed for the sake of garbage collection. - processedCertificates mapset.Set - certificateGateways map[api.ResourceReference]*certificate - tcpRouteGateways map[api.ResourceReference]*tcpRoute - httpRouteGateways map[api.ResourceReference]*httpRoute - gatewayResources map[api.ResourceReference]*resourceSet - externalFilters map[corev1.ObjectReference]client.Object - - // consul resources for a gateway - consulTCPRoutes map[api.ResourceReference]*consulTCPRoute - consulHTTPRoutes map[api.ResourceReference]*consulHTTPRoute - - // mutations - consulMutations []*ConsulUpdateOperation -} - -func NewResourceMap(translator ResourceTranslator, validator ReferenceValidator, logger logr.Logger) *ResourceMap { - return &ResourceMap{ - translator: translator, - referenceValidator: validator, - logger: logger, - processedCertificates: mapset.NewSet(), - services: make(map[types.NamespacedName]api.ResourceReference), - meshServices: make(map[types.NamespacedName]api.ResourceReference), - certificates: mapset.NewSet(), - consulTCPRoutes: make(map[api.ResourceReference]*consulTCPRoute), - consulHTTPRoutes: make(map[api.ResourceReference]*consulHTTPRoute), - certificateGateways: make(map[api.ResourceReference]*certificate), - tcpRouteGateways: make(map[api.ResourceReference]*tcpRoute), - httpRouteGateways: make(map[api.ResourceReference]*httpRoute), - gatewayResources: make(map[api.ResourceReference]*resourceSet), - } -} - -func (s *ResourceMap) AddService(id types.NamespacedName, name string) { - // this needs to be not-normalized since it gets written straight - // to Consul's configuration, including in non-enterprise builds. - s.services[id] = api.ResourceReference{ - Name: name, - Namespace: s.translator.Namespace(id.Namespace), - Partition: s.translator.ConsulPartition, - } -} - -func (s *ResourceMap) Service(id types.NamespacedName) api.ResourceReference { - return s.services[id] -} - -func (s *ResourceMap) HasService(id types.NamespacedName) bool { - _, ok := s.services[id] - return ok -} - -func (s *ResourceMap) AddMeshService(service v1alpha1.MeshService) { - // this needs to be not-normalized since it gets written straight - // to Consul's configuration, including in non-enterprise builds. - key := client.ObjectKeyFromObject(&service) - s.meshServices[key] = api.ResourceReference{ - Name: service.Spec.Name, - Namespace: s.translator.Namespace(service.Namespace), - Partition: s.translator.ConsulPartition, - } -} - -func (s *ResourceMap) MeshService(id types.NamespacedName) api.ResourceReference { - return s.meshServices[id] -} - -func (s *ResourceMap) HasMeshService(id types.NamespacedName) bool { - _, ok := s.meshServices[id] - return ok -} - -func (s *ResourceMap) Certificate(key types.NamespacedName) *corev1.Secret { - if !s.certificates.Contains(key) { - return nil - } - consulKey := NormalizeMeta(s.toConsulReference(api.InlineCertificate, key)) - if secret, ok := s.certificateGateways[consulKey]; ok { - return secret.secret - } - return nil -} - -func (s *ResourceMap) ReferenceCountCertificate(secret corev1.Secret) { - key := client.ObjectKeyFromObject(&secret) - s.certificates.Add(key) - consulKey := NormalizeMeta(s.toConsulReference(api.InlineCertificate, key)) - if _, ok := s.certificateGateways[consulKey]; !ok { - s.certificateGateways[consulKey] = &certificate{ - secret: &secret, - gateways: mapset.NewSet(), - } - } -} - -func (s *ResourceMap) ReferenceCountGateway(gateway gwv1beta1.Gateway) { - key := client.ObjectKeyFromObject(&gateway) - consulKey := NormalizeMeta(s.toConsulReference(api.APIGateway, key)) - - set := &resourceSet{ - httpRoutes: mapset.NewSet(), - tcpRoutes: mapset.NewSet(), - certificates: mapset.NewSet(), - consulObjects: NewReferenceSet(), - } - - for _, listener := range gateway.Spec.Listeners { - if listener.TLS == nil || (listener.TLS.Mode != nil && *listener.TLS.Mode != gwv1beta1.TLSModeTerminate) { - continue - } - for _, cert := range listener.TLS.CertificateRefs { - if NilOrEqual(cert.Group, "") && NilOrEqual(cert.Kind, "Secret") { - certificateKey := IndexedNamespacedNameWithDefault(cert.Name, cert.Namespace, gateway.Namespace) - - set.certificates.Add(certificateKey) - - consulCertificateKey := s.toConsulReference(api.InlineCertificate, certificateKey) - certificate, ok := s.certificateGateways[NormalizeMeta(consulCertificateKey)] - if ok { - certificate.gateways.Add(key) - set.consulObjects.Mark(consulCertificateKey) - } - } - } - } - - s.gatewayResources[consulKey] = set -} - -func (s *ResourceMap) ResourcesToGC(key types.NamespacedName) []api.ResourceReference { - consulKey := NormalizeMeta(s.toConsulReference(api.APIGateway, key)) - - resources, ok := s.gatewayResources[consulKey] - if !ok { - return nil - } - - var toGC []api.ResourceReference - - for _, id := range resources.consulObjects.IDs() { - // if any of these objects exist in the below maps - // it means we haven't "popped" it to be created - switch id.Kind { - case api.HTTPRoute: - if route, ok := s.consulHTTPRoutes[NormalizeMeta(id)]; ok && route.gateways.Cardinality() <= 1 { - // we only have a single reference, which will be this gateway, so drop - // the route altogether - toGC = append(toGC, id) - } - case api.TCPRoute: - if route, ok := s.consulTCPRoutes[NormalizeMeta(id)]; ok && route.gateways.Cardinality() <= 1 { - // we only have a single reference, which will be this gateway, so drop - // the route altogether - toGC = append(toGC, id) - } - case api.InlineCertificate: - if s.processedCertificates.Contains(id) { - continue - } - if route, ok := s.certificateGateways[NormalizeMeta(id)]; ok && route.gateways.Cardinality() <= 1 { - // we only have a single reference, which will be this gateway, so drop - // the route altogether - toGC = append(toGC, id) - } - } - } - - return toGC -} - -func (s *ResourceMap) ReferenceCountConsulHTTPRoute(route api.HTTPRouteConfigEntry) { - key := s.objectReference(&route) - - set := &consulHTTPRoute{ - route: route, - gateways: mapset.NewSet(), - } - - for gatewayKey := range s.consulGatewaysForRoute(route.Namespace, route.Parents).Iter() { - if gateway, ok := s.gatewayResources[gatewayKey.(api.ResourceReference)]; ok { - gateway.consulObjects.Mark(key) - } - - set.gateways.Add(gatewayKey) - } - - s.consulHTTPRoutes[NormalizeMeta(key)] = set -} - -func (s *ResourceMap) ReferenceCountConsulTCPRoute(route api.TCPRouteConfigEntry) { - key := s.objectReference(&route) - - set := &consulTCPRoute{ - route: route, - gateways: mapset.NewSet(), - } - - for gatewayKey := range s.consulGatewaysForRoute(route.Namespace, route.Parents).Iter() { - if gateway, ok := s.gatewayResources[gatewayKey.(api.ResourceReference)]; ok { - gateway.consulObjects.Mark(key) - } - - set.gateways.Add(gatewayKey) - } - - s.consulTCPRoutes[NormalizeMeta(key)] = set -} - -func (s *ResourceMap) ReferenceCountConsulCertificate(cert api.InlineCertificateConfigEntry) { - key := s.objectReference(&cert) - - var referenced *certificate - if existing, ok := s.certificateGateways[NormalizeMeta(key)]; ok { - referenced = existing - } else { - referenced = &certificate{ - gateways: mapset.NewSet(), - } - } - - s.certificateGateways[NormalizeMeta(key)] = referenced -} - -func (s *ResourceMap) consulGatewaysForRoute(namespace string, refs []api.ResourceReference) mapset.Set { - gateways := mapset.NewSet() - - for _, parent := range refs { - if EmptyOrEqual(parent.Kind, api.APIGateway) { - key := s.sectionlessParentReference(api.APIGateway, namespace, parent) - gateways.Add(key) - } - } - - return gateways -} - -func (s *ResourceMap) ReferenceCountHTTPRoute(route gwv1beta1.HTTPRoute) { - key := client.ObjectKeyFromObject(&route) - consulKey := NormalizeMeta(s.toConsulReference(api.HTTPRoute, key)) - - set := &httpRoute{ - route: route, - gateways: mapset.NewSet(), - } - - for gatewayKey := range s.gatewaysForRoute(route.Namespace, route.Spec.ParentRefs).Iter() { - set.gateways.Add(gatewayKey.(api.ResourceReference)) - - gateway := s.gatewayResources[gatewayKey.(api.ResourceReference)] - gateway.httpRoutes.Add(consulKey) - } - - s.httpRouteGateways[consulKey] = set -} - -func localObjectReferenceToObjectReference(filterRef gwv1beta1.LocalObjectReference, namespace string) corev1.ObjectReference { - return corev1.ObjectReference{ - Kind: string(filterRef.Kind), - Name: string(filterRef.Name), - Namespace: namespace, - } -} - -func objectToObjectReference(object client.Object) corev1.ObjectReference { - return corev1.ObjectReference{ - Kind: object.GetObjectKind().GroupVersionKind().Kind, - Name: object.GetName(), - Namespace: object.GetNamespace(), - } -} - -func (s *ResourceMap) AddExternalFilter(filter client.Object) { - if s.externalFilters == nil { - s.externalFilters = make(map[corev1.ObjectReference]client.Object) - } - - key := objectToObjectReference(filter) - s.externalFilters[key] = filter -} - -func (s *ResourceMap) GetExternalFilter(filterRef gwv1beta1.LocalObjectReference, namespace string) (client.Object, bool) { - key := localObjectReferenceToObjectReference(filterRef, namespace) - filter, ok := s.externalFilters[key] - return filter, ok -} - -func (s *ResourceMap) ExternalFilterExists(filterRef gwv1beta1.LocalObjectReference, namespace string) bool { - _, ok := s.GetExternalFilter(filterRef, namespace) - return ok -} - -func (s *ResourceMap) ReferenceCountTCPRoute(route gwv1alpha2.TCPRoute) { - key := client.ObjectKeyFromObject(&route) - consulKey := NormalizeMeta(s.toConsulReference(api.TCPRoute, key)) - - set := &tcpRoute{ - route: route, - gateways: mapset.NewSet(), - } - - for gatewayKey := range s.gatewaysForRoute(route.Namespace, route.Spec.ParentRefs).Iter() { - set.gateways.Add(gatewayKey.(api.ResourceReference)) - - gateway := s.gatewayResources[gatewayKey.(api.ResourceReference)] - gateway.tcpRoutes.Add(consulKey) - } - - s.tcpRouteGateways[consulKey] = set -} - -func (s *ResourceMap) gatewaysForRoute(namespace string, refs []gwv1beta1.ParentReference) mapset.Set { - gateways := mapset.NewSet() - - for _, parent := range refs { - if NilOrEqual(parent.Group, gwv1beta1.GroupVersion.Group) && NilOrEqual(parent.Kind, "Gateway") { - key := IndexedNamespacedNameWithDefault(parent.Name, parent.Namespace, namespace) - consulKey := NormalizeMeta(s.toConsulReference(api.APIGateway, key)) - - if _, ok := s.gatewayResources[consulKey]; ok { - gateways.Add(consulKey) - } - } - } - - return gateways -} - -func (s *ResourceMap) TranslateAndMutateHTTPRoute(key types.NamespacedName, onUpdate func(error, api.ConfigEntryStatus), mutateFn func(old *api.HTTPRouteConfigEntry, new api.HTTPRouteConfigEntry) api.HTTPRouteConfigEntry) { - consulKey := NormalizeMeta(s.toConsulReference(api.HTTPRoute, key)) - - route, ok := s.httpRouteGateways[consulKey] - if !ok { - return - } - - translated := s.translator.ToHTTPRoute(route.route, s) - - consulRoute, ok := s.consulHTTPRoutes[consulKey] - if ok { - mutated := mutateFn(&consulRoute.route, *translated) - if len(mutated.Parents) != 0 { - // if we don't have any parents set, we keep this around to allow the route - // to be GC'd. - delete(s.consulHTTPRoutes, consulKey) - s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ - Entry: &mutated, - OnUpdate: func(err error) { - onUpdate(err, mutated.Status) - }, - }) - } - return - } - mutated := mutateFn(nil, *translated) - if len(mutated.Parents) != 0 { - // if we don't have any parents set, we keep this around to allow the route - // to be GC'd. - delete(s.consulHTTPRoutes, consulKey) - s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ - Entry: &mutated, - OnUpdate: func(err error) { - onUpdate(err, mutated.Status) - }, - }) - } -} - -func (s *ResourceMap) MutateHTTPRoute(key types.NamespacedName, onUpdate func(error, api.ConfigEntryStatus), mutateFn func(api.HTTPRouteConfigEntry) api.HTTPRouteConfigEntry) { - consulKey := NormalizeMeta(s.toConsulReference(api.HTTPRoute, key)) - - consulRoute, ok := s.consulHTTPRoutes[consulKey] - if ok { - mutated := mutateFn(consulRoute.route) - if len(mutated.Parents) != 0 { - // if we don't have any parents set, we keep this around to allow the route - // to be GC'd. - delete(s.consulHTTPRoutes, consulKey) - s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ - Entry: &mutated, - OnUpdate: func(err error) { - onUpdate(err, mutated.Status) - }, - }) - } - } -} - -func (s *ResourceMap) CanGCHTTPRouteOnUnbind(id api.ResourceReference) bool { - if set := s.httpRouteGateways[NormalizeMeta(id)]; set != nil { - return set.gateways.Cardinality() <= 1 - } - return true -} - -func (s *ResourceMap) TranslateAndMutateTCPRoute(key types.NamespacedName, onUpdate func(error, api.ConfigEntryStatus), mutateFn func(*api.TCPRouteConfigEntry, api.TCPRouteConfigEntry) api.TCPRouteConfigEntry) { - consulKey := NormalizeMeta(s.toConsulReference(api.TCPRoute, key)) - - route, ok := s.tcpRouteGateways[consulKey] - if !ok { - return - } - - translated := s.translator.ToTCPRoute(route.route, s) - - consulRoute, ok := s.consulTCPRoutes[consulKey] - if ok { - mutated := mutateFn(&consulRoute.route, *translated) - if len(mutated.Parents) != 0 { - // if we don't have any parents set, we keep this around to allow the route - // to be GC'd. - delete(s.consulTCPRoutes, consulKey) - s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ - Entry: &mutated, - OnUpdate: func(err error) { - onUpdate(err, mutated.Status) - }, - }) - } - return - } - mutated := mutateFn(nil, *translated) - if len(mutated.Parents) != 0 { - // if we don't have any parents set, we keep this around to allow the route - // to be GC'd. - delete(s.consulTCPRoutes, consulKey) - s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ - Entry: &mutated, - OnUpdate: func(err error) { - onUpdate(err, mutated.Status) - }, - }) - } -} - -func (s *ResourceMap) MutateTCPRoute(key types.NamespacedName, onUpdate func(error, api.ConfigEntryStatus), mutateFn func(api.TCPRouteConfigEntry) api.TCPRouteConfigEntry) { - consulKey := NormalizeMeta(s.toConsulReference(api.TCPRoute, key)) - - consulRoute, ok := s.consulTCPRoutes[consulKey] - if ok { - mutated := mutateFn(consulRoute.route) - if len(mutated.Parents) != 0 { - // if we don't have any parents set, we keep this around to allow the route - // to be GC'd. - delete(s.consulTCPRoutes, consulKey) - s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ - Entry: &mutated, - OnUpdate: func(err error) { - onUpdate(err, mutated.Status) - }, - }) - } - } -} - -func (s *ResourceMap) CanGCTCPRouteOnUnbind(id api.ResourceReference) bool { - if set := s.tcpRouteGateways[NormalizeMeta(id)]; set != nil { - return set.gateways.Cardinality() <= 1 - } - return true -} - -func (s *ResourceMap) TranslateInlineCertificate(key types.NamespacedName) error { - consulKey := s.toConsulReference(api.InlineCertificate, key) - - certificate, ok := s.certificateGateways[NormalizeMeta(consulKey)] - if !ok { - return nil - } - - if certificate.secret == nil { - return nil - } - - consulCertificate, err := s.translator.ToInlineCertificate(*certificate.secret) - if err != nil { - return err - } - - // add to the processed set so we don't GC it. - s.processedCertificates.Add(consulKey) - s.consulMutations = append(s.consulMutations, &ConsulUpdateOperation{ - Entry: consulCertificate, - // just swallow the error and log it since we can't propagate status back on a certificate. - OnUpdate: func(error) { - if err != nil { - s.logger.Error(err, "error syncing certificate to Consul") - } - }, - }) - - return nil -} - -func (s *ResourceMap) Mutations() []*ConsulUpdateOperation { - return s.consulMutations -} - -func (s *ResourceMap) objectReference(o api.ConfigEntry) api.ResourceReference { - return api.ResourceReference{ - Kind: o.GetKind(), - Name: o.GetName(), - Namespace: o.GetNamespace(), - Partition: s.translator.ConsulPartition, - } -} - -func (s *ResourceMap) sectionlessParentReference(kind, namespace string, parent api.ResourceReference) api.ResourceReference { - return NormalizeMeta(api.ResourceReference{ - Kind: kind, - Name: parent.Name, - Namespace: orDefault(parent.Namespace, namespace), - Partition: s.translator.ConsulPartition, - }) -} - -func (s *ResourceMap) toConsulReference(kind string, key types.NamespacedName) api.ResourceReference { - return api.ResourceReference{ - Kind: kind, - Name: key.Name, - Namespace: s.translator.Namespace(key.Namespace), - Partition: s.translator.ConsulPartition, - } -} - -func (s *ResourceMap) GatewayCanReferenceSecret(gateway gwv1beta1.Gateway, ref gwv1beta1.SecretObjectReference) bool { - return s.referenceValidator.GatewayCanReferenceSecret(gateway, ref) -} - -func (s *ResourceMap) HTTPRouteCanReferenceBackend(route gwv1beta1.HTTPRoute, ref gwv1beta1.BackendRef) bool { - return s.referenceValidator.HTTPRouteCanReferenceBackend(route, ref) -} - -func (s *ResourceMap) TCPRouteCanReferenceBackend(route gwv1alpha2.TCPRoute, ref gwv1beta1.BackendRef) bool { - return s.referenceValidator.TCPRouteCanReferenceBackend(route, ref) -} diff --git a/control-plane/api-gateway/common/secrets.go b/control-plane/api-gateway/common/secrets.go deleted file mode 100644 index 1b7d8dec33..0000000000 --- a/control-plane/api-gateway/common/secrets.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package common - -import ( - "crypto/tls" - "crypto/x509" - "encoding/pem" - "errors" - "fmt" - - "github.com/miekg/dns" - corev1 "k8s.io/api/core/v1" - - "github.com/hashicorp/consul-k8s/control-plane/version" -) - -var ( - errFailedToParsePrivateKeyPem = errors.New("failed to parse private key PEM") - errKeyLengthTooShort = errors.New("RSA key length must be at least 2048-bit") - errKeyLengthTooShortFIPS = errors.New("RSA key length must be at either 2048-bit, 3072-bit, or 4096-bit in FIPS mode") -) - -func ParseCertificateData(secret corev1.Secret) (cert string, privateKey string, err error) { - decodedPrivateKey := secret.Data[corev1.TLSPrivateKeyKey] - decodedCertificate := secret.Data[corev1.TLSCertKey] - - privateKeyBlock, _ := pem.Decode(decodedPrivateKey) - if privateKeyBlock == nil { - return "", "", errFailedToParsePrivateKeyPem - } - - certificateBlock, _ := pem.Decode(decodedCertificate) - if certificateBlock == nil { - return "", "", errors.New("failed to parse certificate PEM") - } - - // make sure we have a valid x509 certificate - certificate, err := x509.ParseCertificate(certificateBlock.Bytes) - if err != nil { - return "", "", err - } - - // validate that the cert was generated with the given private key - _, err = tls.X509KeyPair(decodedCertificate, decodedPrivateKey) - if err != nil { - return "", "", err - } - - // validate that each host referenced in the CN, DNSSans, and IPSans - // are valid hostnames - if err := validateCertificateHosts(certificate); err != nil { - return "", "", err - } - - return string(decodedCertificate), string(decodedPrivateKey), nil -} - -func validateCertificateHosts(certificate *x509.Certificate) error { - hosts := []string{certificate.Subject.CommonName} - - hosts = append(hosts, certificate.DNSNames...) - - for _, ip := range certificate.IPAddresses { - hosts = append(hosts, ip.String()) - } - - for _, host := range hosts { - if _, ok := dns.IsDomainName(host); !ok { - return fmt.Errorf("host %q must be a valid DNS hostname", host) - } - } - - return nil -} - -// Envoy will silently reject any keys that are less than 2048 bytes long -// https://github.com/envoyproxy/envoy/blob/main/source/extensions/transport_sockets/tls/context_impl.cc#L238 -const MinKeyLength = 2048 - -// ValidateKeyLength ensures that the key length for a certificate is of a valid length -// for envoy dependent on if consul is running in FIPS mode or not. -func ValidateKeyLength(privateKey string) error { - privateKeyBlock, _ := pem.Decode([]byte(privateKey)) - - if privateKeyBlock == nil { - return errFailedToParsePrivateKeyPem - } - - if privateKeyBlock.Type != "RSA PRIVATE KEY" { - return nil - } - - key, err := x509.ParsePKCS1PrivateKey(privateKeyBlock.Bytes) - if err != nil { - return err - } - - keyBitLen := key.N.BitLen() - - if version.IsFIPS() { - return fipsLenCheck(keyBitLen) - } - - return nonFipsLenCheck(keyBitLen) -} - -func nonFipsLenCheck(keyLen int) error { - // ensure private key is of the correct length - if keyLen < MinKeyLength { - return errKeyLengthTooShort - } - - return nil -} - -func fipsLenCheck(keyLen int) error { - if keyLen != 2048 && keyLen != 3072 && keyLen != 4096 { - return errKeyLengthTooShortFIPS - } - return nil -} diff --git a/control-plane/api-gateway/common/secrets_test.go b/control-plane/api-gateway/common/secrets_test.go deleted file mode 100644 index 223e8aa24e..0000000000 --- a/control-plane/api-gateway/common/secrets_test.go +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package common - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestValidateKeyLength(t *testing.T) { - tooShortPrivateKey := `-----BEGIN RSA PRIVATE KEY----- -MIICXAIBAAKBgQCtmK1VjmXJ7vm4CZkkOSjc+kjGNMlyce5rXxwlDRz9LcGGc3Tg -kwUJesyBpDtxLLVHXQIPr5mWYbX/W/ezQ9sntxrATbDek8pBgoOlARebwkD2ivVW -BWfVhlryVihWlXApKiJ2n3i0m+OVtdrceC9Bv2hEMhYVOwzxtb3O0YFkbwIDAQAB -AoGAIxgnipFUEKPIRiVimUkY8ruCdNd9Fi7kNT6wEOl6v9A9PHIg4bm3Hfh+WYMb -JUEVkMzDuuoUEavFQE+WXt5L8oE1lEBmN2++FQsvllN+MRBTRg2sfw4mUWDI6S4r -h8+XNTzTIg2sUd2J3o2qNmQoOheYb+iuYDj76IFoEdwwZ0kCQQDYKKs5HAbnrLj1 -UrOp8TyHdFf0YNw5tGdbNTbffq4rlBD6SW70+Sj624i2UqdnYwRiWzdXv3zN08aI -Vfoh2cGlAkEAzZe5B6BhiX/PcIYutMtuT3K+mysFNlowrutXWoQOpR7gGAkgEt6e -oCDgx1QJRjsp6NFQxKc6l034Hzs17gqJgwJAcu9U873aUg9+HTuHOoKB28haCCAE -mU46cr3d2oKCW7uUN3EaZXmid5iJneBfENMOfrnfuHGiC9NiShXlNWCS3QJAO5Ne -w83+1ahaxUGs4SkeExmuECrcPM7P0rBRxOIFmGWlDHIAgFdQYhiE6l34vghA8b1O -CV5oRRYL84jl7M/S3wJBALDfL5YXcc8P6scLJJ1biqhLYppvGN5CUwbsJsluvHCW -XCTVIbPOaS42A0xUfpoiTcdbNSFRvdCzPR5nsGy8Y7g= ------END RSA PRIVATE KEY-----` - validPrivateKey := `-----BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAzVKRcYlTHHPjPbCieOFIUT2hCouRYe4N8ZhNrSpZf/BAAn4M -d/LWn/9OrLagbxrRF6cWdWGNEI2COnBRLgNVxyPXneaHaYFqOBRi9GWhuD3sw1jn -7gf4/m/AVO8cu2JYjEX+s9RjSRzpjx+4nhit46bGNUyb9qUeQwoBidAzOSmU8nHY -y3LpuuzkjS3FEyNXHxqgpTJnV4ytx8YGkPnG92GBAlrZnr4Eclv0/Sq6OViTpeuh -z8noNkbugYWHMXGlTZ4lPnELJW2fx/HIpD2ovOO3X8XYBo5KDzs9qyKzDgIOMZLF -i/qLCLHgfosb4TMaXCeVu4fA7Y47jtGOO4mbiwIDAQABAoIBAFhicDibIDtRyaLv -K+l0NPC/4liLPwCUfM0gvmNKJS/VSICqKQzjbK+ANCpWDVb2iMaxRxItdY+IEuS8 -H736cozgaXtP1r+8lXBhmj1RmJ2ajpaC6YgGR5GjonwNWGVzjuGHaf6YcUryVrol -MhBgWE50psMf4M16Q74hCwt7o+k5Lz55xKasgc9dtSnvyCupPBwrOT+d55C1P2Wn -2oebWM4WKtCZIgvlvZrt4xQkGWy9qloxL6V1F67ZbizAyFMZUMmJv+4/whF8tmXi -aydleL64K23ZSK1pM/x0JI+7qo0GpEoA4k+2fdmh5dAOM0TrXhV5Kv01efLIaITT -s7lYjG0CgYEA4qGIM7qO3e9fHgSK/9UdxnpL/1OvfYATBMhEtR46sAxmKQGC8fTM -iTBkmLAKn3zBgDghCbygPIQjex+W+Ra7JkQIcGB6KLR8rr5GkOuF6vkqHV93RQRT -lT/1quqq3fVH6V4ymifKJCDNg0IEPcmo+M8RnXBgpFsCN4b5UyjXNScCgYEA5+4h -LITPJxGytlWzwtsy44U2PvafJYJCktW+LYqhk3xzz4qWX5ubmPz18LrEyybgcy/W -Dm4JCu+TOS2gvf2WbJKR/tKdgRN7dkU/dbgMtRL8QW5ir+5qqRITYOhiSZPIOpbP -5zg+c/ZvmK/t5h35/8l7b0bu/E1FOEF27ADpzP0CgYEArqch2gup0muI+A80N9i7 -q5vQOaL6mVM8VPEp0hLL06Sajnt1uJWZkxhSTkFMzoBMd03KWECflEOZPGep56iW -7fR8NG6Fdh0yAVDt/P0lJWKEDELoHa4p49l4sBFNQOSoWLaZdKe5ZoJJHyCfOCbT -K3wY7SYPtFnWqYhBWM8emv0CgYBdrNqNRp78orNR3c+bNjmZl6ZPTAD/f1swP1Bu -yH12Ol/0RX9y4kC4TANx1Z3Ch9ND8uA8N8lDN3x5Laqs0g29kH2TNLIU/i9xl4qI -G2xWfnKQYutNL7i4zOoyy+lW2m+W6m7Sbu8am0B7pSMrPJRK8a//Q+Em2nbIv/gu -XjgQaQKBgHKZUKkMv597vpAjgTNsKIl5RDFONBq3omnAwlK9EDLVeAxIrvrvMHBW -H/ZMFpSGp1eQgKyu1xkEqGdkYXx7BKtdTHK+Thqif2ZGWczy5rVSAIsBYDo1DGE2 -wbocWxkWNb5o2ZZtis5lTB6nr9EWo0zyaPqIh0pfjqVEES2YDEx6 ------END RSA PRIVATE KEY-----` - nonTraditionalRSAKey := `-----BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCcrB9oNKLtzA3Q -02KDgtsnrxns7vJ5aCkjJCm/h0Ju7a2mel5YHSN5iLlU5oTMJVIMpWlW9E8P76/a -GLGMNfSBRVJdfW71iks/ddp4SjpDe9Bo+aY2snrR2/AP7eQepVNjFbg4YLQqvENh -05k1FuuP1/AgGVNn0kGEwzKxz35shmhRKBCvaRaHLz/fdkDIeIrVLON4FnmAmpOZ -AztZCwAZc6HZfj8Nh9Wlaw6Dg2boIgxTU160pwpX+nUxcJ9M5sUP9DBuNL0Mdrqi -U+R49uqG/5ssSk+xVik3q+WF+XySJ6H21fttWDJS2OTm/Nx/wHlBC73mthbA0emB -rkiBy9SBAgMBAAECggEAOhybz6aKcmKYE0d8yGPejwMjPh9JH+ATNh4hQBHXAdc1 -7ESCPvOb52XfvE5+nkwPeXJXNrIKq1IPq3kyTdvrc5F3Ygb3A6tGiuTXYnvBzasc -m/tRfANKjBGkovvte7J90ghJ2tt/qERJR/1Y2/jC6glB314VcjJqK+jNImfgsDa7 -1r47efKG7B5eUGvhQDTpL5ENXKxIdvCghHrLqj19QGUZ5MbXsEYrso0lxKw2Xk39 -uM8p3WTxIy0LQGyCm+FYlJ7r61tm7tUOGuNT0YiptVavIw1QPgIbRWdS2gnJu3+J -kHS0vu6AW1fJav48TA9hXcIQR70alrJA2VVqsvQouwKBgQDNs96l8BfWD6s/urIw -yzC3/VZPLFJ3BlxvkdP1UDC0S+7pgQ6qdEmJg0z5IfYzDB1PK2X/DS/70JA1LRSS -MRmjQGHCYIp9g8EqmABwfKf4YnN53KPRyR8Yq1pwaq7wKowtW+5GH95qQPINZsNO -J21AENEzq7IoB4gpM3tIaX73YwKBgQDC+yl5JvoV7e6FIpFrwL62aKrWmpidML/G -stdrg9ylCSM9SIVFINMhmFPicW1+DrkQ5HRV7DG//ZcOZNbbNmSu32PVcQI1MJgQ -rkMZ3ukUURnlvQYOEmZY4zHzTJ+jcw6kEH/+b47Bv13PpD7ZqA4/28dpU9wi9gt3 -+GiSnkKDywKBgHqjr63dPEjapK3lQFHJAu3fM7MWaMAf4cJ+/hD202LbFsDOuhC0 -Lhe3WY/7SI7cvSizZicvFJmcmi2qB+a1MWTcgKxj5I26nNMpNrHaEEcNY22XN3Be -6ZRKrSvy3wO/Sj3M3n2eiHtu5yFIUE7rQL5+iEu3JQuqmep+kBT3GMSjAoGAP77B -VlyJ0nWRT3F3vZSsRRJ/F94/GtT/PcTmbL4Vetc78CMvfuQ2YntcoWGX/Ghv1Lf7 -2MN5mF0d75TEMbLcw9dA2l0x7ZXPgVSXl3OrG/tPzi44No2JbHIKuJJKdrN9C+Jh -Fhv+vhUEZIg8DAjHb9U4opTKGZv7L+PEvHqFIHUCgYBTB2TxTgEMNZSsRwrhQRMh -tsz5rS2MoTgzk4BlSsv6xVC4GnBJ2HlNAjYEsBEg50zCCTPlZXcsNjrAxFrwWhLJ -DjN2iMsYFz4WHS94W5UYl6/35ye25KsHuS9vnNeidhFAvYgC1nIkh4mFhLoSeSCG -GODy2KwC2ssLuUHb6WoJ6A== ------END PRIVATE KEY-----` - - testCases := map[string]struct { - key string - expectedError error - }{ - "key is RSA and of the correct length": { - key: validPrivateKey, - expectedError: nil, - }, - "key is RSA and too short": { - key: tooShortPrivateKey, - expectedError: errKeyLengthTooShort, - }, - "key is non-traditional RSA key": { - key: nonTraditionalRSAKey, - expectedError: nil, - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - err := ValidateKeyLength(tc.key) - require.ErrorIs(t, err, tc.expectedError) - }) - } -} diff --git a/control-plane/api-gateway/common/translation.go b/control-plane/api-gateway/common/translation.go deleted file mode 100644 index d3627b11b4..0000000000 --- a/control-plane/api-gateway/common/translation.go +++ /dev/null @@ -1,446 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package common - -import ( - "strings" - - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" - "github.com/hashicorp/consul-k8s/control-plane/namespaces" - "github.com/hashicorp/consul/api" -) - -// ResourceTranslator handles translating K8s resources into Consul config entries. -type ResourceTranslator struct { - EnableConsulNamespaces bool - ConsulDestNamespace string - EnableK8sMirroring bool - MirroringPrefix string - ConsulPartition string - Datacenter string -} - -func (t ResourceTranslator) NonNormalizedConfigEntryReference(kind string, id types.NamespacedName) api.ResourceReference { - return api.ResourceReference{ - Kind: kind, - Name: id.Name, - Namespace: t.Namespace(id.Namespace), - Partition: t.ConsulPartition, - } -} - -func (t ResourceTranslator) ConfigEntryReference(kind string, id types.NamespacedName) api.ResourceReference { - return NormalizeMeta(t.NonNormalizedConfigEntryReference(kind, id)) -} - -func (t ResourceTranslator) NormalizedResourceReference(kind, namespace string, ref api.ResourceReference) api.ResourceReference { - return NormalizeMeta(api.ResourceReference{ - Kind: kind, - Name: ref.Name, - SectionName: ref.SectionName, - Namespace: t.Namespace(namespace), - Partition: t.ConsulPartition, - }) -} - -func (t ResourceTranslator) Namespace(namespace string) string { - return namespaces.ConsulNamespace(namespace, t.EnableConsulNamespaces, t.ConsulDestNamespace, t.EnableK8sMirroring, t.MirroringPrefix) -} - -// ToAPIGateway translates a kuberenetes API gateway into a Consul APIGateway Config Entry. -func (t ResourceTranslator) ToAPIGateway(gateway gwv1beta1.Gateway, resources *ResourceMap, gwcc *v1alpha1.GatewayClassConfig) *api.APIGatewayConfigEntry { - namespace := t.Namespace(gateway.Namespace) - - listeners := ConvertSliceFuncIf(gateway.Spec.Listeners, func(listener gwv1beta1.Listener) (api.APIGatewayListener, bool) { - return t.toAPIGatewayListener(gateway, listener, resources, gwcc) - }) - - return &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: gateway.Name, - Namespace: namespace, - Partition: t.ConsulPartition, - Meta: t.addDatacenterToMeta(map[string]string{ - constants.MetaKeyKubeNS: gateway.Namespace, - constants.MetaKeyKubeName: gateway.Name, - }), - Listeners: listeners, - } -} - -var listenerProtocolMap = map[string]string{ - "https": "http", - "http": "http", - "tcp": "tcp", -} - -func (t ResourceTranslator) toAPIGatewayListener(gateway gwv1beta1.Gateway, listener gwv1beta1.Listener, resources *ResourceMap, gwcc *v1alpha1.GatewayClassConfig) (api.APIGatewayListener, bool) { - namespace := gateway.Namespace - - var certificates []api.ResourceReference - var cipherSuites []string - var maxVersion, minVersion string - - if listener.TLS != nil { - cipherSuitesVal := string(listener.TLS.Options[TLSCipherSuitesAnnotationKey]) - if cipherSuitesVal != "" { - cipherSuites = strings.Split(cipherSuitesVal, ",") - } - maxVersion = string(listener.TLS.Options[TLSMaxVersionAnnotationKey]) - minVersion = string(listener.TLS.Options[TLSMinVersionAnnotationKey]) - - for _, ref := range listener.TLS.CertificateRefs { - if !resources.GatewayCanReferenceSecret(gateway, ref) { - return api.APIGatewayListener{}, false - } - - if !NilOrEqual(ref.Group, "") || !NilOrEqual(ref.Kind, "Secret") { - // only translate the valid types we support - continue - } - - ref := IndexedNamespacedNameWithDefault(ref.Name, ref.Namespace, namespace) - if resources.Certificate(ref) != nil { - certificates = append(certificates, t.NonNormalizedConfigEntryReference(api.InlineCertificate, ref)) - } - } - } - - portMapping := int32(0) - if gwcc != nil { - portMapping = gwcc.Spec.MapPrivilegedContainerPorts - } - - return api.APIGatewayListener{ - Name: string(listener.Name), - Hostname: DerefStringOr(listener.Hostname, ""), - Port: ToContainerPort(listener.Port, portMapping), - Protocol: listenerProtocolMap[strings.ToLower(string(listener.Protocol))], - TLS: api.APIGatewayTLSConfiguration{ - Certificates: certificates, - CipherSuites: cipherSuites, - MaxVersion: maxVersion, - MinVersion: minVersion, - }, - }, true -} - -func ToContainerPort(portNumber gwv1beta1.PortNumber, mapPrivilegedContainerPorts int32) int { - if portNumber >= 1024 { - // We don't care about privileged port-mapping, this is a non-privileged port - return int(portNumber) - } - - return int(portNumber) + int(mapPrivilegedContainerPorts) -} - -func (t ResourceTranslator) ToHTTPRoute(route gwv1beta1.HTTPRoute, resources *ResourceMap) *api.HTTPRouteConfigEntry { - namespace := t.Namespace(route.Namespace) - - // we don't translate parent refs - - hostnames := StringLikeSlice(route.Spec.Hostnames) - rules := ConvertSliceFuncIf(route.Spec.Rules, func(rule gwv1beta1.HTTPRouteRule) (api.HTTPRouteRule, bool) { - return t.translateHTTPRouteRule(route, rule, resources) - }) - - configEntry := api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: route.Name, - Namespace: namespace, - Partition: t.ConsulPartition, - Meta: t.addDatacenterToMeta(map[string]string{ - constants.MetaKeyKubeNS: route.Namespace, - constants.MetaKeyKubeName: route.Name, - }), - Hostnames: hostnames, - Rules: rules, - } - - return &configEntry -} - -func (t ResourceTranslator) translateHTTPRouteRule(route gwv1beta1.HTTPRoute, rule gwv1beta1.HTTPRouteRule, resources *ResourceMap) (api.HTTPRouteRule, bool) { - services := ConvertSliceFuncIf(rule.BackendRefs, func(ref gwv1beta1.HTTPBackendRef) (api.HTTPService, bool) { - - return t.translateHTTPBackendRef(route, ref, resources) - }) - - if len(services) == 0 { - return api.HTTPRouteRule{}, false - } - - matches := ConvertSliceFunc(rule.Matches, t.translateHTTPMatch) - filters := t.translateHTTPFilters(rule.Filters, resources, route.Namespace) - - return api.HTTPRouteRule{ - Services: services, - Matches: matches, - Filters: filters, - }, true -} - -func (t ResourceTranslator) translateHTTPBackendRef(route gwv1beta1.HTTPRoute, ref gwv1beta1.HTTPBackendRef, resources *ResourceMap) (api.HTTPService, bool) { - id := types.NamespacedName{ - Name: string(ref.Name), - Namespace: DerefStringOr(ref.Namespace, route.Namespace), - } - - isServiceRef := NilOrEqual(ref.Group, "") && NilOrEqual(ref.Kind, "Service") - - if isServiceRef && resources.HasService(id) && resources.HTTPRouteCanReferenceBackend(route, ref.BackendRef) { - filters := t.translateHTTPFilters(ref.Filters, resources, route.Namespace) - service := resources.Service(id) - return api.HTTPService{ - Name: service.Name, - Namespace: service.Namespace, - Partition: t.ConsulPartition, - Filters: filters, - Weight: DerefIntOr(ref.Weight, 1), - }, true - } - - isMeshServiceRef := DerefEqual(ref.Group, v1alpha1.ConsulHashicorpGroup) && DerefEqual(ref.Kind, v1alpha1.MeshServiceKind) - if isMeshServiceRef && resources.HasMeshService(id) && resources.HTTPRouteCanReferenceBackend(route, ref.BackendRef) { - filters := t.translateHTTPFilters(ref.Filters, resources, route.Namespace) - service := resources.MeshService(id) - - return api.HTTPService{ - Name: service.Name, - Namespace: service.Namespace, - Partition: t.ConsulPartition, - Filters: filters, - Weight: DerefIntOr(ref.Weight, 1), - }, true - } - - return api.HTTPService{}, false -} - -var headerMatchTypeTranslation = map[gwv1beta1.HeaderMatchType]api.HTTPHeaderMatchType{ - gwv1beta1.HeaderMatchExact: api.HTTPHeaderMatchExact, - gwv1beta1.HeaderMatchRegularExpression: api.HTTPHeaderMatchRegularExpression, -} - -var headerPathMatchTypeTranslation = map[gwv1beta1.PathMatchType]api.HTTPPathMatchType{ - gwv1beta1.PathMatchExact: api.HTTPPathMatchExact, - gwv1beta1.PathMatchPathPrefix: api.HTTPPathMatchPrefix, - gwv1beta1.PathMatchRegularExpression: api.HTTPPathMatchRegularExpression, -} - -var queryMatchTypeTranslation = map[gwv1beta1.QueryParamMatchType]api.HTTPQueryMatchType{ - gwv1beta1.QueryParamMatchExact: api.HTTPQueryMatchExact, - gwv1beta1.QueryParamMatchRegularExpression: api.HTTPQueryMatchRegularExpression, -} - -func (t ResourceTranslator) translateHTTPMatch(match gwv1beta1.HTTPRouteMatch) api.HTTPMatch { - headers := ConvertSliceFunc(match.Headers, t.translateHTTPHeaderMatch) - queries := ConvertSliceFunc(match.QueryParams, t.translateHTTPQueryMatch) - - return api.HTTPMatch{ - Headers: headers, - Query: queries, - Path: DerefConvertFunc(match.Path, t.translateHTTPPathMatch), - Method: api.HTTPMatchMethod(DerefStringOr(match.Method, "")), - } -} - -func (t ResourceTranslator) translateHTTPPathMatch(match gwv1beta1.HTTPPathMatch) api.HTTPPathMatch { - return api.HTTPPathMatch{ - Match: DerefLookup(match.Type, headerPathMatchTypeTranslation), - Value: DerefStringOr(match.Value, ""), - } -} - -func (t ResourceTranslator) translateHTTPHeaderMatch(match gwv1beta1.HTTPHeaderMatch) api.HTTPHeaderMatch { - return api.HTTPHeaderMatch{ - Name: string(match.Name), - Value: match.Value, - Match: DerefLookup(match.Type, headerMatchTypeTranslation), - } -} - -func (t ResourceTranslator) translateHTTPQueryMatch(match gwv1beta1.HTTPQueryParamMatch) api.HTTPQueryMatch { - return api.HTTPQueryMatch{ - Name: string(match.Name), - Value: match.Value, - Match: DerefLookup(match.Type, queryMatchTypeTranslation), - } -} - -func (t ResourceTranslator) translateHTTPFilters(filters []gwv1beta1.HTTPRouteFilter, resourceMap *ResourceMap, namespace string) api.HTTPFilters { - var urlRewrite *api.URLRewrite - consulFilter := api.HTTPHeaderFilter{ - Add: make(map[string]string), - Set: make(map[string]string), - } - var retryFilter *api.RetryFilter - var timeoutFilter *api.TimeoutFilter - - for _, filter := range filters { - if filter.RequestHeaderModifier != nil { - consulFilter.Remove = append(consulFilter.Remove, filter.RequestHeaderModifier.Remove...) - - for _, toAdd := range filter.RequestHeaderModifier.Add { - consulFilter.Add[string(toAdd.Name)] = toAdd.Value - } - - for _, toSet := range filter.RequestHeaderModifier.Set { - consulFilter.Set[string(toSet.Name)] = toSet.Value - } - } - - // we drop any path rewrites that are not prefix matches as we don't support those - if filter.URLRewrite != nil && - filter.URLRewrite.Path != nil && - filter.URLRewrite.Path.Type == gwv1beta1.PrefixMatchHTTPPathModifier { - urlRewrite = &api.URLRewrite{Path: DerefStringOr(filter.URLRewrite.Path.ReplacePrefixMatch, "")} - } - - if filter.ExtensionRef != nil { - //get crd from resources map - crdFilter, exists := resourceMap.GetExternalFilter(*filter.ExtensionRef, namespace) - if !exists { - // this should never be the case because we only translate a route if it's actually valid, and if we're missing filters during the validation step, then we won't get here - continue - } - switch filter.ExtensionRef.Kind { - case v1alpha1.RouteRetryFilterKind: - - retryFilterCRD := crdFilter.(*v1alpha1.RouteRetryFilter) - //new filter that needs to be appended - - retryFilter = &api.RetryFilter{ - NumRetries: retryFilterCRD.Spec.NumRetries, - RetryOn: retryFilterCRD.Spec.RetryOn, - RetryOnStatusCodes: retryFilterCRD.Spec.RetryOnStatusCodes, - RetryOnConnectFailure: retryFilterCRD.Spec.RetryOnConnectFailure, - } - - case v1alpha1.RouteTimeoutFilterKind: - - timeoutFilterCRD := crdFilter.(*v1alpha1.RouteTimeoutFilter) - //new filter that needs to be appended - - timeoutFilter = &api.TimeoutFilter{ - RequestTimeout: timeoutFilterCRD.Spec.RequestTimeout, - IdleTimeout: timeoutFilterCRD.Spec.IdleTimeout, - } - - } - } - - } - return api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{consulFilter}, - URLRewrite: urlRewrite, - RetryFilter: retryFilter, - TimeoutFilter: timeoutFilter, - } -} - -func (t ResourceTranslator) ToTCPRoute(route gwv1alpha2.TCPRoute, resources *ResourceMap) *api.TCPRouteConfigEntry { - namespace := t.Namespace(route.Namespace) - - // we don't translate parent refs - - backendRefs := ConvertSliceFunc(route.Spec.Rules, func(rule gwv1alpha2.TCPRouteRule) []gwv1beta1.BackendRef { return rule.BackendRefs }) - flattenedRefs := Flatten(backendRefs) - services := ConvertSliceFuncIf(flattenedRefs, func(ref gwv1beta1.BackendRef) (api.TCPService, bool) { - return t.translateTCPRouteRule(route, ref, resources) - }) - - return &api.TCPRouteConfigEntry{ - Kind: api.TCPRoute, - Name: route.Name, - Namespace: namespace, - Partition: t.ConsulPartition, - Meta: t.addDatacenterToMeta(map[string]string{ - constants.MetaKeyKubeNS: route.Namespace, - constants.MetaKeyKubeName: route.Name, - }), - Services: services, - } -} - -func (t ResourceTranslator) translateTCPRouteRule(route gwv1alpha2.TCPRoute, ref gwv1beta1.BackendRef, resources *ResourceMap) (api.TCPService, bool) { - // we ignore weight for now - - id := types.NamespacedName{ - Name: string(ref.Name), - Namespace: DerefStringOr(ref.Namespace, route.Namespace), - } - - isServiceRef := NilOrEqual(ref.Group, "") && NilOrEqual(ref.Kind, "Service") - if isServiceRef && resources.HasService(id) && resources.TCPRouteCanReferenceBackend(route, ref) { - service := resources.Service(id) - - return api.TCPService{ - Name: service.Name, - Namespace: service.Namespace, - }, true - } - - isMeshServiceRef := DerefEqual(ref.Group, v1alpha1.ConsulHashicorpGroup) && DerefEqual(ref.Kind, v1alpha1.MeshServiceKind) - if isMeshServiceRef && resources.HasMeshService(id) && resources.TCPRouteCanReferenceBackend(route, ref) { - service := resources.MeshService(id) - - return api.TCPService{ - Name: service.Name, - Namespace: service.Namespace, - }, true - } - - return api.TCPService{}, false -} - -func (t ResourceTranslator) ToInlineCertificate(secret corev1.Secret) (*api.InlineCertificateConfigEntry, error) { - certificate, privateKey, err := ParseCertificateData(secret) - if err != nil { - return nil, err - } - - err = ValidateKeyLength(privateKey) - if err != nil { - return nil, err - } - - namespace := t.Namespace(secret.Namespace) - - return &api.InlineCertificateConfigEntry{ - Kind: api.InlineCertificate, - Name: secret.Name, - Namespace: namespace, - Partition: t.ConsulPartition, - Certificate: strings.TrimSpace(certificate), - PrivateKey: strings.TrimSpace(privateKey), - Meta: t.addDatacenterToMeta(map[string]string{ - constants.MetaKeyKubeNS: secret.Namespace, - constants.MetaKeyKubeName: secret.Name, - }), - }, nil -} - -func EntryToNamespacedName(entry api.ConfigEntry) types.NamespacedName { - meta := entry.GetMeta() - - return types.NamespacedName{ - Namespace: meta[constants.MetaKeyKubeNS], - Name: meta[constants.MetaKeyKubeName], - } -} - -func (t ResourceTranslator) addDatacenterToMeta(meta map[string]string) map[string]string { - if t.Datacenter == "" { - return meta - } - meta[constants.MetaKeyDatacenter] = t.Datacenter - return meta -} diff --git a/control-plane/api-gateway/common/translation_test.go b/control-plane/api-gateway/common/translation_test.go deleted file mode 100644 index d562845fc8..0000000000 --- a/control-plane/api-gateway/common/translation_test.go +++ /dev/null @@ -1,1645 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package common - -import ( - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "fmt" - "k8s.io/utils/pointer" - "math/big" - "sigs.k8s.io/controller-runtime/pkg/client" - "strings" - "testing" - "time" - - "github.com/google/go-cmp/cmp" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - logrtest "github.com/go-logr/logr/testing" - - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" - "github.com/hashicorp/consul/api" -) - -type fakeReferenceValidator struct{} - -func (v fakeReferenceValidator) GatewayCanReferenceSecret(gateway gwv1beta1.Gateway, secretRef gwv1beta1.SecretObjectReference) bool { - return true -} - -func (v fakeReferenceValidator) HTTPRouteCanReferenceBackend(httproute gwv1beta1.HTTPRoute, backendRef gwv1beta1.BackendRef) bool { - return true -} - -func (v fakeReferenceValidator) TCPRouteCanReferenceBackend(tcpRoute gwv1alpha2.TCPRoute, backendRef gwv1beta1.BackendRef) bool { - return true -} - -func TestTranslator_Namespace(t *testing.T) { - testCases := []struct { - EnableConsulNamespaces bool - ConsulDestNamespace string - EnableK8sMirroring bool - MirroringPrefix string - Input, ExpectedOutput string - }{ - { - EnableConsulNamespaces: false, - ConsulDestNamespace: "default", - EnableK8sMirroring: false, - MirroringPrefix: "", - Input: "namespace-1", - ExpectedOutput: "", - }, - { - EnableConsulNamespaces: false, - ConsulDestNamespace: "default", - EnableK8sMirroring: true, - MirroringPrefix: "", - Input: "namespace-1", - ExpectedOutput: "", - }, - { - EnableConsulNamespaces: false, - ConsulDestNamespace: "default", - EnableK8sMirroring: true, - MirroringPrefix: "pre-", - Input: "namespace-1", - ExpectedOutput: "", - }, - { - EnableConsulNamespaces: true, - ConsulDestNamespace: "default", - EnableK8sMirroring: false, - MirroringPrefix: "", - Input: "namespace-1", - ExpectedOutput: "default", - }, - { - EnableConsulNamespaces: true, - ConsulDestNamespace: "default", - EnableK8sMirroring: true, - MirroringPrefix: "", - Input: "namespace-1", - ExpectedOutput: "namespace-1", - }, - { - EnableConsulNamespaces: true, - ConsulDestNamespace: "default", - EnableK8sMirroring: true, - MirroringPrefix: "pre-", - Input: "namespace-1", - ExpectedOutput: "pre-namespace-1", - }, - } - - for i, tc := range testCases { - t.Run(fmt.Sprintf("%s_%d", t.Name(), i), func(t *testing.T) { - translator := ResourceTranslator{ - EnableConsulNamespaces: tc.EnableConsulNamespaces, - ConsulDestNamespace: tc.ConsulDestNamespace, - EnableK8sMirroring: tc.EnableK8sMirroring, - MirroringPrefix: tc.MirroringPrefix, - } - assert.Equal(t, tc.ExpectedOutput, translator.Namespace(tc.Input)) - }) - } -} - -func TestTranslator_ToAPIGateway(t *testing.T) { - t.Parallel() - k8sObjectName := "my-k8s-gw" - k8sNamespace := "my-k8s-namespace" - - // gw status - gwLastTransmissionTime := time.Now() - - // listener one configuration - listenerOneName := "listener-one" - listenerOneHostname := "*.consul.io" - listenerOnePort := 3366 - listenerOneProtocol := "http" - - // listener one tls config - listenerOneCertName := "one-cert" - listenerOneCertK8sNamespace := "one-cert-ns" - listenerOneCertConsulNamespace := "one-cert-ns" - listenerOneCert := generateTestCertificate(t, "one-cert-ns", "one-cert") - listenerOneMaxVersion := "TLSv1_2" - listenerOneMinVersion := "TLSv1_3" - listenerOneCipherSuites := []string{"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256"} - - // listener one status - listenerOneLastTransmissionTime := time.Now() - - // listener two configuration - listenerTwoName := "listener-two" - listenerTwoHostname := "*.consul.io" - listenerTwoPort := 5432 - listenerTwoProtocol := "http" - - // listener one tls config - listenerTwoCertName := "two-cert" - listenerTwoCertK8sNamespace := "two-cert-ns" - listenerTwoCertConsulNamespace := "two-cert-ns" - listenerTwoCert := generateTestCertificate(t, "two-cert-ns", "two-cert") - - // listener two status - listenerTwoLastTransmissionTime := time.Now() - - testCases := map[string]struct { - annotations map[string]string - expectedGWName string - listenerOneK8sCertRefs []gwv1beta1.SecretObjectReference - listenerOneTLSOptions map[gwv1beta1.AnnotationKey]gwv1beta1.AnnotationValue - }{ - "gw name": { - annotations: make(map[string]string), - expectedGWName: k8sObjectName, - listenerOneK8sCertRefs: []gwv1beta1.SecretObjectReference{ - { - Name: gwv1beta1.ObjectName(listenerOneCertName), - Namespace: PointerTo(gwv1beta1.Namespace(listenerOneCertK8sNamespace)), - }, - }, - listenerOneTLSOptions: map[gwv1beta1.AnnotationKey]gwv1beta1.AnnotationValue{ - TLSMaxVersionAnnotationKey: gwv1beta1.AnnotationValue(listenerOneMaxVersion), - TLSMinVersionAnnotationKey: gwv1beta1.AnnotationValue(listenerOneMinVersion), - TLSCipherSuitesAnnotationKey: gwv1beta1.AnnotationValue(strings.Join(listenerOneCipherSuites, ",")), - }, - }, - "when k8s has certs that are not referenced in consul": { - annotations: make(map[string]string), - expectedGWName: k8sObjectName, - listenerOneK8sCertRefs: []gwv1beta1.SecretObjectReference{ - { - Name: gwv1beta1.ObjectName(listenerOneCertName), - Namespace: PointerTo(gwv1beta1.Namespace(listenerOneCertK8sNamespace)), - }, - { - Name: gwv1beta1.ObjectName("cert that won't exist in the translated type"), - Namespace: PointerTo(gwv1beta1.Namespace(listenerOneCertK8sNamespace)), - }, - }, - listenerOneTLSOptions: map[gwv1beta1.AnnotationKey]gwv1beta1.AnnotationValue{ - TLSMaxVersionAnnotationKey: gwv1beta1.AnnotationValue(listenerOneMaxVersion), - TLSMinVersionAnnotationKey: gwv1beta1.AnnotationValue(listenerOneMinVersion), - TLSCipherSuitesAnnotationKey: gwv1beta1.AnnotationValue(strings.Join(listenerOneCipherSuites, ",")), - }, - }, - } - - for name, tc := range testCases { - tc := tc - t.Run(name, func(t *testing.T) { - t.Parallel() - - input := gwv1beta1.Gateway{ - TypeMeta: metav1.TypeMeta{ - Kind: "Gateway", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: k8sObjectName, - Namespace: k8sNamespace, - Annotations: tc.annotations, - }, - Spec: gwv1beta1.GatewaySpec{ - Listeners: []gwv1beta1.Listener{ - { - Name: gwv1beta1.SectionName(listenerOneName), - Hostname: PointerTo(gwv1beta1.Hostname(listenerOneHostname)), - Port: gwv1beta1.PortNumber(listenerOnePort), - Protocol: gwv1beta1.ProtocolType(listenerOneProtocol), - TLS: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: tc.listenerOneK8sCertRefs, - Options: tc.listenerOneTLSOptions, - }, - }, - { - Name: gwv1beta1.SectionName(listenerTwoName), - Hostname: PointerTo(gwv1beta1.Hostname(listenerTwoHostname)), - Port: gwv1beta1.PortNumber(listenerTwoPort), - Protocol: gwv1beta1.ProtocolType(listenerTwoProtocol), - TLS: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{ - { - Name: gwv1beta1.ObjectName(listenerTwoCertName), - Namespace: PointerTo(gwv1beta1.Namespace(listenerTwoCertK8sNamespace)), - }, - }, - }, - }, - }, - }, - Status: gwv1beta1.GatewayStatus{ - Conditions: []metav1.Condition{ - { - Type: string(gwv1beta1.GatewayConditionAccepted), - Status: metav1.ConditionTrue, - LastTransitionTime: metav1.Time{Time: gwLastTransmissionTime}, - Reason: string(gwv1beta1.GatewayReasonAccepted), - Message: "I'm accepted", - }, - }, - Listeners: []gwv1beta1.ListenerStatus{ - { - Name: gwv1beta1.SectionName(listenerOneName), - AttachedRoutes: 5, - Conditions: []metav1.Condition{ - { - Type: string(gwv1beta1.GatewayConditionReady), - Status: metav1.ConditionTrue, - LastTransitionTime: metav1.Time{Time: listenerOneLastTransmissionTime}, - Reason: string(gwv1beta1.GatewayConditionReady), - Message: "I'm ready", - }, - }, - }, - - { - Name: gwv1beta1.SectionName(listenerTwoName), - AttachedRoutes: 3, - Conditions: []metav1.Condition{ - { - Type: string(gwv1beta1.GatewayConditionReady), - Status: metav1.ConditionTrue, - LastTransitionTime: metav1.Time{Time: listenerTwoLastTransmissionTime}, - Reason: string(gwv1beta1.GatewayConditionReady), - Message: "I'm also ready", - }, - }, - }, - }, - }, - } - - expectedConfigEntry := &api.APIGatewayConfigEntry{ - Kind: api.APIGateway, - Name: tc.expectedGWName, - Meta: map[string]string{ - constants.MetaKeyKubeNS: k8sNamespace, - constants.MetaKeyKubeName: k8sObjectName, - }, - Listeners: []api.APIGatewayListener{ - { - Name: listenerOneName, - Hostname: listenerOneHostname, - Port: listenerOnePort, - Protocol: listenerOneProtocol, - TLS: api.APIGatewayTLSConfiguration{ - Certificates: []api.ResourceReference{ - { - Kind: api.InlineCertificate, - Name: listenerOneCertName, - Namespace: listenerOneCertConsulNamespace, - }, - }, - CipherSuites: listenerOneCipherSuites, - MaxVersion: listenerOneMaxVersion, - MinVersion: listenerOneMinVersion, - }, - }, - { - Name: listenerTwoName, - Hostname: listenerTwoHostname, - Port: listenerTwoPort, - Protocol: listenerTwoProtocol, - TLS: api.APIGatewayTLSConfiguration{ - Certificates: []api.ResourceReference{ - { - Kind: api.InlineCertificate, - Name: listenerTwoCertName, - Namespace: listenerTwoCertConsulNamespace, - }, - }, - CipherSuites: nil, - MaxVersion: "", - MinVersion: "", - }, - }, - }, - Status: api.ConfigEntryStatus{}, - Namespace: k8sNamespace, - } - translator := ResourceTranslator{ - EnableConsulNamespaces: true, - ConsulDestNamespace: "", - EnableK8sMirroring: true, - MirroringPrefix: "", - } - - resources := NewResourceMap(translator, fakeReferenceValidator{}, logrtest.NewTestLogger(t)) - resources.ReferenceCountCertificate(listenerOneCert) - resources.ReferenceCountCertificate(listenerTwoCert) - - actualConfigEntry := translator.ToAPIGateway(input, resources, &v1alpha1.GatewayClassConfig{}) - - if diff := cmp.Diff(expectedConfigEntry, actualConfigEntry); diff != "" { - t.Errorf("Translator.GatewayToAPIGateway() mismatch (-want +got):\n%s", diff) - } - }) - } -} - -func TestTranslator_ToHTTPRoute(t *testing.T) { - t.Parallel() - type args struct { - k8sHTTPRoute gwv1beta1.HTTPRoute - services []types.NamespacedName - meshServices []v1alpha1.MeshService - externalFilters []client.Object - } - - tests := map[string]struct { - args args - want api.HTTPRouteConfigEntry - }{ - "base test": { - args: args{ - k8sHTTPRoute: gwv1beta1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Name: "k8s-http-route", - Namespace: "k8s-ns", - Annotations: map[string]string{}, - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - { - Namespace: PointerTo(gwv1beta1.Namespace("k8s-gw-ns")), - Name: gwv1beta1.ObjectName("api-gw"), - Kind: PointerTo(gwv1beta1.Kind("Gateway")), - SectionName: PointerTo(gwv1beta1.SectionName("listener-1")), - }, - }, - }, - Hostnames: []gwv1beta1.Hostname{ - "host-name.example.com", - "consul.io", - }, - Rules: []gwv1beta1.HTTPRouteRule{ - { - Matches: []gwv1beta1.HTTPRouteMatch{ - { - Path: &gwv1beta1.HTTPPathMatch{ - Type: PointerTo(gwv1beta1.PathMatchPathPrefix), - Value: PointerTo("/v1"), - }, - Headers: []gwv1beta1.HTTPHeaderMatch{ - { - Type: PointerTo(gwv1beta1.HeaderMatchExact), - Name: "my header match", - Value: "the value", - }, - }, - QueryParams: []gwv1beta1.HTTPQueryParamMatch{ - { - Type: PointerTo(gwv1beta1.QueryParamMatchExact), - Name: "search", - Value: "term", - }, - }, - Method: PointerTo(gwv1beta1.HTTPMethodGet), - }, - }, - Filters: []gwv1beta1.HTTPRouteFilter{ - { - RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ - Set: []gwv1beta1.HTTPHeader{ - { - Name: "Magic", - Value: "v2", - }, - { - Name: "Another One", - Value: "dj khaled", - }, - }, - Add: []gwv1beta1.HTTPHeader{ - { - Name: "add it on", - Value: "the value", - }, - }, - Remove: []string{"time to go"}, - }, - URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ - Path: &gwv1beta1.HTTPPathModifier{ - Type: gwv1beta1.PrefixMatchHTTPPathModifier, - ReplacePrefixMatch: PointerTo("v1"), - }, - }, - }, - }, - BackendRefs: []gwv1beta1.HTTPBackendRef{ - { - BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "service one", - Namespace: PointerTo(gwv1beta1.Namespace("other")), - }, - Weight: PointerTo(int32(45)), - }, - Filters: []gwv1beta1.HTTPRouteFilter{ - { - RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ - Set: []gwv1beta1.HTTPHeader{ - { - Name: "svc - Magic", - Value: "svc - v2", - }, - { - Name: "svc - Another One", - Value: "svc - dj khaled", - }, - }, - Add: []gwv1beta1.HTTPHeader{ - { - Name: "svc - add it on", - Value: "svc - the value", - }, - }, - Remove: []string{"svc - time to go"}, - }, - URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ - Path: &gwv1beta1.HTTPPathModifier{ - Type: gwv1beta1.PrefixMatchHTTPPathModifier, - ReplacePrefixMatch: PointerTo("path"), - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - services: []types.NamespacedName{ - {Name: "service one", Namespace: "other"}, - }, - }, - want: api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "k8s-http-route", - Rules: []api.HTTPRouteRule{ - { - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "add it on": "the value", - }, - Remove: []string{"time to go"}, - Set: map[string]string{ - "Magic": "v2", - "Another One": "dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{Path: "v1"}, - }, - Matches: []api.HTTPMatch{ - { - Headers: []api.HTTPHeaderMatch{ - { - Match: api.HTTPHeaderMatchExact, - Name: "my header match", - Value: "the value", - }, - }, - Method: api.HTTPMatchMethodGet, - Path: api.HTTPPathMatch{ - Match: api.HTTPPathMatchPrefix, - Value: "/v1", - }, - Query: []api.HTTPQueryMatch{ - { - Match: api.HTTPQueryMatchExact, - Name: "search", - Value: "term", - }, - }, - }, - }, - Services: []api.HTTPService{ - { - Name: "service one", - Weight: 45, - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "svc - add it on": "svc - the value", - }, - Remove: []string{"svc - time to go"}, - Set: map[string]string{ - "svc - Magic": "svc - v2", - "svc - Another One": "svc - dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{ - Path: "path", - }, - }, - Namespace: "other", - }, - }, - }, - }, - Hostnames: []string{ - "host-name.example.com", - "consul.io", - }, - Meta: map[string]string{ - constants.MetaKeyKubeNS: "k8s-ns", - constants.MetaKeyKubeName: "k8s-http-route", - }, - Namespace: "k8s-ns", - }, - }, - "dropping path rewrites that are not prefix match": { - args: args{ - k8sHTTPRoute: gwv1beta1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Name: "k8s-http-route", - Namespace: "k8s-ns", - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - { - Namespace: PointerTo(gwv1beta1.Namespace("k8s-gw-ns")), - Name: gwv1beta1.ObjectName("api-gw"), - SectionName: PointerTo(gwv1beta1.SectionName("listener-1")), - Kind: PointerTo(gwv1beta1.Kind("Gateway")), - }, - }, - }, - Hostnames: []gwv1beta1.Hostname{ - "host-name.example.com", - "consul.io", - }, - Rules: []gwv1beta1.HTTPRouteRule{ - { - Matches: []gwv1beta1.HTTPRouteMatch{ - { - Path: &gwv1beta1.HTTPPathMatch{ - Type: PointerTo(gwv1beta1.PathMatchPathPrefix), - Value: PointerTo("/v1"), - }, - Headers: []gwv1beta1.HTTPHeaderMatch{ - { - Type: PointerTo(gwv1beta1.HeaderMatchExact), - Name: "my header match", - Value: "the value", - }, - }, - QueryParams: []gwv1beta1.HTTPQueryParamMatch{ - { - Type: PointerTo(gwv1beta1.QueryParamMatchExact), - Name: "search", - Value: "term", - }, - }, - Method: PointerTo(gwv1beta1.HTTPMethodGet), - }, - }, - Filters: []gwv1beta1.HTTPRouteFilter{ - { - RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ - Set: []gwv1beta1.HTTPHeader{ - { - Name: "Magic", - Value: "v2", - }, - { - Name: "Another One", - Value: "dj khaled", - }, - }, - Add: []gwv1beta1.HTTPHeader{ - { - Name: "add it on", - Value: "the value", - }, - }, - Remove: []string{"time to go"}, - }, - // THIS IS THE CHANGE - URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ - Path: &gwv1beta1.HTTPPathModifier{ - Type: gwv1beta1.FullPathHTTPPathModifier, - ReplaceFullPath: PointerTo("v1"), - }, - }, - }, - }, - BackendRefs: []gwv1beta1.HTTPBackendRef{ - { - BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "service one", - Namespace: PointerTo(gwv1beta1.Namespace("some ns")), - }, - Weight: PointerTo(int32(45)), - }, - Filters: []gwv1beta1.HTTPRouteFilter{ - { - RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ - Set: []gwv1beta1.HTTPHeader{ - { - Name: "svc - Magic", - Value: "svc - v2", - }, - { - Name: "svc - Another One", - Value: "svc - dj khaled", - }, - }, - Add: []gwv1beta1.HTTPHeader{ - { - Name: "svc - add it on", - Value: "svc - the value", - }, - }, - Remove: []string{"svc - time to go"}, - }, - URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ - Path: &gwv1beta1.HTTPPathModifier{ - Type: gwv1beta1.PrefixMatchHTTPPathModifier, - ReplacePrefixMatch: PointerTo("path"), - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - services: []types.NamespacedName{ - {Name: "service one", Namespace: "some ns"}, - }, - }, - want: api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "k8s-http-route", - Rules: []api.HTTPRouteRule{ - { - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "add it on": "the value", - }, - Remove: []string{"time to go"}, - Set: map[string]string{ - "Magic": "v2", - "Another One": "dj khaled", - }, - }, - }, - }, - Matches: []api.HTTPMatch{ - { - Headers: []api.HTTPHeaderMatch{ - { - Match: api.HTTPHeaderMatchExact, - Name: "my header match", - Value: "the value", - }, - }, - Method: api.HTTPMatchMethodGet, - Path: api.HTTPPathMatch{ - Match: api.HTTPPathMatchPrefix, - Value: "/v1", - }, - Query: []api.HTTPQueryMatch{ - { - Match: api.HTTPQueryMatchExact, - Name: "search", - Value: "term", - }, - }, - }, - }, - Services: []api.HTTPService{ - { - Name: "service one", - Weight: 45, - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "svc - add it on": "svc - the value", - }, - Remove: []string{"svc - time to go"}, - Set: map[string]string{ - "svc - Magic": "svc - v2", - "svc - Another One": "svc - dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{ - Path: "path", - }, - }, - Namespace: "some ns", - }, - }, - }, - }, - Hostnames: []string{ - "host-name.example.com", - "consul.io", - }, - Meta: map[string]string{ - constants.MetaKeyKubeNS: "k8s-ns", - constants.MetaKeyKubeName: "k8s-http-route", - }, - Namespace: "k8s-ns", - }, - }, - "parent ref that is not registered with consul is dropped": { - args: args{ - k8sHTTPRoute: gwv1beta1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Name: "k8s-http-route", - Namespace: "k8s-ns", - Annotations: map[string]string{}, - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - { - Namespace: PointerTo(gwv1beta1.Namespace("k8s-gw-ns")), - Name: gwv1beta1.ObjectName("api-gw"), - Kind: PointerTo(gwv1beta1.Kind("Gateway")), - SectionName: PointerTo(gwv1beta1.SectionName("listener-1")), - }, - - { - Namespace: PointerTo(gwv1beta1.Namespace("k8s-gw-ns")), - Name: gwv1beta1.ObjectName("consul don't know about me"), - Kind: PointerTo(gwv1beta1.Kind("Gateway")), - SectionName: PointerTo(gwv1beta1.SectionName("listener-1")), - }, - }, - }, - Hostnames: []gwv1beta1.Hostname{ - "host-name.example.com", - "consul.io", - }, - Rules: []gwv1beta1.HTTPRouteRule{ - { - Matches: []gwv1beta1.HTTPRouteMatch{ - { - Path: &gwv1beta1.HTTPPathMatch{ - Type: PointerTo(gwv1beta1.PathMatchPathPrefix), - Value: PointerTo("/v1"), - }, - Headers: []gwv1beta1.HTTPHeaderMatch{ - { - Type: PointerTo(gwv1beta1.HeaderMatchExact), - Name: "my header match", - Value: "the value", - }, - }, - QueryParams: []gwv1beta1.HTTPQueryParamMatch{ - { - Type: PointerTo(gwv1beta1.QueryParamMatchExact), - Name: "search", - Value: "term", - }, - }, - Method: PointerTo(gwv1beta1.HTTPMethodGet), - }, - }, - Filters: []gwv1beta1.HTTPRouteFilter{ - { - RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ - Set: []gwv1beta1.HTTPHeader{ - { - Name: "Magic", - Value: "v2", - }, - { - Name: "Another One", - Value: "dj khaled", - }, - }, - Add: []gwv1beta1.HTTPHeader{ - { - Name: "add it on", - Value: "the value", - }, - }, - Remove: []string{"time to go"}, - }, - URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ - Path: &gwv1beta1.HTTPPathModifier{ - Type: gwv1beta1.PrefixMatchHTTPPathModifier, - ReplacePrefixMatch: PointerTo("v1"), - }, - }, - }, - }, - BackendRefs: []gwv1beta1.HTTPBackendRef{ - { - BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "service one", - Namespace: PointerTo(gwv1beta1.Namespace("some ns")), - }, - Weight: PointerTo(int32(45)), - }, - Filters: []gwv1beta1.HTTPRouteFilter{ - { - RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ - Set: []gwv1beta1.HTTPHeader{ - { - Name: "svc - Magic", - Value: "svc - v2", - }, - { - Name: "svc - Another One", - Value: "svc - dj khaled", - }, - }, - Add: []gwv1beta1.HTTPHeader{ - { - Name: "svc - add it on", - Value: "svc - the value", - }, - }, - Remove: []string{"svc - time to go"}, - }, - URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ - Path: &gwv1beta1.HTTPPathModifier{ - Type: gwv1beta1.PrefixMatchHTTPPathModifier, - ReplacePrefixMatch: PointerTo("path"), - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - services: []types.NamespacedName{ - {Name: "service one", Namespace: "some ns"}, - }, - }, - want: api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "k8s-http-route", - Rules: []api.HTTPRouteRule{ - { - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "add it on": "the value", - }, - Remove: []string{"time to go"}, - Set: map[string]string{ - "Magic": "v2", - "Another One": "dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{Path: "v1"}, - }, - Matches: []api.HTTPMatch{ - { - Headers: []api.HTTPHeaderMatch{ - { - Match: api.HTTPHeaderMatchExact, - Name: "my header match", - Value: "the value", - }, - }, - Method: api.HTTPMatchMethodGet, - Path: api.HTTPPathMatch{ - Match: api.HTTPPathMatchPrefix, - Value: "/v1", - }, - Query: []api.HTTPQueryMatch{ - { - Match: api.HTTPQueryMatchExact, - Name: "search", - Value: "term", - }, - }, - }, - }, - Services: []api.HTTPService{ - { - Name: "service one", - Weight: 45, - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "svc - add it on": "svc - the value", - }, - Remove: []string{"svc - time to go"}, - Set: map[string]string{ - "svc - Magic": "svc - v2", - "svc - Another One": "svc - dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{ - Path: "path", - }, - }, - Namespace: "some ns", - }, - }, - }, - }, - Hostnames: []string{ - "host-name.example.com", - "consul.io", - }, - Meta: map[string]string{ - constants.MetaKeyKubeNS: "k8s-ns", - constants.MetaKeyKubeName: "k8s-http-route", - }, - Namespace: "k8s-ns", - }, - }, - "when section name on apigw is not supplied": { - args: args{ - k8sHTTPRoute: gwv1beta1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Name: "k8s-http-route", - Namespace: "k8s-ns", - Annotations: map[string]string{}, - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - { - Namespace: PointerTo(gwv1beta1.Namespace("k8s-gw-ns")), - Name: gwv1beta1.ObjectName("api-gw"), - Kind: PointerTo(gwv1beta1.Kind("Gateway")), - }, - }, - }, - Hostnames: []gwv1beta1.Hostname{ - "host-name.example.com", - "consul.io", - }, - Rules: []gwv1beta1.HTTPRouteRule{ - { - Matches: []gwv1beta1.HTTPRouteMatch{ - { - Path: &gwv1beta1.HTTPPathMatch{ - Type: PointerTo(gwv1beta1.PathMatchPathPrefix), - Value: PointerTo("/v1"), - }, - Headers: []gwv1beta1.HTTPHeaderMatch{ - { - Type: PointerTo(gwv1beta1.HeaderMatchExact), - Name: "my header match", - Value: "the value", - }, - }, - QueryParams: []gwv1beta1.HTTPQueryParamMatch{ - { - Type: PointerTo(gwv1beta1.QueryParamMatchExact), - Name: "search", - Value: "term", - }, - }, - Method: PointerTo(gwv1beta1.HTTPMethodGet), - }, - }, - Filters: []gwv1beta1.HTTPRouteFilter{ - { - RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ - Set: []gwv1beta1.HTTPHeader{ - { - Name: "Magic", - Value: "v2", - }, - { - Name: "Another One", - Value: "dj khaled", - }, - }, - Add: []gwv1beta1.HTTPHeader{ - { - Name: "add it on", - Value: "the value", - }, - }, - Remove: []string{"time to go"}, - }, - URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ - Path: &gwv1beta1.HTTPPathModifier{ - Type: gwv1beta1.PrefixMatchHTTPPathModifier, - ReplacePrefixMatch: PointerTo("v1"), - }, - }, - }, - }, - BackendRefs: []gwv1beta1.HTTPBackendRef{ - { - // this ref should get dropped - BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "service two", - Namespace: PointerTo(gwv1beta1.Namespace("some ns")), - }, - }, - }, - { - BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "some-service-part-three", - Namespace: PointerTo(gwv1beta1.Namespace("svc-ns")), - Group: PointerTo(gwv1beta1.Group(v1alpha1.ConsulHashicorpGroup)), - Kind: PointerTo(gwv1beta1.Kind(v1alpha1.MeshServiceKind)), - }, - }, - }, - { - BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "service one", - Namespace: PointerTo(gwv1beta1.Namespace("some ns")), - }, - Weight: PointerTo(int32(45)), - }, - Filters: []gwv1beta1.HTTPRouteFilter{ - { - RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ - Set: []gwv1beta1.HTTPHeader{ - { - Name: "svc - Magic", - Value: "svc - v2", - }, - { - Name: "svc - Another One", - Value: "svc - dj khaled", - }, - }, - Add: []gwv1beta1.HTTPHeader{ - { - Name: "svc - add it on", - Value: "svc - the value", - }, - }, - Remove: []string{"svc - time to go"}, - }, - URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ - Path: &gwv1beta1.HTTPPathModifier{ - Type: gwv1beta1.PrefixMatchHTTPPathModifier, - ReplacePrefixMatch: PointerTo("path"), - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - services: []types.NamespacedName{ - {Name: "service one", Namespace: "some ns"}, - }, - meshServices: []v1alpha1.MeshService{ - {ObjectMeta: metav1.ObjectMeta{Name: "some-service-part-three", Namespace: "svc-ns"}, Spec: v1alpha1.MeshServiceSpec{Name: "some-override"}}, - }, - }, - want: api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "k8s-http-route", - Rules: []api.HTTPRouteRule{ - { - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "add it on": "the value", - }, - Remove: []string{"time to go"}, - Set: map[string]string{ - "Magic": "v2", - "Another One": "dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{Path: "v1"}, - }, - Matches: []api.HTTPMatch{ - { - Headers: []api.HTTPHeaderMatch{ - { - Match: api.HTTPHeaderMatchExact, - Name: "my header match", - Value: "the value", - }, - }, - Method: api.HTTPMatchMethodGet, - Path: api.HTTPPathMatch{ - Match: api.HTTPPathMatchPrefix, - Value: "/v1", - }, - Query: []api.HTTPQueryMatch{ - { - Match: api.HTTPQueryMatchExact, - Name: "search", - Value: "term", - }, - }, - }, - }, - Services: []api.HTTPService{ - {Name: "some-override", Namespace: "svc-ns", Weight: 1, Filters: api.HTTPFilters{Headers: []api.HTTPHeaderFilter{{Add: make(map[string]string), Set: make(map[string]string)}}}}, - { - Name: "service one", - Weight: 45, - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{ - "svc - add it on": "svc - the value", - }, - Remove: []string{"svc - time to go"}, - Set: map[string]string{ - "svc - Magic": "svc - v2", - "svc - Another One": "svc - dj khaled", - }, - }, - }, - URLRewrite: &api.URLRewrite{ - Path: "path", - }, - }, - Namespace: "some ns", - }, - }, - }, - }, - Hostnames: []string{ - "host-name.example.com", - "consul.io", - }, - Meta: map[string]string{ - constants.MetaKeyKubeNS: "k8s-ns", - constants.MetaKeyKubeName: "k8s-http-route", - }, - Namespace: "k8s-ns", - }, - }, - "test with external filters": { - args: args{ - k8sHTTPRoute: gwv1beta1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Name: "k8s-http-route", - Namespace: "k8s-ns", - Annotations: map[string]string{}, - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - { - Namespace: PointerTo(gwv1beta1.Namespace("k8s-gw-ns")), - Name: gwv1beta1.ObjectName("api-gw"), - Kind: PointerTo(gwv1beta1.Kind("Gateway")), - SectionName: PointerTo(gwv1beta1.SectionName("listener-1")), - }, - }, - }, - Hostnames: []gwv1beta1.Hostname{ - "host-name.example.com", - "consul.io", - }, - Rules: []gwv1beta1.HTTPRouteRule{ - { - Matches: []gwv1beta1.HTTPRouteMatch{ - { - Path: &gwv1beta1.HTTPPathMatch{ - Type: PointerTo(gwv1beta1.PathMatchPathPrefix), - Value: PointerTo("/v1"), - }, - Headers: []gwv1beta1.HTTPHeaderMatch{ - { - Type: PointerTo(gwv1beta1.HeaderMatchExact), - Name: "my header match", - Value: "the value", - }, - }, - QueryParams: []gwv1beta1.HTTPQueryParamMatch{ - { - Type: PointerTo(gwv1beta1.QueryParamMatchExact), - Name: "search", - Value: "term", - }, - }, - Method: PointerTo(gwv1beta1.HTTPMethodGet), - }, - }, - Filters: []gwv1beta1.HTTPRouteFilter{ - { - ExtensionRef: &gwv1beta1.LocalObjectReference{ - Name: "test", - Kind: v1alpha1.RouteRetryFilterKind, - Group: "consul.hashicorp.com/v1alpha1", - }, - }, - }, - BackendRefs: []gwv1beta1.HTTPBackendRef{ - { - BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "service one", - Namespace: PointerTo(gwv1beta1.Namespace("other")), - }, - Weight: PointerTo(int32(45)), - }, - Filters: []gwv1beta1.HTTPRouteFilter{ - { - ExtensionRef: &gwv1beta1.LocalObjectReference{ - Name: "test", - Kind: v1alpha1.RouteRetryFilterKind, - Group: "consul.hashicorp.com/v1alpha1", - }, - }, - }, - }, - }, - }, - }, - }, - }, - services: []types.NamespacedName{ - {Name: "service one", Namespace: "other"}, - }, - externalFilters: []client.Object{ - &v1alpha1.RouteRetryFilter{ - TypeMeta: metav1.TypeMeta{ - Kind: v1alpha1.RouteRetryFilterKind, - APIVersion: "consul.hashicorp.com/v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "k8s-ns", - }, - Spec: v1alpha1.RouteRetryFilterSpec{ - NumRetries: pointer.Uint32(3), - RetryOn: []string{"cancelled"}, - RetryOnStatusCodes: []uint32{500, 502}, - RetryOnConnectFailure: pointer.Bool(false), - }, - }, - - &v1alpha1.RouteRetryFilter{ - TypeMeta: metav1.TypeMeta{ - Kind: v1alpha1.RouteRetryFilterKind, - APIVersion: "consul.hashicorp.com/v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "other-namespace-even-though-same-name", - }, - Spec: v1alpha1.RouteRetryFilterSpec{ - NumRetries: pointer.Uint32(3), - RetryOn: []string{"don't"}, - RetryOnStatusCodes: []uint32{404}, - RetryOnConnectFailure: pointer.Bool(true), - }, - }, - }, - }, - want: api.HTTPRouteConfigEntry{ - Kind: api.HTTPRoute, - Name: "k8s-http-route", - Rules: []api.HTTPRouteRule{ - { - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{{Add: map[string]string{}, Set: map[string]string{}}}, - URLRewrite: nil, - RetryFilter: &api.RetryFilter{ - NumRetries: pointer.Uint32(3), - RetryOn: []string{"cancelled"}, - RetryOnStatusCodes: []uint32{500, 502}, - RetryOnConnectFailure: pointer.Bool(false), - }, - }, - Matches: []api.HTTPMatch{ - { - Headers: []api.HTTPHeaderMatch{ - { - Match: api.HTTPHeaderMatchExact, - Name: "my header match", - Value: "the value", - }, - }, - Method: api.HTTPMatchMethodGet, - Path: api.HTTPPathMatch{ - Match: api.HTTPPathMatchPrefix, - Value: "/v1", - }, - Query: []api.HTTPQueryMatch{ - { - Match: api.HTTPQueryMatchExact, - Name: "search", - Value: "term", - }, - }, - }, - }, - Services: []api.HTTPService{ - { - Name: "service one", - Weight: 45, - Filters: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{{Add: map[string]string{}, Set: map[string]string{}}}, - RetryFilter: &api.RetryFilter{ - NumRetries: pointer.Uint32(3), - RetryOn: []string{"cancelled"}, - RetryOnStatusCodes: []uint32{500, 502}, - RetryOnConnectFailure: pointer.Bool(false), - }, - }, - Namespace: "other", - }, - }, - }, - }, - Hostnames: []string{ - "host-name.example.com", - "consul.io", - }, - Meta: map[string]string{ - constants.MetaKeyKubeNS: "k8s-ns", - constants.MetaKeyKubeName: "k8s-http-route", - }, - Namespace: "k8s-ns", - }, - }, - } - for name, tc := range tests { - t.Run(name, func(t *testing.T) { - tr := ResourceTranslator{ - EnableConsulNamespaces: true, - EnableK8sMirroring: true, - } - - resources := NewResourceMap(tr, fakeReferenceValidator{}, logrtest.NewTestLogger(t)) - for _, service := range tc.args.services { - resources.AddService(service, service.Name) - } - for _, service := range tc.args.meshServices { - resources.AddMeshService(service) - } - - for _, filterToAdd := range tc.args.externalFilters { - resources.AddExternalFilter(filterToAdd) - } - - got := tr.ToHTTPRoute(tc.args.k8sHTTPRoute, resources) - if diff := cmp.Diff(&tc.want, got); diff != "" { - t.Errorf("Translator.ToHTTPRoute() mismatch (-want +got):\n%s", diff) - } - }) - } -} - -func TestTranslator_ToTCPRoute(t *testing.T) { - t.Parallel() - type args struct { - k8sRoute gwv1alpha2.TCPRoute - services []types.NamespacedName - meshServices []v1alpha1.MeshService - } - tests := map[string]struct { - args args - want api.TCPRouteConfigEntry - }{ - "base test": { - args: args{ - k8sRoute: gwv1alpha2.TCPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Name: "tcp-route", - Namespace: "k8s-ns", - }, - Spec: gwv1alpha2.TCPRouteSpec{ - Rules: []gwv1alpha2.TCPRouteRule{ - { - BackendRefs: []gwv1beta1.BackendRef{ - { - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "some-service", - Namespace: PointerTo(gwv1beta1.Namespace("svc-ns")), - }, - Weight: new(int32), - }, - }, - }, - { - BackendRefs: []gwv1beta1.BackendRef{ - { - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "some-service-part-two", - Namespace: PointerTo(gwv1beta1.Namespace("svc-ns")), - }, - Weight: new(int32), - }, - { - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Group: PointerTo(gwv1beta1.Group(v1alpha1.ConsulHashicorpGroup)), - Kind: PointerTo(gwv1beta1.Kind(v1alpha1.MeshServiceKind)), - Name: "some-service-part-three", - Namespace: PointerTo(gwv1beta1.Namespace("svc-ns")), - }, - Weight: new(int32), - }, - }, - }, - }, - }, - }, - services: []types.NamespacedName{ - {Name: "some-service", Namespace: "svc-ns"}, - {Name: "some-service-part-two", Namespace: "svc-ns"}, - }, - meshServices: []v1alpha1.MeshService{ - {ObjectMeta: metav1.ObjectMeta{Name: "some-service-part-three", Namespace: "svc-ns"}, Spec: v1alpha1.MeshServiceSpec{Name: "some-override"}}, - }, - }, - want: api.TCPRouteConfigEntry{ - Kind: api.TCPRoute, - Name: "tcp-route", - Namespace: "k8s-ns", - Services: []api.TCPService{ - { - Name: "some-service", - Partition: "", - Namespace: "svc-ns", - }, - { - Name: "some-service-part-two", - Partition: "", - Namespace: "svc-ns", - }, - { - Name: "some-override", - Partition: "", - Namespace: "svc-ns", - }, - }, - Meta: map[string]string{ - constants.MetaKeyKubeNS: "k8s-ns", - constants.MetaKeyKubeName: "tcp-route", - }, - }, - }, - } - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - tr := ResourceTranslator{ - EnableConsulNamespaces: true, - EnableK8sMirroring: true, - } - - resources := NewResourceMap(tr, fakeReferenceValidator{}, logrtest.NewTestLogger(t)) - for _, service := range tt.args.services { - resources.AddService(service, service.Name) - } - for _, service := range tt.args.meshServices { - resources.AddMeshService(service) - } - - got := tr.ToTCPRoute(tt.args.k8sRoute, resources) - if diff := cmp.Diff(&tt.want, got); diff != "" { - t.Errorf("Translator.TCPRouteToTCPRoute() mismatch (-want +got):\n%s", diff) - } - }) - } -} - -func generateTestCertificate(t *testing.T, namespace, name string) corev1.Secret { - privateKey, err := rsa.GenerateKey(rand.Reader, 1024) - require.NoError(t, err) - - usage := x509.KeyUsageCertSign - expiration := time.Now().AddDate(10, 0, 0) - - cert := &x509.Certificate{ - SerialNumber: big.NewInt(1), - Subject: pkix.Name{ - CommonName: "consul.test", - }, - IsCA: true, - NotBefore: time.Now().Add(-10 * time.Minute), - NotAfter: expiration, - SubjectKeyId: []byte{1, 2, 3, 4, 6}, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, - KeyUsage: usage, - BasicConstraintsValid: true, - } - caCert := cert - caPrivateKey := privateKey - - data, err := x509.CreateCertificate(rand.Reader, cert, caCert, &privateKey.PublicKey, caPrivateKey) - require.NoError(t, err) - - certBytes := pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: data, - }) - - privateKeyBytes := pem.EncodeToMemory(&pem.Block{ - Type: "RSA PRIVATE KEY", - Bytes: x509.MarshalPKCS1PrivateKey(privateKey), - }) - - return corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - }, - Data: map[string][]byte{ - corev1.TLSCertKey: certBytes, - corev1.TLSPrivateKeyKey: privateKeyBytes, - }, - } -} - -func TestResourceTranslator_translateHTTPFilters(t1 *testing.T) { - type fields struct { - EnableConsulNamespaces bool - ConsulDestNamespace string - EnableK8sMirroring bool - MirroringPrefix string - ConsulPartition string - Datacenter string - } - type args struct { - filters []gwv1beta1.HTTPRouteFilter - } - tests := []struct { - name string - fields fields - args args - want api.HTTPFilters - }{ - { - name: "no httproutemodifier set", - fields: fields{}, - args: args{ - filters: []gwv1beta1.HTTPRouteFilter{ - { - URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{}, - }, - }, - }, - want: api.HTTPFilters{ - Headers: []api.HTTPHeaderFilter{ - { - Add: map[string]string{}, - Set: map[string]string{}, - }, - }, - URLRewrite: nil, - }, - }, - } - for _, tt := range tests { - t1.Run(tt.name, func(t1 *testing.T) { - t := ResourceTranslator{ - EnableConsulNamespaces: tt.fields.EnableConsulNamespaces, - ConsulDestNamespace: tt.fields.ConsulDestNamespace, - EnableK8sMirroring: tt.fields.EnableK8sMirroring, - MirroringPrefix: tt.fields.MirroringPrefix, - ConsulPartition: tt.fields.ConsulPartition, - Datacenter: tt.fields.Datacenter, - } - assert.Equalf(t1, tt.want, t.translateHTTPFilters(tt.args.filters, nil, ""), "translateHTTPFilters(%v)", tt.args.filters) - }) - } -} diff --git a/control-plane/api-gateway/controllers/finalizer.go b/control-plane/api-gateway/controllers/finalizer.go deleted file mode 100644 index c12f5f29e7..0000000000 --- a/control-plane/api-gateway/controllers/finalizer.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers - -import ( - "context" - - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// EnsureFinalizer ensures that the given object has the given finalizer. -func EnsureFinalizer(ctx context.Context, client client.Client, object client.Object, finalizer string) (didUpdate bool, err error) { - finalizers := object.GetFinalizers() - for _, f := range finalizers { - if f == finalizer { - return false, nil - } - } - object.SetFinalizers(append(finalizers, finalizer)) - if err := client.Update(ctx, object); err != nil { - return false, err - } - - return true, nil -} - -// RemoveFinalizer removes the given finalizer from the given object. -func RemoveFinalizer(ctx context.Context, client client.Client, object client.Object, finalizer string) (didUpdate bool, err error) { - finalizers := object.GetFinalizers() - - for i, f := range finalizers { - if f == finalizer { - finalizers = append(finalizers[:i], finalizers[i+1:]...) - object.SetFinalizers(finalizers) - if err := client.Update(ctx, object); err != nil { - return false, err - } - return true, nil - } - } - - return false, nil -} diff --git a/control-plane/api-gateway/controllers/finalizer_test.go b/control-plane/api-gateway/controllers/finalizer_test.go deleted file mode 100644 index dc265ef6ca..0000000000 --- a/control-plane/api-gateway/controllers/finalizer_test.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers - -import ( - "context" - "testing" - - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client/fake" -) - -func TestEnsureFinalizer(t *testing.T) { - t.Parallel() - - finalizer := "test-finalizer" - - cases := map[string]struct { - initialFinalizers []string - finalizerToAdd string - expectedDidUpdate bool - }{ - "should update": {[]string{}, finalizer, true}, - "should not update": {[]string{finalizer}, finalizer, false}, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - // It doesn't matter what the object is, as long as it implements client.Object. - // A Pod was as good as any other object here. - testObj := &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-obj", - Finalizers: tc.initialFinalizers, - }, - } - - client := fake.NewClientBuilder().WithObjects(testObj).Build() - - didUpdate, err := EnsureFinalizer(context.Background(), client, testObj, tc.finalizerToAdd) - - require.NoError(t, err) - require.Equal(t, tc.expectedDidUpdate, didUpdate) - }) - } -} - -func TestRemoveFinalizer(t *testing.T) { - t.Parallel() - - finalizer := "test-finalizer" - - cases := map[string]struct { - initialFinalizers []string - finalizerToRemove string - expectedDidUpdate bool - }{ - "should update": {[]string{finalizer}, finalizer, true}, - "should not update": {[]string{}, finalizer, false}, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - // It doesn't matter what the object is, as long as it implements client.Object. - // A Pod was as good as any other object here. - testObj := &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-obj", - Finalizers: tc.initialFinalizers, - }, - } - - client := fake.NewClientBuilder().WithObjects(testObj).Build() - - didUpdate, err := RemoveFinalizer(context.Background(), client, testObj, tc.finalizerToRemove) - - require.NoError(t, err) - require.Equal(t, tc.expectedDidUpdate, didUpdate) - }) - } -} diff --git a/control-plane/api-gateway/controllers/gateway_controller.go b/control-plane/api-gateway/controllers/gateway_controller.go deleted file mode 100644 index f7ef3af83b..0000000000 --- a/control-plane/api-gateway/controllers/gateway_controller.go +++ /dev/null @@ -1,1166 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers - -import ( - "context" - "fmt" - "reflect" - "strconv" - "strings" - - mapset "github.com/deckarep/golang-set" - "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" - - "github.com/go-logr/logr" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/types" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/builder" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/predicate" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/binding" - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/cache" - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/gatekeeper" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul-k8s/control-plane/consul" - "github.com/hashicorp/consul/api" -) - -// GatewayControllerConfig holds the values necessary for configuring the GatewayController. -type GatewayControllerConfig struct { - HelmConfig common.HelmConfig - ConsulClientConfig *consul.Config - ConsulServerConnMgr consul.ServerConnectionManager - NamespacesEnabled bool - CrossNamespaceACLPolicy string - Partition string - Datacenter string - AllowK8sNamespacesSet mapset.Set - DenyK8sNamespacesSet mapset.Set -} - -// GatewayController reconciles a Gateway object. -// The Gateway is responsible for defining the behavior of API gateways. -type GatewayController struct { - HelmConfig common.HelmConfig - Log logr.Logger - Translator common.ResourceTranslator - - cache *cache.Cache - gatewayCache *cache.GatewayCache - allowK8sNamespacesSet mapset.Set - denyK8sNamespacesSet mapset.Set - client.Client -} - -// Reconcile handles the reconciliation loop for Gateway objects. -func (r *GatewayController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - consulKey := r.Translator.ConfigEntryReference(api.APIGateway, req.NamespacedName) - nonNormalizedConsulKey := r.Translator.NonNormalizedConfigEntryReference(api.APIGateway, req.NamespacedName) - - var gateway gwv1beta1.Gateway - - log := r.Log.V(1).WithValues("gateway", req.NamespacedName) - log.Info("Reconciling Gateway") - - // get the gateway - if err := r.Client.Get(ctx, req.NamespacedName, &gateway); err != nil { - if !k8serrors.IsNotFound(err) { - log.Error(err, "unable to get Gateway") - } - return ctrl.Result{}, client.IgnoreNotFound(err) - } - - // get the gateway class - gatewayClass, err := r.getGatewayClassForGateway(ctx, gateway) - if err != nil { - log.Error(err, "unable to get GatewayClass") - return ctrl.Result{}, err - } - - // get the gateway class config - gatewayClassConfig, err := r.getConfigForGatewayClass(ctx, gatewayClass) - if err != nil { - log.Error(err, "error fetching the gateway class config") - return ctrl.Result{}, err - } - - // get all namespaces - namespaces, err := r.getNamespaces(ctx) - if err != nil { - log.Error(err, "unable to list Namespaces") - return ctrl.Result{}, err - } - - // get all reference grants - grants, err := r.getReferenceGrants(ctx) - if err != nil { - log.Error(err, "unable to list ReferenceGrants") - return ctrl.Result{}, err - } - - // get related gateway service - service, err := r.getDeployedGatewayService(ctx, req.NamespacedName) - if err != nil { - log.Error(err, "unable to fetch service for Gateway") - } - - // get related gateway pods - pods, err := r.getDeployedGatewayPods(ctx, gateway) - if err != nil { - log.Error(err, "unable to list Pods for Gateway") - return ctrl.Result{}, err - } - - // construct our resource map - referenceValidator := binding.NewReferenceValidator(grants) - resources := common.NewResourceMap(r.Translator, referenceValidator, log) - - if err := r.fetchCertificatesForGateway(ctx, resources, gateway); err != nil { - log.Error(err, "unable to fetch certificates for gateway") - return ctrl.Result{}, err - } - - // fetch our inline certificates from cache, this needs to happen - // here since the certificates need to be reference counted before - // the gateways. - r.fetchConsulInlineCertificates(resources) - - // add our current gateway even if it's not controlled by us so we - // can garbage collect any resources for it. - resources.ReferenceCountGateway(gateway) - - if err := r.fetchControlledGateways(ctx, resources); err != nil { - log.Error(err, "unable to fetch controlled gateways") - return ctrl.Result{}, err - } - - // get all http routes referencing this gateway - httpRoutes, err := r.getRelatedHTTPRoutes(ctx, req.NamespacedName, resources) - if err != nil { - log.Error(err, "unable to list HTTPRoutes") - return ctrl.Result{}, err - } - - // get all tcp routes referencing this gateway - tcpRoutes, err := r.getRelatedTCPRoutes(ctx, req.NamespacedName, resources) - if err != nil { - log.Error(err, "unable to list TCPRoutes") - return ctrl.Result{}, err - } - - if err := r.fetchServicesForRoutes(ctx, resources, tcpRoutes, httpRoutes); err != nil { - log.Error(err, "unable to fetch services for routes") - return ctrl.Result{}, err - } - - // fetch the rest of the consul objects from cache - consulServices := r.getConsulServices(consulKey) - consulGateway := r.getConsulGateway(consulKey) - r.fetchConsulHTTPRoutes(consulKey, resources) - r.fetchConsulTCPRoutes(consulKey, resources) - - binder := binding.NewBinder(binding.BinderConfig{ - Logger: log, - Translator: r.Translator, - ControllerName: common.GatewayClassControllerName, - Namespaces: namespaces, - GatewayClassConfig: gatewayClassConfig, - GatewayClass: gatewayClass, - Gateway: gateway, - Pods: pods, - Service: service, - HTTPRoutes: httpRoutes, - TCPRoutes: tcpRoutes, - Resources: resources, - ConsulGateway: consulGateway, - ConsulGatewayServices: consulServices, - }) - - updates := binder.Snapshot() - - if updates.UpsertGatewayDeployment { - if err := r.cache.EnsureRoleBinding(r.HelmConfig.AuthMethod, gateway.Name, gateway.Namespace); err != nil { - log.Error(err, "error creating role binding") - return ctrl.Result{}, err - } - - err := r.updateGatekeeperResources(ctx, log, &gateway, updates.GatewayClassConfig) - if err != nil { - if k8serrors.IsConflict(err) { - log.Info("error updating object when updating gateway resources, will try to re-reconcile") - - return ctrl.Result{Requeue: true}, nil - } - log.Error(err, "unable to update gateway resources") - return ctrl.Result{}, err - } - r.gatewayCache.EnsureSubscribed(nonNormalizedConsulKey, req.NamespacedName) - } else { - err := r.deleteGatekeeperResources(ctx, log, &gateway) - if err != nil { - if k8serrors.IsConflict(err) { - log.Info("error updating object when deleting gateway resources, will try to re-reconcile") - - return ctrl.Result{Requeue: true}, nil - } - log.Error(err, "unable to delete gateway resources") - return ctrl.Result{}, err - } - r.gatewayCache.RemoveSubscription(nonNormalizedConsulKey) - // make sure we have deregistered all services even if they haven't - // hit cache yet - if err := r.deregisterAllServices(ctx, nonNormalizedConsulKey); err != nil { - log.Error(err, "error deregistering services") - return ctrl.Result{}, err - } - } - - for _, deletion := range updates.Consul.Deletions { - log.Info("deleting from Consul", "kind", deletion.Kind, "namespace", deletion.Namespace, "name", deletion.Name) - if err := r.cache.Delete(ctx, deletion); err != nil { - log.Error(err, "error deleting config entry") - return ctrl.Result{}, err - } - } - - for _, update := range updates.Consul.Updates { - entry := update.Entry - log.Info("updating in Consul", "kind", entry.GetKind(), "namespace", entry.GetNamespace(), "name", entry.GetName()) - err := r.cache.Write(ctx, entry) - if update.OnUpdate != nil { - // swallow any potential error with our handler if one is provided - update.OnUpdate(err) - continue - } - - if err != nil { - log.Error(err, "error updating config entry") - return ctrl.Result{}, err - } - } - - if updates.UpsertGatewayDeployment { - // We only do some registration/deregistraion if we still have a valid gateway - // otherwise, we've already deregistered everything related to the gateway, so - // no need to do any of the following. - for _, registration := range updates.Consul.Registrations { - log.Info("registering service in Consul", "service", registration.Service.Service, "id", registration.Service.ID) - if err := r.cache.Register(ctx, registration); err != nil { - log.Error(err, "error registering service") - return ctrl.Result{}, err - } - } - - for _, deregistration := range updates.Consul.Deregistrations { - log.Info("deregistering service in Consul", "id", deregistration.ServiceID) - if err := r.cache.Deregister(ctx, deregistration); err != nil { - log.Error(err, "error deregistering service") - return ctrl.Result{}, err - } - } - } - - for _, update := range updates.Kubernetes.Updates.Operations() { - log.Info("update in Kubernetes", "kind", update.GetObjectKind().GroupVersionKind().Kind, "namespace", update.GetNamespace(), "name", update.GetName()) - if err := r.updateAndResetStatus(ctx, update); err != nil { - if k8serrors.IsConflict(err) { - log.Info("error updating object for gateway, will try to re-reconcile") - - return ctrl.Result{Requeue: true}, nil - } - log.Error(err, "error updating object") - return ctrl.Result{}, err - } - } - - for _, update := range updates.Kubernetes.StatusUpdates.Operations() { - log.Info("update status in Kubernetes", "kind", update.GetObjectKind().GroupVersionKind().Kind, "namespace", update.GetNamespace(), "name", update.GetName()) - if err := r.Client.Status().Update(ctx, update); err != nil { - if k8serrors.IsConflict(err) { - log.Info("error updating status for gateway, will try to re-reconcile") - - return ctrl.Result{Requeue: true}, nil - } - log.Error(err, "error updating status") - return ctrl.Result{}, err - } - } - - return ctrl.Result{}, nil -} - -func (r *GatewayController) deregisterAllServices(ctx context.Context, consulKey api.ResourceReference) error { - services, err := r.gatewayCache.FetchServicesFor(ctx, consulKey) - if err != nil { - return err - } - for _, service := range services { - if err := r.cache.Deregister(ctx, api.CatalogDeregistration{ - Node: service.Node, - ServiceID: service.ServiceID, - Namespace: service.Namespace, - }); err != nil { - return err - } - } - return nil -} - -func (r *GatewayController) updateAndResetStatus(ctx context.Context, o client.Object) error { - // we create a copy so that we can re-update its status if need be - status := reflect.ValueOf(o.DeepCopyObject()).Elem().FieldByName("Status") - if err := r.Client.Update(ctx, o); err != nil { - return err - } - - // reset the status in case it needs to be updated below - reflect.ValueOf(o).Elem().FieldByName("Status").Set(status) - return nil -} - -func configEntriesTo[T api.ConfigEntry](entries []api.ConfigEntry) []T { - es := []T{} - for _, e := range entries { - es = append(es, e.(T)) - } - return es -} - -func (r *GatewayController) deleteGatekeeperResources(ctx context.Context, log logr.Logger, gw *gwv1beta1.Gateway) error { - gk := gatekeeper.New(log, r.Client) - err := gk.Delete(ctx, types.NamespacedName{ - Namespace: gw.Namespace, - Name: gw.Name, - }) - if err != nil { - return err - } - - return nil -} - -func (r *GatewayController) updateGatekeeperResources(ctx context.Context, log logr.Logger, gw *gwv1beta1.Gateway, gwcc *v1alpha1.GatewayClassConfig) error { - gk := gatekeeper.New(log, r.Client) - err := gk.Upsert(ctx, *gw, *gwcc, r.HelmConfig) - if err != nil { - return err - } - - return nil -} - -// SetupWithGatewayControllerManager registers the controller with the given manager. -func SetupGatewayControllerWithManager(ctx context.Context, mgr ctrl.Manager, config GatewayControllerConfig) (*cache.Cache, error) { - cacheConfig := cache.Config{ - ConsulClientConfig: config.ConsulClientConfig, - ConsulServerConnMgr: config.ConsulServerConnMgr, - NamespacesEnabled: config.NamespacesEnabled, - Datacenter: config.Datacenter, - CrossNamespaceACLPolicy: config.CrossNamespaceACLPolicy, - Logger: mgr.GetLogger(), - } - c := cache.New(cacheConfig) - gwc := cache.NewGatewayCache(ctx, cacheConfig) - - predicate, _ := predicate.LabelSelectorPredicate( - *metav1.SetAsLabelSelector(map[string]string{ - common.ManagedLabel: "true", - }), - ) - - r := &GatewayController{ - Client: mgr.GetClient(), - Log: mgr.GetLogger(), - HelmConfig: config.HelmConfig.Normalize(), - Translator: common.ResourceTranslator{ - EnableConsulNamespaces: config.HelmConfig.EnableNamespaces, - ConsulDestNamespace: config.HelmConfig.ConsulDestinationNamespace, - EnableK8sMirroring: config.HelmConfig.EnableNamespaceMirroring, - MirroringPrefix: config.HelmConfig.NamespaceMirroringPrefix, - ConsulPartition: config.HelmConfig.ConsulPartition, - Datacenter: config.Datacenter, - }, - denyK8sNamespacesSet: config.DenyK8sNamespacesSet, - allowK8sNamespacesSet: config.AllowK8sNamespacesSet, - cache: c, - gatewayCache: gwc, - } - - return c, ctrl.NewControllerManagedBy(mgr). - For(&gwv1beta1.Gateway{}). - Owns(&appsv1.Deployment{}). - Owns(&corev1.Service{}). - Owns(&corev1.Pod{}). - Watches( - source.NewKindWithCache(&gwv1beta1.ReferenceGrant{}, mgr.GetCache()), - handler.EnqueueRequestsFromMapFunc(r.transformReferenceGrant(ctx)), - ). - Watches( - source.NewKindWithCache(&gwv1beta1.GatewayClass{}, mgr.GetCache()), - handler.EnqueueRequestsFromMapFunc(r.transformGatewayClass(ctx)), - ). - Watches( - source.NewKindWithCache(&gwv1beta1.HTTPRoute{}, mgr.GetCache()), - handler.EnqueueRequestsFromMapFunc(r.transformHTTPRoute(ctx)), - ). - Watches( - source.NewKindWithCache(&gwv1alpha2.TCPRoute{}, mgr.GetCache()), - handler.EnqueueRequestsFromMapFunc(r.transformTCPRoute(ctx)), - ). - Watches( - source.NewKindWithCache(&corev1.Secret{}, mgr.GetCache()), - handler.EnqueueRequestsFromMapFunc(r.transformSecret(ctx)), - ). - Watches( - source.NewKindWithCache(&v1alpha1.MeshService{}, mgr.GetCache()), - handler.EnqueueRequestsFromMapFunc(r.transformMeshService(ctx)), - ). - Watches( - source.NewKindWithCache(&corev1.Endpoints{}, mgr.GetCache()), - handler.EnqueueRequestsFromMapFunc(r.transformEndpoints(ctx)), - ). - Watches( - &source.Kind{Type: &corev1.Pod{}}, - handler.EnqueueRequestsFromMapFunc(r.transformPods(ctx)), - builder.WithPredicates(predicate), - ). - Watches( - // Subscribe to changes from Consul for APIGateways - &source.Channel{Source: c.Subscribe(ctx, api.APIGateway, r.transformConsulGateway).Events()}, - &handler.EnqueueRequestForObject{}, - ). - Watches( - // Subscribe to changes from Consul for HTTPRoutes - &source.Channel{Source: c.Subscribe(ctx, api.HTTPRoute, r.transformConsulHTTPRoute(ctx)).Events()}, - &handler.EnqueueRequestForObject{}, - ). - Watches( - // Subscribe to changes from Consul for TCPRoutes - &source.Channel{Source: c.Subscribe(ctx, api.TCPRoute, r.transformConsulTCPRoute(ctx)).Events()}, - &handler.EnqueueRequestForObject{}, - ). - Watches( - // Subscribe to changes from Consul for InlineCertificates - &source.Channel{Source: c.Subscribe(ctx, api.InlineCertificate, r.transformConsulInlineCertificate(ctx)).Events()}, - &handler.EnqueueRequestForObject{}, - ). - Watches( - source.NewKindWithCache((&v1alpha1.RouteRetryFilter{}), mgr.GetCache()), - handler.EnqueueRequestsFromMapFunc(r.transformRouteRetryFilter(ctx)), - ). - Watches( - source.NewKindWithCache((&v1alpha1.RouteTimeoutFilter{}), mgr.GetCache()), - handler.EnqueueRequestsFromMapFunc(r.transformRouteTimeoutFilter(ctx)), - ).Complete(r) -} - -// transformGatewayClass will check the list of GatewayClass objects for a matching -// class, then return a list of reconcile Requests for it. -func (r *GatewayController) transformGatewayClass(ctx context.Context) func(o client.Object) []reconcile.Request { - return func(o client.Object) []reconcile.Request { - gatewayClass := o.(*gwv1beta1.GatewayClass) - gatewayList := &gwv1beta1.GatewayList{} - if err := r.Client.List(ctx, gatewayList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(Gateway_GatewayClassIndex, gatewayClass.Name), - }); err != nil { - return nil - } - return common.ObjectsToReconcileRequests(pointersOf(gatewayList.Items)) - } -} - -// transformHTTPRoute will check the HTTPRoute object for a matching -// class, then return a list of reconcile Requests for Gateways referring to it. -func (r *GatewayController) transformHTTPRoute(ctx context.Context) func(o client.Object) []reconcile.Request { - return func(o client.Object) []reconcile.Request { - route := o.(*gwv1beta1.HTTPRoute) - - refs := refsToRequests(common.ParentRefs(common.BetaGroup, common.KindGateway, route.Namespace, route.Spec.ParentRefs)) - statusRefs := refsToRequests(common.ParentRefs(common.BetaGroup, common.KindGateway, route.Namespace, common.ConvertSliceFunc(route.Status.Parents, func(parentStatus gwv1beta1.RouteParentStatus) gwv1beta1.ParentReference { - return parentStatus.ParentRef - }))) - return append(refs, statusRefs...) - } -} - -// transformTCPRoute will check the TCPRoute object for a matching -// class, then return a list of reconcile Requests for Gateways referring to it. -func (r *GatewayController) transformTCPRoute(ctx context.Context) func(o client.Object) []reconcile.Request { - return func(o client.Object) []reconcile.Request { - route := o.(*gwv1alpha2.TCPRoute) - - refs := refsToRequests(common.ParentRefs(common.BetaGroup, common.KindGateway, route.Namespace, route.Spec.ParentRefs)) - statusRefs := refsToRequests(common.ParentRefs(common.BetaGroup, common.KindGateway, route.Namespace, common.ConvertSliceFunc(route.Status.Parents, func(parentStatus gwv1beta1.RouteParentStatus) gwv1beta1.ParentReference { - return parentStatus.ParentRef - }))) - return append(refs, statusRefs...) - } -} - -// transformSecret will check the Secret object for a matching -// class, then return a list of reconcile Requests for Gateways referring to it. -func (r *GatewayController) transformSecret(ctx context.Context) func(o client.Object) []reconcile.Request { - return func(o client.Object) []reconcile.Request { - secret := o.(*corev1.Secret) - gatewayList := &gwv1beta1.GatewayList{} - if err := r.Client.List(ctx, gatewayList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(Secret_GatewayIndex, client.ObjectKeyFromObject(secret).String()), - }); err != nil { - return nil - } - return common.ObjectsToReconcileRequests(pointersOf(gatewayList.Items)) - } -} - -// transformReferenceGrant will check the ReferenceGrant object for a matching -// class, then return a list of reconcile Requests for Gateways referring to it. -func (r *GatewayController) transformReferenceGrant(ctx context.Context) func(o client.Object) []reconcile.Request { - return func(o client.Object) []reconcile.Request { - // just re-reconcile all gateways for now ideally this will filter down to gateways - // affected, but technically the blast radius is gateways in the namespace + referencing - // the namespace + the routes that bind to them. - gatewayList := &gwv1beta1.GatewayList{} - if err := r.Client.List(ctx, gatewayList); err != nil { - return nil - } - - return common.ObjectsToReconcileRequests(pointersOf(gatewayList.Items)) - } -} - -// transformMeshService will return a list of gateways that are referenced -// by a TCPRoute or HTTPRoute that references the mesh service. -func (r *GatewayController) transformMeshService(ctx context.Context) func(o client.Object) []reconcile.Request { - return func(o client.Object) []reconcile.Request { - service := o.(*v1alpha1.MeshService) - key := client.ObjectKeyFromObject(service).String() - - return r.gatewaysForRoutesReferencing(ctx, TCPRoute_MeshServiceIndex, HTTPRoute_MeshServiceIndex, key) - } -} - -// transformConsulGateway will return a list of gateways that this corresponds to. -func (r *GatewayController) transformConsulGateway(entry api.ConfigEntry) []types.NamespacedName { - return []types.NamespacedName{common.EntryToNamespacedName(entry)} -} - -// transformConsulHTTPRoute will return a list of gateways that need to be reconciled. -func (r *GatewayController) transformConsulHTTPRoute(ctx context.Context) func(entry api.ConfigEntry) []types.NamespacedName { - return func(entry api.ConfigEntry) []types.NamespacedName { - parents := mapset.NewSet() - for _, parent := range entry.(*api.HTTPRouteConfigEntry).Parents { - parents.Add(api.ResourceReference{ - Kind: parent.Kind, - Name: parent.Name, - Namespace: parent.Namespace, - Partition: parent.Partition, - }) - } - - var gateways []types.NamespacedName - for parent := range parents.Iter() { - if gateway := r.cache.Get(parent.(api.ResourceReference)); gateway != nil { - gateways = append(gateways, common.EntryToNamespacedName(gateway)) - } - } - return gateways - } -} - -// transformRouteRetryFilter will return a list of routes that need to be reconciled. -func (r *GatewayController) transformRouteRetryFilter(ctx context.Context) func(object client.Object) []reconcile.Request { - return func(o client.Object) []reconcile.Request { - return r.gatewaysForRoutesReferencing(ctx, "", HTTPRoute_RouteRetryFilterIndex, client.ObjectKeyFromObject(o).String()) - } -} - -// transformTimeoutRetryFilter will return a list of routes that need to be reconciled. -func (r *GatewayController) transformRouteTimeoutFilter(ctx context.Context) func(object client.Object) []reconcile.Request { - return func(o client.Object) []reconcile.Request { - return r.gatewaysForRoutesReferencing(ctx, "", HTTPRoute_RouteTimeoutFilterIndex, client.ObjectKeyFromObject(o).String()) - } -} - -func (r *GatewayController) transformConsulTCPRoute(ctx context.Context) func(entry api.ConfigEntry) []types.NamespacedName { - return func(entry api.ConfigEntry) []types.NamespacedName { - parents := mapset.NewSet() - for _, parent := range entry.(*api.TCPRouteConfigEntry).Parents { - parents.Add(api.ResourceReference{ - Kind: parent.Kind, - Name: parent.Name, - Namespace: parent.Namespace, - Partition: parent.Partition, - }) - } - - var gateways []types.NamespacedName - for parent := range parents.Iter() { - if gateway := r.cache.Get(parent.(api.ResourceReference)); gateway != nil { - gateways = append(gateways, common.EntryToNamespacedName(gateway)) - } - } - return gateways - } -} - -func (r *GatewayController) transformConsulInlineCertificate(ctx context.Context) func(entry api.ConfigEntry) []types.NamespacedName { - return func(entry api.ConfigEntry) []types.NamespacedName { - certificateKey := api.ResourceReference{ - Kind: entry.GetKind(), - Name: entry.GetName(), - Namespace: entry.GetNamespace(), - Partition: entry.GetPartition(), - } - - var gateways []types.NamespacedName - for _, entry := range r.cache.List(api.APIGateway) { - gateway := entry.(*api.APIGatewayConfigEntry) - if gatewayReferencesCertificate(certificateKey, gateway) { - gateways = append(gateways, common.EntryToNamespacedName(gateway)) - } - } - - return gateways - } -} - -func gatewayReferencesCertificate(certificateKey api.ResourceReference, gateway *api.APIGatewayConfigEntry) bool { - for _, listener := range gateway.Listeners { - for _, cert := range listener.TLS.Certificates { - if cert == certificateKey { - return true - } - } - } - return false -} - -func (r *GatewayController) transformPods(ctx context.Context) func(o client.Object) []reconcile.Request { - return func(o client.Object) []reconcile.Request { - pod := o.(*corev1.Pod) - - if gateway, managed := common.GatewayFromPod(pod); managed { - return []reconcile.Request{ - {NamespacedName: gateway}, - } - } - - return nil - } -} - -// transformEndpoints will return a list of gateways that are referenced -// by a TCPRoute or HTTPRoute that references the service. -func (r *GatewayController) transformEndpoints(ctx context.Context) func(o client.Object) []reconcile.Request { - return func(o client.Object) []reconcile.Request { - key := client.ObjectKeyFromObject(o) - endpoints := o.(*corev1.Endpoints) - - if shouldIgnore(key.Namespace, r.denyK8sNamespacesSet, r.allowK8sNamespacesSet) || isLabeledIgnore(endpoints.Labels) { - return nil - } - - return r.gatewaysForRoutesReferencing(ctx, TCPRoute_ServiceIndex, HTTPRoute_ServiceIndex, key.String()) - } -} - -// gatewaysForRoutesReferencing returns a mapping of all gateways that are referenced by routes that -// have a backend associated with the given key and index. -func (r *GatewayController) gatewaysForRoutesReferencing(ctx context.Context, tcpIndex, httpIndex, key string) []reconcile.Request { - requestSet := make(map[types.NamespacedName]struct{}) - - if tcpIndex != "" { - tcpRouteList := &gwv1alpha2.TCPRouteList{} - if err := r.Client.List(ctx, tcpRouteList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(tcpIndex, key), - }); err != nil { - r.Log.Error(err, "unable to list TCPRoutes") - } - for _, route := range tcpRouteList.Items { - for _, ref := range common.ParentRefs(common.BetaGroup, common.KindGateway, route.Namespace, route.Spec.ParentRefs) { - requestSet[ref] = struct{}{} - } - } - } - - httpRouteList := &gwv1beta1.HTTPRouteList{} - if err := r.Client.List(ctx, httpRouteList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(httpIndex, key), - }); err != nil { - r.Log.Error(err, "unable to list HTTPRoutes") - } - for _, route := range httpRouteList.Items { - for _, ref := range common.ParentRefs(common.BetaGroup, common.KindGateway, route.Namespace, route.Spec.ParentRefs) { - requestSet[ref] = struct{}{} - } - } - - requests := []reconcile.Request{} - for request := range requestSet { - requests = append(requests, reconcile.Request{NamespacedName: request}) - } - return requests -} - -// pointersOf returns a list of pointers to the list of objects passed in. -func pointersOf[T any](objects []T) []*T { - pointers := make([]*T, 0, len(objects)) - for _, object := range objects { - pointers = append(pointers, pointerTo(object)) - } - return pointers -} - -// pointerTo returns a pointer to the object type passed in. -func pointerTo[T any](v T) *T { - return &v -} - -// refsToRequests takes a list of NamespacedName objects and returns a list of -// reconcile Requests. -func refsToRequests(objects []types.NamespacedName) []reconcile.Request { - requests := make([]reconcile.Request, 0, len(objects)) - for _, object := range objects { - requests = append(requests, reconcile.Request{ - NamespacedName: object, - }) - } - return requests -} - -// kubernetes helpers - -func (c *GatewayController) getNamespaces(ctx context.Context) (map[string]corev1.Namespace, error) { - var list corev1.NamespaceList - - if err := c.Client.List(ctx, &list); err != nil { - return nil, err - } - namespaces := map[string]corev1.Namespace{} - for _, namespace := range list.Items { - namespaces[namespace.Name] = namespace - } - - return namespaces, nil -} - -func (c *GatewayController) getReferenceGrants(ctx context.Context) ([]gwv1beta1.ReferenceGrant, error) { - var list gwv1beta1.ReferenceGrantList - - if err := c.Client.List(ctx, &list); err != nil { - return nil, err - } - - return list.Items, nil -} - -func (c *GatewayController) getDeployedGatewayService(ctx context.Context, gateway types.NamespacedName) (*corev1.Service, error) { - service := &corev1.Service{} - - // we use the implicit association of a service name/namespace with a corresponding gateway - if err := c.Client.Get(ctx, gateway, service); err != nil { - return nil, client.IgnoreNotFound(err) - } - - return service, nil -} - -func (c *GatewayController) getDeployedGatewayPods(ctx context.Context, gateway gwv1beta1.Gateway) ([]corev1.Pod, error) { - labels := common.LabelsForGateway(&gateway) - - var list corev1.PodList - - if err := c.Client.List(ctx, &list, client.MatchingLabels(labels)); err != nil { - return nil, err - } - - return list.Items, nil -} - -func (c *GatewayController) getRelatedHTTPRoutes(ctx context.Context, gateway types.NamespacedName, resources *common.ResourceMap) ([]gwv1beta1.HTTPRoute, error) { - var list gwv1beta1.HTTPRouteList - - if err := c.Client.List(ctx, &list, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(HTTPRoute_GatewayIndex, gateway.String()), - }); err != nil { - return nil, err - } - - for _, route := range list.Items { - resources.ReferenceCountHTTPRoute(route) - - _, err := c.getExternalFiltersForHTTPRoute(ctx, route, resources) - if err != nil { - c.Log.Error(err, "unable to list HTTPRoute ExternalFilters") - return nil, err - } - } - - return list.Items, nil -} - -func (c *GatewayController) getExternalFiltersForHTTPRoute(ctx context.Context, route gwv1beta1.HTTPRoute, resources *common.ResourceMap) ([]interface{}, error) { - var externalFilters []interface{} - for _, rule := range route.Spec.Rules { - ruleFilters, err := c.filterFiltersForExternalRefs(ctx, route, rule.Filters, resources) - if err != nil { - return nil, err - } - externalFilters = append(externalFilters, ruleFilters...) - - for _, backendRef := range rule.BackendRefs { - backendRefFilter, err := c.filterFiltersForExternalRefs(ctx, route, backendRef.Filters, resources) - if err != nil { - return nil, err - } - - externalFilters = append(externalFilters, backendRefFilter...) - } - } - - return externalFilters, nil -} - -func (c *GatewayController) filterFiltersForExternalRefs(ctx context.Context, route gwv1beta1.HTTPRoute, filters []gwv1beta1.HTTPRouteFilter, resources *common.ResourceMap) ([]interface{}, error) { - var externalFilters []interface{} - - for _, filter := range filters { - var externalFilter client.Object - - //check to see if we need to grab this filter - if filter.ExtensionRef == nil { - continue - } - switch kind := filter.ExtensionRef.Kind; kind { - case v1alpha1.RouteRetryFilterKind: - externalFilter = &v1alpha1.RouteRetryFilter{} - case v1alpha1.RouteTimeoutFilterKind: - externalFilter = &v1alpha1.RouteTimeoutFilter{} - default: - continue - } - - //get object from API - err := c.Client.Get(ctx, client.ObjectKey{ - Name: string(filter.ExtensionRef.Name), - Namespace: route.Namespace, - }, externalFilter) - - if err != nil { - if k8serrors.IsNotFound(err) { - c.Log.Info(fmt.Sprintf("externalref %s:%s not found: %v", filter.ExtensionRef.Kind, filter.ExtensionRef.Name, err)) - //ignore, the validation call should mark this route as error - continue - } else { - return nil, err - } - } - - //add external ref (or error) to resource map for this route - resources.AddExternalFilter(externalFilter) - externalFilters = append(externalFilters, externalFilter) - } - return externalFilters, nil -} - -func (c *GatewayController) getRelatedTCPRoutes(ctx context.Context, gateway types.NamespacedName, resources *common.ResourceMap) ([]gwv1alpha2.TCPRoute, error) { - var list gwv1alpha2.TCPRouteList - - if err := c.Client.List(ctx, &list, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(TCPRoute_GatewayIndex, gateway.String()), - }); err != nil { - return nil, err - } - - for _, route := range list.Items { - resources.ReferenceCountTCPRoute(route) - } - - return list.Items, nil -} - -func (c *GatewayController) getConfigForGatewayClass(ctx context.Context, gatewayClassConfig *gwv1beta1.GatewayClass) (*v1alpha1.GatewayClassConfig, error) { - if gatewayClassConfig == nil { - // if we don't have a gateway class we can't fetch the corresponding config - return nil, nil - } - - config := &v1alpha1.GatewayClassConfig{} - if ref := gatewayClassConfig.Spec.ParametersRef; ref != nil { - if string(ref.Group) != v1alpha1.GroupVersion.Group || - ref.Kind != v1alpha1.GatewayClassConfigKind || - gatewayClassConfig.Spec.ControllerName != common.GatewayClassControllerName { - // we don't have supported params, so return nil - return nil, nil - } - - if err := c.Client.Get(ctx, types.NamespacedName{Name: ref.Name}, config); err != nil { - return nil, client.IgnoreNotFound(err) - } - } - return config, nil -} - -func (c *GatewayController) getGatewayClassForGateway(ctx context.Context, gateway gwv1beta1.Gateway) (*gwv1beta1.GatewayClass, error) { - var gatewayClass gwv1beta1.GatewayClass - if err := c.Client.Get(ctx, types.NamespacedName{Name: string(gateway.Spec.GatewayClassName)}, &gatewayClass); err != nil { - return nil, client.IgnoreNotFound(err) - } - return &gatewayClass, nil -} - -// resource map construction routines - -func (c *GatewayController) fetchControlledGateways(ctx context.Context, resources *common.ResourceMap) error { - set := mapset.NewSet() - - list := gwv1beta1.GatewayClassList{} - if err := c.Client.List(ctx, &list, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(GatewayClass_ControllerNameIndex, common.GatewayClassControllerName), - }); err != nil { - return err - } - for _, gatewayClass := range list.Items { - set.Add(gatewayClass.Name) - } - - gateways := &gwv1beta1.GatewayList{} - if err := c.Client.List(ctx, gateways); err != nil { - return err - } - - for _, gateway := range gateways.Items { - if set.Contains(string(gateway.Spec.GatewayClassName)) { - resources.ReferenceCountGateway(gateway) - } - } - return nil -} - -func (c *GatewayController) fetchCertificatesForGateway(ctx context.Context, resources *common.ResourceMap, gateway gwv1beta1.Gateway) error { - certificates := mapset.NewSet() - - for _, listener := range gateway.Spec.Listeners { - if listener.TLS != nil { - for _, cert := range listener.TLS.CertificateRefs { - if common.NilOrEqual(cert.Group, "") && common.NilOrEqual(cert.Kind, common.KindSecret) { - certificates.Add(common.IndexedNamespacedNameWithDefault(cert.Name, cert.Namespace, gateway.Namespace)) - } - } - } - } - - for key := range certificates.Iter() { - if err := c.fetchSecret(ctx, resources, key.(types.NamespacedName)); err != nil { - return err - } - } - - return nil -} - -func (c *GatewayController) fetchSecret(ctx context.Context, resources *common.ResourceMap, key types.NamespacedName) error { - var secret corev1.Secret - if err := c.Client.Get(ctx, key, &secret); err != nil { - return client.IgnoreNotFound(err) - } - - resources.ReferenceCountCertificate(secret) - - return nil -} - -func (c *GatewayController) fetchServicesForRoutes(ctx context.Context, resources *common.ResourceMap, tcpRoutes []gwv1alpha2.TCPRoute, httpRoutes []gwv1beta1.HTTPRoute) error { - serviceBackends := mapset.NewSet() - meshServiceBackends := mapset.NewSet() - - for _, route := range httpRoutes { - for _, rule := range route.Spec.Rules { - for _, backend := range rule.BackendRefs { - if common.DerefEqual(backend.Group, v1alpha1.ConsulHashicorpGroup) && - common.DerefEqual(backend.Kind, v1alpha1.MeshServiceKind) { - meshServiceBackends.Add(common.IndexedNamespacedNameWithDefault(backend.Name, backend.Namespace, route.Namespace)) - } else if common.NilOrEqual(backend.Group, "") && common.NilOrEqual(backend.Kind, "Service") { - serviceBackends.Add(common.IndexedNamespacedNameWithDefault(backend.Name, backend.Namespace, route.Namespace)) - } - } - } - } - - for _, route := range tcpRoutes { - for _, rule := range route.Spec.Rules { - for _, backend := range rule.BackendRefs { - if common.DerefEqual(backend.Group, v1alpha1.ConsulHashicorpGroup) && - common.DerefEqual(backend.Kind, v1alpha1.MeshServiceKind) { - meshServiceBackends.Add(common.IndexedNamespacedNameWithDefault(backend.Name, backend.Namespace, route.Namespace)) - } else if common.NilOrEqual(backend.Group, "") && common.NilOrEqual(backend.Kind, "Service") { - serviceBackends.Add(common.IndexedNamespacedNameWithDefault(backend.Name, backend.Namespace, route.Namespace)) - } - } - } - } - - for key := range meshServiceBackends.Iter() { - if err := c.fetchMeshService(ctx, resources, key.(types.NamespacedName)); err != nil { - return err - } - } - - for key := range serviceBackends.Iter() { - if err := c.fetchServicesForEndpoints(ctx, resources, key.(types.NamespacedName)); err != nil { - return err - } - } - return nil -} - -func (c *GatewayController) fetchMeshService(ctx context.Context, resources *common.ResourceMap, key types.NamespacedName) error { - var service v1alpha1.MeshService - if err := c.Client.Get(ctx, key, &service); err != nil { - return client.IgnoreNotFound(err) - } - - resources.AddMeshService(service) - - return nil -} - -func (c *GatewayController) fetchServicesForEndpoints(ctx context.Context, resources *common.ResourceMap, key types.NamespacedName) error { - - if shouldIgnore(key.Namespace, c.denyK8sNamespacesSet, c.allowK8sNamespacesSet) { - return nil - } - - var endpoints corev1.Endpoints - if err := c.Client.Get(ctx, key, &endpoints); err != nil { - return client.IgnoreNotFound(err) - } - - if isLabeledIgnore(endpoints.Labels) { - return nil - } - - for _, subset := range endpoints.Subsets { - for _, address := range subset.Addresses { - if address.TargetRef != nil && address.TargetRef.Kind == "Pod" { - objectKey := types.NamespacedName{Name: address.TargetRef.Name, Namespace: address.TargetRef.Namespace} - - var pod corev1.Pod - if err := c.Client.Get(ctx, objectKey, &pod); err != nil { - if k8serrors.IsNotFound(err) { - continue - } - return err - } - - resources.AddService(key, serviceName(pod, endpoints)) - - } - } - } - - return nil -} - -// cache routines - -func (c *GatewayController) getConsulServices(ref api.ResourceReference) []api.CatalogService { - return c.gatewayCache.ServicesFor(ref) -} - -func (c *GatewayController) getConsulGateway(ref api.ResourceReference) *api.APIGatewayConfigEntry { - if entry := c.cache.Get(ref); entry != nil { - return entry.(*api.APIGatewayConfigEntry) - } - return nil -} - -func (c *GatewayController) fetchConsulHTTPRoutes(ref api.ResourceReference, resources *common.ResourceMap) { - for _, route := range configEntriesTo[*api.HTTPRouteConfigEntry](c.cache.List(api.HTTPRoute)) { - if routeReferencesGateway(route.Namespace, ref, route.Parents) { - resources.ReferenceCountConsulHTTPRoute(*route) - } - } -} - -func (c *GatewayController) fetchConsulTCPRoutes(ref api.ResourceReference, resources *common.ResourceMap) { - for _, route := range configEntriesTo[*api.TCPRouteConfigEntry](c.cache.List(api.TCPRoute)) { - if routeReferencesGateway(route.Namespace, ref, route.Parents) { - resources.ReferenceCountConsulTCPRoute(*route) - } - } -} - -func (c *GatewayController) fetchConsulInlineCertificates(resources *common.ResourceMap) { - for _, cert := range configEntriesTo[*api.InlineCertificateConfigEntry](c.cache.List(api.InlineCertificate)) { - resources.ReferenceCountConsulCertificate(*cert) - } -} - -func routeReferencesGateway(namespace string, ref api.ResourceReference, refs []api.ResourceReference) bool { - // we don't need to check partition here since they're all in the same partition - if namespace == "" { - namespace = "default" - } - - for _, parent := range refs { - if common.EmptyOrEqual(parent.Kind, api.APIGateway) { - if common.DefaultOrEqual(parent.Namespace, namespace, ref.Namespace) && - parent.Name == ref.Name { - return true - } - } - } - - return false -} - -func serviceName(pod corev1.Pod, serviceEndpoints corev1.Endpoints) string { - svcName := serviceEndpoints.Name - // If the annotation has a comma, it is a multi port Pod. In that case we always use the name of the endpoint. - if serviceNameFromAnnotation, ok := pod.Annotations[constants.AnnotationService]; ok && serviceNameFromAnnotation != "" && !strings.Contains(serviceNameFromAnnotation, ",") { - svcName = serviceNameFromAnnotation - } - return svcName -} - -func isLabeledIgnore(labels map[string]string) bool { - value, labelExists := labels[constants.LabelServiceIgnore] - shouldIgnore, err := strconv.ParseBool(value) - - return shouldIgnore && labelExists && err == nil -} - -// shouldIgnore ignores namespaces where we don't connect-inject. -func shouldIgnore(namespace string, denySet, allowSet mapset.Set) bool { - // Ignores system namespaces. - if namespace == metav1.NamespaceSystem || namespace == metav1.NamespacePublic || namespace == "local-path-storage" { - return true - } - - // Ignores deny list. - if denySet.Contains(namespace) { - return true - } - - // Ignores if not in allow list or allow list is not *. - if !allowSet.Contains("*") && !allowSet.Contains(namespace) { - return true - } - - return false -} diff --git a/control-plane/api-gateway/controllers/gateway_controller_integration_test.go b/control-plane/api-gateway/controllers/gateway_controller_integration_test.go deleted file mode 100644 index 2605991238..0000000000 --- a/control-plane/api-gateway/controllers/gateway_controller_integration_test.go +++ /dev/null @@ -1,1325 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers - -import ( - "context" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "fmt" - "math/big" - "sync" - "testing" - "time" - - mapset "github.com/deckarep/golang-set" - logrtest "github.com/go-logr/logr/testr" - "github.com/stretchr/testify/require" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/cache" - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul-k8s/control-plane/helper/test" - "github.com/hashicorp/consul/api" -) - -func TestControllerDoesNotInfinitelyReconcile(t *testing.T) { - //TODO @apigatewayteam test is consistently failing on main after merge, fix in a follow up PR - t.Skip() - s := runtime.NewScheme() - require.NoError(t, clientgoscheme.AddToScheme(s)) - require.NoError(t, gwv1alpha2.Install(s)) - require.NoError(t, gwv1beta1.Install(s)) - require.NoError(t, v1alpha1.AddToScheme(s)) - - testCases := map[string]struct { - namespace string - certFn func(*testing.T, context.Context, client.WithWatch, string) *corev1.Secret - gwFn func(*testing.T, context.Context, client.WithWatch, string) *gwv1beta1.Gateway - httpRouteFn func(*testing.T, context.Context, client.WithWatch, *gwv1beta1.Gateway) *gwv1beta1.HTTPRoute - tcpRouteFn func(*testing.T, context.Context, client.WithWatch, *gwv1beta1.Gateway) *v1alpha2.TCPRoute - }{ - "all fields set": { - namespace: "consul", - certFn: createCert, - gwFn: createAllFieldsSetAPIGW, - httpRouteFn: createAllFieldsSetHTTPRoute, - tcpRouteFn: createAllFieldsSetTCPRoute, - }, - "minimal fields set": { - namespace: "", - certFn: createCert, - gwFn: minimalFieldsSetAPIGW, - httpRouteFn: minimalFieldsSetHTTPRoute, - tcpRouteFn: minimalFieldsSetTCPRoute, - }, - "funky casing to test normalization doesnt cause infinite reconciliation": { - namespace: "", - certFn: createCert, - gwFn: createFunkyCasingFieldsAPIGW, - httpRouteFn: createFunkyCasingFieldsHTTPRoute, - tcpRouteFn: createFunkyCasingFieldsTCPRoute, - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - k8sClient := registerFieldIndexersForTest(fake.NewClientBuilder().WithScheme(s)).Build() - consulTestServerClient := test.TestServerWithMockConnMgrWatcher(t, nil) - ctx, cancel := context.WithCancel(context.Background()) - - t.Cleanup(func() { - cancel() - }) - logger := logrtest.New(t) - - cacheCfg := cache.Config{ - ConsulClientConfig: consulTestServerClient.Cfg, - ConsulServerConnMgr: consulTestServerClient.Watcher, - Logger: logger, - } - resourceCache := cache.New(cacheCfg) - - gwCache := cache.NewGatewayCache(ctx, cacheCfg) - - gwCtrl := GatewayController{ - HelmConfig: common.HelmConfig{}, - Log: logger, - Translator: common.ResourceTranslator{}, - cache: resourceCache, - gatewayCache: gwCache, - Client: k8sClient, - allowK8sNamespacesSet: mapset.NewSet(), - denyK8sNamespacesSet: mapset.NewSet(), - } - - go func() { - resourceCache.Run(ctx) - }() - - resourceCache.WaitSynced(ctx) - - gwSub := resourceCache.Subscribe(ctx, api.APIGateway, gwCtrl.transformConsulGateway) - httpRouteSub := resourceCache.Subscribe(ctx, api.HTTPRoute, gwCtrl.transformConsulHTTPRoute(ctx)) - tcpRouteSub := resourceCache.Subscribe(ctx, api.TCPRoute, gwCtrl.transformConsulTCPRoute(ctx)) - inlineCertSub := resourceCache.Subscribe(ctx, api.InlineCertificate, gwCtrl.transformConsulInlineCertificate(ctx)) - - cert := tc.certFn(t, ctx, k8sClient, tc.namespace) - k8sGWObj := tc.gwFn(t, ctx, k8sClient, tc.namespace) - - // reconcile so we add the finalizer - _, err := gwCtrl.Reconcile(ctx, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Namespace: k8sGWObj.Namespace, - Name: k8sGWObj.Name, - }, - }) - require.NoError(t, err) - - // reconcile again so that we get the creation with the finalizer - _, err = gwCtrl.Reconcile(ctx, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Namespace: k8sGWObj.Namespace, - Name: k8sGWObj.Name, - }, - }) - require.NoError(t, err) - - httpRouteObj := tc.httpRouteFn(t, ctx, k8sClient, k8sGWObj) - tcpRouteObj := tc.tcpRouteFn(t, ctx, k8sClient, k8sGWObj) - - // reconcile again so that we get the route bound to the gateway - _, err = gwCtrl.Reconcile(ctx, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Namespace: k8sGWObj.Namespace, - Name: k8sGWObj.Name, - }, - }) - require.NoError(t, err) - - // reconcile again so that we get the route bound to the gateway - _, err = gwCtrl.Reconcile(ctx, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Namespace: k8sGWObj.Namespace, - Name: k8sGWObj.Name, - }, - }) - require.NoError(t, err) - - wg := &sync.WaitGroup{} - // we never get the event from the cert because when it's created there are no gateways that reference it - wg.Add(3) - go func(w *sync.WaitGroup) { - gwDone := false - httpRouteDone := false - tcpRouteDone := false - for { - // get the creation events from the upsert and then continually read from channel so we dont block other subs - select { - case <-ctx.Done(): - return - case <-gwSub.Events(): - if !gwDone { - gwDone = true - wg.Done() - } - case <-httpRouteSub.Events(): - if !httpRouteDone { - httpRouteDone = true - wg.Done() - } - case <-tcpRouteSub.Events(): - if !tcpRouteDone { - tcpRouteDone = true - wg.Done() - } - case <-inlineCertSub.Events(): - } - } - }(wg) - - wg.Wait() - - gwNamespaceName := types.NamespacedName{ - Name: k8sGWObj.Name, - Namespace: k8sGWObj.Namespace, - } - - httpRouteNamespaceName := types.NamespacedName{ - Name: httpRouteObj.Name, - Namespace: httpRouteObj.Namespace, - } - - tcpRouteNamespaceName := types.NamespacedName{ - Name: tcpRouteObj.Name, - Namespace: tcpRouteObj.Namespace, - } - - certNamespaceName := types.NamespacedName{ - Name: cert.Name, - Namespace: cert.Namespace, - } - - gwRef := gwCtrl.Translator.ConfigEntryReference(api.APIGateway, gwNamespaceName) - httpRouteRef := gwCtrl.Translator.ConfigEntryReference(api.HTTPRoute, httpRouteNamespaceName) - tcpRouteRef := gwCtrl.Translator.ConfigEntryReference(api.TCPRoute, tcpRouteNamespaceName) - certRef := gwCtrl.Translator.ConfigEntryReference(api.InlineCertificate, certNamespaceName) - - curGWModifyIndex := resourceCache.Get(gwRef).GetModifyIndex() - curHTTPRouteModifyIndex := resourceCache.Get(httpRouteRef).GetModifyIndex() - curTCPRouteModifyIndex := resourceCache.Get(tcpRouteRef).GetModifyIndex() - curCertModifyIndex := resourceCache.Get(certRef).GetModifyIndex() - - err = k8sClient.Get(ctx, gwNamespaceName, k8sGWObj) - require.NoError(t, err) - curGWResourceVersion := k8sGWObj.ResourceVersion - - err = k8sClient.Get(ctx, httpRouteNamespaceName, httpRouteObj) - require.NoError(t, err) - curHTTPRouteResourceVersion := httpRouteObj.ResourceVersion - - err = k8sClient.Get(ctx, tcpRouteNamespaceName, tcpRouteObj) - require.NoError(t, err) - curTCPRouteResourceVersion := tcpRouteObj.ResourceVersion - - err = k8sClient.Get(ctx, certNamespaceName, cert) - require.NoError(t, err) - curCertResourceVersion := cert.ResourceVersion - - go func() { - // reconcile multiple times with no changes to be sure - for i := 0; i < 5; i++ { - _, err = gwCtrl.Reconcile(ctx, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Namespace: k8sGWObj.Namespace, - }, - }) - require.NoError(t, err) - } - }() - - require.Never(t, func() bool { - err = k8sClient.Get(ctx, gwNamespaceName, k8sGWObj) - require.NoError(t, err) - newGWResourceVersion := k8sGWObj.ResourceVersion - - err = k8sClient.Get(ctx, httpRouteNamespaceName, httpRouteObj) - require.NoError(t, err) - newHTTPRouteResourceVersion := httpRouteObj.ResourceVersion - - err = k8sClient.Get(ctx, tcpRouteNamespaceName, tcpRouteObj) - require.NoError(t, err) - newTCPRouteResourceVersion := tcpRouteObj.ResourceVersion - - err = k8sClient.Get(ctx, certNamespaceName, cert) - require.NoError(t, err) - newCertResourceVersion := cert.ResourceVersion - - return curGWModifyIndex == resourceCache.Get(gwRef).GetModifyIndex() && - curGWResourceVersion == newGWResourceVersion && - curHTTPRouteModifyIndex == resourceCache.Get(httpRouteRef).GetModifyIndex() && - curHTTPRouteResourceVersion == newHTTPRouteResourceVersion && - curTCPRouteModifyIndex == resourceCache.Get(tcpRouteRef).GetModifyIndex() && - curTCPRouteResourceVersion == newTCPRouteResourceVersion && - curCertModifyIndex == resourceCache.Get(certRef).GetModifyIndex() && - curCertResourceVersion == newCertResourceVersion - }, time.Duration(2*time.Second), time.Duration(500*time.Millisecond), fmt.Sprintf("curGWModifyIndex: %d, newIndx: %d", curGWModifyIndex, resourceCache.Get(gwRef).GetModifyIndex()), - ) - }) - } -} - -func createAllFieldsSetAPIGW(t *testing.T, ctx context.Context, k8sClient client.WithWatch, namespace string) *gwv1beta1.Gateway { - // listener one configuration - listenerOneName := "listener-one" - listenerOneHostname := "*.consul.io" - listenerOnePort := 3366 - listenerOneProtocol := "https" - - // listener two configuration - listenerTwoName := "listener-two" - listenerTwoHostname := "*.consul.io" - listenerTwoPort := 5432 - listenerTwoProtocol := "http" - - // listener three configuration - listenerThreeName := "listener-three" - listenerThreePort := 8081 - listenerThreeProtocol := "tcp" - - // listener four configuration - listenerFourName := "listener-four" - listenerFourHostname := "*.consul.io" - listenerFourPort := 5433 - listenerFourProtocol := "http" - - // Write gw to k8s - gwClassCfg := &v1alpha1.GatewayClassConfig{ - TypeMeta: metav1.TypeMeta{ - Kind: "GatewayClassConfig", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway-class-config", - }, - Spec: v1alpha1.GatewayClassConfigSpec{}, - } - gwClass := &gwv1beta1.GatewayClass{ - TypeMeta: metav1.TypeMeta{ - Kind: "GatewayClass", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "gatewayclass", - }, - Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: "consul.hashicorp.com/gateway-controller", - ParametersRef: &gwv1beta1.ParametersReference{ - Group: "consul.hashicorp.com", - Kind: "GatewayClassConfig", - Name: "gateway-class-config", - }, - Description: new(string), - }, - } - gw := &gwv1beta1.Gateway{ - TypeMeta: metav1.TypeMeta{ - Kind: "Gateway", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "gw", - Namespace: namespace, - Annotations: make(map[string]string), - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gwv1beta1.ObjectName(gwClass.Name), - Listeners: []gwv1beta1.Listener{ - { - Name: gwv1beta1.SectionName(listenerOneName), - Hostname: common.PointerTo(gwv1beta1.Hostname(listenerOneHostname)), - Port: gwv1beta1.PortNumber(listenerOnePort), - Protocol: gwv1beta1.ProtocolType(listenerOneProtocol), - TLS: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{ - { - Kind: common.PointerTo(gwv1beta1.Kind("Secret")), - Name: gwv1beta1.ObjectName("one-cert"), - Namespace: common.PointerTo(gwv1beta1.Namespace(namespace)), - }, - }, - }, - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.FromNamespaces("All")), - }, - }, - }, - { - Name: gwv1beta1.SectionName(listenerTwoName), - Hostname: common.PointerTo(gwv1beta1.Hostname(listenerTwoHostname)), - Port: gwv1beta1.PortNumber(listenerTwoPort), - Protocol: gwv1beta1.ProtocolType(listenerTwoProtocol), - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.FromNamespaces("Same")), - }, - }, - }, - { - Name: gwv1beta1.SectionName(listenerThreeName), - Port: gwv1beta1.PortNumber(listenerThreePort), - Protocol: gwv1beta1.ProtocolType(listenerThreeProtocol), - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.FromNamespaces("All")), - }, - }, - }, - { - Name: gwv1beta1.SectionName(listenerFourName), - Hostname: common.PointerTo(gwv1beta1.Hostname(listenerFourHostname)), - Port: gwv1beta1.PortNumber(listenerFourPort), - Protocol: gwv1beta1.ProtocolType(listenerFourProtocol), - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.FromNamespaces("Selector")), - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - common.NamespaceNameLabel: "consul", - }, - MatchExpressions: []metav1.LabelSelectorRequirement{}, - }, - }, - }, - }, - }, - }, - } - - err := k8sClient.Create(ctx, gwClassCfg) - require.NoError(t, err) - - err = k8sClient.Create(ctx, gwClass) - require.NoError(t, err) - - err = k8sClient.Create(ctx, gw) - require.NoError(t, err) - - return gw -} - -func createAllFieldsSetHTTPRoute(t *testing.T, ctx context.Context, k8sClient client.WithWatch, gw *gwv1beta1.Gateway) *gwv1beta1.HTTPRoute { - svcDefault := &v1alpha1.ServiceDefaults{ - TypeMeta: metav1.TypeMeta{ - Kind: "ServiceDefaults", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "Service", - }, - Spec: v1alpha1.ServiceDefaultsSpec{ - Protocol: "http", - }, - } - - svc := &corev1.Service{ - TypeMeta: metav1.TypeMeta{ - Kind: "Service", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "Service", - Labels: map[string]string{"app": "Service"}, - }, - Spec: corev1.ServiceSpec{ - Ports: []corev1.ServicePort{ - { - Name: "high", - Protocol: "TCP", - Port: 8080, - }, - }, - Selector: map[string]string{"app": "Service"}, - }, - } - - serviceAccount := &corev1.ServiceAccount{ - TypeMeta: metav1.TypeMeta{ - Kind: "ServiceAccount", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "Service", - }, - } - - deployment := &appsv1.Deployment{ - TypeMeta: metav1.TypeMeta{ - Kind: "Deployment", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "Service", - Labels: map[string]string{"app": "Service"}, - }, - Spec: appsv1.DeploymentSpec{ - Replicas: common.PointerTo(int32(1)), - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"app": "Service"}, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{}, - Spec: corev1.PodSpec{}, - }, - }, - } - - err := k8sClient.Create(ctx, svcDefault) - require.NoError(t, err) - - err = k8sClient.Create(ctx, svc) - require.NoError(t, err) - - err = k8sClient.Create(ctx, serviceAccount) - require.NoError(t, err) - - err = k8sClient.Create(ctx, deployment) - require.NoError(t, err) - - route := &gwv1beta1.HTTPRoute{ - TypeMeta: metav1.TypeMeta{ - Kind: "HTTPRoute", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "http-route", - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - { - Kind: (*gwv1beta1.Kind)(&gw.Kind), - Namespace: (*gwv1beta1.Namespace)(&gw.Namespace), - Name: gwv1beta1.ObjectName(gw.Name), - SectionName: &gw.Spec.Listeners[0].Name, - Port: &gw.Spec.Listeners[0].Port, - }, - }, - }, - Hostnames: []gwv1beta1.Hostname{"route.consul.io"}, - Rules: []gwv1beta1.HTTPRouteRule{ - { - Matches: []gwv1beta1.HTTPRouteMatch{ - { - Path: &gwv1beta1.HTTPPathMatch{ - Type: common.PointerTo(gwv1beta1.PathMatchType("PathPrefix")), - Value: common.PointerTo("/v1"), - }, - Headers: []gwv1beta1.HTTPHeaderMatch{ - { - Type: common.PointerTo(gwv1beta1.HeaderMatchExact), - Name: "version", - Value: "version", - }, - }, - QueryParams: []gwv1beta1.HTTPQueryParamMatch{ - { - Type: common.PointerTo(gwv1beta1.QueryParamMatchExact), - Name: "search", - Value: "q", - }, - }, - Method: common.PointerTo(gwv1beta1.HTTPMethod("GET")), - }, - }, - Filters: []gwv1beta1.HTTPRouteFilter{ - { - Type: gwv1beta1.HTTPRouteFilterRequestHeaderModifier, - RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ - Set: []gwv1beta1.HTTPHeader{ - { - Name: "foo", - Value: "bax", - }, - }, - Add: []gwv1beta1.HTTPHeader{ - { - Name: "arc", - Value: "reactor", - }, - }, - Remove: []string{"remove"}, - }, - }, - { - Type: gwv1beta1.HTTPRouteFilterURLRewrite, - URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ - Hostname: common.PointerTo(gwv1beta1.PreciseHostname("host.com")), - Path: &gwv1beta1.HTTPPathModifier{ - Type: gwv1beta1.FullPathHTTPPathModifier, - ReplaceFullPath: common.PointerTo("/foobar"), - }, - }, - }, - - { - Type: gwv1beta1.HTTPRouteFilterURLRewrite, - URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ - Hostname: common.PointerTo(gwv1beta1.PreciseHostname("host.com")), - Path: &gwv1beta1.HTTPPathModifier{ - Type: gwv1beta1.PrefixMatchHTTPPathModifier, - ReplacePrefixMatch: common.PointerTo("/foo"), - }, - }, - }, - }, - BackendRefs: []gwv1beta1.HTTPBackendRef{ - { - BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "Service", - Port: common.PointerTo(gwv1beta1.PortNumber(8080)), - }, - Weight: common.PointerTo(int32(50)), - }, - }, - }, - }, - }, - }, - } - - err = k8sClient.Create(ctx, route) - require.NoError(t, err) - - return route -} - -func createAllFieldsSetTCPRoute(t *testing.T, ctx context.Context, k8sClient client.WithWatch, gw *gwv1beta1.Gateway) *v1alpha2.TCPRoute { - route := &v1alpha2.TCPRoute{ - TypeMeta: metav1.TypeMeta{ - Kind: "TCPRoute", - APIVersion: "gateway.networking.k8s.io/v1alpha2", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "tcp-route", - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - { - Kind: (*gwv1beta1.Kind)(&gw.Kind), - Namespace: (*gwv1beta1.Namespace)(&gw.Namespace), - Name: gwv1beta1.ObjectName(gw.Name), - SectionName: &gw.Spec.Listeners[2].Name, - Port: &gw.Spec.Listeners[2].Port, - }, - }, - }, - Rules: []gwv1alpha2.TCPRouteRule{ - { - BackendRefs: []gwv1beta1.BackendRef{ - { - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "Service", - Port: common.PointerTo(gwv1beta1.PortNumber(25000)), - }, - Weight: common.PointerTo(int32(50)), - }, - }, - }, - }, - }, - } - - err := k8sClient.Create(ctx, route) - require.NoError(t, err) - - return route -} - -func createCert(t *testing.T, ctx context.Context, k8sClient client.WithWatch, certNS string) *corev1.Secret { - // listener one tls config - certName := "one-cert" - - privateKey, err := rsa.GenerateKey(rand.Reader, 2048) - require.NoError(t, err) - - usage := x509.KeyUsageCertSign - expiration := time.Now().AddDate(10, 0, 0) - - cert := &x509.Certificate{ - SerialNumber: big.NewInt(1), - Subject: pkix.Name{ - CommonName: "consul.test", - }, - IsCA: true, - NotBefore: time.Now().Add(-10 * time.Minute), - NotAfter: expiration, - SubjectKeyId: []byte{1, 2, 3, 4, 6}, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, - KeyUsage: usage, - BasicConstraintsValid: true, - } - caCert := cert - caPrivateKey := privateKey - - data, err := x509.CreateCertificate(rand.Reader, cert, caCert, &privateKey.PublicKey, caPrivateKey) - require.NoError(t, err) - - certBytes := pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: data, - }) - - privateKeyBytes := pem.EncodeToMemory(&pem.Block{ - Type: "RSA PRIVATE KEY", - Bytes: x509.MarshalPKCS1PrivateKey(privateKey), - }) - - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: certNS, - Name: certName, - }, - Data: map[string][]byte{ - corev1.TLSCertKey: certBytes, - corev1.TLSPrivateKeyKey: privateKeyBytes, - }, - } - - err = k8sClient.Create(ctx, secret) - require.NoError(t, err) - - return secret -} - -func minimalFieldsSetAPIGW(t *testing.T, ctx context.Context, k8sClient client.WithWatch, namespace string) *gwv1beta1.Gateway { - // listener one configuration - listenerOneName := "listener-one" - listenerOneHostname := "*.consul.io" - listenerOnePort := 3366 - listenerOneProtocol := "https" - - // listener three configuration - listenerThreeName := "listener-three" - listenerThreePort := 8081 - listenerThreeProtocol := "tcp" - - // Write gw to k8s - gwClassCfg := &v1alpha1.GatewayClassConfig{ - TypeMeta: metav1.TypeMeta{ - Kind: "GatewayClassConfig", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway-class-config", - }, - Spec: v1alpha1.GatewayClassConfigSpec{}, - } - gwClass := &gwv1beta1.GatewayClass{ - TypeMeta: metav1.TypeMeta{ - Kind: "GatewayClass", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "gatewayclass", - }, - Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: "consul.hashicorp.com/gateway-controller", - ParametersRef: &gwv1beta1.ParametersReference{ - Group: "consul.hashicorp.com", - Kind: "GatewayClassConfig", - Name: "gateway-class-config", - }, - Description: new(string), - }, - } - gw := &gwv1beta1.Gateway{ - TypeMeta: metav1.TypeMeta{ - Kind: "Gateway", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "gw", - Annotations: make(map[string]string), - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gwv1beta1.ObjectName(gwClass.Name), - Listeners: []gwv1beta1.Listener{ - { - Name: gwv1beta1.SectionName(listenerOneName), - Hostname: common.PointerTo(gwv1beta1.Hostname(listenerOneHostname)), - Port: gwv1beta1.PortNumber(listenerOnePort), - Protocol: gwv1beta1.ProtocolType(listenerOneProtocol), - TLS: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{ - { - Kind: common.PointerTo(gwv1beta1.Kind("Secret")), - Name: gwv1beta1.ObjectName("one-cert"), - Namespace: common.PointerTo(gwv1beta1.Namespace(namespace)), - }, - }, - }, - }, - { - Name: gwv1beta1.SectionName(listenerThreeName), - Port: gwv1beta1.PortNumber(listenerThreePort), - Protocol: gwv1beta1.ProtocolType(listenerThreeProtocol), - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.FromNamespaces("All")), - }, - }, - }, - }, - }, - } - - err := k8sClient.Create(ctx, gwClassCfg) - require.NoError(t, err) - - err = k8sClient.Create(ctx, gwClass) - require.NoError(t, err) - - err = k8sClient.Create(ctx, gw) - require.NoError(t, err) - - return gw -} - -func minimalFieldsSetHTTPRoute(t *testing.T, ctx context.Context, k8sClient client.WithWatch, gw *gwv1beta1.Gateway) *gwv1beta1.HTTPRoute { - svcDefault := &v1alpha1.ServiceDefaults{ - TypeMeta: metav1.TypeMeta{ - Kind: "ServiceDefaults", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "Service", - }, - Spec: v1alpha1.ServiceDefaultsSpec{ - Protocol: "http", - }, - } - - svc := &corev1.Service{ - TypeMeta: metav1.TypeMeta{ - Kind: "Service", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "Service", - Labels: map[string]string{"app": "Service"}, - }, - Spec: corev1.ServiceSpec{ - Ports: []corev1.ServicePort{ - { - Name: "high", - Protocol: "TCP", - Port: 8080, - }, - }, - Selector: map[string]string{"app": "Service"}, - }, - } - - serviceAccount := &corev1.ServiceAccount{ - TypeMeta: metav1.TypeMeta{ - Kind: "ServiceAccount", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "Service", - }, - } - - deployment := &appsv1.Deployment{ - TypeMeta: metav1.TypeMeta{ - Kind: "Deployment", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "Service", - Labels: map[string]string{"app": "Service"}, - }, - Spec: appsv1.DeploymentSpec{ - Replicas: common.PointerTo(int32(1)), - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"app": "Service"}, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{}, - Spec: corev1.PodSpec{}, - }, - }, - } - - err := k8sClient.Create(ctx, svcDefault) - require.NoError(t, err) - - err = k8sClient.Create(ctx, svc) - require.NoError(t, err) - - err = k8sClient.Create(ctx, serviceAccount) - require.NoError(t, err) - - err = k8sClient.Create(ctx, deployment) - require.NoError(t, err) - - route := &gwv1beta1.HTTPRoute{ - TypeMeta: metav1.TypeMeta{ - Kind: "HTTPRoute", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "http-route", - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - { - Kind: (*gwv1beta1.Kind)(&gw.Kind), - Namespace: (*gwv1beta1.Namespace)(&gw.Namespace), - Name: gwv1beta1.ObjectName(gw.Name), - SectionName: &gw.Spec.Listeners[0].Name, - Port: &gw.Spec.Listeners[0].Port, - }, - }, - }, - Hostnames: []gwv1beta1.Hostname{"route.consul.io"}, - Rules: []gwv1beta1.HTTPRouteRule{ - { - BackendRefs: []gwv1beta1.HTTPBackendRef{ - { - BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "Service", - Port: common.PointerTo(gwv1beta1.PortNumber(8080)), - }, - }, - }, - }, - }, - }, - }, - } - - err = k8sClient.Create(ctx, route) - require.NoError(t, err) - - return route -} - -func minimalFieldsSetTCPRoute(t *testing.T, ctx context.Context, k8sClient client.WithWatch, gw *gwv1beta1.Gateway) *v1alpha2.TCPRoute { - route := &v1alpha2.TCPRoute{ - TypeMeta: metav1.TypeMeta{ - Kind: "TCPRoute", - APIVersion: "gateway.networking.k8s.io/v1alpha2", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "tcp-route", - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - { - Kind: (*gwv1beta1.Kind)(&gw.Kind), - Namespace: (*gwv1beta1.Namespace)(&gw.Namespace), - Name: gwv1beta1.ObjectName(gw.Name), - SectionName: &gw.Spec.Listeners[1].Name, - Port: &gw.Spec.Listeners[1].Port, - }, - }, - }, - Rules: []gwv1alpha2.TCPRouteRule{ - { - BackendRefs: []gwv1beta1.BackendRef{ - { - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "Service", - Port: common.PointerTo(gwv1beta1.PortNumber(25000)), - }, - }, - }, - }, - }, - }, - } - - err := k8sClient.Create(ctx, route) - require.NoError(t, err) - - return route -} - -func createFunkyCasingFieldsAPIGW(t *testing.T, ctx context.Context, k8sClient client.WithWatch, namespace string) *gwv1beta1.Gateway { - // listener one configuration - listenerOneName := "listener-one" - listenerOneHostname := "*.consul.io" - listenerOnePort := 3366 - listenerOneProtocol := "hTtPs" - - // listener two configuration - listenerTwoName := "listener-two" - listenerTwoHostname := "*.consul.io" - listenerTwoPort := 5432 - listenerTwoProtocol := "HTTP" - - // listener three configuration - listenerThreeName := "listener-three" - listenerThreePort := 8081 - listenerThreeProtocol := "tCp" - - // listener four configuration - listenerFourName := "listener-four" - listenerFourHostname := "*.consul.io" - listenerFourPort := 5433 - listenerFourProtocol := "hTTp" - - // Write gw to k8s - gwClassCfg := &v1alpha1.GatewayClassConfig{ - TypeMeta: metav1.TypeMeta{ - Kind: "GatewayClassConfig", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway-class-config", - }, - Spec: v1alpha1.GatewayClassConfigSpec{}, - } - gwClass := &gwv1beta1.GatewayClass{ - TypeMeta: metav1.TypeMeta{ - Kind: "GatewayClass", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "gatewayclass", - }, - Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: "consul.hashicorp.com/gateway-controller", - ParametersRef: &gwv1beta1.ParametersReference{ - Group: "consul.hashicorp.com", - Kind: "GatewayClassConfig", - Name: "gateway-class-config", - }, - Description: new(string), - }, - } - gw := &gwv1beta1.Gateway{ - TypeMeta: metav1.TypeMeta{ - Kind: "Gateway", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "gw", - Namespace: namespace, - Annotations: make(map[string]string), - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: gwv1beta1.ObjectName(gwClass.Name), - Listeners: []gwv1beta1.Listener{ - { - Name: gwv1beta1.SectionName(listenerOneName), - Hostname: common.PointerTo(gwv1beta1.Hostname(listenerOneHostname)), - Port: gwv1beta1.PortNumber(listenerOnePort), - Protocol: gwv1beta1.ProtocolType(listenerOneProtocol), - TLS: &gwv1beta1.GatewayTLSConfig{ - CertificateRefs: []gwv1beta1.SecretObjectReference{ - { - Kind: common.PointerTo(gwv1beta1.Kind("Secret")), - Name: gwv1beta1.ObjectName("one-cert"), - Namespace: common.PointerTo(gwv1beta1.Namespace(namespace)), - }, - }, - }, - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.FromNamespaces("All")), - }, - }, - }, - { - Name: gwv1beta1.SectionName(listenerTwoName), - Hostname: common.PointerTo(gwv1beta1.Hostname(listenerTwoHostname)), - Port: gwv1beta1.PortNumber(listenerTwoPort), - Protocol: gwv1beta1.ProtocolType(listenerTwoProtocol), - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.FromNamespaces("Same")), - }, - }, - }, - { - Name: gwv1beta1.SectionName(listenerThreeName), - Port: gwv1beta1.PortNumber(listenerThreePort), - Protocol: gwv1beta1.ProtocolType(listenerThreeProtocol), - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.FromNamespaces("All")), - }, - }, - }, - { - Name: gwv1beta1.SectionName(listenerFourName), - Hostname: common.PointerTo(gwv1beta1.Hostname(listenerFourHostname)), - Port: gwv1beta1.PortNumber(listenerFourPort), - Protocol: gwv1beta1.ProtocolType(listenerFourProtocol), - AllowedRoutes: &gwv1beta1.AllowedRoutes{ - Namespaces: &gwv1beta1.RouteNamespaces{ - From: common.PointerTo(gwv1beta1.FromNamespaces("Selector")), - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - common.NamespaceNameLabel: "consul", - }, - MatchExpressions: []metav1.LabelSelectorRequirement{}, - }, - }, - }, - }, - }, - }, - } - - err := k8sClient.Create(ctx, gwClassCfg) - require.NoError(t, err) - - err = k8sClient.Create(ctx, gwClass) - require.NoError(t, err) - - err = k8sClient.Create(ctx, gw) - require.NoError(t, err) - - return gw -} - -func createFunkyCasingFieldsHTTPRoute(t *testing.T, ctx context.Context, k8sClient client.WithWatch, gw *gwv1beta1.Gateway) *gwv1beta1.HTTPRoute { - svcDefault := &v1alpha1.ServiceDefaults{ - TypeMeta: metav1.TypeMeta{ - Kind: "ServiceDefaults", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "Service", - }, - Spec: v1alpha1.ServiceDefaultsSpec{ - Protocol: "hTtp", - }, - } - - svc := &corev1.Service{ - TypeMeta: metav1.TypeMeta{ - Kind: "Service", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "Service", - Labels: map[string]string{"app": "Service"}, - }, - Spec: corev1.ServiceSpec{ - Ports: []corev1.ServicePort{ - { - Name: "high", - Protocol: "TCP", - Port: 8080, - }, - }, - Selector: map[string]string{"app": "Service"}, - }, - } - - serviceAccount := &corev1.ServiceAccount{ - TypeMeta: metav1.TypeMeta{ - Kind: "ServiceAccount", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "Service", - }, - } - - deployment := &appsv1.Deployment{ - TypeMeta: metav1.TypeMeta{ - Kind: "Deployment", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "Service", - Labels: map[string]string{"app": "Service"}, - }, - Spec: appsv1.DeploymentSpec{ - Replicas: common.PointerTo(int32(1)), - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"app": "Service"}, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{}, - Spec: corev1.PodSpec{}, - }, - }, - } - - err := k8sClient.Create(ctx, svcDefault) - require.NoError(t, err) - - err = k8sClient.Create(ctx, svc) - require.NoError(t, err) - - err = k8sClient.Create(ctx, serviceAccount) - require.NoError(t, err) - - err = k8sClient.Create(ctx, deployment) - require.NoError(t, err) - - route := &gwv1beta1.HTTPRoute{ - TypeMeta: metav1.TypeMeta{ - Kind: "HTTPRoute", - APIVersion: "gateway.networking.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "http-route", - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - { - Namespace: (*gwv1beta1.Namespace)(&gw.Namespace), - Name: gwv1beta1.ObjectName(gw.Name), - SectionName: &gw.Spec.Listeners[0].Name, - Port: &gw.Spec.Listeners[0].Port, - }, - }, - }, - Hostnames: []gwv1beta1.Hostname{"route.consul.io"}, - Rules: []gwv1beta1.HTTPRouteRule{ - { - Matches: []gwv1beta1.HTTPRouteMatch{ - { - Path: &gwv1beta1.HTTPPathMatch{ - Type: common.PointerTo(gwv1beta1.PathMatchPathPrefix), - }, - Headers: []gwv1beta1.HTTPHeaderMatch{ - { - Type: common.PointerTo(gwv1beta1.HeaderMatchExact), - Name: "version", - Value: "version", - }, - }, - QueryParams: []gwv1beta1.HTTPQueryParamMatch{ - { - Type: common.PointerTo(gwv1beta1.QueryParamMatchExact), - Name: "search", - Value: "q", - }, - }, - Method: common.PointerTo(gwv1beta1.HTTPMethod("geT")), - }, - }, - Filters: []gwv1beta1.HTTPRouteFilter{ - { - Type: gwv1beta1.HTTPRouteFilterRequestHeaderModifier, - RequestHeaderModifier: &gwv1beta1.HTTPHeaderFilter{ - Set: []gwv1beta1.HTTPHeader{ - { - Name: "foo", - Value: "bax", - }, - }, - Add: []gwv1beta1.HTTPHeader{ - { - Name: "arc", - Value: "reactor", - }, - }, - Remove: []string{"remove"}, - }, - }, - { - Type: gwv1beta1.HTTPRouteFilterURLRewrite, - URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ - Hostname: common.PointerTo(gwv1beta1.PreciseHostname("host.com")), - Path: &gwv1beta1.HTTPPathModifier{ - Type: gwv1beta1.FullPathHTTPPathModifier, - ReplaceFullPath: common.PointerTo("/foobar"), - }, - }, - }, - - { - Type: gwv1beta1.HTTPRouteFilterURLRewrite, - URLRewrite: &gwv1beta1.HTTPURLRewriteFilter{ - Hostname: common.PointerTo(gwv1beta1.PreciseHostname("host.com")), - Path: &gwv1beta1.HTTPPathModifier{ - Type: gwv1beta1.PrefixMatchHTTPPathModifier, - ReplacePrefixMatch: common.PointerTo("/foo"), - }, - }, - }, - }, - BackendRefs: []gwv1beta1.HTTPBackendRef{ - { - BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "Service", - Port: common.PointerTo(gwv1beta1.PortNumber(8080)), - }, - Weight: common.PointerTo(int32(-50)), - }, - }, - }, - }, - }, - }, - } - - err = k8sClient.Create(ctx, route) - require.NoError(t, err) - - return route -} - -func createFunkyCasingFieldsTCPRoute(t *testing.T, ctx context.Context, k8sClient client.WithWatch, gw *gwv1beta1.Gateway) *v1alpha2.TCPRoute { - route := &v1alpha2.TCPRoute{ - TypeMeta: metav1.TypeMeta{ - Kind: "TCPRoute", - APIVersion: "gateway.networking.k8s.io/v1alpha2", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "tcp-route", - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - { - Namespace: (*gwv1beta1.Namespace)(&gw.Namespace), - Name: gwv1beta1.ObjectName(gw.Name), - SectionName: &gw.Spec.Listeners[2].Name, - Port: &gw.Spec.Listeners[2].Port, - }, - }, - }, - Rules: []gwv1alpha2.TCPRouteRule{ - { - BackendRefs: []gwv1beta1.BackendRef{ - { - BackendObjectReference: gwv1beta1.BackendObjectReference{ - Name: "Service", - Port: common.PointerTo(gwv1beta1.PortNumber(25000)), - }, - Weight: common.PointerTo(int32(-50)), - }, - }, - }, - }, - }, - } - - err := k8sClient.Create(ctx, route) - require.NoError(t, err) - - return route -} diff --git a/control-plane/api-gateway/controllers/gateway_controller_test.go b/control-plane/api-gateway/controllers/gateway_controller_test.go deleted file mode 100644 index 7b21d01ff8..0000000000 --- a/control-plane/api-gateway/controllers/gateway_controller_test.go +++ /dev/null @@ -1,642 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers - -import ( - "context" - "testing" - - mapset "github.com/deckarep/golang-set" - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" - - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -func TestTransformEndpoints(t *testing.T) { - t.Parallel() - - httpRoute := &gwv1beta1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Name: "http", - Namespace: "test", - }, - Spec: gwv1beta1.HTTPRouteSpec{ - Rules: []gwv1beta1.HTTPRouteRule{ - {BackendRefs: []gwv1beta1.HTTPBackendRef{ - {BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{Name: "http-test-namespace"}, - }}, - {BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{Name: "http-other-namespace", Namespace: common.PointerTo(gwv1beta1.Namespace("other"))}, - }}, - {BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{Name: "http-system-namespace", Namespace: common.PointerTo(gwv1beta1.Namespace("system"))}, - }}, - {BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{Name: "http-public-namespace", Namespace: common.PointerTo(gwv1beta1.Namespace("public"))}, - }}, - {BackendRef: gwv1beta1.BackendRef{ - BackendObjectReference: gwv1beta1.BackendObjectReference{Name: "http-local-path-storage-namespace", Namespace: common.PointerTo(gwv1beta1.Namespace("local-path-storage"))}, - }}}, - }, - }, - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - {Name: "http-gateway"}, - {Name: "general-gateway"}, - }, - }, - }, - } - - tcpRoute := &gwv1alpha2.TCPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Name: "tcp", - Namespace: "test", - }, - Spec: gwv1alpha2.TCPRouteSpec{ - Rules: []gwv1alpha2.TCPRouteRule{ - {BackendRefs: []gwv1beta1.BackendRef{ - {BackendObjectReference: gwv1beta1.BackendObjectReference{Name: "tcp-test-namespace"}}, - {BackendObjectReference: gwv1beta1.BackendObjectReference{Name: "tcp-other-namespace", Namespace: common.PointerTo(gwv1beta1.Namespace("other"))}}, - {BackendObjectReference: gwv1beta1.BackendObjectReference{Name: "tcp-system-namespace", Namespace: common.PointerTo(gwv1beta1.Namespace("system"))}}, - {BackendObjectReference: gwv1beta1.BackendObjectReference{Name: "tcp-public-namespace", Namespace: common.PointerTo(gwv1beta1.Namespace("public"))}}, - {BackendObjectReference: gwv1beta1.BackendObjectReference{Name: "tcp-local-path-storage-namespace", Namespace: common.PointerTo(gwv1beta1.Namespace("local-path-storage"))}}, - }}, - }, - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - {Name: "tcp-gateway"}, - {Name: "general-gateway"}, - }, - }, - }, - } - - for name, tt := range map[string]struct { - endpoints *corev1.Endpoints - expected []reconcile.Request - allowedNamespaces []string - denyNamespaces []string - }{ - "ignore system namespace": { - endpoints: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Name: "http-system-namespace", - Namespace: metav1.NamespaceSystem, - }, - }, - allowedNamespaces: []string{"*"}, - }, - "ignore public namespace": { - endpoints: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Name: "http-public-namespace", - Namespace: metav1.NamespacePublic, - }, - }, - allowedNamespaces: []string{"*"}, - }, - "ignore local-path-storage namespace": { - endpoints: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Name: "http-local-path-storage-namespace", - Namespace: "local-path-storage", - }, - }, - allowedNamespaces: []string{"*"}, - }, - "explicit deny namespace": { - endpoints: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Name: "http-test-namespace", - Namespace: "test", - }, - }, - allowedNamespaces: []string{"*"}, - denyNamespaces: []string{"test"}, - }, - "ignore labels": { - endpoints: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Name: "http-test-namespace", - Namespace: "test", - Labels: map[string]string{ - constants.LabelServiceIgnore: "true", - }, - }, - }, - allowedNamespaces: []string{"test"}, - }, - "http same namespace wildcard allow": { - endpoints: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Name: "http-test-namespace", - Namespace: "test", - }, - }, - allowedNamespaces: []string{"*"}, - expected: []reconcile.Request{ - {NamespacedName: types.NamespacedName{Name: "http-gateway", Namespace: "test"}}, - {NamespacedName: types.NamespacedName{Name: "general-gateway", Namespace: "test"}}, - }, - }, - "http same namespace explicit allow": { - endpoints: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Name: "http-test-namespace", - Namespace: "test", - }, - }, - allowedNamespaces: []string{"test"}, - expected: []reconcile.Request{ - {NamespacedName: types.NamespacedName{Name: "http-gateway", Namespace: "test"}}, - {NamespacedName: types.NamespacedName{Name: "general-gateway", Namespace: "test"}}, - }, - }, - "http other namespace wildcard allow": { - endpoints: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Name: "http-other-namespace", - Namespace: "other", - }, - }, - allowedNamespaces: []string{"*"}, - expected: []reconcile.Request{ - {NamespacedName: types.NamespacedName{Name: "http-gateway", Namespace: "test"}}, - {NamespacedName: types.NamespacedName{Name: "general-gateway", Namespace: "test"}}, - }, - }, - "http other namespace explicit allow": { - endpoints: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Name: "http-other-namespace", - Namespace: "other", - }, - }, - allowedNamespaces: []string{"other"}, - expected: []reconcile.Request{ - {NamespacedName: types.NamespacedName{Name: "http-gateway", Namespace: "test"}}, - {NamespacedName: types.NamespacedName{Name: "general-gateway", Namespace: "test"}}, - }, - }, - "tcp same namespace wildcard allow": { - endpoints: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Name: "tcp-test-namespace", - Namespace: "test", - }, - }, - allowedNamespaces: []string{"*"}, - expected: []reconcile.Request{ - {NamespacedName: types.NamespacedName{Name: "tcp-gateway", Namespace: "test"}}, - {NamespacedName: types.NamespacedName{Name: "general-gateway", Namespace: "test"}}, - }, - }, - "tcp same namespace explicit allow": { - endpoints: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Name: "tcp-test-namespace", - Namespace: "test", - }, - }, - allowedNamespaces: []string{"test"}, - expected: []reconcile.Request{ - {NamespacedName: types.NamespacedName{Name: "tcp-gateway", Namespace: "test"}}, - {NamespacedName: types.NamespacedName{Name: "general-gateway", Namespace: "test"}}, - }, - }, - "tcp other namespace wildcard allow": { - endpoints: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Name: "tcp-other-namespace", - Namespace: "other", - }, - }, - allowedNamespaces: []string{"*"}, - expected: []reconcile.Request{ - {NamespacedName: types.NamespacedName{Name: "tcp-gateway", Namespace: "test"}}, - {NamespacedName: types.NamespacedName{Name: "general-gateway", Namespace: "test"}}, - }, - }, - "tcp other namespace explicit allow": { - endpoints: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Name: "tcp-other-namespace", - Namespace: "other", - }, - }, - allowedNamespaces: []string{"other"}, - expected: []reconcile.Request{ - {NamespacedName: types.NamespacedName{Name: "tcp-gateway", Namespace: "test"}}, - {NamespacedName: types.NamespacedName{Name: "general-gateway", Namespace: "test"}}, - }, - }, - } { - t.Run(name, func(t *testing.T) { - s := runtime.NewScheme() - require.NoError(t, clientgoscheme.AddToScheme(s)) - require.NoError(t, gwv1alpha2.Install(s)) - require.NoError(t, gwv1beta1.Install(s)) - require.NoError(t, v1alpha1.AddToScheme(s)) - - denySet := mapset.NewSet() - for _, v := range tt.denyNamespaces { - denySet.Add(v) - } - allowSet := mapset.NewSet() - for _, v := range tt.allowedNamespaces { - allowSet.Add(v) - } - - fakeClient := registerFieldIndexersForTest(fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(httpRoute, tcpRoute)).Build() - - controller := GatewayController{ - Client: fakeClient, - denyK8sNamespacesSet: denySet, - allowK8sNamespacesSet: allowSet, - } - - fn := controller.transformEndpoints(context.Background()) - require.ElementsMatch(t, tt.expected, fn(tt.endpoints)) - }) - } -} - -func TestTransformHTTPRoute(t *testing.T) { - t.Parallel() - - for name, tt := range map[string]struct { - route *gwv1beta1.HTTPRoute - expected []reconcile.Request - }{ - "route with parent empty namespace": { - route: &gwv1beta1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - {Name: "gateway"}, - }, - }, - }, - }, - expected: []reconcile.Request{ - {NamespacedName: types.NamespacedName{Name: "gateway", Namespace: "default"}}, - }, - }, - "route with parent with namespace": { - route: &gwv1beta1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - {Name: "gateway", Namespace: common.PointerTo(gwv1beta1.Namespace("other"))}, - }, - }, - }, - }, - expected: []reconcile.Request{ - {NamespacedName: types.NamespacedName{Name: "gateway", Namespace: "other"}}, - }, - }, - "route with non gateway parent with namespace": { - route: &gwv1beta1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - {Name: "gateway", Group: common.PointerTo(gwv1beta1.Group("group"))}, - }, - }, - }, - }, - expected: []reconcile.Request{}, - }, - "route with parent in status and no namespace": { - route: &gwv1beta1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - }, - Status: gwv1beta1.HTTPRouteStatus{ - RouteStatus: gwv1beta1.RouteStatus{ - Parents: []gwv1beta1.RouteParentStatus{ - {ParentRef: gwv1beta1.ParentReference{Name: "gateway"}}, - }, - }, - }, - }, - expected: []reconcile.Request{ - {NamespacedName: types.NamespacedName{Name: "gateway", Namespace: "default"}}, - }, - }, - "route with parent in status and namespace": { - route: &gwv1beta1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - }, - Status: gwv1beta1.HTTPRouteStatus{ - RouteStatus: gwv1beta1.RouteStatus{ - Parents: []gwv1beta1.RouteParentStatus{ - {ParentRef: gwv1beta1.ParentReference{Name: "gateway", Namespace: common.PointerTo(gwv1beta1.Namespace("other"))}}, - }, - }, - }, - }, - expected: []reconcile.Request{ - {NamespacedName: types.NamespacedName{Name: "gateway", Namespace: "other"}}, - }, - }, - "route with non gateway parent in status": { - route: &gwv1beta1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - }, - Status: gwv1beta1.HTTPRouteStatus{ - RouteStatus: gwv1beta1.RouteStatus{ - Parents: []gwv1beta1.RouteParentStatus{ - {ParentRef: gwv1beta1.ParentReference{Name: "gateway", Group: common.PointerTo(gwv1beta1.Group("group"))}}, - }, - }, - }, - }, - expected: []reconcile.Request{}, - }, - "route parent in spec and in status": { - route: &gwv1beta1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - }, - Spec: gwv1beta1.HTTPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - {Name: "gateway-one"}, - }, - }, - }, - Status: gwv1beta1.HTTPRouteStatus{ - RouteStatus: gwv1beta1.RouteStatus{ - Parents: []gwv1beta1.RouteParentStatus{ - {ParentRef: gwv1beta1.ParentReference{Name: "gateway-two"}}, - }, - }, - }, - }, - expected: []reconcile.Request{ - {NamespacedName: types.NamespacedName{Name: "gateway-one", Namespace: "default"}}, - {NamespacedName: types.NamespacedName{Name: "gateway-two", Namespace: "default"}}, - }, - }, - } { - t.Run(name, func(t *testing.T) { - controller := GatewayController{} - - fn := controller.transformHTTPRoute(context.Background()) - require.ElementsMatch(t, tt.expected, fn(tt.route)) - }) - } -} - -func TestTransformTCPRoute(t *testing.T) { - t.Parallel() - - for name, tt := range map[string]struct { - route *gwv1alpha2.TCPRoute - expected []reconcile.Request - }{ - "route with parent empty namespace": { - route: &gwv1alpha2.TCPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - {Name: "gateway"}, - }, - }, - }, - }, - expected: []reconcile.Request{ - {NamespacedName: types.NamespacedName{Name: "gateway", Namespace: "default"}}, - }, - }, - "route with parent with namespace": { - route: &gwv1alpha2.TCPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - {Name: "gateway", Namespace: common.PointerTo(gwv1beta1.Namespace("other"))}, - }, - }, - }, - }, - expected: []reconcile.Request{ - {NamespacedName: types.NamespacedName{Name: "gateway", Namespace: "other"}}, - }, - }, - "route with non gateway parent with namespace": { - route: &gwv1alpha2.TCPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - {Name: "gateway", Group: common.PointerTo(gwv1beta1.Group("group"))}, - }, - }, - }, - }, - expected: []reconcile.Request{}, - }, - "route with parent in status and no namespace": { - route: &gwv1alpha2.TCPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - }, - Status: gwv1alpha2.TCPRouteStatus{ - RouteStatus: gwv1beta1.RouteStatus{ - Parents: []gwv1beta1.RouteParentStatus{ - {ParentRef: gwv1beta1.ParentReference{Name: "gateway"}}, - }, - }, - }, - }, - expected: []reconcile.Request{ - {NamespacedName: types.NamespacedName{Name: "gateway", Namespace: "default"}}, - }, - }, - "route with parent in status and namespace": { - route: &gwv1alpha2.TCPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - }, - Status: gwv1alpha2.TCPRouteStatus{ - RouteStatus: gwv1beta1.RouteStatus{ - Parents: []gwv1beta1.RouteParentStatus{ - {ParentRef: gwv1beta1.ParentReference{Name: "gateway", Namespace: common.PointerTo(gwv1beta1.Namespace("other"))}}, - }, - }, - }, - }, - expected: []reconcile.Request{ - {NamespacedName: types.NamespacedName{Name: "gateway", Namespace: "other"}}, - }, - }, - "route with non gateway parent in status": { - route: &gwv1alpha2.TCPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - }, - Status: gwv1alpha2.TCPRouteStatus{ - RouteStatus: gwv1beta1.RouteStatus{ - Parents: []gwv1beta1.RouteParentStatus{ - {ParentRef: gwv1beta1.ParentReference{Name: "gateway", Group: common.PointerTo(gwv1beta1.Group("group"))}}, - }, - }, - }, - }, - expected: []reconcile.Request{}, - }, - "route parent in spec and in status": { - route: &gwv1alpha2.TCPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - }, - Spec: gwv1alpha2.TCPRouteSpec{ - CommonRouteSpec: gwv1beta1.CommonRouteSpec{ - ParentRefs: []gwv1beta1.ParentReference{ - {Name: "gateway-one"}, - }, - }, - }, - Status: gwv1alpha2.TCPRouteStatus{ - RouteStatus: gwv1beta1.RouteStatus{ - Parents: []gwv1beta1.RouteParentStatus{ - {ParentRef: gwv1beta1.ParentReference{Name: "gateway-two"}}, - }, - }, - }, - }, - expected: []reconcile.Request{ - {NamespacedName: types.NamespacedName{Name: "gateway-one", Namespace: "default"}}, - {NamespacedName: types.NamespacedName{Name: "gateway-two", Namespace: "default"}}, - }, - }, - } { - t.Run(name, func(t *testing.T) { - controller := GatewayController{} - - fn := controller.transformTCPRoute(context.Background()) - require.ElementsMatch(t, tt.expected, fn(tt.route)) - }) - } -} - -func TestTransformSecret(t *testing.T) { - t.Parallel() - - gateway := &gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - Namespace: "test", - }, - Spec: gwv1beta1.GatewaySpec{ - Listeners: []gwv1beta1.Listener{ - {Name: "terminate", TLS: &gwv1beta1.GatewayTLSConfig{ - Mode: common.PointerTo(gwv1beta1.TLSModeTerminate), - CertificateRefs: []gwv1beta1.SecretObjectReference{ - {Name: "secret-no-namespace"}, - {Name: "secret-namespace", Namespace: common.PointerTo(gwv1beta1.Namespace("other"))}, - }, - }}, - {Name: "passthrough", TLS: &gwv1beta1.GatewayTLSConfig{ - Mode: common.PointerTo(gwv1beta1.TLSModePassthrough), - CertificateRefs: []gwv1beta1.SecretObjectReference{ - {Name: "passthrough", Namespace: common.PointerTo(gwv1beta1.Namespace("other"))}, - }, - }}, - }, - }, - } - - for name, tt := range map[string]struct { - secret *corev1.Secret - expected []reconcile.Request - }{ - "explicit namespace from parent": { - secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "secret-namespace", Namespace: "other"}, - }, - expected: []reconcile.Request{ - {NamespacedName: types.NamespacedName{Name: "gateway", Namespace: "test"}}, - }, - }, - "implicit namespace from parent": { - secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "secret-no-namespace", Namespace: "test"}, - }, - expected: []reconcile.Request{ - {NamespacedName: types.NamespacedName{Name: "gateway", Namespace: "test"}}, - }, - }, - "mismatched namespace": { - secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "secret-no-namespace", Namespace: "other"}, - }, - }, - "mismatched names": { - secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "something", Namespace: "test"}, - }, - }, - "passthrough ignored": { - secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "passthrough", Namespace: "other"}, - }, - }, - } { - t.Run(name, func(t *testing.T) { - tt := tt - - t.Parallel() - - s := runtime.NewScheme() - require.NoError(t, clientgoscheme.AddToScheme(s)) - require.NoError(t, gwv1alpha2.Install(s)) - require.NoError(t, gwv1beta1.Install(s)) - require.NoError(t, v1alpha1.AddToScheme(s)) - - fakeClient := registerFieldIndexersForTest(fake.NewClientBuilder().WithScheme(s)).WithRuntimeObjects(gateway).Build() - - controller := GatewayController{ - Client: fakeClient, - } - - fn := controller.transformSecret(context.Background()) - require.ElementsMatch(t, tt.expected, fn(tt.secret)) - }) - } -} diff --git a/control-plane/api-gateway/controllers/gatewayclass_controller.go b/control-plane/api-gateway/controllers/gatewayclass_controller.go deleted file mode 100644 index 3bde2d6ab1..0000000000 --- a/control-plane/api-gateway/controllers/gatewayclass_controller.go +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers - -import ( - "context" - "fmt" - "github.com/go-logr/logr" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/types" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -const ( - gatewayClassFinalizer = "gateway-exists-finalizer.consul.hashicorp.com" - - // GatewayClass status fields. - accepted = "Accepted" - invalidParameters = "InvalidParameters" -) - -// GatewayClassController reconciles a GatewayClass object. -// The GatewayClass is responsible for defining the behavior of API gateways -// which reference the given class. -type GatewayClassController struct { - ControllerName string - Log logr.Logger - - client.Client -} - -// Reconcile handles the reconciliation loop for GatewayClass objects. -func (r *GatewayClassController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := r.Log.WithValues("gatewayClass", req.NamespacedName.Name) - log.V(1).Info("Reconciling GatewayClass") - - gc := &gwv1beta1.GatewayClass{} - - err := r.Client.Get(ctx, req.NamespacedName, gc) - if err != nil { - if k8serrors.IsNotFound(err) { - return ctrl.Result{}, nil - } - log.Error(err, "unable to get GatewayClass") - return ctrl.Result{}, err - } - - if string(gc.Spec.ControllerName) != r.ControllerName { - // This GatewayClass is not for this controller. - _, err := RemoveFinalizer(ctx, r.Client, gc, gatewayClassFinalizer) - if err != nil { - log.Error(err, "unable to remove finalizer") - } - - return ctrl.Result{}, err - } - - if !gc.ObjectMeta.DeletionTimestamp.IsZero() { - // We have a deletion request. Ensure we are not in use. - used, err := r.isGatewayClassInUse(ctx, gc) - if err != nil { - log.Error(err, "unable to check if GatewayClass is in use") - return ctrl.Result{}, err - } - if used { - log.Info("GatewayClass is in use, cannot delete") - return ctrl.Result{}, nil - } - // Remove our finalizer. - if _, err := RemoveFinalizer(ctx, r.Client, gc, gatewayClassFinalizer); err != nil { - if k8serrors.IsConflict(err) { - log.V(1).Info("error removing finalizer for gatewayClass, will try to re-reconcile") - - return ctrl.Result{Requeue: true}, nil - } - log.Error(err, "unable to remove finalizer") - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - - // We are creating or updating the GatewayClass. - didUpdate, err := EnsureFinalizer(ctx, r.Client, gc, gatewayClassFinalizer) - if err != nil { - if k8serrors.IsConflict(err) { - log.V(1).Info("error adding finalizer for gatewayClass, will try to re-reconcile") - - return ctrl.Result{Requeue: true}, nil - } - log.Error(err, "unable to add finalizer") - return ctrl.Result{}, err - } - if didUpdate { - // We updated the GatewayClass, requeue to avoid another update. - return ctrl.Result{}, nil - } - - didUpdate, err = r.validateParametersRef(ctx, gc, log) - if didUpdate { - if err := r.Client.Status().Update(ctx, gc); err != nil { - if k8serrors.IsConflict(err) { - log.V(1).Info("error updating status for gatewayClass, will try to re-reconcile") - - return ctrl.Result{Requeue: true}, nil - } - log.Error(err, "unable to update status for GatewayClass") - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - if err != nil { - log.Error(err, "unable to validate ParametersRef") - } - - return ctrl.Result{}, err -} - -// SetupWithManager registers the controller with the given manager. -func (r *GatewayClassController) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&gwv1beta1.GatewayClass{}). - // Watch for changes to GatewayClassConfig objects. - Watches(source.NewKindWithCache(&v1alpha1.GatewayClassConfig{}, mgr.GetCache()), r.gatewayClassConfigFieldIndexEventHandler(ctx)). - // Watch for changes to Gateway objects that reference this GatewayClass. - Watches(source.NewKindWithCache(&gwv1beta1.Gateway{}, mgr.GetCache()), r.gatewayFieldIndexEventHandler(ctx)). - Complete(r) -} - -// isGatewayClassInUse returns true if the given GatewayClass is referenced by any Gateway objects. -func (r *GatewayClassController) isGatewayClassInUse(ctx context.Context, gc *gwv1beta1.GatewayClass) (bool, error) { - list := &gwv1beta1.GatewayList{} - if err := r.Client.List(ctx, list, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(Gateway_GatewayClassIndex, gc.Name), - }); err != nil { - return false, err - } - - return len(list.Items) != 0, nil -} - -// validateParametersRef validates the ParametersRef field of the given GatewayClass -// if it is set, ensuring that the referenced object is a GatewayClassConfig that exists. -func (r *GatewayClassController) validateParametersRef(ctx context.Context, gc *gwv1beta1.GatewayClass, log logr.Logger) (didUpdate bool, err error) { - parametersRef := gc.Spec.ParametersRef - if parametersRef != nil { - if parametersRef.Kind != v1alpha1.GatewayClassConfigKind { - didUpdate = r.setCondition(gc, metav1.Condition{ - Type: accepted, - Status: metav1.ConditionFalse, - Reason: invalidParameters, - Message: fmt.Sprintf("Incorrect type for parametersRef. Expected GatewayClassConfig, got %q.", parametersRef.Kind), - }) - return didUpdate, nil - } - - err = r.Client.Get(ctx, types.NamespacedName{Name: parametersRef.Name}, &v1alpha1.GatewayClassConfig{}) - if k8serrors.IsNotFound(err) { - didUpdate := r.setCondition(gc, metav1.Condition{ - Type: accepted, - Status: metav1.ConditionFalse, - Reason: invalidParameters, - Message: fmt.Sprintf("GatewayClassConfig not found %q.", parametersRef.Name), - }) - return didUpdate, nil - } - if err != nil { - log.Error(err, "unable to fetch GatewayClassConfig") - return false, err - } - } - - didUpdate = r.setCondition(gc, metav1.Condition{ - Type: accepted, - Status: metav1.ConditionTrue, - Reason: accepted, - Message: "GatewayClass Accepted", - }) - - return didUpdate, err -} - -// setCondition sets the given condition on the given GatewayClass. -func (r *GatewayClassController) setCondition(gc *gwv1beta1.GatewayClass, condition metav1.Condition) (didUpdate bool) { - condition.LastTransitionTime = metav1.Now() - condition.ObservedGeneration = gc.GetGeneration() - - // Set the condition if it already exists. - for i, c := range gc.Status.Conditions { - if c.Type == condition.Type { - // The condition already exists and is up to date. - if equalConditions(condition, c) { - return false - } - - gc.Status.Conditions[i] = condition - - return true - } - } - - // Append the condition if it does not exist. - gc.Status.Conditions = append(gc.Status.Conditions, condition) - - return true -} - -// gatewayClassConfigFieldIndexEventHandler returns an EventHandler that will enqueue -// reconcile.Requests for GatewayClass objects that reference the GatewayClassConfig -// object that triggered the event. -func (r *GatewayClassController) gatewayClassConfigFieldIndexEventHandler(ctx context.Context) handler.EventHandler { - return handler.EnqueueRequestsFromMapFunc(func(o client.Object) []reconcile.Request { - requests := []reconcile.Request{} - - // Get all GatewayClass objects from the field index of the GatewayClassConfig which triggered the event. - var gcList gwv1beta1.GatewayClassList - err := r.Client.List(ctx, &gcList, &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(GatewayClass_GatewayClassConfigIndex, o.GetName()), - }) - if err != nil { - r.Log.Error(err, "unable to list gateway classes") - } - - // Create a reconcile request for each GatewayClass. - for _, gc := range gcList.Items { - requests = append(requests, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Name: gc.Name, - }, - }) - } - - return requests - }) -} - -// gatewayFieldIndexEventHandler returns an EventHandler that will enqueue -// reconcile.Requests for GatewayClass objects from Gateways which reference the GatewayClass -// when those Gateways are updated. -func (r *GatewayClassController) gatewayFieldIndexEventHandler(ctx context.Context) handler.EventHandler { - return handler.EnqueueRequestsFromMapFunc(func(o client.Object) []reconcile.Request { - // Get the Gateway object that triggered the event. - g := o.(*gwv1beta1.Gateway) - - // Return a slice with the single reconcile.Request for the GatewayClass - // that the Gateway references. - return []reconcile.Request{ - { - NamespacedName: types.NamespacedName{ - Name: string(g.Spec.GatewayClassName), - }, - }, - } - }) -} - -func equalConditions(a, b metav1.Condition) bool { - return a.Type == b.Type && - a.Status == b.Status && - a.Reason == b.Reason && - a.Message == b.Message && - a.ObservedGeneration == b.ObservedGeneration -} diff --git a/control-plane/api-gateway/controllers/gatewayclass_controller_test.go b/control-plane/api-gateway/controllers/gatewayclass_controller_test.go deleted file mode 100644 index 0eeaf4c1de..0000000000 --- a/control-plane/api-gateway/controllers/gatewayclass_controller_test.go +++ /dev/null @@ -1,276 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers - -import ( - "context" - "fmt" - "testing" - - logrtest "github.com/go-logr/logr/testr" - "github.com/stretchr/testify/require" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - "sigs.k8s.io/gateway-api/apis/v1beta1" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" -) - -func TestGatewayClassReconciler(t *testing.T) { - t.Parallel() - - namespace := "" // GatewayClass is cluster-scoped. - name := "test-gatewayclass" - - req := ctrl.Request{ - NamespacedName: types.NamespacedName{ - Namespace: namespace, - Name: name, - }, - } - - deletionTimestamp := metav1.Now() - - cases := map[string]struct { - gatewayClass *gwv1beta1.GatewayClass - k8sObjects []runtime.Object - expectedResult ctrl.Result - expectedError error - expectedFinalizers []string - expectedIsDeleted bool - expectedConditions []metav1.Condition - }{ - "successful reconcile with no change": { - gatewayClass: &gwv1beta1.GatewayClass{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - Finalizers: []string{gatewayClassFinalizer}, - }, - Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: common.GatewayClassControllerName, - }, - }, - expectedResult: ctrl.Result{}, - expectedError: nil, - expectedFinalizers: []string{gatewayClassFinalizer}, - expectedIsDeleted: false, - expectedConditions: []metav1.Condition{ - { - Type: accepted, - Status: metav1.ConditionTrue, - Reason: accepted, - Message: "GatewayClass Accepted", - }, - }, - }, - "successful reconcile that adds finalizer": { - gatewayClass: &gwv1beta1.GatewayClass{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - Finalizers: []string{}, - }, - Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: common.GatewayClassControllerName, - }, - }, - expectedResult: ctrl.Result{}, - expectedError: nil, - expectedFinalizers: []string{gatewayClassFinalizer}, - expectedConditions: []metav1.Condition{}, - }, - "attempt to reconcile a GatewayClass with a different controller name": { - gatewayClass: &gwv1beta1.GatewayClass{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - Finalizers: []string{}, - }, - Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: "foo", - }, - }, - expectedResult: ctrl.Result{}, - expectedError: nil, - expectedConditions: []metav1.Condition{}, - }, - "attempt to reconcile a GatewayClass with a different controller name removing our finalizer": { - gatewayClass: &gwv1beta1.GatewayClass{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - Finalizers: []string{gatewayClassFinalizer}, - }, - Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: "foo", - }, - }, - expectedResult: ctrl.Result{}, - expectedError: nil, - expectedConditions: []metav1.Condition{}, - }, - "attempt to reconcile a GatewayClass with an incorrect parametersRef type": { - gatewayClass: &gwv1beta1.GatewayClass{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - Finalizers: []string{gatewayClassFinalizer}, - }, - Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: common.GatewayClassControllerName, - ParametersRef: &gwv1beta1.ParametersReference{ - Kind: "some-nonsense", - }, - }, - }, - expectedResult: ctrl.Result{}, - expectedError: nil, - expectedFinalizers: []string{gatewayClassFinalizer}, - expectedConditions: []metav1.Condition{ - { - Type: accepted, - Status: metav1.ConditionFalse, - Reason: invalidParameters, - Message: fmt.Sprintf("Incorrect type for parametersRef. Expected GatewayClassConfig, got %q.", "some-nonsense"), - }, - }, - }, - "attempt to reconcile a GatewayClass with a GatewayClassConfig that does not exist": { - gatewayClass: &gwv1beta1.GatewayClass{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - Finalizers: []string{gatewayClassFinalizer}, - }, - Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: common.GatewayClassControllerName, - ParametersRef: &gwv1beta1.ParametersReference{ - Kind: v1alpha1.GatewayClassConfigKind, - Name: "does-not-exist", - }, - }, - }, - expectedResult: ctrl.Result{}, - expectedError: nil, - expectedFinalizers: []string{gatewayClassFinalizer}, - expectedConditions: []metav1.Condition{ - { - Type: accepted, - Status: metav1.ConditionFalse, - Reason: invalidParameters, - Message: fmt.Sprintf("GatewayClassConfig not found %q.", "does-not-exist"), - }, - }, - }, - "attempt to reconcile a non-existent object": { - k8sObjects: []runtime.Object{}, - expectedResult: ctrl.Result{}, - expectedError: nil, - expectedConditions: []metav1.Condition{}, - }, - "attempt to remove a GatewayClass that is not in use": { - gatewayClass: &gwv1beta1.GatewayClass{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - Finalizers: []string{ - gatewayClassFinalizer, - }, - DeletionTimestamp: &deletionTimestamp, - }, - Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: common.GatewayClassControllerName, - }, - }, - expectedResult: ctrl.Result{}, - expectedError: nil, - expectedFinalizers: []string{}, - expectedIsDeleted: true, - }, - "attempt to remove a GatewayClass that is in use": { - gatewayClass: &gwv1beta1.GatewayClass{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - Finalizers: []string{ - gatewayClassFinalizer, - }, - DeletionTimestamp: &deletionTimestamp, - }, - Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: common.GatewayClassControllerName, - }, - }, - k8sObjects: []runtime.Object{ - &gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "test-gateway", - }, - Spec: gwv1beta1.GatewaySpec{ - GatewayClassName: v1beta1.ObjectName(name), - }, - }, - }, - expectedResult: ctrl.Result{}, - expectedError: nil, - expectedFinalizers: []string{gatewayClassFinalizer}, - }, - // */ - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - s := runtime.NewScheme() - require.NoError(t, clientgoscheme.AddToScheme(s)) - require.NoError(t, gwv1alpha2.Install(s)) - require.NoError(t, gwv1beta1.Install(s)) - require.NoError(t, v1alpha1.AddToScheme(s)) - - objs := tc.k8sObjects - if tc.gatewayClass != nil { - objs = append(objs, tc.gatewayClass) - } - - fakeClient := registerFieldIndexersForTest(fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(objs...)).Build() - - r := &GatewayClassController{ - Client: fakeClient, - ControllerName: common.GatewayClassControllerName, - Log: logrtest.New(t), - } - result, err := r.Reconcile(context.Background(), req) - - require.Equal(t, tc.expectedResult, result) - require.Equal(t, tc.expectedError, err) - - // Check the GatewayClass after reconciliation. - gc := &gwv1beta1.GatewayClass{} - err = r.Client.Get(context.Background(), req.NamespacedName, gc) - - if tc.gatewayClass == nil || tc.expectedIsDeleted { - // There shouldn't be a GatewayClass to check. - require.True(t, apierrors.IsNotFound(err)) - return - } - - require.NoError(t, client.IgnoreNotFound(err)) - require.Equal(t, tc.expectedFinalizers, gc.ObjectMeta.Finalizers) - require.Equal(t, len(tc.expectedConditions), len(gc.Status.Conditions), "expected %+v, got %+v", tc.expectedConditions, gc.Status.Conditions) - for i, expectedCondition := range tc.expectedConditions { - require.True(t, equalConditions(expectedCondition, gc.Status.Conditions[i]), "expected %+v, got %+v", expectedCondition, gc.Status.Conditions[i]) - } - }) - } -} diff --git a/control-plane/api-gateway/controllers/gatewayclassconfig_controller.go b/control-plane/api-gateway/controllers/gatewayclassconfig_controller.go deleted file mode 100644 index 878d6549f9..0000000000 --- a/control-plane/api-gateway/controllers/gatewayclassconfig_controller.go +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers - -import ( - "context" - "time" - - "github.com/go-logr/logr" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" -) - -const ( - gatewayClassConfigFinalizer = "gateway-class-exists-finalizer.consul.hashicorp.com" -) - -// The GatewayClassConfigController manages the state of GatewayClassConfigs. -type GatewayClassConfigController struct { - client.Client - - Log logr.Logger -} - -// Reconcile is part of the main kubernetes reconciliation loop which aims to -// move the current state of the cluster closer to the desired state. -// For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.8.3/pkg/reconcile -func (r *GatewayClassConfigController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := r.Log.WithValues("gatewayClassConfig", req.NamespacedName.Name) - log.V(1).Info("Reconciling GatewayClassConfig ") - - gcc := &v1alpha1.GatewayClassConfig{} - if err := r.Client.Get(ctx, req.NamespacedName, gcc); err != nil { - if k8serrors.IsNotFound(err) { - return ctrl.Result{}, nil - } - log.Error(err, "failed to get gateway class config") - return ctrl.Result{}, err - } - - if !gcc.ObjectMeta.DeletionTimestamp.IsZero() { - // We have a deletion, ensure we're not in use. - used, err := gatewayClassConfigInUse(ctx, r.Client, gcc) - if err != nil { - log.Error(err, "failed to check if the gateway class config is still in use") - return ctrl.Result{}, err - } - if used { - log.Info("gateway class config still in use") - // Requeue as to not block the reconciliation loop. - return ctrl.Result{RequeueAfter: 10 * time.Second}, nil - } - // gcc is no longer in use. - if _, err := RemoveFinalizer(ctx, r.Client, gcc, gatewayClassConfigFinalizer); err != nil { - if k8serrors.IsConflict(err) { - log.V(1).Info("error removing gateway class config finalizer, will try to re-reconcile") - return ctrl.Result{Requeue: true}, nil - } - log.Error(err, "error removing gateway class config finalizer") - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - - if _, err := EnsureFinalizer(ctx, r.Client, gcc, gatewayClassConfigFinalizer); err != nil { - if k8serrors.IsConflict(err) { - log.V(1).Info("error adding gateway class config finalizer, will try to re-reconcile") - - return ctrl.Result{Requeue: true}, nil - } - log.Error(err, "error adding gateway class config finalizer") - return ctrl.Result{}, err - } - - return ctrl.Result{}, nil -} - -// gatewayClassUsesConfig determines whether a given GatewayClass references a -// given GatewayClassConfig. Since these resources are scoped to the cluster, -// namespace is not considered. -func gatewayClassUsesConfig(gc gwv1beta1.GatewayClass, gcc *v1alpha1.GatewayClassConfig) bool { - parameterRef := gc.Spec.ParametersRef - return parameterRef != nil && - string(parameterRef.Group) == v1alpha1.ConsulHashicorpGroup && - parameterRef.Kind == v1alpha1.GatewayClassConfigKind && - parameterRef.Name == gcc.Name -} - -// GatewayClassConfigInUse determines whether any GatewayClass in the cluster -// references the provided GatewayClassConfig. -func gatewayClassConfigInUse(ctx context.Context, k8sClient client.Client, gcc *v1alpha1.GatewayClassConfig) (bool, error) { - list := &gwv1beta1.GatewayClassList{} - if err := k8sClient.List(ctx, list); err != nil { - return false, err - } - - for _, gc := range list.Items { - if gatewayClassUsesConfig(gc, gcc) { - return true, nil - } - } - - return false, nil -} - -func (r *GatewayClassConfigController) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&v1alpha1.GatewayClassConfig{}). - // Watch for changes to GatewayClass objects associated with this config for purposes of finalizer removal. - Watches(source.NewKindWithCache(&gwv1beta1.GatewayClass{}, mgr.GetCache()), r.transformGatewayClassToGatewayClassConfig(ctx)). - Complete(r) -} - -func (r *GatewayClassConfigController) transformGatewayClassToGatewayClassConfig(ctx context.Context) handler.EventHandler { - return handler.EnqueueRequestsFromMapFunc(func(o client.Object) []reconcile.Request { - gc := o.(*gwv1beta1.GatewayClass) - - pr := gc.Spec.ParametersRef - if pr != nil && pr.Kind == v1alpha1.GatewayClassConfigKind { - return []reconcile.Request{{ - NamespacedName: types.NamespacedName{ - Name: pr.Name, - }, - }} - } - - return nil - }) -} diff --git a/control-plane/api-gateway/controllers/gatewayclassconfig_controller_test.go b/control-plane/api-gateway/controllers/gatewayclassconfig_controller_test.go deleted file mode 100644 index 40023d498f..0000000000 --- a/control-plane/api-gateway/controllers/gatewayclassconfig_controller_test.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers - -import ( - "context" - "testing" - "time" - - logrtest "github.com/go-logr/logr/testr" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/stretchr/testify/require" - meta "k8s.io/apimachinery/pkg/apis/meta/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -func TestGatewayClassConfigReconcile(t *testing.T) { - t.Parallel() - deletionTimestamp := meta.Now() - cases := []struct { - name string - k8sObjects func() []runtime.Object - expErr string - requeue bool - requeueAfter time.Duration - }{ - { - name: "Successfully reconcile without any changes", - k8sObjects: func() []runtime.Object { - gatewayClassConfig := v1alpha1.GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: "consul-api-gateway", - }, - } - return []runtime.Object{&gatewayClassConfig} - }, - }, - { - name: "GatewayClassConfig Does Not Exist", - k8sObjects: func() []runtime.Object { - return []runtime.Object{} - }, - }, - { - name: "Remove not-in-use GatewayClassConfig", - k8sObjects: func() []runtime.Object { - gatewayClassConfig := v1alpha1.GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: "consul-api-gateway", - DeletionTimestamp: &deletionTimestamp, - }, - } - return []runtime.Object{&gatewayClassConfig} - }, - }, - { - name: "Try to remove in-use GatewayClassConfig", - k8sObjects: func() []runtime.Object { - gatewayClassConfig := v1alpha1.GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: "consul-api-gateway", - DeletionTimestamp: &deletionTimestamp, - }, - } - gatewayClass := gwv1beta1.GatewayClass{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "consul-api-gateway-class", - }, - Spec: gwv1beta1.GatewayClassSpec{ - ParametersRef: &gwv1beta1.ParametersReference{ - Group: gwv1beta1.Group(v1alpha1.ConsulHashicorpGroup), - Kind: v1alpha1.GatewayClassConfigKind, - Name: gatewayClassConfig.ObjectMeta.Name, - Namespace: nil, - }, - }, - Status: gwv1beta1.GatewayClassStatus{}, - } - return []runtime.Object{&gatewayClassConfig, &gatewayClass} - }, - requeueAfter: time.Second * 10, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - s := runtime.NewScheme() - require.NoError(t, clientgoscheme.AddToScheme(s)) - require.NoError(t, gwv1alpha2.Install(s)) - require.NoError(t, gwv1beta1.Install(s)) - require.NoError(t, v1alpha1.AddToScheme(s)) - - fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(tt.k8sObjects()...).Build() - - // Create the gateway class config controller. - gcc := &GatewayClassConfigController{ - Client: fakeClient, - Log: logrtest.New(t), - } - - resp, err := gcc.Reconcile(context.Background(), ctrl.Request{ - NamespacedName: types.NamespacedName{ - Namespace: "", - Name: "consul-api-gateway", - }, - }) - if tt.expErr != "" { - require.EqualError(t, err, tt.expErr) - } else { - require.NoError(t, err) - } - require.Equal(t, tt.requeue, resp.Requeue) - }) - } -} diff --git a/control-plane/api-gateway/controllers/index.go b/control-plane/api-gateway/controllers/index.go deleted file mode 100644 index 131ec383e7..0000000000 --- a/control-plane/api-gateway/controllers/index.go +++ /dev/null @@ -1,327 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers - -import ( - "context" - - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" -) - -const ( - // Naming convention: TARGET_REFERENCE. - GatewayClass_GatewayClassConfigIndex = "__gatewayclass_referencing_gatewayclassconfig" - GatewayClass_ControllerNameIndex = "__gatewayclass_controller_name" - Gateway_GatewayClassIndex = "__gateway_referencing_gatewayclass" - HTTPRoute_GatewayIndex = "__httproute_referencing_gateway" - HTTPRoute_ServiceIndex = "__httproute_referencing_service" - HTTPRoute_MeshServiceIndex = "__httproute_referencing_mesh_service" - TCPRoute_GatewayIndex = "__tcproute_referencing_gateway" - TCPRoute_ServiceIndex = "__tcproute_referencing_service" - TCPRoute_MeshServiceIndex = "__tcproute_referencing_mesh_service" - MeshService_PeerIndex = "__meshservice_referencing_peer" - Secret_GatewayIndex = "__secret_referencing_gateway" - HTTPRoute_RouteRetryFilterIndex = "__httproute_referencing_retryfilter" - HTTPRoute_RouteTimeoutFilterIndex = "__httproute_referencing_timeoutfilter" -) - -// RegisterFieldIndexes registers all of the field indexes for the API gateway controllers. -// These indexes are similar to indexes used in databases to speed up queries. -// They allow us to quickly find objects based on a field value. -func RegisterFieldIndexes(ctx context.Context, mgr ctrl.Manager) error { - for _, index := range indexes { - if err := mgr.GetFieldIndexer().IndexField(ctx, index.target, index.name, index.indexerFunc); err != nil { - return err - } - } - return nil -} - -type index struct { - name string - target client.Object - indexerFunc client.IndexerFunc -} - -var indexes = []index{ - { - name: GatewayClass_GatewayClassConfigIndex, - target: &gwv1beta1.GatewayClass{}, - indexerFunc: gatewayClassConfigForGatewayClass, - }, - { - name: GatewayClass_ControllerNameIndex, - target: &gwv1beta1.GatewayClass{}, - indexerFunc: gatewayClassControllerName, - }, - { - name: Gateway_GatewayClassIndex, - target: &gwv1beta1.Gateway{}, - indexerFunc: gatewayClassForGateway, - }, - { - name: Secret_GatewayIndex, - target: &gwv1beta1.Gateway{}, - indexerFunc: gatewayForSecret, - }, - { - name: HTTPRoute_GatewayIndex, - target: &gwv1beta1.HTTPRoute{}, - indexerFunc: gatewaysForHTTPRoute, - }, - { - name: HTTPRoute_ServiceIndex, - target: &gwv1beta1.HTTPRoute{}, - indexerFunc: servicesForHTTPRoute, - }, - { - name: HTTPRoute_MeshServiceIndex, - target: &gwv1beta1.HTTPRoute{}, - indexerFunc: meshServicesForHTTPRoute, - }, - { - name: TCPRoute_GatewayIndex, - target: &gwv1alpha2.TCPRoute{}, - indexerFunc: gatewaysForTCPRoute, - }, - { - name: TCPRoute_ServiceIndex, - target: &gwv1alpha2.TCPRoute{}, - indexerFunc: servicesForTCPRoute, - }, - { - name: TCPRoute_MeshServiceIndex, - target: &gwv1alpha2.TCPRoute{}, - indexerFunc: meshServicesForTCPRoute, - }, - { - name: MeshService_PeerIndex, - target: &v1alpha1.MeshService{}, - indexerFunc: peersForMeshService, - }, - { - name: HTTPRoute_RouteRetryFilterIndex, - target: &gwv1beta1.HTTPRoute{}, - indexerFunc: filtersForHTTPRoute, - }, - { - name: HTTPRoute_RouteTimeoutFilterIndex, - target: &gwv1beta1.HTTPRoute{}, - indexerFunc: filtersForHTTPRoute, - }, -} - -// gatewayClassConfigForGatewayClass creates an index of every GatewayClassConfig referenced by a GatewayClass. -func gatewayClassConfigForGatewayClass(o client.Object) []string { - gc := o.(*gwv1beta1.GatewayClass) - - pr := gc.Spec.ParametersRef - if pr != nil && pr.Kind == v1alpha1.GatewayClassConfigKind { - return []string{pr.Name} - } - - return []string{} -} - -func gatewayClassControllerName(o client.Object) []string { - gc := o.(*gwv1beta1.GatewayClass) - - if gc.Spec.ControllerName != "" { - return []string{string(gc.Spec.ControllerName)} - } - - return []string{} -} - -// gatewayClassForGateway creates an index of every GatewayClass referenced by a Gateway. -func gatewayClassForGateway(o client.Object) []string { - g := o.(*gwv1beta1.Gateway) - return []string{string(g.Spec.GatewayClassName)} -} - -func peersForMeshService(o client.Object) []string { - m := o.(*v1alpha1.MeshService) - if m.Spec.Peer != nil { - return []string{string(*m.Spec.Peer)} - } - return nil -} - -func gatewayForSecret(o client.Object) []string { - gateway := o.(*gwv1beta1.Gateway) - var secretReferences []string - for _, listener := range gateway.Spec.Listeners { - if listener.TLS == nil || *listener.TLS.Mode != gwv1beta1.TLSModeTerminate { - continue - } - for _, cert := range listener.TLS.CertificateRefs { - if common.NilOrEqual(cert.Group, "") && common.NilOrEqual(cert.Kind, "Secret") { - // If an explicit Secret namespace is not provided, use the Gateway namespace to lookup the provided Secret Name. - secretReferences = append(secretReferences, common.IndexedNamespacedNameWithDefault(cert.Name, cert.Namespace, gateway.Namespace).String()) - } - } - } - return secretReferences -} - -func gatewaysForHTTPRoute(o client.Object) []string { - route := o.(*gwv1beta1.HTTPRoute) - statusRefs := common.ConvertSliceFunc(route.Status.Parents, func(parentStatus gwv1beta1.RouteParentStatus) gwv1beta1.ParentReference { - return parentStatus.ParentRef - }) - return gatewaysForRoute(route.Namespace, route.Spec.ParentRefs, statusRefs) -} - -func gatewaysForTCPRoute(o client.Object) []string { - route := o.(*gwv1alpha2.TCPRoute) - statusRefs := common.ConvertSliceFunc(route.Status.Parents, func(parentStatus gwv1beta1.RouteParentStatus) gwv1beta1.ParentReference { - return parentStatus.ParentRef - }) - return gatewaysForRoute(route.Namespace, route.Spec.ParentRefs, statusRefs) -} - -func servicesForHTTPRoute(o client.Object) []string { - route := o.(*gwv1beta1.HTTPRoute) - refs := []string{} - for _, rule := range route.Spec.Rules { - BACKEND_LOOP: - for _, ref := range rule.BackendRefs { - if common.NilOrEqual(ref.Group, "") && common.NilOrEqual(ref.Kind, "Service") { - backendRef := common.IndexedNamespacedNameWithDefault(ref.Name, ref.Namespace, route.Namespace).String() - for _, member := range refs { - if member == backendRef { - continue BACKEND_LOOP - } - } - refs = append(refs, backendRef) - } - } - } - return refs -} - -func meshServicesForHTTPRoute(o client.Object) []string { - route := o.(*gwv1beta1.HTTPRoute) - refs := []string{} - for _, rule := range route.Spec.Rules { - BACKEND_LOOP: - for _, ref := range rule.BackendRefs { - if common.DerefEqual(ref.Group, v1alpha1.ConsulHashicorpGroup) && common.DerefEqual(ref.Kind, v1alpha1.MeshServiceKind) { - backendRef := common.IndexedNamespacedNameWithDefault(ref.Name, ref.Namespace, route.Namespace).String() - for _, member := range refs { - if member == backendRef { - continue BACKEND_LOOP - } - } - refs = append(refs, backendRef) - } - } - } - return refs -} - -func servicesForTCPRoute(o client.Object) []string { - route := o.(*gwv1alpha2.TCPRoute) - refs := []string{} - for _, rule := range route.Spec.Rules { - BACKEND_LOOP: - for _, ref := range rule.BackendRefs { - if common.NilOrEqual(ref.Group, "") && common.NilOrEqual(ref.Kind, common.KindService) { - backendRef := common.IndexedNamespacedNameWithDefault(ref.Name, ref.Namespace, route.Namespace).String() - for _, member := range refs { - if member == backendRef { - continue BACKEND_LOOP - } - } - refs = append(refs, backendRef) - } - } - } - return refs -} - -func meshServicesForTCPRoute(o client.Object) []string { - route := o.(*gwv1alpha2.TCPRoute) - refs := []string{} - for _, rule := range route.Spec.Rules { - BACKEND_LOOP: - for _, ref := range rule.BackendRefs { - if common.DerefEqual(ref.Group, v1alpha1.ConsulHashicorpGroup) && common.DerefEqual(ref.Kind, v1alpha1.MeshServiceKind) { - backendRef := common.IndexedNamespacedNameWithDefault(ref.Name, ref.Namespace, route.Namespace).String() - for _, member := range refs { - if member == backendRef { - continue BACKEND_LOOP - } - } - refs = append(refs, backendRef) - } - } - } - return refs -} - -func gatewaysForRoute(namespace string, refs []gwv1beta1.ParentReference, statusRefs []gwv1beta1.ParentReference) []string { - var references []string - for _, parent := range refs { - if common.NilOrEqual(parent.Group, common.BetaGroup) && common.NilOrEqual(parent.Kind, common.KindGateway) { - // If an explicit Gateway namespace is not provided, use the Route namespace to lookup the provided Gateway Namespace. - references = append(references, common.IndexedNamespacedNameWithDefault(parent.Name, parent.Namespace, namespace).String()) - } - } - for _, parent := range statusRefs { - if common.NilOrEqual(parent.Group, common.BetaGroup) && common.NilOrEqual(parent.Kind, common.KindGateway) { - // If an explicit Gateway namespace is not provided, use the Route namespace to lookup the provided Gateway Namespace. - references = append(references, common.IndexedNamespacedNameWithDefault(parent.Name, parent.Namespace, namespace).String()) - } - } - return references -} - -func filtersForHTTPRoute(o client.Object) []string { - route := o.(*gwv1beta1.HTTPRoute) - filters := []string{} - var nilString *string - - for _, rule := range route.Spec.Rules { - FILTERS_LOOP: - for _, filter := range rule.Filters { - if common.FilterIsExternalFilter(filter) { - //TODO this seems like its type agnostic, so this might just work without having to make - //multiple index functions per custom filter type? - - //index external filters - filter := common.IndexedNamespacedNameWithDefault(string(filter.ExtensionRef.Name), nilString, route.Namespace).String() - for _, member := range filters { - if member == filter { - continue FILTERS_LOOP - } - } - filters = append(filters, filter) - } - } - - //same thing but over the backend refs - BACKEND_LOOP: - for _, ref := range rule.BackendRefs { - for _, filter := range ref.Filters { - if common.FilterIsExternalFilter(filter) { - filter := common.IndexedNamespacedNameWithDefault(string(filter.ExtensionRef.Name), nilString, route.Namespace).String() - for _, member := range filters { - if member == filter { - continue BACKEND_LOOP - } - } - filters = append(filters, filter) - } - } - } - } - return filters -} diff --git a/control-plane/api-gateway/controllers/index_test.go b/control-plane/api-gateway/controllers/index_test.go deleted file mode 100644 index 5655a3c3da..0000000000 --- a/control-plane/api-gateway/controllers/index_test.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers - -import "sigs.k8s.io/controller-runtime/pkg/client/fake" - -func registerFieldIndexersForTest(clientBuilder *fake.ClientBuilder) *fake.ClientBuilder { - for _, index := range indexes { - clientBuilder = clientBuilder.WithIndex(index.target, index.name, index.indexerFunc) - } - return clientBuilder -} diff --git a/control-plane/api-gateway/gatekeeper/dataplane.go b/control-plane/api-gateway/gatekeeper/dataplane.go deleted file mode 100644 index f82e12e8a4..0000000000 --- a/control-plane/api-gateway/gatekeeper/dataplane.go +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package gatekeeper - -import ( - "fmt" - "strconv" - - corev1 "k8s.io/api/core/v1" - "k8s.io/utils/pointer" - - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" - "github.com/hashicorp/consul-k8s/control-plane/namespaces" - "k8s.io/apimachinery/pkg/util/intstr" -) - -const ( - allCapabilities = "all" - netBindCapability = "NET_BIND_SERVICE" - consulDataplaneDNSBindHost = "127.0.0.1" - consulDataplaneDNSBindPort = 8600 - defaultPrometheusScrapePath = "/metrics" - defaultEnvoyProxyConcurrency = 1 - volumeName = "consul-connect-inject-data" -) - -func consulDataplaneContainer(config common.HelmConfig, name, namespace string) (corev1.Container, error) { - // Extract the service account token's volume mount. - var ( - err error - bearerTokenFile string - ) - - if config.AuthMethod != "" { - bearerTokenFile = "/var/run/secrets/kubernetes.io/serviceaccount/token" - } - - args, err := getDataplaneArgs(namespace, config, bearerTokenFile, name) - if err != nil { - return corev1.Container{}, err - } - - probe := &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - HTTPGet: &corev1.HTTPGetAction{ - Port: intstr.FromInt(constants.ProxyDefaultHealthPort), - Path: "/ready", - }, - }, - InitialDelaySeconds: 1, - } - - container := corev1.Container{ - Name: name, - Image: config.ImageDataplane, - - // We need to set tmp dir to an ephemeral volume that we're mounting so that - // consul-dataplane can write files to it. Otherwise, it wouldn't be able to - // because we set file system to be read-only. - Env: []corev1.EnvVar{ - { - Name: "TMPDIR", - Value: "/consul/connect-inject", - }, - { - Name: "NODE_NAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "spec.nodeName", - }, - }, - }, - { - Name: "DP_SERVICE_NODE_NAME", - Value: "$(NODE_NAME)-virtual", - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: volumeName, - MountPath: "/consul/connect-inject", - }, - }, - Args: args, - ReadinessProbe: probe, - } - - // Configure the Readiness Address for the proxy's health check to be the Pod IP. - container.Env = append(container.Env, corev1.EnvVar{ - Name: "DP_ENVOY_READY_BIND_ADDRESS", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{FieldPath: "status.podIP"}, - }, - }) - // Configure the port on which the readiness probe will query the proxy for its health. - container.Ports = append(container.Ports, corev1.ContainerPort{ - Name: "proxy-health", - ContainerPort: int32(constants.ProxyDefaultHealthPort), - }) - - // If not running in an OpenShift environment, - // skip setting the security context and let OpenShift set it for us. - if !config.EnableOpenShift { - container.SecurityContext = &corev1.SecurityContext{ - ReadOnlyRootFilesystem: pointer.Bool(true), - // We have to run as root if we want to bind to any - // sort of privileged ports. The drop "all" is intended - // to drop any Linux capabilities you'd get as root - // other than NET_BIND_SERVICE. - RunAsUser: pointer.Int64(0), - Capabilities: &corev1.Capabilities{ - Add: []corev1.Capability{netBindCapability}, - Drop: []corev1.Capability{allCapabilities}, - }, - } - } - - return container, nil -} - -func getDataplaneArgs(namespace string, config common.HelmConfig, bearerTokenFile string, name string) ([]string, error) { - proxyIDFileName := "/consul/connect-inject/proxyid" - envoyConcurrency := defaultEnvoyProxyConcurrency - - args := []string{ - "-addresses", config.ConsulConfig.Address, - "-grpc-port=" + strconv.Itoa(config.ConsulConfig.GRPCPort), - "-proxy-service-id-path=" + proxyIDFileName, - "-log-level=" + config.LogLevel, - "-log-json=" + strconv.FormatBool(config.LogJSON), - "-envoy-concurrency=" + strconv.Itoa(envoyConcurrency), - } - - consulNamespace := namespaces.ConsulNamespace(namespace, config.EnableNamespaces, config.ConsulDestinationNamespace, config.EnableNamespaceMirroring, config.NamespaceMirroringPrefix) - - if config.AuthMethod != "" { - args = append(args, - "-credential-type=login", - "-login-auth-method="+config.AuthMethod, - "-login-bearer-token-path="+bearerTokenFile, - "-login-meta="+fmt.Sprintf("gateway=%s/%s", namespace, name), - ) - if config.ConsulPartition != "" { - args = append(args, "-login-partition="+config.ConsulPartition) - } - } - if config.EnableNamespaces { - args = append(args, "-service-namespace="+consulNamespace) - } - if config.ConsulPartition != "" { - args = append(args, "-service-partition="+config.ConsulPartition) - } - if config.TLSEnabled { - if config.ConsulTLSServerName != "" { - args = append(args, "-tls-server-name="+config.ConsulTLSServerName) - } - if config.ConsulCACert != "" { - args = append(args, "-ca-certs="+constants.ConsulCAFile) - } - } else { - args = append(args, "-tls-disabled") - } - - // Configure the readiness port on the dataplane sidecar if proxy health checks are enabled. - args = append(args, fmt.Sprintf("%s=%d", "-envoy-ready-bind-port", constants.ProxyDefaultHealthPort)) - - args = append(args, fmt.Sprintf("-envoy-admin-bind-port=%d", 19000)) - - // Set a default scrape path that can be overwritten by the annotation. - prometheusScrapePath := defaultPrometheusScrapePath - args = append(args, "-telemetry-prom-scrape-path="+prometheusScrapePath) - - return args, nil -} diff --git a/control-plane/api-gateway/gatekeeper/deployment.go b/control-plane/api-gateway/gatekeeper/deployment.go deleted file mode 100644 index 3590caaf52..0000000000 --- a/control-plane/api-gateway/gatekeeper/deployment.go +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package gatekeeper - -import ( - "context" - - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "k8s.io/apimachinery/pkg/types" - - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -const ( - defaultInstances int32 = 1 -) - -func (g *Gatekeeper) upsertDeployment(ctx context.Context, gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config common.HelmConfig) error { - // Get Deployment if it exists. - existingDeployment := &appsv1.Deployment{} - exists := false - - err := g.Client.Get(ctx, g.namespacedName(gateway), existingDeployment) - if err != nil && !k8serrors.IsNotFound(err) { - return err - } else if k8serrors.IsNotFound(err) { - exists = false - } else { - exists = true - } - - var currentReplicas *int32 - if exists { - currentReplicas = existingDeployment.Spec.Replicas - } - - deployment, err := g.deployment(gateway, gcc, config, currentReplicas) - if err != nil { - return err - } - - if exists { - g.Log.V(1).Info("Existing Gateway Deployment found.") - - // If the user has set the number of replicas, let's respect that. - deployment.Spec.Replicas = existingDeployment.Spec.Replicas - } - - mutated := deployment.DeepCopy() - mutator := newDeploymentMutator(deployment, mutated, gcc, gateway, g.Client.Scheme()) - - result, err := controllerutil.CreateOrUpdate(ctx, g.Client, mutated, mutator) - if err != nil { - return err - } - - switch result { - case controllerutil.OperationResultCreated: - g.Log.V(1).Info("Created Deployment") - case controllerutil.OperationResultUpdated: - g.Log.V(1).Info("Updated Deployment") - case controllerutil.OperationResultNone: - g.Log.V(1).Info("No change to deployment") - } - - return nil -} - -func (g *Gatekeeper) deleteDeployment(ctx context.Context, gwName types.NamespacedName) error { - err := g.Client.Delete(ctx, &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: gwName.Name, Namespace: gwName.Namespace}}) - if k8serrors.IsNotFound(err) { - return nil - } - - return err -} - -func (g *Gatekeeper) deployment(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config common.HelmConfig, currentReplicas *int32) (*appsv1.Deployment, error) { - initContainer, err := initContainer(config, gateway.Name, gateway.Namespace) - if err != nil { - return nil, err - } - - container, err := consulDataplaneContainer(config, gateway.Name, gateway.Namespace) - if err != nil { - return nil, err - } - - return &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: gateway.Name, - Namespace: gateway.Namespace, - Labels: common.LabelsForGateway(&gateway), - }, - Spec: appsv1.DeploymentSpec{ - Replicas: deploymentReplicas(gcc, currentReplicas), - Selector: &metav1.LabelSelector{ - MatchLabels: common.LabelsForGateway(&gateway), - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: common.LabelsForGateway(&gateway), - Annotations: map[string]string{ - "consul.hashicorp.com/connect-inject": "false", - }, - }, - Spec: corev1.PodSpec{ - Volumes: []corev1.Volume{ - { - Name: volumeName, - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{Medium: corev1.StorageMediumMemory}, - }, - }, - }, - InitContainers: []corev1.Container{ - initContainer, - }, - Containers: []corev1.Container{ - container, - }, - Affinity: &corev1.Affinity{ - PodAntiAffinity: &corev1.PodAntiAffinity{ - PreferredDuringSchedulingIgnoredDuringExecution: []corev1.WeightedPodAffinityTerm{ - { - Weight: 1, - PodAffinityTerm: corev1.PodAffinityTerm{ - LabelSelector: &metav1.LabelSelector{ - MatchLabels: common.LabelsForGateway(&gateway), - }, - TopologyKey: "kubernetes.io/hostname", - }, - }, - }, - }, - }, - NodeSelector: gcc.Spec.NodeSelector, - Tolerations: gcc.Spec.Tolerations, - ServiceAccountName: g.serviceAccountName(gateway, config), - }, - }, - }, - }, nil -} - -func mergeDeployments(gcc v1alpha1.GatewayClassConfig, a, b *appsv1.Deployment) *appsv1.Deployment { - if !compareDeployments(a, b) { - b.Spec.Template = a.Spec.Template - b.Spec.Replicas = deploymentReplicas(gcc, a.Spec.Replicas) - } - - return b -} - -func compareDeployments(a, b *appsv1.Deployment) bool { - // since K8s adds a bunch of defaults when we create a deployment, check that - // they don't differ by the things that we may actually change, namely container - // ports - if len(b.Spec.Template.Spec.Containers) != len(a.Spec.Template.Spec.Containers) { - return false - } - for i, container := range a.Spec.Template.Spec.Containers { - otherPorts := b.Spec.Template.Spec.Containers[i].Ports - if len(container.Ports) != len(otherPorts) { - return false - } - for j, port := range container.Ports { - otherPort := otherPorts[j] - if port.ContainerPort != otherPort.ContainerPort { - return false - } - if port.Protocol != otherPort.Protocol { - return false - } - } - } - - if b.Spec.Replicas == nil && a.Spec.Replicas == nil { - return true - } else if b.Spec.Replicas == nil { - return false - } else if a.Spec.Replicas == nil { - return false - } - - return *b.Spec.Replicas == *a.Spec.Replicas -} - -func newDeploymentMutator(deployment, mutated *appsv1.Deployment, gcc v1alpha1.GatewayClassConfig, gateway gwv1beta1.Gateway, scheme *runtime.Scheme) resourceMutator { - return func() error { - mutated = mergeDeployments(gcc, deployment, mutated) - return ctrl.SetControllerReference(&gateway, mutated, scheme) - } -} - -func deploymentReplicas(gcc v1alpha1.GatewayClassConfig, currentReplicas *int32) *int32 { - instanceValue := defaultInstances - - //if currentReplicas is not nil use current value when building deployment - if currentReplicas != nil { - instanceValue = *currentReplicas - } else if gcc.Spec.DeploymentSpec.DefaultInstances != nil { - // otherwise use the default value on the GatewayClassConfig if set - instanceValue = *gcc.Spec.DeploymentSpec.DefaultInstances - } - - if gcc.Spec.DeploymentSpec.MaxInstances != nil { - - //check if over maximum and lower to maximum - maxValue := *gcc.Spec.DeploymentSpec.MaxInstances - if instanceValue > maxValue { - instanceValue = maxValue - } - } - - if gcc.Spec.DeploymentSpec.MinInstances != nil { - //check if less than minimum and raise to minimum - minValue := *gcc.Spec.DeploymentSpec.MinInstances - if instanceValue < minValue { - instanceValue = minValue - } - - } - return &instanceValue -} diff --git a/control-plane/api-gateway/gatekeeper/gatekeeper.go b/control-plane/api-gateway/gatekeeper/gatekeeper.go deleted file mode 100644 index 6cb7170fc8..0000000000 --- a/control-plane/api-gateway/gatekeeper/gatekeeper.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package gatekeeper - -import ( - "context" - "fmt" - - "github.com/go-logr/logr" - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -// Gatekeeper is used to manage the lifecycle of Gateway deployments and services. -type Gatekeeper struct { - Log logr.Logger - Client client.Client -} - -// New creates a new Gatekeeper from the Config. -func New(log logr.Logger, client client.Client) *Gatekeeper { - return &Gatekeeper{ - Log: log, - Client: client, - } -} - -// Upsert creates or updates the resources for handling routing of network traffic. -// This is done in order based on dependencies between resources. -func (g *Gatekeeper) Upsert(ctx context.Context, gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config common.HelmConfig) error { - g.Log.V(1).Info(fmt.Sprintf("Upsert Gateway Deployment %s/%s", gateway.Namespace, gateway.Name)) - - if err := g.upsertRole(ctx, gateway, gcc, config); err != nil { - return err - } - - if err := g.upsertServiceAccount(ctx, gateway, config); err != nil { - return err - } - - if err := g.upsertRoleBinding(ctx, gateway, gcc, config); err != nil { - return err - } - - if err := g.upsertService(ctx, gateway, gcc, config); err != nil { - return err - } - - if err := g.upsertDeployment(ctx, gateway, gcc, config); err != nil { - return err - } - - return nil -} - -// Delete removes the resources for handling routing of network traffic. -// This is done in the reverse order of Upsert due to dependencies between resources. -func (g *Gatekeeper) Delete(ctx context.Context, gatewayName types.NamespacedName) error { - g.Log.V(1).Info(fmt.Sprintf("Delete Gateway Deployment %s/%s", gatewayName.Namespace, gatewayName.Name)) - - if err := g.deleteDeployment(ctx, gatewayName); err != nil { - return err - } - - if err := g.deleteService(ctx, gatewayName); err != nil { - return err - } - - if err := g.deleteRoleBinding(ctx, gatewayName); err != nil { - return err - } - - if err := g.deleteServiceAccount(ctx, gatewayName); err != nil { - return err - } - - if err := g.deleteRole(ctx, gatewayName); err != nil { - return err - } - - return nil -} - -// resourceMutator is passed to create or update functions to mutate Kubernetes resources. -type resourceMutator = func() error - -func (g *Gatekeeper) namespacedName(gateway gwv1beta1.Gateway) types.NamespacedName { - return types.NamespacedName{ - Namespace: gateway.Namespace, - Name: gateway.Name, - } -} - -func (g *Gatekeeper) serviceAccountName(gateway gwv1beta1.Gateway, config common.HelmConfig) string { - if config.AuthMethod == "" && !config.EnableOpenShift { - return "" - } - return gateway.Name -} diff --git a/control-plane/api-gateway/gatekeeper/gatekeeper_test.go b/control-plane/api-gateway/gatekeeper/gatekeeper_test.go deleted file mode 100644 index 562b139274..0000000000 --- a/control-plane/api-gateway/gatekeeper/gatekeeper_test.go +++ /dev/null @@ -1,1299 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package gatekeeper - -import ( - "context" - "fmt" - "testing" - - logrtest "github.com/go-logr/logr/testr" - common "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/stretchr/testify/require" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - rbac "k8s.io/api/rbac/v1" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/intstr" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -var ( - createdAtLabelKey = "gateway.consul.hashicorp.com/created" - createdAtLabelValue = "101010" - name = "test" - namespace = "default" - labels = map[string]string{ - "gateway.consul.hashicorp.com/name": name, - "gateway.consul.hashicorp.com/namespace": namespace, - createdAtLabelKey: createdAtLabelValue, - "gateway.consul.hashicorp.com/managed": "true", - } - listeners = []gwv1beta1.Listener{ - { - Name: "Listener 1", - Port: 8080, - Protocol: "TCP", - Hostname: common.PointerTo(gwv1beta1.Hostname("example.com")), - }, - { - Name: "Listener 2", - Port: 8081, - Protocol: "TCP", - }, - { - Name: "Listener 3", - Port: 8080, - Protocol: "TCP", - Hostname: common.PointerTo(gwv1beta1.Hostname("example.net")), - }, - } -) - -type testCase struct { - gateway gwv1beta1.Gateway - gatewayClassConfig v1alpha1.GatewayClassConfig - helmConfig common.HelmConfig - - initialResources resources - finalResources resources -} - -type resources struct { - deployments []*appsv1.Deployment - roles []*rbac.Role - roleBindings []*rbac.RoleBinding - services []*corev1.Service - serviceAccounts []*corev1.ServiceAccount -} - -func TestUpsert(t *testing.T) { - t.Parallel() - - cases := map[string]testCase{ - "create a new gateway deployment with only Deployment": { - gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: gwv1beta1.GatewaySpec{ - Listeners: listeners, - }, - }, - gatewayClassConfig: v1alpha1.GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: "consul-gatewayclassconfig", - }, - Spec: v1alpha1.GatewayClassConfigSpec{ - DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: common.PointerTo(int32(3)), - MaxInstances: common.PointerTo(int32(3)), - MinInstances: common.PointerTo(int32(1)), - }, - CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), - }, - }, - helmConfig: common.HelmConfig{}, - initialResources: resources{}, - finalResources: resources{ - deployments: []*appsv1.Deployment{ - configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), - }, - roles: []*rbac.Role{}, - services: []*corev1.Service{}, - serviceAccounts: []*corev1.ServiceAccount{}, - }, - }, - "create a new gateway with service and map privileged ports correctly": { - gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: gwv1beta1.GatewaySpec{ - Listeners: []gwv1beta1.Listener{ - { - Name: "Listener 1", - Port: 80, - Protocol: "TCP", - }, - { - Name: "Listener 2", - Port: 8080, - Protocol: "TCP", - }, - }, - }, - }, - gatewayClassConfig: v1alpha1.GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: "consul-gatewayclassconfig", - }, - Spec: v1alpha1.GatewayClassConfigSpec{ - DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: common.PointerTo(int32(3)), - MaxInstances: common.PointerTo(int32(3)), - MinInstances: common.PointerTo(int32(1)), - }, - CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), - MapPrivilegedContainerPorts: 2000, - }, - }, - helmConfig: common.HelmConfig{}, - initialResources: resources{}, - finalResources: resources{ - deployments: []*appsv1.Deployment{ - configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), - }, - roles: []*rbac.Role{}, - services: []*corev1.Service{ - configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ - { - Name: "Listener 1", - Protocol: "TCP", - Port: 80, - TargetPort: intstr.FromInt(2080), - }, - { - Name: "Listener 2", - Protocol: "TCP", - Port: 8080, - TargetPort: intstr.FromInt(8080), - }, - }, "1"), - }, - serviceAccounts: []*corev1.ServiceAccount{}, - }, - }, - "create a new gateway deployment with managed Service": { - gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: gwv1beta1.GatewaySpec{ - Listeners: listeners, - }, - }, - gatewayClassConfig: v1alpha1.GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: "consul-gatewayclassconfig", - }, - Spec: v1alpha1.GatewayClassConfigSpec{ - DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: common.PointerTo(int32(3)), - MaxInstances: common.PointerTo(int32(3)), - MinInstances: common.PointerTo(int32(1)), - }, - CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), - }, - }, - helmConfig: common.HelmConfig{}, - initialResources: resources{}, - finalResources: resources{ - deployments: []*appsv1.Deployment{ - configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), - }, - roles: []*rbac.Role{}, - services: []*corev1.Service{ - configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ - { - Name: "Listener 1", - Protocol: "TCP", - Port: 8080, - TargetPort: intstr.FromInt(8080), - }, - { - Name: "Listener 2", - Protocol: "TCP", - Port: 8081, - TargetPort: intstr.FromInt(8081), - }, - }, "1"), - }, - serviceAccounts: []*corev1.ServiceAccount{}, - }, - }, - "create a new gateway deployment with managed Service and ACLs": { - gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: gwv1beta1.GatewaySpec{ - Listeners: listeners, - }, - }, - gatewayClassConfig: v1alpha1.GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: "consul-gatewayclassconfig", - }, - Spec: v1alpha1.GatewayClassConfigSpec{ - DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: common.PointerTo(int32(3)), - MaxInstances: common.PointerTo(int32(3)), - MinInstances: common.PointerTo(int32(1)), - }, - CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), - }, - }, - helmConfig: common.HelmConfig{ - AuthMethod: "method", - }, - initialResources: resources{}, - finalResources: resources{ - deployments: []*appsv1.Deployment{ - configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), - }, - roles: []*rbac.Role{ - configureRole(name, namespace, labels, "1", false), - }, - roleBindings: []*rbac.RoleBinding{ - configureRoleBinding(name, namespace, labels, "1"), - }, - services: []*corev1.Service{ - configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ - { - Name: "Listener 1", - Protocol: "TCP", - Port: 8080, - TargetPort: intstr.FromInt(8080), - }, - { - Name: "Listener 2", - Protocol: "TCP", - Port: 8081, - TargetPort: intstr.FromInt(8081), - }, - }, "1"), - }, - serviceAccounts: []*corev1.ServiceAccount{ - configureServiceAccount(name, namespace, labels, "1"), - }, - }, - }, - "create a new gateway where the GatewayClassConfig has a default number of instances greater than the max on the GatewayClassConfig": { - gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: gwv1beta1.GatewaySpec{ - Listeners: listeners, - }, - }, - gatewayClassConfig: v1alpha1.GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: "consul-gatewayclassconfig", - }, - Spec: v1alpha1.GatewayClassConfigSpec{ - DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: common.PointerTo(int32(8)), - MaxInstances: common.PointerTo(int32(5)), - MinInstances: common.PointerTo(int32(2)), - }, - CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), - }, - }, - helmConfig: common.HelmConfig{}, - initialResources: resources{}, - finalResources: resources{ - deployments: []*appsv1.Deployment{ - configureDeployment(name, namespace, labels, 5, nil, nil, "", "1"), - }, - roles: []*rbac.Role{}, - services: []*corev1.Service{}, - serviceAccounts: []*corev1.ServiceAccount{}, - }, - }, - "create a new gateway where the GatewayClassConfig has a default number of instances lesser than the min on the GatewayClassConfig": { - gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: gwv1beta1.GatewaySpec{ - Listeners: listeners, - }, - }, - gatewayClassConfig: v1alpha1.GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: "consul-gatewayclassconfig", - }, - Spec: v1alpha1.GatewayClassConfigSpec{ - DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: common.PointerTo(int32(1)), - MaxInstances: common.PointerTo(int32(5)), - MinInstances: common.PointerTo(int32(2)), - }, - CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), - }, - }, - helmConfig: common.HelmConfig{}, - initialResources: resources{}, - finalResources: resources{ - deployments: []*appsv1.Deployment{ - configureDeployment(name, namespace, labels, 2, nil, nil, "", "1"), - }, - roles: []*rbac.Role{}, - services: []*corev1.Service{}, - serviceAccounts: []*corev1.ServiceAccount{}, - }, - }, - "update a gateway, adding a listener to a service": { - gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: gwv1beta1.GatewaySpec{ - Listeners: listeners, - }, - }, - gatewayClassConfig: v1alpha1.GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: "consul-gatewayclassconfig", - }, - Spec: v1alpha1.GatewayClassConfigSpec{ - DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: common.PointerTo(int32(3)), - MaxInstances: common.PointerTo(int32(3)), - MinInstances: common.PointerTo(int32(1)), - }, - CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), - }, - }, - helmConfig: common.HelmConfig{ - AuthMethod: "method", - }, - initialResources: resources{ - deployments: []*appsv1.Deployment{ - configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), - }, - roles: []*rbac.Role{ - configureRole(name, namespace, labels, "1", false), - }, - roleBindings: []*rbac.RoleBinding{ - configureRoleBinding(name, namespace, labels, "1"), - }, - services: []*corev1.Service{ - configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ - { - Name: "Listener 1", - Protocol: "TCP", - Port: 8080, - }, - }, "1"), - }, - serviceAccounts: []*corev1.ServiceAccount{ - configureServiceAccount(name, namespace, labels, "1"), - }, - }, - finalResources: resources{ - deployments: []*appsv1.Deployment{ - configureDeployment(name, namespace, labels, 3, nil, nil, "", "2"), - }, - roles: []*rbac.Role{ - configureRole(name, namespace, labels, "1", false), - }, - roleBindings: []*rbac.RoleBinding{ - configureRoleBinding(name, namespace, labels, "1"), - }, - services: []*corev1.Service{ - configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ - { - Name: "Listener 1", - Protocol: "TCP", - Port: 8080, - TargetPort: intstr.FromInt(8080), - }, - { - Name: "Listener 2", - Protocol: "TCP", - Port: 8081, - TargetPort: intstr.FromInt(8081), - }, - }, "2"), - }, - serviceAccounts: []*corev1.ServiceAccount{ - configureServiceAccount(name, namespace, labels, "1"), - }, - }, - }, - "update a gateway, removing a listener from a service": { - gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: gwv1beta1.GatewaySpec{ - Listeners: []gwv1beta1.Listener{ - listeners[0], - }, - }, - }, - gatewayClassConfig: v1alpha1.GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: "consul-gatewayclassconfig", - }, - Spec: v1alpha1.GatewayClassConfigSpec{ - DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: common.PointerTo(int32(3)), - MaxInstances: common.PointerTo(int32(3)), - MinInstances: common.PointerTo(int32(1)), - }, - CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), - }, - }, - helmConfig: common.HelmConfig{ - AuthMethod: "method", - }, - initialResources: resources{ - deployments: []*appsv1.Deployment{ - configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), - }, - roles: []*rbac.Role{ - configureRole(name, namespace, labels, "1", false), - }, - roleBindings: []*rbac.RoleBinding{ - configureRoleBinding(name, namespace, labels, "1"), - }, - services: []*corev1.Service{ - configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ - { - Name: "Listener 1", - Protocol: "TCP", - Port: 8080, - }, - { - Name: "Listener 2", - Protocol: "TCP", - Port: 8081, - }, - }, "1"), - }, - serviceAccounts: []*corev1.ServiceAccount{ - configureServiceAccount(name, namespace, labels, "1"), - }, - }, - finalResources: resources{ - deployments: []*appsv1.Deployment{ - configureDeployment(name, namespace, labels, 3, nil, nil, "", "2"), - }, - roles: []*rbac.Role{ - configureRole(name, namespace, labels, "1", false), - }, - roleBindings: []*rbac.RoleBinding{ - configureRoleBinding(name, namespace, labels, "1"), - }, - services: []*corev1.Service{ - configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ - { - Name: "Listener 1", - Protocol: "TCP", - Port: 8080, - TargetPort: intstr.FromInt(8080), - }, - }, "2"), - }, - serviceAccounts: []*corev1.ServiceAccount{ - configureServiceAccount(name, namespace, labels, "1"), - }, - }, - }, - "updating a gateway deployment respects the number of replicas a user has set": { - gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: gwv1beta1.GatewaySpec{ - Listeners: listeners, - }, - }, - gatewayClassConfig: v1alpha1.GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: "consul-gatewayclassconfig", - }, - Spec: v1alpha1.GatewayClassConfigSpec{ - DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: common.PointerTo(int32(5)), - MaxInstances: common.PointerTo(int32(7)), - MinInstances: common.PointerTo(int32(1)), - }, - CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), - }, - }, - helmConfig: common.HelmConfig{}, - initialResources: resources{ - deployments: []*appsv1.Deployment{ - configureDeployment(name, namespace, labels, 5, nil, nil, "", "1"), - }, - }, - finalResources: resources{ - deployments: []*appsv1.Deployment{ - configureDeployment(name, namespace, labels, 5, nil, nil, "", "1"), - }, - roles: []*rbac.Role{}, - services: []*corev1.Service{}, - serviceAccounts: []*corev1.ServiceAccount{}, - }, - }, - "update a gateway deployment by scaling it when no min or max number of instances is defined on the GatewayClassConfig": { - gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: gwv1beta1.GatewaySpec{ - Listeners: listeners, - }, - }, - gatewayClassConfig: v1alpha1.GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: "consul-gatewayclassconfig", - }, - Spec: v1alpha1.GatewayClassConfigSpec{ - DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: common.PointerTo(int32(3)), - MaxInstances: nil, - MinInstances: nil, - }, - CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), - }, - }, - helmConfig: common.HelmConfig{}, - initialResources: resources{ - deployments: []*appsv1.Deployment{ - configureDeployment(name, namespace, labels, 8, nil, nil, "", "1"), - }, - }, - finalResources: resources{ - deployments: []*appsv1.Deployment{ - configureDeployment(name, namespace, labels, 8, nil, nil, "", "1"), - }, - roles: []*rbac.Role{}, - services: []*corev1.Service{}, - serviceAccounts: []*corev1.ServiceAccount{}, - }, - }, - "update a gateway deployment by scaling it lower than the min number of instances on the GatewayClassConfig": { - gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: gwv1beta1.GatewaySpec{ - Listeners: listeners, - }, - }, - gatewayClassConfig: v1alpha1.GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: "consul-gatewayclassconfig", - }, - Spec: v1alpha1.GatewayClassConfigSpec{ - DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: common.PointerTo(int32(3)), - MaxInstances: common.PointerTo(int32(5)), - MinInstances: common.PointerTo(int32(2)), - }, - CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), - }, - }, - helmConfig: common.HelmConfig{}, - initialResources: resources{ - deployments: []*appsv1.Deployment{ - configureDeployment(name, namespace, labels, 1, nil, nil, "", "1"), - }, - }, - finalResources: resources{ - deployments: []*appsv1.Deployment{ - configureDeployment(name, namespace, labels, 2, nil, nil, "", "1"), - }, - roles: []*rbac.Role{}, - services: []*corev1.Service{}, - serviceAccounts: []*corev1.ServiceAccount{}, - }, - }, - "update a gateway deployment by scaling it higher than the max number of instances on the GatewayClassConfig": { - gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: gwv1beta1.GatewaySpec{ - Listeners: listeners, - }, - }, - gatewayClassConfig: v1alpha1.GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: "consul-gatewayclassconfig", - }, - Spec: v1alpha1.GatewayClassConfigSpec{ - DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: common.PointerTo(int32(3)), - MaxInstances: common.PointerTo(int32(5)), - MinInstances: common.PointerTo(int32(2)), - }, - CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), - }, - }, - helmConfig: common.HelmConfig{}, - initialResources: resources{ - deployments: []*appsv1.Deployment{ - configureDeployment(name, namespace, labels, 10, nil, nil, "", "1"), - }, - }, - finalResources: resources{ - deployments: []*appsv1.Deployment{ - configureDeployment(name, namespace, labels, 5, nil, nil, "", "1"), - }, - roles: []*rbac.Role{}, - services: []*corev1.Service{}, - serviceAccounts: []*corev1.ServiceAccount{}, - }, - }, - "create a new gateway with openshift enabled": { - gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: gwv1beta1.GatewaySpec{ - Listeners: listeners, - }, - }, - gatewayClassConfig: v1alpha1.GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: "consul-gatewayclassconfig", - }, - Spec: v1alpha1.GatewayClassConfigSpec{ - DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: common.PointerTo(int32(3)), - MaxInstances: common.PointerTo(int32(3)), - MinInstances: common.PointerTo(int32(1)), - }, - CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - OpenshiftSCCName: "test-api-gateway", - }, - }, - helmConfig: common.HelmConfig{ - EnableOpenShift: true, - }, - initialResources: resources{}, - finalResources: resources{ - deployments: []*appsv1.Deployment{ - configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), - }, - roles: []*rbac.Role{ - configureRole(name, namespace, labels, "1", true), - }, - roleBindings: []*rbac.RoleBinding{ - configureRoleBinding(name, namespace, labels, "1"), - }, - services: []*corev1.Service{}, - serviceAccounts: []*corev1.ServiceAccount{ - configureServiceAccount(name, namespace, labels, "1"), - }, - }, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - s := runtime.NewScheme() - require.NoError(t, gwv1beta1.Install(s)) - require.NoError(t, v1alpha1.AddToScheme(s)) - require.NoError(t, rbac.AddToScheme(s)) - require.NoError(t, corev1.AddToScheme(s)) - require.NoError(t, appsv1.AddToScheme(s)) - - log := logrtest.New(t) - - objs := append(joinResources(tc.initialResources), &tc.gateway, &tc.gatewayClassConfig) - client := fake.NewClientBuilder().WithScheme(s).WithObjects(objs...).Build() - - gatekeeper := New(log, client) - - err := gatekeeper.Upsert(context.Background(), tc.gateway, tc.gatewayClassConfig, tc.helmConfig) - require.NoError(t, err) - require.NoError(t, validateResourcesExist(t, client, tc.finalResources)) - }) - } -} - -func TestDelete(t *testing.T) { - t.Parallel() - - cases := map[string]testCase{ - "delete a gateway deployment with only Deployment": { - gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: gwv1beta1.GatewaySpec{ - Listeners: listeners, - }, - }, - gatewayClassConfig: v1alpha1.GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: "consul-gatewayclassconfig", - }, - Spec: v1alpha1.GatewayClassConfigSpec{ - DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: common.PointerTo(int32(3)), - MaxInstances: common.PointerTo(int32(3)), - MinInstances: common.PointerTo(int32(1)), - }, - CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), - }, - }, - helmConfig: common.HelmConfig{}, - initialResources: resources{ - deployments: []*appsv1.Deployment{ - configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), - }, - }, - finalResources: resources{ - deployments: []*appsv1.Deployment{}, - roles: []*rbac.Role{}, - services: []*corev1.Service{}, - serviceAccounts: []*corev1.ServiceAccount{}, - }, - }, - "delete a gateway deployment with a managed Service": { - gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: gwv1beta1.GatewaySpec{ - Listeners: listeners, - }, - }, - gatewayClassConfig: v1alpha1.GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: "consul-gatewayclassconfig", - }, - Spec: v1alpha1.GatewayClassConfigSpec{ - DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: common.PointerTo(int32(3)), - MaxInstances: common.PointerTo(int32(3)), - MinInstances: common.PointerTo(int32(1)), - }, - CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), - }, - }, - helmConfig: common.HelmConfig{}, - initialResources: resources{ - deployments: []*appsv1.Deployment{ - configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), - }, - roles: []*rbac.Role{}, - services: []*corev1.Service{ - configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ - { - Name: "Listener 1", - Protocol: "TCP", - Port: 8080, - }, - { - Name: "Listener 2", - Protocol: "TCP", - Port: 8081, - }, - }, "1"), - }, - serviceAccounts: []*corev1.ServiceAccount{}, - }, - finalResources: resources{ - deployments: []*appsv1.Deployment{}, - roles: []*rbac.Role{}, - services: []*corev1.Service{}, - serviceAccounts: []*corev1.ServiceAccount{}, - }, - }, - "delete a gateway deployment with managed Service and ACLs": { - gateway: gwv1beta1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: gwv1beta1.GatewaySpec{ - Listeners: listeners, - }, - }, - gatewayClassConfig: v1alpha1.GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: "consul-gatewayclassconfig", - }, - Spec: v1alpha1.GatewayClassConfigSpec{ - DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: common.PointerTo(int32(3)), - MaxInstances: common.PointerTo(int32(3)), - MinInstances: common.PointerTo(int32(1)), - }, - CopyAnnotations: v1alpha1.CopyAnnotationsSpec{}, - ServiceType: (*corev1.ServiceType)(common.PointerTo("NodePort")), - }, - }, - helmConfig: common.HelmConfig{ - AuthMethod: "method", - }, - initialResources: resources{ - deployments: []*appsv1.Deployment{ - configureDeployment(name, namespace, labels, 3, nil, nil, "", "1"), - }, - roles: []*rbac.Role{ - configureRole(name, namespace, labels, "1", false), - }, - roleBindings: []*rbac.RoleBinding{ - configureRoleBinding(name, namespace, labels, "1"), - }, - services: []*corev1.Service{ - configureService(name, namespace, labels, nil, (corev1.ServiceType)("NodePort"), []corev1.ServicePort{ - { - Name: "Listener 1", - Protocol: "TCP", - Port: 8080, - }, - { - Name: "Listener 2", - Protocol: "TCP", - Port: 8081, - }, - }, "1"), - }, - serviceAccounts: []*corev1.ServiceAccount{ - configureServiceAccount(name, namespace, labels, "1"), - }, - }, - finalResources: resources{ - deployments: []*appsv1.Deployment{}, - roles: []*rbac.Role{}, - services: []*corev1.Service{}, - serviceAccounts: []*corev1.ServiceAccount{}, - }, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - s := runtime.NewScheme() - require.NoError(t, gwv1beta1.Install(s)) - require.NoError(t, v1alpha1.AddToScheme(s)) - require.NoError(t, rbac.AddToScheme(s)) - require.NoError(t, corev1.AddToScheme(s)) - require.NoError(t, appsv1.AddToScheme(s)) - - log := logrtest.New(t) - - objs := append(joinResources(tc.initialResources), &tc.gateway, &tc.gatewayClassConfig) - client := fake.NewClientBuilder().WithScheme(s).WithObjects(objs...).Build() - - gatekeeper := New(log, client) - - err := gatekeeper.Delete(context.Background(), types.NamespacedName{ - Namespace: tc.gateway.Namespace, - Name: tc.gateway.Name, - }) - require.NoError(t, err) - require.NoError(t, validateResourcesExist(t, client, tc.finalResources)) - require.NoError(t, validateResourcesAreDeleted(t, client, tc.initialResources)) - }) - } -} - -func joinResources(resources resources) (objs []client.Object) { - for _, deployment := range resources.deployments { - objs = append(objs, deployment) - } - - for _, role := range resources.roles { - objs = append(objs, role) - } - - for _, roleBinding := range resources.roleBindings { - objs = append(objs, roleBinding) - } - - for _, service := range resources.services { - objs = append(objs, service) - } - - for _, serviceAccount := range resources.serviceAccounts { - objs = append(objs, serviceAccount) - } - - return objs -} - -func validateResourcesExist(t *testing.T, client client.Client, resources resources) error { - t.Helper() - - for _, expected := range resources.deployments { - actual := &appsv1.Deployment{} - err := client.Get(context.Background(), types.NamespacedName{ - Name: expected.Name, - Namespace: expected.Namespace, - }, actual) - if err != nil { - return err - } - - // Patch the createdAt label - actual.Labels[createdAtLabelKey] = createdAtLabelValue - actual.Spec.Selector.MatchLabels[createdAtLabelKey] = createdAtLabelValue - actual.Spec.Template.ObjectMeta.Labels[createdAtLabelKey] = createdAtLabelValue - - require.Equal(t, expected.Name, actual.Name) - require.Equal(t, expected.Namespace, actual.Namespace) - require.Equal(t, expected.APIVersion, actual.APIVersion) - require.Equal(t, expected.Labels, actual.Labels) - if expected.Spec.Replicas != nil { - require.NotNil(t, actual.Spec.Replicas) - require.EqualValues(t, *expected.Spec.Replicas, *actual.Spec.Replicas) - } - } - - for _, expected := range resources.roles { - actual := &rbac.Role{} - err := client.Get(context.Background(), types.NamespacedName{ - Name: expected.Name, - Namespace: expected.Namespace, - }, actual) - if err != nil { - return err - } - - // Patch the createdAt label - actual.Labels[createdAtLabelKey] = createdAtLabelValue - - require.Equal(t, expected, actual) - } - - for _, expected := range resources.roleBindings { - actual := &rbac.RoleBinding{} - err := client.Get(context.Background(), types.NamespacedName{ - Name: expected.Name, - Namespace: expected.Namespace, - }, actual) - if err != nil { - return err - } - - // Patch the createdAt label - actual.Labels[createdAtLabelKey] = createdAtLabelValue - - require.Equal(t, expected, actual) - } - - for _, expected := range resources.services { - actual := &corev1.Service{} - err := client.Get(context.Background(), types.NamespacedName{ - Name: expected.Name, - Namespace: expected.Namespace, - }, actual) - if err != nil { - return err - } - - // Patch the createdAt label - actual.Labels[createdAtLabelKey] = createdAtLabelValue - actual.Spec.Selector[createdAtLabelKey] = createdAtLabelValue - - require.Equal(t, expected, actual) - } - - for _, expected := range resources.serviceAccounts { - actual := &corev1.ServiceAccount{} - err := client.Get(context.Background(), types.NamespacedName{ - Name: expected.Name, - Namespace: expected.Namespace, - }, actual) - if err != nil { - return err - } - - // Patch the createdAt label - actual.Labels[createdAtLabelKey] = createdAtLabelValue - - require.Equal(t, expected, actual) - } - - return nil -} - -func validateResourcesAreDeleted(t *testing.T, k8sClient client.Client, resources resources) error { - t.Helper() - - for _, expected := range resources.deployments { - actual := &appsv1.Deployment{} - err := k8sClient.Get(context.Background(), types.NamespacedName{ - Name: expected.Name, - Namespace: expected.Namespace, - }, actual) - if !k8serrors.IsNotFound(err) { - return fmt.Errorf("expected deployment %s to be deleted", expected.Name) - } - require.Error(t, err) - } - - for _, expected := range resources.roles { - actual := &rbac.Role{} - err := k8sClient.Get(context.Background(), types.NamespacedName{ - Name: expected.Name, - Namespace: expected.Namespace, - }, actual) - if !k8serrors.IsNotFound(err) { - return fmt.Errorf("expected role %s to be deleted", expected.Name) - } - require.Error(t, err) - } - - for _, expected := range resources.roleBindings { - actual := &rbac.RoleBinding{} - err := k8sClient.Get(context.Background(), types.NamespacedName{ - Name: expected.Name, - Namespace: expected.Namespace, - }, actual) - if !k8serrors.IsNotFound(err) { - return fmt.Errorf("expected rolebinding %s to be deleted", expected.Name) - } - require.Error(t, err) - } - - for _, expected := range resources.services { - actual := &corev1.Service{} - err := k8sClient.Get(context.Background(), types.NamespacedName{ - Name: expected.Name, - Namespace: expected.Namespace, - }, actual) - if !k8serrors.IsNotFound(err) { - return fmt.Errorf("expected service %s to be deleted", expected.Name) - } - require.Error(t, err) - } - - for _, expected := range resources.serviceAccounts { - actual := &corev1.ServiceAccount{} - err := k8sClient.Get(context.Background(), types.NamespacedName{ - Name: expected.Name, - Namespace: expected.Namespace, - }, actual) - if !k8serrors.IsNotFound(err) { - return fmt.Errorf("expected service account %s to be deleted", expected.Name) - } - require.Error(t, err) - } - - return nil -} - -func configureDeployment(name, namespace string, labels map[string]string, replicas int32, nodeSelector map[string]string, tolerations []corev1.Toleration, serviceAccoutName, resourceVersion string) *appsv1.Deployment { - return &appsv1.Deployment{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "apps/v1", - Kind: "Deployment", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - Labels: labels, - ResourceVersion: resourceVersion, - OwnerReferences: []metav1.OwnerReference{ - { - APIVersion: "gateway.networking.k8s.io/v1beta1", - Kind: "Gateway", - Name: name, - Controller: common.PointerTo(true), - BlockOwnerDeletion: common.PointerTo(true), - }, - }, - }, - Spec: appsv1.DeploymentSpec{ - Replicas: &replicas, - Selector: &metav1.LabelSelector{ - MatchLabels: labels, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: labels, - Annotations: map[string]string{ - "consul.hashicorp.com/connect-inject": "false", - }, - }, - Spec: corev1.PodSpec{ - Affinity: &corev1.Affinity{ - PodAntiAffinity: &corev1.PodAntiAffinity{ - PreferredDuringSchedulingIgnoredDuringExecution: []corev1.WeightedPodAffinityTerm{ - { - Weight: 1, - PodAffinityTerm: corev1.PodAffinityTerm{ - LabelSelector: &metav1.LabelSelector{ - MatchLabels: labels, - }, - TopologyKey: "kubernetes.io/hostname", - }, - }, - }, - }, - }, - NodeSelector: nodeSelector, - Tolerations: tolerations, - ServiceAccountName: serviceAccoutName, - }, - }, - }, - } -} - -func configureRole(name, namespace string, labels map[string]string, resourceVersion string, openshiftEnabled bool) *rbac.Role { - rules := []rbac.PolicyRule{} - - if openshiftEnabled { - rules = []rbac.PolicyRule{ - { - APIGroups: []string{"security.openshift.io"}, - Resources: []string{"securitycontextconstraints"}, - ResourceNames: []string{name + "-api-gateway"}, - Verbs: []string{"use"}, - }, - } - } - return &rbac.Role{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "rbac.authorization.k8s.io/v1", - Kind: "Role", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - Labels: labels, - ResourceVersion: resourceVersion, - OwnerReferences: []metav1.OwnerReference{ - { - APIVersion: "gateway.networking.k8s.io/v1beta1", - Kind: "Gateway", - Name: name, - Controller: common.PointerTo(true), - BlockOwnerDeletion: common.PointerTo(true), - }, - }, - }, - Rules: rules, - } -} - -func configureRoleBinding(name, namespace string, labels map[string]string, resourceVersion string) *rbac.RoleBinding { - return &rbac.RoleBinding{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "rbac.authorization.k8s.io/v1", - Kind: "RoleBinding", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - Labels: labels, - ResourceVersion: resourceVersion, - OwnerReferences: []metav1.OwnerReference{ - { - APIVersion: "gateway.networking.k8s.io/v1beta1", - Kind: "Gateway", - Name: name, - Controller: common.PointerTo(true), - BlockOwnerDeletion: common.PointerTo(true), - }, - }, - }, - RoleRef: rbac.RoleRef{ - APIGroup: "rbac.authorization.k8s.io", - Kind: "Role", - Name: name, - }, - Subjects: []rbac.Subject{ - { - Kind: "ServiceAccount", - Name: name, - Namespace: namespace, - }, - }, - } -} - -func configureService(name, namespace string, labels, annotations map[string]string, serviceType corev1.ServiceType, ports []corev1.ServicePort, resourceVersion string) *corev1.Service { - return &corev1.Service{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "Service", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - Labels: labels, - Annotations: annotations, - ResourceVersion: resourceVersion, - OwnerReferences: []metav1.OwnerReference{ - { - APIVersion: "gateway.networking.k8s.io/v1beta1", - Kind: "Gateway", - Name: name, - Controller: common.PointerTo(true), - BlockOwnerDeletion: common.PointerTo(true), - }, - }, - }, - Spec: corev1.ServiceSpec{ - Selector: labels, - Type: serviceType, - Ports: ports, - }, - } -} - -func configureServiceAccount(name, namespace string, labels map[string]string, resourceVersion string) *corev1.ServiceAccount { - return &corev1.ServiceAccount{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "ServiceAccount", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - Labels: labels, - ResourceVersion: resourceVersion, - OwnerReferences: []metav1.OwnerReference{ - { - APIVersion: "gateway.networking.k8s.io/v1beta1", - Kind: "Gateway", - Name: name, - Controller: common.PointerTo(true), - BlockOwnerDeletion: common.PointerTo(true), - }, - }, - }, - } -} diff --git a/control-plane/api-gateway/gatekeeper/init.go b/control-plane/api-gateway/gatekeeper/init.go deleted file mode 100644 index f3d4ad1f95..0000000000 --- a/control-plane/api-gateway/gatekeeper/init.go +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package gatekeeper - -import ( - "bytes" - "strconv" - "strings" - "text/template" - - corev1 "k8s.io/api/core/v1" - - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/namespaces" - "k8s.io/utils/pointer" -) - -const ( - injectInitContainerName = "consul-connect-inject-init" - initContainersUserAndGroupID = 5996 -) - -type initContainerCommandData struct { - ServiceName string - ServiceAccountName string - AuthMethod string - - // Log settings for the connect-init command. - LogLevel string - LogJSON bool -} - -// containerInit returns the init container spec for connect-init that polls for the service and the connect proxy service to be registered -// so that it can save the proxy service id to the shared volume and boostrap Envoy with the proxy-id. -func initContainer(config common.HelmConfig, name, namespace string) (corev1.Container, error) { - data := initContainerCommandData{ - AuthMethod: config.AuthMethod, - LogLevel: config.LogLevel, - LogJSON: config.LogJSON, - ServiceName: name, - ServiceAccountName: name, - } - - // Create expected volume mounts - volMounts := []corev1.VolumeMount{ - { - Name: volumeName, - MountPath: "/consul/connect-inject", - }, - } - - var bearerTokenFile string - if config.AuthMethod != "" { - bearerTokenFile = "/var/run/secrets/kubernetes.io/serviceaccount/token" - } - - // Render the command - var buf bytes.Buffer - tpl := template.Must(template.New("root").Parse(strings.TrimSpace(initContainerCommandTpl))) - - if err := tpl.Execute(&buf, &data); err != nil { - return corev1.Container{}, err - } - - consulNamespace := namespaces.ConsulNamespace(namespace, config.EnableNamespaces, config.ConsulDestinationNamespace, config.EnableNamespaceMirroring, config.NamespaceMirroringPrefix) - - initContainerName := injectInitContainerName - container := corev1.Container{ - Name: initContainerName, - Image: config.ImageConsulK8S, - - Env: []corev1.EnvVar{ - { - Name: "POD_NAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{FieldPath: "metadata.name"}, - }, - }, - { - Name: "POD_NAMESPACE", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{FieldPath: "metadata.namespace"}, - }, - }, - { - Name: "NODE_NAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "spec.nodeName", - }, - }, - }, - { - Name: "CONSUL_ADDRESSES", - Value: config.ConsulConfig.Address, - }, - { - Name: "CONSUL_GRPC_PORT", - Value: strconv.Itoa(config.ConsulConfig.GRPCPort), - }, - { - Name: "CONSUL_HTTP_PORT", - Value: strconv.Itoa(config.ConsulConfig.HTTPPort), - }, - { - Name: "CONSUL_API_TIMEOUT", - Value: config.ConsulConfig.APITimeout.String(), - }, - { - Name: "CONSUL_NODE_NAME", - Value: "$(NODE_NAME)-virtual", - }, - }, - VolumeMounts: volMounts, - Command: []string{"/bin/sh", "-ec", buf.String()}, - } - - if config.TLSEnabled { - container.Env = append(container.Env, - corev1.EnvVar{ - Name: "CONSUL_USE_TLS", - Value: "true", - }, - corev1.EnvVar{ - Name: "CONSUL_CACERT_PEM", - Value: config.ConsulCACert, - }, - corev1.EnvVar{ - Name: "CONSUL_TLS_SERVER_NAME", - Value: config.ConsulTLSServerName, - }) - } - - if config.AuthMethod != "" { - container.Env = append(container.Env, - corev1.EnvVar{ - Name: "CONSUL_LOGIN_AUTH_METHOD", - Value: config.AuthMethod, - }, - corev1.EnvVar{ - Name: "CONSUL_LOGIN_BEARER_TOKEN_FILE", - Value: bearerTokenFile, - }, - corev1.EnvVar{ - Name: "CONSUL_LOGIN_META", - Value: "pod=$(POD_NAMESPACE)/$(POD_NAME)", - }) - - if config.ConsulPartition != "" { - container.Env = append(container.Env, corev1.EnvVar{ - Name: "CONSUL_LOGIN_PARTITION", - Value: config.ConsulPartition, - }) - } - } - container.Env = append(container.Env, - corev1.EnvVar{ - Name: "CONSUL_NAMESPACE", - Value: consulNamespace, - }) - - if config.ConsulPartition != "" { - container.Env = append(container.Env, - corev1.EnvVar{ - Name: "CONSUL_PARTITION", - Value: config.ConsulPartition, - }) - } - - // Openshift Assigns the security context for us, do not enable if it is enabled. - if !config.EnableOpenShift { - container.SecurityContext = &corev1.SecurityContext{ - RunAsUser: pointer.Int64(initContainersUserAndGroupID), - RunAsGroup: pointer.Int64(initContainersUserAndGroupID), - RunAsNonRoot: pointer.Bool(true), - Privileged: pointer.Bool(false), - Capabilities: &corev1.Capabilities{ - Drop: []corev1.Capability{"ALL"}, - }, - } - } - - return container, nil -} - -// initContainerCommandTpl is the template for the command executed by -// the init container. -const initContainerCommandTpl = ` -consul-k8s-control-plane connect-init -pod-name=${POD_NAME} -pod-namespace=${POD_NAMESPACE} \ - -gateway-kind="api-gateway" \ - -log-json={{ .LogJSON }} \ - {{- if .AuthMethod }} - -service-account-name="{{ .ServiceAccountName }}" \ - {{- end }} - -service-name="{{ .ServiceName }}" -` diff --git a/control-plane/api-gateway/gatekeeper/role.go b/control-plane/api-gateway/gatekeeper/role.go deleted file mode 100644 index 705e9bffff..0000000000 --- a/control-plane/api-gateway/gatekeeper/role.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package gatekeeper - -import ( - "context" - "errors" - - "k8s.io/apimachinery/pkg/types" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - rbac "k8s.io/api/rbac/v1" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - ctrl "sigs.k8s.io/controller-runtime" -) - -func (g *Gatekeeper) upsertRole(ctx context.Context, gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config common.HelmConfig) error { - if config.AuthMethod == "" && !config.EnableOpenShift { - return g.deleteRole(ctx, types.NamespacedName{Namespace: gateway.Namespace, Name: gateway.Name}) - } - - role := &rbac.Role{} - - // If the Role already exists, ensure that we own the Role - err := g.Client.Get(ctx, g.namespacedName(gateway), role) - if err != nil && !k8serrors.IsNotFound(err) { - return err - } else if !k8serrors.IsNotFound(err) { - // Ensure we own the Role. - for _, ref := range role.GetOwnerReferences() { - if ref.UID == gateway.GetUID() && ref.Name == gateway.GetName() { - // We found ourselves! - return nil - } - } - return errors.New("role not owned by controller") - } - - role = g.role(gateway, gcc, config) - if err := ctrl.SetControllerReference(&gateway, role, g.Client.Scheme()); err != nil { - return err - } - if err := g.Client.Create(ctx, role); err != nil { - return err - } - - return nil -} - -func (g *Gatekeeper) deleteRole(ctx context.Context, gwName types.NamespacedName) error { - if err := g.Client.Delete(ctx, &rbac.Role{ObjectMeta: metav1.ObjectMeta{Name: gwName.Name, Namespace: gwName.Namespace}}); err != nil { - if k8serrors.IsNotFound(err) { - return nil - } - return err - } - - return nil -} - -func (g *Gatekeeper) role(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config common.HelmConfig) *rbac.Role { - role := &rbac.Role{ - ObjectMeta: metav1.ObjectMeta{ - Name: gateway.Name, - Namespace: gateway.Namespace, - Labels: common.LabelsForGateway(&gateway), - }, - Rules: []rbac.PolicyRule{}, - } - - if gcc.Spec.PodSecurityPolicy != "" { - role.Rules = append(role.Rules, rbac.PolicyRule{ - APIGroups: []string{"policy"}, - Resources: []string{"podsecuritypolicies"}, - ResourceNames: []string{gcc.Spec.PodSecurityPolicy}, - Verbs: []string{"use"}, - }) - } - - if config.EnableOpenShift { - role.Rules = append(role.Rules, rbac.PolicyRule{ - APIGroups: []string{"security.openshift.io"}, - Resources: []string{"securitycontextconstraints"}, - ResourceNames: []string{gcc.Spec.OpenshiftSCCName}, - Verbs: []string{"use"}, - }) - } - - return role -} diff --git a/control-plane/api-gateway/gatekeeper/rolebinding.go b/control-plane/api-gateway/gatekeeper/rolebinding.go deleted file mode 100644 index 1a60e752c8..0000000000 --- a/control-plane/api-gateway/gatekeeper/rolebinding.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package gatekeeper - -import ( - "context" - "errors" - - "k8s.io/apimachinery/pkg/types" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - rbac "k8s.io/api/rbac/v1" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - ctrl "sigs.k8s.io/controller-runtime" -) - -func (g *Gatekeeper) upsertRoleBinding(ctx context.Context, gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config common.HelmConfig) error { - if config.AuthMethod == "" && !config.EnableOpenShift { - return g.deleteRole(ctx, types.NamespacedName{Namespace: gateway.Namespace, Name: gateway.Name}) - } - - roleBinding := &rbac.RoleBinding{} - - // If the RoleBinding already exists, ensure that we own the RoleBinding - err := g.Client.Get(ctx, g.namespacedName(gateway), roleBinding) - if err != nil && !k8serrors.IsNotFound(err) { - return err - } else if !k8serrors.IsNotFound(err) { - // Ensure we own the Role. - for _, ref := range roleBinding.GetOwnerReferences() { - if ref.UID == gateway.GetUID() && ref.Name == gateway.GetName() { - // We found ourselves! - return nil - } - } - return errors.New("role not owned by controller") - } - - // Create or update the RoleBinding - roleBinding = g.roleBinding(gateway, gcc, config) - if err := ctrl.SetControllerReference(&gateway, roleBinding, g.Client.Scheme()); err != nil { - return err - } - if err := g.Client.Create(ctx, roleBinding); err != nil { - return err - } - - return nil -} - -func (g *Gatekeeper) deleteRoleBinding(ctx context.Context, gwName types.NamespacedName) error { - if err := g.Client.Delete(ctx, &rbac.RoleBinding{ObjectMeta: metav1.ObjectMeta{Name: gwName.Name, Namespace: gwName.Namespace}}); err != nil { - if k8serrors.IsNotFound(err) { - return nil - } - return err - } - - return nil -} - -func (g *Gatekeeper) roleBinding(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config common.HelmConfig) *rbac.RoleBinding { - // Create resources for reference. This avoids bugs if naming patterns change. - serviceAccount := g.serviceAccount(gateway) - role := g.role(gateway, gcc, config) - - return &rbac.RoleBinding{ - ObjectMeta: metav1.ObjectMeta{ - Name: gateway.Name, - Namespace: gateway.Namespace, - Labels: common.LabelsForGateway(&gateway), - }, - RoleRef: rbac.RoleRef{ - APIGroup: "rbac.authorization.k8s.io", - Kind: "Role", - Name: role.Name, - }, - Subjects: []rbac.Subject{ - { - Kind: "ServiceAccount", - Name: serviceAccount.Name, - Namespace: serviceAccount.Namespace, - }, - }, - } -} diff --git a/control-plane/api-gateway/gatekeeper/service.go b/control-plane/api-gateway/gatekeeper/service.go deleted file mode 100644 index a30a3df89f..0000000000 --- a/control-plane/api-gateway/gatekeeper/service.go +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package gatekeeper - -import ( - "context" - - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "k8s.io/apimachinery/pkg/types" - - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/equality" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/intstr" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -var ( - defaultServiceAnnotations = []string{ - "external-dns.alpha.kubernetes.io/hostname", - } -) - -func (g *Gatekeeper) upsertService(ctx context.Context, gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config common.HelmConfig) error { - if gcc.Spec.ServiceType == nil { - return g.deleteService(ctx, types.NamespacedName{Namespace: gateway.Namespace, Name: gateway.Name}) - } - - service := g.service(gateway, gcc) - - mutated := service.DeepCopy() - mutator := newServiceMutator(service, mutated, gateway, g.Client.Scheme()) - - result, err := controllerutil.CreateOrUpdate(ctx, g.Client, mutated, mutator) - if err != nil { - return err - } - - switch result { - case controllerutil.OperationResultCreated: - g.Log.V(1).Info("Created Service") - case controllerutil.OperationResultUpdated: - g.Log.V(1).Info("Updated Service") - case controllerutil.OperationResultNone: - g.Log.V(1).Info("No change to service") - } - - return nil -} - -func (g *Gatekeeper) deleteService(ctx context.Context, gwName types.NamespacedName) error { - if err := g.Client.Delete(ctx, &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: gwName.Name, Namespace: gwName.Namespace}}); err != nil { - if k8serrors.IsNotFound(err) { - return nil - } - return err - } - - return nil -} - -func (g *Gatekeeper) service(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig) *corev1.Service { - seenPorts := map[gwv1beta1.PortNumber]struct{}{} - ports := []corev1.ServicePort{} - for _, listener := range gateway.Spec.Listeners { - if _, seen := seenPorts[listener.Port]; seen { - // We've already added this listener's port to the Service - continue - } - - ports = append(ports, corev1.ServicePort{ - Name: string(listener.Name), - // only TCP-based services are supported for now - Protocol: corev1.ProtocolTCP, - Port: int32(listener.Port), - TargetPort: intstr.FromInt(common.ToContainerPort(listener.Port, gcc.Spec.MapPrivilegedContainerPorts)), - }) - - seenPorts[listener.Port] = struct{}{} - } - - // Copy annotations from the Gateway, filtered by those allowed by the GatewayClassConfig. - allowedAnnotations := gcc.Spec.CopyAnnotations.Service - if allowedAnnotations == nil { - allowedAnnotations = defaultServiceAnnotations - } - annotations := make(map[string]string) - for _, allowedAnnotation := range allowedAnnotations { - if value, found := gateway.Annotations[allowedAnnotation]; found { - annotations[allowedAnnotation] = value - } - } - - return &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: gateway.Name, - Namespace: gateway.Namespace, - Labels: common.LabelsForGateway(&gateway), - Annotations: annotations, - }, - Spec: corev1.ServiceSpec{ - Selector: common.LabelsForGateway(&gateway), - Type: *gcc.Spec.ServiceType, - Ports: ports, - }, - } -} - -// mergeService is used to keep annotations and ports from the `from` Service -// to the `to` service. This prevents an infinite reconciliation loop when -// Kubernetes adds this configuration back in. -func mergeService(from, to *corev1.Service) *corev1.Service { - if areServicesEqual(from, to) { - return to - } - - to.Annotations = from.Annotations - to.Spec.Ports = from.Spec.Ports - - return to -} - -func areServicesEqual(a, b *corev1.Service) bool { - if !equality.Semantic.DeepEqual(a.Annotations, b.Annotations) { - return false - } - if len(b.Spec.Ports) != len(a.Spec.Ports) { - return false - } - - for i, port := range a.Spec.Ports { - otherPort := b.Spec.Ports[i] - if port.Port != otherPort.Port { - return false - } - if port.Protocol != otherPort.Protocol { - return false - } - } - return true -} - -func newServiceMutator(service, mutated *corev1.Service, gateway gwv1beta1.Gateway, scheme *runtime.Scheme) resourceMutator { - return func() error { - mutated = mergeService(service, mutated) - return ctrl.SetControllerReference(&gateway, mutated, scheme) - } -} diff --git a/control-plane/api-gateway/gatekeeper/serviceaccount.go b/control-plane/api-gateway/gatekeeper/serviceaccount.go deleted file mode 100644 index d1c5c9883a..0000000000 --- a/control-plane/api-gateway/gatekeeper/serviceaccount.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package gatekeeper - -import ( - "context" - "errors" - - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "k8s.io/apimachinery/pkg/types" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - - corev1 "k8s.io/api/core/v1" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - ctrl "sigs.k8s.io/controller-runtime" -) - -func (g *Gatekeeper) upsertServiceAccount(ctx context.Context, gateway gwv1beta1.Gateway, config common.HelmConfig) error { - if config.AuthMethod == "" && !config.EnableOpenShift { - return g.deleteServiceAccount(ctx, types.NamespacedName{Namespace: gateway.Namespace, Name: gateway.Name}) - } - - serviceAccount := &corev1.ServiceAccount{} - exists := false - - // Get ServiceAccount if it exists. - err := g.Client.Get(ctx, g.namespacedName(gateway), serviceAccount) - if err != nil && !k8serrors.IsNotFound(err) { - return err - } else if k8serrors.IsNotFound(err) { - exists = false - } else { - exists = true - } - - if exists { - // Ensure we own the ServiceAccount. - for _, ref := range serviceAccount.GetOwnerReferences() { - if ref.UID == gateway.GetUID() && ref.Name == gateway.GetName() { - // We found ourselves! - return nil - } - } - return errors.New("ServiceAccount not owned by controller") - } - - // Create the ServiceAccount. - serviceAccount = g.serviceAccount(gateway) - if err := ctrl.SetControllerReference(&gateway, serviceAccount, g.Client.Scheme()); err != nil { - return err - } - if err := g.Client.Create(ctx, serviceAccount); err != nil { - return err - } - - return nil -} - -func (g *Gatekeeper) deleteServiceAccount(ctx context.Context, gwName types.NamespacedName) error { - if err := g.Client.Delete(ctx, &corev1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: gwName.Name, Namespace: gwName.Namespace}}); err != nil { - if k8serrors.IsNotFound(err) { - return nil - } - return err - } - - return nil -} - -func (g *Gatekeeper) serviceAccount(gateway gwv1beta1.Gateway) *corev1.ServiceAccount { - return &corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: gateway.Name, - Namespace: gateway.Namespace, - Labels: common.LabelsForGateway(&gateway), - }, - } -} diff --git a/control-plane/api/common/common.go b/control-plane/api/common/common.go index a4063d6147..7c761b6477 100644 --- a/control-plane/api/common/common.go +++ b/control-plane/api/common/common.go @@ -1,22 +1,16 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - // Package common holds code that isn't tied to a particular CRD version or type. package common const ( - ServiceDefaults string = "servicedefaults" - ProxyDefaults string = "proxydefaults" - ServiceResolver string = "serviceresolver" - ServiceRouter string = "servicerouter" - ServiceSplitter string = "servicesplitter" - ServiceIntentions string = "serviceintentions" - ExportedServices string = "exportedservices" - IngressGateway string = "ingressgateway" - TerminatingGateway string = "terminatinggateway" - SamenessGroup string = "samenessgroup" - JWTProvider string = "jwtprovider" - ControlPlaneRequestLimit string = "controlplanerequestlimit" + ServiceDefaults string = "servicedefaults" + ProxyDefaults string = "proxydefaults" + ServiceResolver string = "serviceresolver" + ServiceRouter string = "servicerouter" + ServiceSplitter string = "servicesplitter" + ServiceIntentions string = "serviceintentions" + ExportedServices string = "exportedservices" + IngressGateway string = "ingressgateway" + TerminatingGateway string = "terminatinggateway" Global string = "global" Mesh string = "mesh" diff --git a/control-plane/api/common/configentry.go b/control-plane/api/common/configentry.go index 3559d8a6bc..2d83ce05b0 100644 --- a/control-plane/api/common/configentry.go +++ b/control-plane/api/common/configentry.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package common import ( diff --git a/control-plane/api/common/configentry_webhook.go b/control-plane/api/common/configentry_webhook.go index 4b8a482d6c..4028a5e3cb 100644 --- a/control-plane/api/common/configentry_webhook.go +++ b/control-plane/api/common/configentry_webhook.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package common import ( diff --git a/control-plane/api/common/configentry_webhook_test.go b/control-plane/api/common/configentry_webhook_test.go index 2760ff15ff..3a2dc9098b 100644 --- a/control-plane/api/common/configentry_webhook_test.go +++ b/control-plane/api/common/configentry_webhook_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package common import ( diff --git a/control-plane/api/v1alpha1/api_gateway_types.go b/control-plane/api/v1alpha1/api_gateway_types.go deleted file mode 100644 index c06ac3825f..0000000000 --- a/control-plane/api/v1alpha1/api_gateway_types.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package v1alpha1 - -import ( - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -const ( - GatewayClassConfigKind = "GatewayClassConfig" - MeshServiceKind = "MeshService" -) - -func init() { - SchemeBuilder.Register(&GatewayClassConfig{}, &GatewayClassConfigList{}) - SchemeBuilder.Register(&MeshService{}, &MeshServiceList{}) -} - -// +genclient -// +kubebuilder:object:root=true -// +kubebuilder:resource:scope=Cluster - -// GatewayClassConfig defines the values that may be set on a GatewayClass for Consul API Gateway. -type GatewayClassConfig struct { - // Standard Kubernetes resource metadata. - metav1.TypeMeta `json:",inline"` - - // Standard object's metadata. - metav1.ObjectMeta `json:"metadata,omitempty"` - - // Spec defines the desired state of GatewayClassConfig. - Spec GatewayClassConfigSpec `json:"spec,omitempty"` -} - -// +k8s:deepcopy-gen=true - -// GatewayClassConfigSpec specifies the desired state of the Config CRD. -type GatewayClassConfigSpec struct { - - // +kubebuilder:validation:Enum=ClusterIP;NodePort;LoadBalancer - ServiceType *corev1.ServiceType `json:"serviceType,omitempty"` - - // NodeSelector is a selector which must be true for the pod to fit on a node. - // Selector which must match a node's labels for the pod to be scheduled on that node. - // More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ - NodeSelector map[string]string `json:"nodeSelector,omitempty"` - - // Tolerations allow the scheduler to schedule nodes with matching taints. - // More Info: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ - Tolerations []corev1.Toleration `json:"tolerations,omitempty"` - - // Deployment defines the deployment configuration for the gateway. - DeploymentSpec DeploymentSpec `json:"deployment,omitempty"` - - // Annotation Information to copy to services or deployments - CopyAnnotations CopyAnnotationsSpec `json:"copyAnnotations,omitempty"` - - // The name of an existing Kubernetes PodSecurityPolicy to bind to the managed ServiceAccount if ACLs are managed. - PodSecurityPolicy string `json:"podSecurityPolicy,omitempty"` - - // The name of the OpenShift SecurityContextConstraints resource for this gateway class to use. - OpenshiftSCCName string `json:"openshiftSCCName,omitempty"` - - // The value to add to privileged ports ( ports < 1024) for gateway containers - MapPrivilegedContainerPorts int32 `json:"mapPrivilegedContainerPorts,omitempty"` -} - -// +k8s:deepcopy-gen=true - -type DeploymentSpec struct { - // +kubebuilder:default:=1 - // +kubebuilder:validation:Maximum=8 - // +kubebuilder:validation:Minimum=1 - // Number of gateway instances that should be deployed by default - DefaultInstances *int32 `json:"defaultInstances,omitempty"` - // +kubebuilder:default:=8 - // +kubebuilder:validation:Maximum=8 - // +kubebuilder:validation:Minimum=1 - // Max allowed number of gateway instances - MaxInstances *int32 `json:"maxInstances,omitempty"` - // +kubebuilder:default:=1 - // +kubebuilder:validation:Maximum=8 - // +kubebuilder:validation:Minimum=1 - // Minimum allowed number of gateway instances - MinInstances *int32 `json:"minInstances,omitempty"` -} - -//+kubebuilder:object:generate=true - -// CopyAnnotationsSpec defines the annotations that should be copied to the gateway service. -type CopyAnnotationsSpec struct { - // List of annotations to copy to the gateway service. - Service []string `json:"service,omitempty"` -} - -// +kubebuilder:object:root=true - -// GatewayClassConfigList is a list of Config resources. -type GatewayClassConfigList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` - - // Items is the list of Configs. - Items []GatewayClassConfig `json:"items"` -} - -// +genclient -// +kubebuilder:object:root=true - -// MeshService holds a reference to an externally managed Consul Service Mesh service. -type MeshService struct { - metav1.TypeMeta `json:",inline"` - // Standard object's metadata. - metav1.ObjectMeta `json:"metadata,omitempty"` - - // Spec defines the desired state of MeshService. - Spec MeshServiceSpec `json:"spec,omitempty"` -} - -// +k8s:deepcopy-gen=true - -// MeshServiceSpec specifies the 'spec' of the MeshService CRD. -type MeshServiceSpec struct { - // Name holds the service name for a Consul service. - Name string `json:"name,omitempty"` - // Peer optionally specifies the name of the peer exporting the Consul service. - // If not specified, the Consul service is assumed to be in the local datacenter. - Peer *string `json:"peer,omitempty"` -} - -// +kubebuilder:object:root=true - -// MeshServiceList is a list of MeshService resources. -type MeshServiceList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` - - Items []MeshService `json:"items"` -} diff --git a/control-plane/api/v1alpha1/api_gateway_types_test.go b/control-plane/api/v1alpha1/api_gateway_types_test.go deleted file mode 100644 index 6e0690b9b2..0000000000 --- a/control-plane/api/v1alpha1/api_gateway_types_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package v1alpha1 - -import ( - "testing" - - "github.com/stretchr/testify/require" - core "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func TestGatewayClassConfigDeepCopy(t *testing.T) { - var nilConfig *GatewayClassConfig - require.Nil(t, nilConfig.DeepCopy()) - require.Nil(t, nilConfig.DeepCopyObject()) - lbType := core.ServiceTypeLoadBalancer - spec := GatewayClassConfigSpec{ - ServiceType: &lbType, - NodeSelector: map[string]string{ - "test": "test", - }, - OpenshiftSCCName: "restricted-v2", - } - config := &GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: spec, - } - copy := config.DeepCopy() - copyObject := config.DeepCopyObject() - require.Equal(t, copy, copyObject) - - var nilSpec *GatewayClassConfigSpec - require.Nil(t, nilSpec.DeepCopy()) - specCopy := (&spec).DeepCopy() - require.Equal(t, spec.NodeSelector, specCopy.NodeSelector) - - var nilConfigList *GatewayClassConfigList - require.Nil(t, nilConfigList.DeepCopyObject()) - configList := &GatewayClassConfigList{ - Items: []GatewayClassConfig{*config}, - } - copyConfigList := configList.DeepCopy() - copyConfigListObject := configList.DeepCopyObject() - require.Equal(t, copyConfigList, copyConfigListObject) -} diff --git a/control-plane/api/v1alpha1/controlplanerequestlimit_types.go b/control-plane/api/v1alpha1/controlplanerequestlimit_types.go deleted file mode 100644 index ac2c05ded0..0000000000 --- a/control-plane/api/v1alpha1/controlplanerequestlimit_types.go +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package v1alpha1 - -import ( - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - consul "github.com/hashicorp/consul/api" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/validation/field" - - "github.com/hashicorp/consul-k8s/control-plane/api/common" -) - -const ( - ControlPlaneRequestLimitKubeKind = "controlplanerequestlimit" -) - -func init() { - SchemeBuilder.Register(&ControlPlaneRequestLimit{}, &ControlPlaneRequestLimitList{}) -} - -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status - -// ControlPlaneRequestLimit is the Schema for the controlplanerequestlimits API. -// +kubebuilder:printcolumn:name="Synced",type="string",JSONPath=".status.conditions[?(@.type==\"Synced\")].status",description="The sync status of the resource with Consul" -// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="The age of the resource" -type ControlPlaneRequestLimit struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec ControlPlaneRequestLimitSpec `json:"spec,omitempty"` - Status `json:"status,omitempty"` -} - -// +kubebuilder:object:root=true - -// ControlPlaneRequestLimitList contains a list of ControlPlaneRequestLimit. -type ControlPlaneRequestLimitList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []ControlPlaneRequestLimit `json:"items"` -} - -type ReadWriteRatesConfig struct { - ReadRate float64 `json:"readRate,omitempty"` - WriteRate float64 `json:"writeRate,omitempty"` -} - -func (c *ReadWriteRatesConfig) toConsul() *consul.ReadWriteRatesConfig { - if c == nil { - return nil - } - return &consul.ReadWriteRatesConfig{ - ReadRate: c.ReadRate, - WriteRate: c.WriteRate, - } -} - -func (c *ReadWriteRatesConfig) validate(path *field.Path) field.ErrorList { - if c == nil { - return nil - } - - var errs field.ErrorList - - if c.ReadRate < 0 { - errs = append(errs, field.Invalid(path.Child("readRate"), c.ReadRate, "readRate must be >= 0")) - } - - if c.WriteRate <= 0 { - errs = append(errs, field.Invalid(path.Child("writeRate"), c.WriteRate, "writeRate must be > 0")) - } - return errs -} - -// ControlPlaneRequestLimitSpec defines the desired state of ControlPlaneRequestLimit. -type ControlPlaneRequestLimitSpec struct { - Mode string `json:"mode,omitempty"` - ReadWriteRatesConfig `json:",inline"` - ACL *ReadWriteRatesConfig `json:"acl,omitempty"` - Catalog *ReadWriteRatesConfig `json:"catalog,omitempty"` - ConfigEntry *ReadWriteRatesConfig `json:"configEntry,omitempty"` - ConnectCA *ReadWriteRatesConfig `json:"connectCA,omitempty"` - Coordinate *ReadWriteRatesConfig `json:"coordinate,omitempty"` - DiscoveryChain *ReadWriteRatesConfig `json:"discoveryChain,omitempty"` - Health *ReadWriteRatesConfig `json:"health,omitempty"` - Intention *ReadWriteRatesConfig `json:"intention,omitempty"` - KV *ReadWriteRatesConfig `json:"kv,omitempty"` - Tenancy *ReadWriteRatesConfig `json:"tenancy,omitempty"` - PreparedQuery *ReadWriteRatesConfig `json:"perparedQuery,omitempty"` - Session *ReadWriteRatesConfig `json:"session,omitempty"` - Txn *ReadWriteRatesConfig `json:"txn,omitempty"` -} - -// GetObjectMeta returns object meta. -func (c *ControlPlaneRequestLimit) GetObjectMeta() metav1.ObjectMeta { - return c.ObjectMeta -} - -// AddFinalizer adds a finalizer to the list of finalizers. -func (c *ControlPlaneRequestLimit) AddFinalizer(name string) { - c.ObjectMeta.Finalizers = append(c.ObjectMeta.Finalizers, name) -} - -// RemoveFinalizer removes this finalizer from the list. -func (c *ControlPlaneRequestLimit) RemoveFinalizer(name string) { - for i, n := range c.ObjectMeta.Finalizers { - if n == name { - c.ObjectMeta.Finalizers = append(c.ObjectMeta.Finalizers[:i], c.ObjectMeta.Finalizers[i+1:]...) - return - } - } -} - -// Finalizers returns the list of finalizers for this object. -func (c *ControlPlaneRequestLimit) Finalizers() []string { - return c.ObjectMeta.Finalizers -} - -// ConsulKind returns the Consul config entry kind, i.e. service-defaults, not -// servicedefaults. -func (c *ControlPlaneRequestLimit) ConsulKind() string { - return consul.RateLimitIPConfig -} - -// ConsulGlobalResource returns if the resource exists in the default -// Consul namespace only. -func (c *ControlPlaneRequestLimit) ConsulGlobalResource() bool { - return true -} - -// ConsulMirroringNS returns the Consul namespace that the config entry should -// be created in if namespaces and mirroring are enabled. -func (c *ControlPlaneRequestLimit) ConsulMirroringNS() string { - return common.DefaultConsulNamespace -} - -// KubeKind returns the Kube config entry kind, i.e. servicedefaults, not -// service-defaults. -func (c *ControlPlaneRequestLimit) KubeKind() string { - return ControlPlaneRequestLimitKubeKind -} - -// ConsulName returns the name of the config entry as saved in Consul. -// This may be different than KubernetesName() in the case of a ServiceIntentions -// config entry. -func (c *ControlPlaneRequestLimit) ConsulName() string { - return c.ObjectMeta.Name -} - -// KubernetesName returns the name of the Kubernetes resource. -func (c *ControlPlaneRequestLimit) KubernetesName() string { - return c.ObjectMeta.Name -} - -// SetSyncedCondition updates the synced condition. -func (c *ControlPlaneRequestLimit) SetSyncedCondition(status corev1.ConditionStatus, reason, message string) { - c.Status.Conditions = Conditions{ - { - Type: ConditionSynced, - Status: status, - LastTransitionTime: metav1.Now(), - Reason: reason, - Message: message, - }, - } -} - -// SetLastSyncedTime updates the last synced time. -func (c *ControlPlaneRequestLimit) SetLastSyncedTime(time *metav1.Time) { - c.Status.LastSyncedTime = time -} - -// SyncedCondition gets the synced condition. -func (c *ControlPlaneRequestLimit) SyncedCondition() (status corev1.ConditionStatus, reason, message string) { - cond := c.Status.GetCondition(ConditionSynced) - if cond == nil { - return corev1.ConditionUnknown, "", "" - } - return cond.Status, cond.Reason, cond.Message -} - -// SyncedConditionStatus returns the status of the synced condition. -func (c *ControlPlaneRequestLimit) SyncedConditionStatus() corev1.ConditionStatus { - condition := c.Status.GetCondition(ConditionSynced) - if condition == nil { - return corev1.ConditionUnknown - } - return condition.Status -} - -// ToConsul converts the resource to the corresponding Consul API definition. -// Its return type is the generic ConfigEntry but a specific config entry -// type should be constructed e.g. ServiceConfigEntry. -func (c *ControlPlaneRequestLimit) ToConsul(datacenter string) consul.ConfigEntry { - return &consul.RateLimitIPConfigEntry{ - Kind: c.ConsulKind(), - Name: c.ConsulName(), - Mode: c.Spec.Mode, - ReadRate: c.Spec.ReadRate, - WriteRate: c.Spec.WriteRate, - Meta: meta(datacenter), - ACL: c.Spec.ACL.toConsul(), - Catalog: c.Spec.Catalog.toConsul(), - ConfigEntry: c.Spec.ConfigEntry.toConsul(), - ConnectCA: c.Spec.ConnectCA.toConsul(), - Coordinate: c.Spec.Coordinate.toConsul(), - DiscoveryChain: c.Spec.DiscoveryChain.toConsul(), - Health: c.Spec.Health.toConsul(), - Intention: c.Spec.Intention.toConsul(), - KV: c.Spec.KV.toConsul(), - Tenancy: c.Spec.Tenancy.toConsul(), - PreparedQuery: c.Spec.PreparedQuery.toConsul(), - Session: c.Spec.Session.toConsul(), - Txn: c.Spec.Txn.toConsul(), - } -} - -// MatchesConsul returns true if the resource has the same fields as the Consul -// config entry. -func (c *ControlPlaneRequestLimit) MatchesConsul(candidate consul.ConfigEntry) bool { - configEntry, ok := candidate.(*consul.RateLimitIPConfigEntry) - if !ok { - return false - } - // No datacenter is passed to ToConsul as we ignore the Meta field when checking for equality. - return cmp.Equal(c.ToConsul(""), configEntry, cmpopts.IgnoreFields(consul.RateLimitIPConfigEntry{}, "Partition", "Namespace", "Meta", "ModifyIndex", "CreateIndex"), cmpopts.IgnoreUnexported(), cmpopts.EquateEmpty()) -} - -// Validate returns an error if the resource is invalid. -func (c *ControlPlaneRequestLimit) Validate(consulMeta common.ConsulMeta) error { - var errs field.ErrorList - path := field.NewPath("spec") - - if c.Spec.Mode != "permissive" && c.Spec.Mode != "enforcing" && c.Spec.Mode != "disabled" { - errs = append(errs, field.Invalid(path.Child("mode"), c.Spec.Mode, "mode must be one of: permissive, enforcing, disabled")) - } - - errs = append(errs, c.Spec.ReadWriteRatesConfig.validate(path)...) - errs = append(errs, c.Spec.ACL.validate(path.Child("acl"))...) - errs = append(errs, c.Spec.Catalog.validate(path.Child("catalog"))...) - errs = append(errs, c.Spec.ConfigEntry.validate(path.Child("configEntry"))...) - errs = append(errs, c.Spec.ConnectCA.validate(path.Child("connectCA"))...) - errs = append(errs, c.Spec.Coordinate.validate(path.Child("coordinate"))...) - errs = append(errs, c.Spec.DiscoveryChain.validate(path.Child("discoveryChain"))...) - errs = append(errs, c.Spec.Health.validate(path.Child("health"))...) - errs = append(errs, c.Spec.Intention.validate(path.Child("intention"))...) - errs = append(errs, c.Spec.KV.validate(path.Child("kv"))...) - errs = append(errs, c.Spec.Tenancy.validate(path.Child("tenancy"))...) - errs = append(errs, c.Spec.PreparedQuery.validate(path.Child("preparedQuery"))...) - errs = append(errs, c.Spec.Session.validate(path.Child("session"))...) - errs = append(errs, c.Spec.Txn.validate(path.Child("txn"))...) - - if len(errs) > 0 { - return apierrors.NewInvalid( - schema.GroupKind{Group: ConsulHashicorpGroup, Kind: ControlPlaneRequestLimitKubeKind}, - c.KubernetesName(), errs) - } - - return nil -} - -// DefaultNamespaceFields has no behaviour here as control-plane-request-limit have no namespace specific fields. -func (s *ControlPlaneRequestLimit) DefaultNamespaceFields(_ common.ConsulMeta) { -} diff --git a/control-plane/api/v1alpha1/controlplanerequestlimit_types_test.go b/control-plane/api/v1alpha1/controlplanerequestlimit_types_test.go deleted file mode 100644 index 12633250ab..0000000000 --- a/control-plane/api/v1alpha1/controlplanerequestlimit_types_test.go +++ /dev/null @@ -1,569 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package v1alpha1 - -import ( - "testing" - "time" - - consul "github.com/hashicorp/consul/api" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/hashicorp/consul-k8s/control-plane/api/common" -) - -func TestControlPlaneRequestLimit_ToConsul(t *testing.T) { - cases := map[string]struct { - input *ControlPlaneRequestLimit - expected *consul.RateLimitIPConfigEntry - }{ - "empty fields": { - &ControlPlaneRequestLimit{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - }, - Spec: ControlPlaneRequestLimitSpec{ - Mode: "disabled", - ReadWriteRatesConfig: ReadWriteRatesConfig{ - ReadRate: 0, - WriteRate: 0, - }, - }, - }, - &consul.RateLimitIPConfigEntry{ - Name: "foo", - Kind: consul.RateLimitIPConfig, - Mode: "disabled", - Meta: map[string]string{ - common.DatacenterKey: "datacenter", - common.SourceKey: common.SourceValue, - }, - ReadRate: 0, - WriteRate: 0, - }, - }, - "every field set": { - &ControlPlaneRequestLimit{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - }, - Spec: ControlPlaneRequestLimitSpec{ - Mode: "permissive", - ReadWriteRatesConfig: ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ACL: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Catalog: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ConfigEntry: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ConnectCA: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Coordinate: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - DiscoveryChain: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Health: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Intention: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - KV: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Tenancy: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - PreparedQuery: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Session: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Txn: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - }, - }, - &consul.RateLimitIPConfigEntry{ - Kind: consul.RateLimitIPConfig, - Name: "foo", - Mode: "permissive", - ReadRate: 100.0, - WriteRate: 100.0, - Meta: map[string]string{ - common.DatacenterKey: "datacenter", - common.SourceKey: common.SourceValue, - }, - ACL: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Catalog: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ConfigEntry: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ConnectCA: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Coordinate: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - DiscoveryChain: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Health: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Intention: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - KV: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Tenancy: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - PreparedQuery: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Session: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Txn: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - }, - }, - } - - for name, testCase := range cases { - t.Run(name, func(t *testing.T) { - output := testCase.input.ToConsul("datacenter") - require.Equal(t, testCase.expected, output) - }) - } -} - -func TestControlPlaneRequestLimit_MatchesConsul(t *testing.T) { - cases := map[string]struct { - internal *ControlPlaneRequestLimit - consul consul.ConfigEntry - matches bool - }{ - "empty fields matches": { - &ControlPlaneRequestLimit{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - }, - Spec: ControlPlaneRequestLimitSpec{}, - }, - &consul.RateLimitIPConfigEntry{ - Kind: consul.RateLimitIPConfig, - Name: "my-test-service", - Namespace: "namespace", - CreateIndex: 1, - ModifyIndex: 2, - Meta: map[string]string{ - common.SourceKey: common.SourceValue, - common.DatacenterKey: "datacenter", - }, - }, - true, - }, - "all fields populated matches": { - &ControlPlaneRequestLimit{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - }, - Spec: ControlPlaneRequestLimitSpec{ - Mode: "permissive", - ReadWriteRatesConfig: ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ACL: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Catalog: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ConfigEntry: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ConnectCA: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Coordinate: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - DiscoveryChain: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Health: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Intention: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - KV: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Tenancy: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - PreparedQuery: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Session: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Txn: &ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - }, - }, - &consul.RateLimitIPConfigEntry{ - Kind: consul.RateLimitIPConfig, - Name: "my-test-service", - Mode: "permissive", - ReadRate: 100.0, - WriteRate: 100.0, - Meta: map[string]string{ - common.DatacenterKey: "datacenter", - common.SourceKey: common.SourceValue, - }, - ACL: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Catalog: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ConfigEntry: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ConnectCA: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Coordinate: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - DiscoveryChain: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Health: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Intention: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - KV: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Tenancy: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - PreparedQuery: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Session: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Txn: &consul.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - }, - true, - }, - "mismatched types does not match": { - &ControlPlaneRequestLimit{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-service", - }, - Spec: ControlPlaneRequestLimitSpec{}, - }, - &consul.ProxyConfigEntry{ - Kind: consul.RateLimitIPConfig, - Name: "my-test-service", - Namespace: "namespace", - CreateIndex: 1, - ModifyIndex: 2, - }, - false, - }, - } - - for name, testCase := range cases { - t.Run(name, func(t *testing.T) { - require.Equal(t, testCase.matches, testCase.internal.MatchesConsul(testCase.consul)) - }) - } -} - -func TestControlPlaneRequestLimit_Validate(t *testing.T) { - invalidReadWriteRatesConfig := &ReadWriteRatesConfig{ - ReadRate: -1, - WriteRate: 0, - } - - validReadWriteRatesConfig := &ReadWriteRatesConfig{ - ReadRate: 100, - WriteRate: 100, - } - - cases := map[string]struct { - input *ControlPlaneRequestLimit - expectedErrMsgs []string - }{ - "invalid": { - input: &ControlPlaneRequestLimit{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.ControlPlaneRequestLimit, - }, - Spec: ControlPlaneRequestLimitSpec{ - Mode: "invalid", - ACL: invalidReadWriteRatesConfig, - Catalog: invalidReadWriteRatesConfig, - ConfigEntry: invalidReadWriteRatesConfig, - ConnectCA: invalidReadWriteRatesConfig, - Coordinate: invalidReadWriteRatesConfig, - DiscoveryChain: invalidReadWriteRatesConfig, - Health: invalidReadWriteRatesConfig, - Intention: invalidReadWriteRatesConfig, - KV: invalidReadWriteRatesConfig, - Tenancy: invalidReadWriteRatesConfig, - PreparedQuery: invalidReadWriteRatesConfig, - Session: invalidReadWriteRatesConfig, - Txn: invalidReadWriteRatesConfig, - }, - }, - expectedErrMsgs: []string{ - `spec.mode: Invalid value: "invalid": mode must be one of: permissive, enforcing, disabled`, - `spec.acl.readRate: Invalid value: -1: readRate must be >= 0, spec.acl.writeRate: Invalid value: 0: writeRate must be > 0`, - `spec.catalog.readRate: Invalid value: -1: readRate must be >= 0, spec.catalog.writeRate: Invalid value: 0: writeRate must be > 0`, - `spec.configEntry.readRate: Invalid value: -1: readRate must be >= 0, spec.configEntry.writeRate: Invalid value: 0: writeRate must be > 0`, - `spec.connectCA.readRate: Invalid value: -1: readRate must be >= 0, spec.connectCA.writeRate: Invalid value: 0: writeRate must be > 0`, - `spec.coordinate.readRate: Invalid value: -1: readRate must be >= 0, spec.coordinate.writeRate: Invalid value: 0: writeRate must be > 0`, - `spec.discoveryChain.readRate: Invalid value: -1: readRate must be >= 0, spec.discoveryChain.writeRate: Invalid value: 0: writeRate must be > 0`, - `spec.health.readRate: Invalid value: -1: readRate must be >= 0, spec.health.writeRate: Invalid value: 0: writeRate must be > 0`, - `spec.intention.readRate: Invalid value: -1: readRate must be >= 0, spec.intention.writeRate: Invalid value: 0: writeRate must be > 0`, - `spec.kv.readRate: Invalid value: -1: readRate must be >= 0, spec.kv.writeRate: Invalid value: 0: writeRate must be > 0`, - `spec.tenancy.readRate: Invalid value: -1: readRate must be >= 0, spec.tenancy.writeRate: Invalid value: 0: writeRate must be > 0`, - `spec.preparedQuery.readRate: Invalid value: -1: readRate must be >= 0, spec.preparedQuery.writeRate: Invalid value: 0: writeRate must be > 0`, - `spec.session.readRate: Invalid value: -1: readRate must be >= 0, spec.session.writeRate: Invalid value: 0: writeRate must be > 0`, - `spec.txn.readRate: Invalid value: -1: readRate must be >= 0, spec.txn.writeRate: Invalid value: 0: writeRate must be > 0`, - }, - }, - "valid": { - input: &ControlPlaneRequestLimit{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.ControlPlaneRequestLimit, - }, - Spec: ControlPlaneRequestLimitSpec{ - Mode: "permissive", - ReadWriteRatesConfig: *validReadWriteRatesConfig, - ACL: validReadWriteRatesConfig, - Catalog: validReadWriteRatesConfig, - ConfigEntry: validReadWriteRatesConfig, - ConnectCA: validReadWriteRatesConfig, - Coordinate: validReadWriteRatesConfig, - DiscoveryChain: validReadWriteRatesConfig, - Health: validReadWriteRatesConfig, - Intention: validReadWriteRatesConfig, - KV: validReadWriteRatesConfig, - Tenancy: validReadWriteRatesConfig, - PreparedQuery: validReadWriteRatesConfig, - Session: validReadWriteRatesConfig, - Txn: validReadWriteRatesConfig, - }, - }, - expectedErrMsgs: []string{}, - }, - } - - for name, testCase := range cases { - t.Run(name, func(t *testing.T) { - err := testCase.input.Validate(common.ConsulMeta{}) - if len(testCase.expectedErrMsgs) != 0 { - require.Error(t, err) - for _, s := range testCase.expectedErrMsgs { - require.Contains(t, err.Error(), s) - } - } else { - require.NoError(t, err) - } - }) - } -} - -func TestControlPlaneRequestLimit_AddFinalizer(t *testing.T) { - controlPlaneRequestLimit := &ControlPlaneRequestLimit{} - controlPlaneRequestLimit.AddFinalizer("finalizer") - require.Equal(t, []string{"finalizer"}, controlPlaneRequestLimit.ObjectMeta.Finalizers) -} - -func TestControlPlaneRequestLimit_RemoveFinalizer(t *testing.T) { - controlPlaneRequestLimit := &ControlPlaneRequestLimit{ - ObjectMeta: metav1.ObjectMeta{ - Finalizers: []string{"f1", "f2"}, - }, - } - controlPlaneRequestLimit.RemoveFinalizer("f1") - require.Equal(t, []string{"f2"}, controlPlaneRequestLimit.ObjectMeta.Finalizers) -} - -func TestControlPlaneRequestLimit_SetSyncedCondition(t *testing.T) { - controlPlaneRequestLimit := &ControlPlaneRequestLimit{} - controlPlaneRequestLimit.SetSyncedCondition(corev1.ConditionTrue, "reason", "message") - - require.Equal(t, corev1.ConditionTrue, controlPlaneRequestLimit.Status.Conditions[0].Status) - require.Equal(t, "reason", controlPlaneRequestLimit.Status.Conditions[0].Reason) - require.Equal(t, "message", controlPlaneRequestLimit.Status.Conditions[0].Message) - now := metav1.Now() - require.True(t, controlPlaneRequestLimit.Status.Conditions[0].LastTransitionTime.Before(&now)) -} - -func TestControlPlaneRequestLimit_SetLastSyncedTime(t *testing.T) { - controlPlaneRequestLimit := &ControlPlaneRequestLimit{} - syncedTime := metav1.NewTime(time.Now()) - controlPlaneRequestLimit.SetLastSyncedTime(&syncedTime) - - require.Equal(t, &syncedTime, controlPlaneRequestLimit.Status.LastSyncedTime) -} - -func TestControlPlaneRequestLimit_GetSyncedConditionStatus(t *testing.T) { - cases := []corev1.ConditionStatus{ - corev1.ConditionUnknown, - corev1.ConditionFalse, - corev1.ConditionTrue, - } - for _, status := range cases { - t.Run(string(status), func(t *testing.T) { - controlPlaneRequestLimit := &ControlPlaneRequestLimit{ - Status: Status{ - Conditions: []Condition{{ - Type: ConditionSynced, - Status: status, - }}, - }, - } - - require.Equal(t, status, controlPlaneRequestLimit.SyncedConditionStatus()) - }) - } -} - -func TestControlPlaneRequestLimit_GetConditionWhenStatusNil(t *testing.T) { - require.Nil(t, (&ControlPlaneRequestLimit{}).GetCondition(ConditionSynced)) -} - -func TestControlPlaneRequestLimit_SyncedConditionStatusWhenStatusNil(t *testing.T) { - require.Equal(t, corev1.ConditionUnknown, (&ControlPlaneRequestLimit{}).SyncedConditionStatus()) -} - -func TestControlPlaneRequestLimit_SyncedConditionWhenStatusNil(t *testing.T) { - status, reason, message := (&ControlPlaneRequestLimit{}).SyncedCondition() - require.Equal(t, corev1.ConditionUnknown, status) - require.Equal(t, "", reason) - require.Equal(t, "", message) -} - -func TestControlPlaneRequestLimit_ConsulKind(t *testing.T) { - require.Equal(t, consul.RateLimitIPConfig, (&ControlPlaneRequestLimit{}).ConsulKind()) -} - -func TestControlPlaneRequestLimit_KubeKind(t *testing.T) { - require.Equal(t, "controlplanerequestlimit", (&ControlPlaneRequestLimit{}).KubeKind()) -} - -func TestControlPlaneRequestLimit_ConsulName(t *testing.T) { - require.Equal(t, "foo", (&ControlPlaneRequestLimit{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}).ConsulName()) -} - -func TestControlPlaneRequestLimit_KubernetesName(t *testing.T) { - require.Equal(t, "foo", (&ControlPlaneRequestLimit{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}).KubernetesName()) -} - -func TestControlPlaneRequestLimit_ConsulNamespace(t *testing.T) { - require.Equal(t, "default", (&ControlPlaneRequestLimit{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}}).ConsulMirroringNS()) -} - -func TestControlPlaneRequestLimit_ConsulGlobalResource(t *testing.T) { - require.True(t, (&ControlPlaneRequestLimit{}).ConsulGlobalResource()) -} - -func TestControlPlaneRequestLimit_ObjectMeta(t *testing.T) { - meta := metav1.ObjectMeta{ - Name: "name", - Namespace: "namespace", - } - controlPlaneRequestLimit := &ControlPlaneRequestLimit{ - ObjectMeta: meta, - } - require.Equal(t, meta, controlPlaneRequestLimit.GetObjectMeta()) -} diff --git a/control-plane/api/v1alpha1/controlplanerequestlimit_webhook.go b/control-plane/api/v1alpha1/controlplanerequestlimit_webhook.go deleted file mode 100644 index d99d9143f7..0000000000 --- a/control-plane/api/v1alpha1/controlplanerequestlimit_webhook.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package v1alpha1 - -import ( - "context" - "fmt" - "net/http" - - "github.com/go-logr/logr" - admissionv1 "k8s.io/api/admission/v1" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - - "github.com/hashicorp/consul-k8s/control-plane/api/common" -) - -// +kubebuilder:object:generate=false - -type ControlPlaneRequestLimitWebhook struct { - client.Client - Logger logr.Logger - decoder *admission.Decoder - ConsulMeta common.ConsulMeta -} - -// NOTE: The path value in the below line is the path to the webhook. -// If it is updated, run code-gen, update subcommand/controller/command.go -// and the consul-helm value for the path to the webhook. -// -// NOTE: The below line cannot be combined with any other comment. If it is -// it will break the code generation. -// -// +kubebuilder:webhook:verbs=create;update,path=/mutate-v1alpha1-controlplanerequestlimits,mutating=true,failurePolicy=fail,groups=consul.hashicorp.com,resources=controlplanerequestlimits,versions=v1alpha1,name=mutate-controlplanerequestlimits.consul.hashicorp.com,sideEffects=None,admissionReviewVersions=v1beta1;v1 - -func (v *ControlPlaneRequestLimitWebhook) Handle(ctx context.Context, req admission.Request) admission.Response { - var limit ControlPlaneRequestLimit - var limitList ControlPlaneRequestLimitList - err := v.decoder.Decode(req, &limit) - if err != nil { - return admission.Errored(http.StatusBadRequest, err) - } - - if req.Operation == admissionv1.Create { - v.Logger.Info("validate create", "name", limit.KubernetesName()) - - if limit.KubernetesName() != common.ControlPlaneRequestLimit { - return admission.Errored(http.StatusBadRequest, - fmt.Errorf(`%s resource name must be "%s"`, - limit.KubeKind(), common.ControlPlaneRequestLimit)) - } - - if err := v.Client.List(ctx, &limitList); err != nil { - return admission.Errored(http.StatusInternalServerError, err) - } - - if len(limitList.Items) > 0 { - return admission.Errored(http.StatusBadRequest, - fmt.Errorf("%s resource already defined - only one control plane request limit entry is supported", - limit.KubeKind())) - } - } - - return common.ValidateConfigEntry(ctx, req, v.Logger, v, &limit, v.ConsulMeta) -} - -func (v *ControlPlaneRequestLimitWebhook) List(ctx context.Context) ([]common.ConfigEntryResource, error) { - var limitList ControlPlaneRequestLimitList - if err := v.Client.List(ctx, &limitList); err != nil { - return nil, err - } - var entries []common.ConfigEntryResource - for _, item := range limitList.Items { - entries = append(entries, common.ConfigEntryResource(&item)) - } - return entries, nil -} - -func (v *ControlPlaneRequestLimitWebhook) InjectDecoder(d *admission.Decoder) error { - v.decoder = d - return nil -} diff --git a/control-plane/api/v1alpha1/controlplanerequestlimit_webhook_test.go b/control-plane/api/v1alpha1/controlplanerequestlimit_webhook_test.go deleted file mode 100644 index c1ab7cc6af..0000000000 --- a/control-plane/api/v1alpha1/controlplanerequestlimit_webhook_test.go +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package v1alpha1 - -import ( - "context" - "encoding/json" - "testing" - - logrtest "github.com/go-logr/logr/testr" - "github.com/stretchr/testify/require" - admissionv1 "k8s.io/api/admission/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - - "github.com/hashicorp/consul-k8s/control-plane/api/common" -) - -func TestValidateControlPlaneRequestLimit(t *testing.T) { - otherNS := "other" - - cases := map[string]struct { - existingResources []runtime.Object - newResource *ControlPlaneRequestLimit - expAllow bool - expErrMessage string - }{ - "no duplicates, valid": { - existingResources: nil, - newResource: &ControlPlaneRequestLimit{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.ControlPlaneRequestLimit, - }, - Spec: ControlPlaneRequestLimitSpec{ - Mode: "permissive", - ReadWriteRatesConfig: ReadWriteRatesConfig{ - ReadRate: 100, - WriteRate: 100, - }, - }, - }, - expAllow: true, - }, - "invalid resource name": { - existingResources: nil, - newResource: &ControlPlaneRequestLimit{ - ObjectMeta: metav1.ObjectMeta{ - Name: "invalid", - }, - Spec: ControlPlaneRequestLimitSpec{ - Mode: "permissive", - ReadWriteRatesConfig: ReadWriteRatesConfig{ - ReadRate: 100, - WriteRate: 100, - }, - }, - }, - expAllow: false, - expErrMessage: `controlplanerequestlimit resource name must be "controlplanerequestlimit"`, - }, - "resource already exists": { - existingResources: []runtime.Object{ - &ControlPlaneRequestLimit{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.ControlPlaneRequestLimit, - }, - Spec: ControlPlaneRequestLimitSpec{ - Mode: "permissive", - ReadWriteRatesConfig: ReadWriteRatesConfig{ - ReadRate: 100, - WriteRate: 100, - }, - }, - }, - }, - newResource: &ControlPlaneRequestLimit{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.ControlPlaneRequestLimit, - }, - Spec: ControlPlaneRequestLimitSpec{ - Mode: "permissive", - ReadWriteRatesConfig: ReadWriteRatesConfig{ - ReadRate: 100, - WriteRate: 100, - }, - }, - }, - expAllow: false, - expErrMessage: `controlplanerequestlimit resource already defined - only one control plane request limit entry is supported`, - }, - "invalid spec": { - existingResources: nil, - newResource: &ControlPlaneRequestLimit{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.ControlPlaneRequestLimit, - }, - Spec: ControlPlaneRequestLimitSpec{ - Mode: "invalid", - ReadWriteRatesConfig: ReadWriteRatesConfig{ - ReadRate: 100, - WriteRate: 100, - }, - }, - }, - expAllow: false, - expErrMessage: `controlplanerequestlimit.consul.hashicorp.com "controlplanerequestlimit" is invalid: spec.mode: Invalid value: "invalid": mode must be one of: permissive, enforcing, disabled`, - }, - } - for name, c := range cases { - t.Run(name, func(t *testing.T) { - ctx := context.Background() - marshalledRequestObject, err := json.Marshal(c.newResource) - require.NoError(t, err) - s := runtime.NewScheme() - s.AddKnownTypes(GroupVersion, &ControlPlaneRequestLimit{}, &ControlPlaneRequestLimitList{}) - client := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(c.existingResources...).Build() - decoder, err := admission.NewDecoder(s) - require.NoError(t, err) - - validator := &ControlPlaneRequestLimitWebhook{ - Client: client, - Logger: logrtest.New(t), - decoder: decoder, - } - response := validator.Handle(ctx, admission.Request{ - AdmissionRequest: admissionv1.AdmissionRequest{ - Name: c.newResource.KubernetesName(), - Namespace: otherNS, - Operation: admissionv1.Create, - Object: runtime.RawExtension{ - Raw: marshalledRequestObject, - }, - }, - }) - - require.Equal(t, c.expAllow, response.Allowed) - if c.expErrMessage != "" { - require.Equal(t, c.expErrMessage, response.AdmissionResponse.Result.Message) - } - }) - } -} diff --git a/control-plane/api/v1alpha1/exportedservices_types.go b/control-plane/api/v1alpha1/exportedservices_types.go index 06d6ce30cf..e05f17a177 100644 --- a/control-plane/api/v1alpha1/exportedservices_types.go +++ b/control-plane/api/v1alpha1/exportedservices_types.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( @@ -19,7 +16,6 @@ import ( ) const ExportedServicesKubeKind = "exportedservices" -const WildcardSpecifier = "*" func init() { SchemeBuilder.Register(&ExportedServices{}, &ExportedServicesList{}) @@ -72,10 +68,8 @@ type ExportedService struct { type ServiceConsumer struct { // Partition is the admin partition to export the service to. Partition string `json:"partition,omitempty"` - // Peer is the name of the peer to export the service to. + // [Experimental] Peer is the name of the peer to export the service to. Peer string `json:"peer,omitempty"` - // SamenessGroup is the name of the sameness group to export the service to. - SamenessGroup string `json:"samenessGroup,omitempty"` } func (in *ExportedServices) GetObjectMeta() metav1.ObjectMeta { @@ -172,9 +166,8 @@ func (in *ExportedService) toConsul() capi.ExportedService { var consumers []capi.ServiceConsumer for _, consumer := range in.Consumers { consumers = append(consumers, capi.ServiceConsumer{ - Partition: consumer.Partition, - Peer: consumer.Peer, - SamenessGroup: consumer.SamenessGroup, + Partition: consumer.Partition, + Peer: consumer.Peer, }) } return capi.ExportedService{ @@ -234,34 +227,14 @@ func (in *ExportedService) validate(path *field.Path, consulMeta common.ConsulMe } func (in *ServiceConsumer) validate(path *field.Path, consulMeta common.ConsulMeta) *field.Error { - count := 0 - - if in.Partition != "" { - count++ - } - if in.Peer != "" { - count++ - } - if in.SamenessGroup != "" { - count++ + if in.Partition != "" && in.Peer != "" { + return field.Invalid(path, *in, "both partition and peer cannot be specified.") } - if count > 1 { - return field.Invalid(path, *in, "service consumer must define at most one of Peer, Partition, or SamenessGroup") - } - if count == 0 { - return field.Invalid(path, *in, "service consumer must define at least one of Peer, Partition, or SamenessGroup") + if in.Partition == "" && in.Peer == "" { + return field.Invalid(path, *in, "either partition or peer must be specified.") } if !consulMeta.PartitionsEnabled && in.Partition != "" { - return field.Invalid(path.Child("partition"), in.Partition, "Consul Admin Partitions need to be enabled to specify partition.") - } - if in.Partition == WildcardSpecifier { - return field.Invalid(path.Child("partition"), "", "exporting to all partitions (wildcard) is not supported") - } - if in.Peer == WildcardSpecifier { - return field.Invalid(path.Child("peer"), "", "exporting to all peers (wildcard) is not supported") - } - if in.SamenessGroup == WildcardSpecifier { - return field.Invalid(path.Child("samenessgroup"), "", "exporting to all sameness groups (wildcard) is not supported") + return field.Invalid(path.Child("partitions"), in.Partition, "Consul Admin Partitions need to be enabled to specify partition.") } return nil } diff --git a/control-plane/api/v1alpha1/exportedservices_types_test.go b/control-plane/api/v1alpha1/exportedservices_types_test.go index d759a6f270..8826166a76 100644 --- a/control-plane/api/v1alpha1/exportedservices_types_test.go +++ b/control-plane/api/v1alpha1/exportedservices_types_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( @@ -59,9 +56,6 @@ func TestExportedServices_MatchesConsul(t *testing.T) { { Peer: "second-peer", }, - { - SamenessGroup: "sg1", - }, }, }, { @@ -77,9 +71,6 @@ func TestExportedServices_MatchesConsul(t *testing.T) { { Peer: "third-peer", }, - { - SamenessGroup: "sg2", - }, }, }, }, @@ -101,9 +92,6 @@ func TestExportedServices_MatchesConsul(t *testing.T) { { Peer: "second-peer", }, - { - SamenessGroup: "sg1", - }, }, }, { @@ -119,9 +107,6 @@ func TestExportedServices_MatchesConsul(t *testing.T) { { Peer: "third-peer", }, - { - SamenessGroup: "sg2", - }, }, }, }, @@ -195,9 +180,6 @@ func TestExportedServices_ToConsul(t *testing.T) { { Peer: "second-peer", }, - { - SamenessGroup: "sg2", - }, }, }, { @@ -213,9 +195,6 @@ func TestExportedServices_ToConsul(t *testing.T) { { Peer: "third-peer", }, - { - SamenessGroup: "sg3", - }, }, }, }, @@ -237,9 +216,6 @@ func TestExportedServices_ToConsul(t *testing.T) { { Peer: "second-peer", }, - { - SamenessGroup: "sg2", - }, }, }, { @@ -255,9 +231,6 @@ func TestExportedServices_ToConsul(t *testing.T) { { Peer: "third-peer", }, - { - SamenessGroup: "sg3", - }, }, }, }, @@ -302,9 +275,6 @@ func TestExportedServices_Validate(t *testing.T) { { Peer: "second-peer", }, - { - SamenessGroup: "sg2", - }, }, }, }, @@ -358,10 +328,10 @@ func TestExportedServices_Validate(t *testing.T) { namespaceEnabled: true, partitionsEnabled: true, expectedErrMsgs: []string{ - `service consumer must define at most one of Peer, Partition, or SamenessGroup`, + `spec.services[0].consumers[0]: Invalid value: v1alpha1.ServiceConsumer{Partition:"second", Peer:"second-peer"}: both partition and peer cannot be specified.`, }, }, - "none of peer, partition, or sameness group defined": { + "neither partition nor peer name specified": { input: &ExportedServices{ ObjectMeta: metav1.ObjectMeta{ Name: common.DefaultConsulPartition, @@ -381,7 +351,7 @@ func TestExportedServices_Validate(t *testing.T) { namespaceEnabled: true, partitionsEnabled: true, expectedErrMsgs: []string{ - `service consumer must define at least one of Peer, Partition, or SamenessGroup`, + `spec.services[0].consumers[0]: Invalid value: v1alpha1.ServiceConsumer{Partition:"", Peer:""}: either partition or peer must be specified.`, }, }, "partition provided when partitions are disabled": { @@ -406,7 +376,7 @@ func TestExportedServices_Validate(t *testing.T) { namespaceEnabled: true, partitionsEnabled: false, expectedErrMsgs: []string{ - `spec.services[0].consumers[0].partition: Invalid value: "test-partition": Consul Admin Partitions need to be enabled to specify partition.`, + `spec.services[0].consumers[0].partitions: Invalid value: "test-partition": Consul Admin Partitions need to be enabled to specify partition.`, }, }, "namespace provided when namespaces are disabled": { @@ -434,81 +404,6 @@ func TestExportedServices_Validate(t *testing.T) { `spec.services[0]: Invalid value: "frontend": Consul Namespaces must be enabled to specify service namespace.`, }, }, - "exporting to all partitions is not supported": { - input: &ExportedServices{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.DefaultConsulPartition, - }, - Spec: ExportedServicesSpec{ - Services: []ExportedService{ - { - Name: "service-frontend", - Namespace: "frontend", - Consumers: []ServiceConsumer{ - { - Partition: "*", - }, - }, - }, - }, - }, - }, - namespaceEnabled: true, - partitionsEnabled: true, - expectedErrMsgs: []string{ - `exporting to all partitions (wildcard) is not supported`, - }, - }, - "exporting to all peers (wildcard) is not supported": { - input: &ExportedServices{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.DefaultConsulPartition, - }, - Spec: ExportedServicesSpec{ - Services: []ExportedService{ - { - Name: "service-frontend", - Namespace: "frontend", - Consumers: []ServiceConsumer{ - { - Peer: "*", - }, - }, - }, - }, - }, - }, - namespaceEnabled: true, - partitionsEnabled: true, - expectedErrMsgs: []string{ - `exporting to all peers (wildcard) is not supported`, - }, - }, - "exporting to all sameness groups (wildcard) is not supported": { - input: &ExportedServices{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.DefaultConsulPartition, - }, - Spec: ExportedServicesSpec{ - Services: []ExportedService{ - { - Name: "service-frontend", - Namespace: "frontend", - Consumers: []ServiceConsumer{ - { - SamenessGroup: "*", - }, - }, - }, - }, - }, - }, - namespaceEnabled: true, - partitionsEnabled: true, - expectedErrMsgs: []string{ - `exporting to all sameness groups (wildcard) is not supported`, - }, - }, "multiple errors": { input: &ExportedServices{ ObjectMeta: metav1.ObjectMeta{ @@ -525,10 +420,6 @@ func TestExportedServices_Validate(t *testing.T) { Peer: "second-peer", }, {}, - { - SamenessGroup: "sg2", - Partition: "partition2", - }, }, }, }, @@ -537,9 +428,8 @@ func TestExportedServices_Validate(t *testing.T) { namespaceEnabled: true, partitionsEnabled: true, expectedErrMsgs: []string{ - `spec.services[0].consumers[0]: Invalid value: v1alpha1.ServiceConsumer{Partition:"second", Peer:"second-peer", SamenessGroup:""}: service consumer must define at most one of Peer, Partition, or SamenessGroup`, - `spec.services[0].consumers[1]: Invalid value: v1alpha1.ServiceConsumer{Partition:"", Peer:"", SamenessGroup:""}: service consumer must define at least one of Peer, Partition, or SamenessGroup`, - `spec.services[0].consumers[2]: Invalid value: v1alpha1.ServiceConsumer{Partition:"partition2", Peer:"", SamenessGroup:"sg2"}: service consumer must define at most one of Peer, Partition, or SamenessGroup`, + `spec.services[0].consumers[0]: Invalid value: v1alpha1.ServiceConsumer{Partition:"second", Peer:"second-peer"}: both partition and peer cannot be specified.`, + `spec.services[0].consumers[1]: Invalid value: v1alpha1.ServiceConsumer{Partition:"", Peer:""}: either partition or peer must be specified.`, }, }, } diff --git a/control-plane/api/v1alpha1/exportedservices_webhook.go b/control-plane/api/v1alpha1/exportedservices_webhook.go index 6c870427ac..5a3d2cb2f1 100644 --- a/control-plane/api/v1alpha1/exportedservices_webhook.go +++ b/control-plane/api/v1alpha1/exportedservices_webhook.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/exportedservices_webhook_test.go b/control-plane/api/v1alpha1/exportedservices_webhook_test.go index 7cca7ff915..3a66fbdd9c 100644 --- a/control-plane/api/v1alpha1/exportedservices_webhook_test.go +++ b/control-plane/api/v1alpha1/exportedservices_webhook_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( @@ -123,7 +120,7 @@ func TestValidateExportedServices(t *testing.T) { Partition: "", }, expAllow: false, - expErrMessage: "exportedservices.consul.hashicorp.com \"default\" is invalid: spec.services[0].consumers[0].partition: Invalid value: \"other\": Consul Admin Partitions need to be enabled to specify partition.", + expErrMessage: "exportedservices.consul.hashicorp.com \"default\" is invalid: spec.services[0].consumers[0].partitions: Invalid value: \"other\": Consul Admin Partitions need to be enabled to specify partition.", }, "no services": { existingResources: []runtime.Object{}, diff --git a/control-plane/api/v1alpha1/groupversion_info.go b/control-plane/api/v1alpha1/groupversion_info.go index 3657e9b048..cdbe085af4 100644 --- a/control-plane/api/v1alpha1/groupversion_info.go +++ b/control-plane/api/v1alpha1/groupversion_info.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - // Package v1alpha1 contains API Schema definitions for the consul.hashicorp.com v1alpha1 API group // +kubebuilder:object:generate=true // +groupName=consul.hashicorp.com diff --git a/control-plane/api/v1alpha1/ingressgateway_types.go b/control-plane/api/v1alpha1/ingressgateway_types.go index 64e024fbd5..c94b6e1458 100644 --- a/control-plane/api/v1alpha1/ingressgateway_types.go +++ b/control-plane/api/v1alpha1/ingressgateway_types.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/ingressgateway_types_test.go b/control-plane/api/v1alpha1/ingressgateway_types_test.go index dd1c3835e0..4942d38e11 100644 --- a/control-plane/api/v1alpha1/ingressgateway_types_test.go +++ b/control-plane/api/v1alpha1/ingressgateway_types_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/ingressgateway_webhook.go b/control-plane/api/v1alpha1/ingressgateway_webhook.go index 04e31a0a3e..7f8ba37558 100644 --- a/control-plane/api/v1alpha1/ingressgateway_webhook.go +++ b/control-plane/api/v1alpha1/ingressgateway_webhook.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/jwtprovider_types.go b/control-plane/api/v1alpha1/jwtprovider_types.go deleted file mode 100644 index fee0ef9a78..0000000000 --- a/control-plane/api/v1alpha1/jwtprovider_types.go +++ /dev/null @@ -1,663 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package v1alpha1 - -import ( - "encoding/base64" - "encoding/json" - "net/url" - "time" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/hashicorp/consul-k8s/control-plane/api/common" - "github.com/hashicorp/consul/api" - capi "github.com/hashicorp/consul/api" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/validation/field" -) - -const ( - JWTProviderKubeKind string = "jwtprovider" -) - -func init() { - SchemeBuilder.Register(&JWTProvider{}, &JWTProviderList{}) -} - -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status - -// JWTProvider is the Schema for the jwtproviders API. -type JWTProvider struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - Spec JWTProviderSpec `json:"spec,omitempty"` - Status `json:"status,omitempty"` -} - -//+kubebuilder:object:root=true - -// JWTProviderList contains a list of JWTProvider. -type JWTProviderList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []JWTProvider `json:"items"` -} - -// JWTProviderSpec defines the desired state of JWTProvider -// +kubebuilder:printcolumn:name="Synced",type="string",JSONPath=".status.conditions[?(@.type==\"Synced\")].status",description="The sync status of the resource with Consul" -// +kubebuilder:printcolumn:name="Last Synced",type="date",JSONPath=".status.lastSyncedTime",description="The last successful synced time of the resource with Consul" -// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="The age of the resource" -type JWTProviderSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file - - // JSONWebKeySet defines a JSON Web Key Set, its location on disk, or the - // means with which to fetch a key set from a remote server. - JSONWebKeySet *JSONWebKeySet `json:"jsonWebKeySet,omitempty"` - - // Issuer is the entity that must have issued the JWT. - // This value must match the "iss" claim of the token. - Issuer string `json:"issuer,omitempty"` - - // Audiences is the set of audiences the JWT is allowed to access. - // If specified, all JWTs verified with this provider must address - // at least one of these to be considered valid. - Audiences []string `json:"audiences,omitempty"` - - // Locations where the JWT will be present in requests. - // Envoy will check all of these locations to extract a JWT. - // If no locations are specified Envoy will default to: - // 1. Authorization header with Bearer schema: - // "Authorization: Bearer " - // 2. accessToken query parameter. - Locations []*JWTLocation `json:"locations,omitempty"` - - // Forwarding defines rules for forwarding verified JWTs to the backend. - Forwarding *JWTForwardingConfig `json:"forwarding,omitempty"` - - // ClockSkewSeconds specifies the maximum allowable time difference - // from clock skew when validating the "exp" (Expiration) and "nbf" - // (Not Before) claims. - // - // Default value is 30 seconds. - ClockSkewSeconds int `json:"clockSkewSeconds,omitempty"` - - // CacheConfig defines configuration for caching the validation - // result for previously seen JWTs. Caching results can speed up - // verification when individual tokens are expected to be handled - // multiple times. - CacheConfig *JWTCacheConfig `json:"cacheConfig,omitempty"` -} - -type JWTLocations []*JWTLocation - -func (j JWTLocations) toConsul() []*capi.JWTLocation { - if j == nil { - return nil - } - result := make([]*capi.JWTLocation, 0, len(j)) - for _, loc := range j { - result = append(result, loc.toConsul()) - } - return result -} - -func (j JWTLocations) validate(path *field.Path) field.ErrorList { - var errs field.ErrorList - for i, loc := range j { - errs = append(errs, loc.validate(path.Index(i))...) - } - return errs -} - -// JWTLocation is a location where the JWT could be present in requests. -// -// Only one of Header, QueryParam, or Cookie can be specified. -type JWTLocation struct { - // Header defines how to extract a JWT from an HTTP request header. - Header *JWTLocationHeader `json:"header,omitempty"` - - // QueryParam defines how to extract a JWT from an HTTP request - // query parameter. - QueryParam *JWTLocationQueryParam `json:"queryParam,omitempty"` - - // Cookie defines how to extract a JWT from an HTTP request cookie. - Cookie *JWTLocationCookie `json:"cookie,omitempty"` -} - -func (j *JWTLocation) toConsul() *capi.JWTLocation { - if j == nil { - return nil - } - return &capi.JWTLocation{ - Header: j.Header.toConsul(), - QueryParam: j.QueryParam.toConsul(), - Cookie: j.Cookie.toConsul(), - } -} - -func (j *JWTLocation) validate(path *field.Path) field.ErrorList { - var errs field.ErrorList - if j == nil { - return append(errs, field.Invalid(path, j, "location must not be nil")) - } - - if 1 != countTrue( - j.Header != nil, - j.QueryParam != nil, - j.Cookie != nil, - ) { - asJSON, _ := json.Marshal(j) - return append(errs, field.Invalid(path, string(asJSON), "exactly one of 'header', 'queryParam', or 'cookie' is required")) - } - - errs = append(errs, j.Header.validate(path.Child("header"))...) - errs = append(errs, j.QueryParam.validate(path.Child("queryParam"))...) - errs = append(errs, j.Cookie.validate(path.Child("cookie"))...) - return errs -} - -// JWTLocationHeader defines how to extract a JWT from an HTTP -// request header. -type JWTLocationHeader struct { - // Name is the name of the header containing the token. - Name string `json:"name,omitempty"` - - // ValuePrefix is an optional prefix that precedes the token in the - // header value. - // For example, "Bearer " is a standard value prefix for a header named - // "Authorization", but the prefix is not part of the token itself: - // "Authorization: Bearer " - ValuePrefix string `json:"valuePrefix,omitempty"` - - // Forward defines whether the header with the JWT should be - // forwarded after the token has been verified. If false, the - // header will not be forwarded to the backend. - // - // Default value is false. - Forward bool `json:"forward,omitempty"` -} - -func (j *JWTLocationHeader) toConsul() *capi.JWTLocationHeader { - if j == nil { - return nil - } - return &capi.JWTLocationHeader{ - Name: j.Name, - ValuePrefix: j.ValuePrefix, - Forward: j.Forward, - } -} - -func (j *JWTLocationHeader) validate(path *field.Path) field.ErrorList { - var errs field.ErrorList - if j == nil { - return errs - } - - if j.Name == "" { - errs = append(errs, field.Invalid(path.Child("name"), j.Name, "JWT location header name is required")) - } - return errs -} - -// JWTLocationQueryParam defines how to extract a JWT from an HTTP request query parameter. -type JWTLocationQueryParam struct { - // Name is the name of the query param containing the token. - Name string `json:"name,omitempty"` -} - -func (j *JWTLocationQueryParam) toConsul() *capi.JWTLocationQueryParam { - if j == nil { - return nil - } - return &capi.JWTLocationQueryParam{ - Name: j.Name, - } -} - -func (j *JWTLocationQueryParam) validate(path *field.Path) field.ErrorList { - var errs field.ErrorList - if j == nil { - return nil - } - if j.Name == "" { - errs = append(errs, field.Invalid(path.Child("name"), j.Name, "JWT location query parameter name is required")) - } - return errs -} - -// JWTLocationCookie defines how to extract a JWT from an HTTP request cookie. -type JWTLocationCookie struct { - // Name is the name of the cookie containing the token. - Name string `json:"name,omitempty"` -} - -func (j *JWTLocationCookie) toConsul() *capi.JWTLocationCookie { - if j == nil { - return nil - } - return &capi.JWTLocationCookie{ - Name: j.Name, - } -} - -func (j *JWTLocationCookie) validate(path *field.Path) field.ErrorList { - var errs field.ErrorList - if j == nil { - return nil - } - if j.Name == "" { - errs = append(errs, field.Invalid(path.Child("name"), j.Name, "JWT location cookie name is required")) - } - return errs -} - -type JWTForwardingConfig struct { - // HeaderName is a header name to use when forwarding a verified - // JWT to the backend. The verified JWT could have been extracted - // from any location (query param, header, or cookie). - // - // The header value will be base64-URL-encoded, and will not be - // padded unless PadForwardPayloadHeader is true. - HeaderName string `json:"headerName,omitempty"` - - // PadForwardPayloadHeader determines whether padding should be added - // to the base64 encoded token forwarded with ForwardPayloadHeader. - // - // Default value is false. - PadForwardPayloadHeader bool `json:"padForwardPayloadHeader,omitempty"` -} - -func (j *JWTForwardingConfig) toConsul() *capi.JWTForwardingConfig { - if j == nil { - return nil - } - return &capi.JWTForwardingConfig{ - HeaderName: j.HeaderName, - PadForwardPayloadHeader: j.PadForwardPayloadHeader, - } -} - -func (j *JWTForwardingConfig) validate(path *field.Path) field.ErrorList { - var errs field.ErrorList - if j == nil { - return nil - } - - if j.HeaderName == "" { - errs = append(errs, field.Invalid(path.Child("HeaderName"), j.HeaderName, "JWT forwarding header name is required")) - } - return errs -} - -// JSONWebKeySet defines a key set, its location on disk, or the -// means with which to fetch a key set from a remote server. -// -// Exactly one of Local or Remote must be specified. -type JSONWebKeySet struct { - // Local specifies a local source for the key set. - Local *LocalJWKS `json:"local,omitempty"` - - // Remote specifies how to fetch a key set from a remote server. - Remote *RemoteJWKS `json:"remote,omitempty"` -} - -func (j *JSONWebKeySet) toConsul() *capi.JSONWebKeySet { - if j == nil { - return nil - } - - return &capi.JSONWebKeySet{ - Local: j.Local.toConsul(), - Remote: j.Remote.toConsul(), - } -} - -func (j *JSONWebKeySet) validate(path *field.Path) field.ErrorList { - var errs field.ErrorList - if j == nil { - return append(errs, field.Invalid(path, j, "jsonWebKeySet is required")) - } - - if countTrue(j.Local != nil, j.Remote != nil) != 1 { - asJSON, _ := json.Marshal(j) - return append(errs, field.Invalid(path, string(asJSON), "exactly one of 'local' or 'remote' is required")) - } - errs = append(errs, j.Local.validate(path.Child("local"))...) - errs = append(errs, j.Remote.validate(path.Child("remote"))...) - return errs -} - -// LocalJWKS specifies a location for a local JWKS. -// -// Only one of String and Filename can be specified. -type LocalJWKS struct { - // JWKS contains a base64 encoded JWKS. - JWKS string `json:"jwks,omitempty"` - - // Filename configures a location on disk where the JWKS can be - // found. If specified, the file must be present on the disk of ALL - // proxies with intentions referencing this provider. - Filename string `json:"filename,omitempty"` -} - -func (l *LocalJWKS) toConsul() *capi.LocalJWKS { - if l == nil { - return nil - } - return &capi.LocalJWKS{ - JWKS: l.JWKS, - Filename: l.Filename, - } -} - -func (l *LocalJWKS) validate(path *field.Path) field.ErrorList { - var errs field.ErrorList - if l == nil { - return errs - } - - if countTrue(l.JWKS != "", l.Filename != "") != 1 { - asJSON, _ := json.Marshal(l) - return append(errs, field.Invalid(path, string(asJSON), "Exactly one of 'jwks' or 'filename' is required")) - } - if l.JWKS != "" { - if _, err := base64.StdEncoding.DecodeString(l.JWKS); err != nil { - return append(errs, field.Invalid(path.Child("jwks"), l.JWKS, "JWKS must be a valid base64-encoded string")) - } - } - return errs -} - -// RemoteJWKS specifies how to fetch a JWKS from a remote server. -type RemoteJWKS struct { - // URI is the URI of the server to query for the JWKS. - URI string `json:"uri,omitempty"` - - // RequestTimeoutMs is the number of milliseconds to - // time out when making a request for the JWKS. - RequestTimeoutMs int `json:"requestTimeoutMs,omitempty"` - - // CacheDuration is the duration after which cached keys - // should be expired. - // - // Default value is 5 minutes. - CacheDuration time.Duration `json:"cacheDuration,omitempty"` - - // FetchAsynchronously indicates that the JWKS should be fetched - // when a client request arrives. Client requests will be paused - // until the JWKS is fetched. - // If false, the proxy listener will wait for the JWKS to be - // fetched before being activated. - // - // Default value is false. - FetchAsynchronously bool `json:"fetchAsynchronously,omitempty"` - - // RetryPolicy defines a retry policy for fetching JWKS. - // - // There is no retry by default. - RetryPolicy *JWKSRetryPolicy `json:"retryPolicy,omitempty"` -} - -func (r *RemoteJWKS) toConsul() *capi.RemoteJWKS { - if r == nil { - return nil - } - return &capi.RemoteJWKS{ - URI: r.URI, - RequestTimeoutMs: r.RequestTimeoutMs, - CacheDuration: r.CacheDuration, - FetchAsynchronously: r.FetchAsynchronously, - RetryPolicy: r.RetryPolicy.toConsul(), - } -} - -func (r *RemoteJWKS) validate(path *field.Path) field.ErrorList { - var errs field.ErrorList - if r == nil { - return errs - } - - if r.URI == "" { - errs = append(errs, field.Invalid(path.Child("uri"), r.URI, "remote JWKS URI is required")) - } else if _, err := url.ParseRequestURI(r.URI); err != nil { - errs = append(errs, field.Invalid(path.Child("uri"), r.URI, "remote JWKS URI is invalid")) - } - - errs = append(errs, r.RetryPolicy.validate(path.Child("retryPolicy"))...) - return errs -} - -type JWKSRetryPolicy struct { - // NumRetries is the number of times to retry fetching the JWKS. - // The retry strategy uses jittered exponential backoff with - // a base interval of 1s and max of 10s. - // - // Default value is 0. - NumRetries int `json:"numRetries,omitempty"` - - // Backoff policy - // - // Defaults to Envoy's backoff policy - RetryPolicyBackOff *RetryPolicyBackOff `json:"retryPolicyBackOff,omitempty"` -} - -func (j *JWKSRetryPolicy) toConsul() *capi.JWKSRetryPolicy { - if j == nil { - return nil - } - return &capi.JWKSRetryPolicy{ - NumRetries: j.NumRetries, - RetryPolicyBackOff: j.RetryPolicyBackOff.toConsul(), - } -} - -func (j *JWKSRetryPolicy) validate(path *field.Path) field.ErrorList { - var errs field.ErrorList - if j == nil { - return errs - } - - return append(errs, j.RetryPolicyBackOff.validate(path.Child("retryPolicyBackOff"))...) -} - -type RetryPolicyBackOff struct { - // BaseInterval to be used for the next back off computation - // - // The default value from envoy is 1s - BaseInterval time.Duration `json:"baseInterval,omitempty"` - - // MaxInternal to be used to specify the maximum interval between retries. - // Optional but should be greater or equal to BaseInterval. - // - // Defaults to 10 times BaseInterval - MaxInterval time.Duration `json:"maxInterval,omitempty"` -} - -func (r *RetryPolicyBackOff) toConsul() *capi.RetryPolicyBackOff { - if r == nil { - return nil - } - return &capi.RetryPolicyBackOff{ - BaseInterval: r.BaseInterval, - MaxInterval: r.MaxInterval, - } -} - -func (r *RetryPolicyBackOff) validate(path *field.Path) field.ErrorList { - var errs field.ErrorList - if r == nil { - return errs - } - - if (r.MaxInterval != 0) && (r.BaseInterval > r.MaxInterval) { - asJSON, _ := json.Marshal(r) - errs = append(errs, field.Invalid(path, string(asJSON), "maxInterval should be greater or equal to baseInterval")) - } - return errs -} - -type JWTCacheConfig struct { - // Size specifies the maximum number of JWT verification - // results to cache. - // - // Defaults to 0, meaning that JWT caching is disabled. - Size int `json:"size,omitempty"` -} - -func (j *JWTCacheConfig) toConsul() *capi.JWTCacheConfig { - if j == nil { - return nil - } - return &capi.JWTCacheConfig{ - Size: j.Size, - } -} - -func (j *JWTProvider) GetObjectMeta() metav1.ObjectMeta { - return j.ObjectMeta -} - -func (j *JWTProvider) AddFinalizer(name string) { - j.ObjectMeta.Finalizers = append(j.Finalizers(), name) -} - -func (j *JWTProvider) RemoveFinalizer(name string) { - var newFinalizers []string - for _, oldF := range j.Finalizers() { - if oldF != name { - newFinalizers = append(newFinalizers, oldF) - } - } - j.ObjectMeta.Finalizers = newFinalizers -} - -func (j *JWTProvider) Finalizers() []string { - return j.ObjectMeta.Finalizers -} - -func (j *JWTProvider) ConsulKind() string { - return capi.JWTProvider -} - -func (j *JWTProvider) ConsulGlobalResource() bool { - return true -} - -func (j *JWTProvider) ConsulMirroringNS() string { - return common.DefaultConsulNamespace -} - -func (j *JWTProvider) KubeKind() string { - return JWTProviderKubeKind -} - -func (j *JWTProvider) ConsulName() string { - return j.ObjectMeta.Name -} - -func (j *JWTProvider) KubernetesName() string { - return j.ObjectMeta.Name -} - -func (j *JWTProvider) SetSyncedCondition(status corev1.ConditionStatus, reason, message string) { - j.Status.Conditions = Conditions{ - { - Type: ConditionSynced, - Status: status, - LastTransitionTime: metav1.Now(), - Reason: reason, - Message: message, - }, - } -} - -func (j *JWTProvider) SetLastSyncedTime(time *metav1.Time) { - j.Status.LastSyncedTime = time -} - -// SyncedCondition gets the synced condition. -func (j *JWTProvider) SyncedCondition() (status corev1.ConditionStatus, reason, message string) { - cond := j.Status.GetCondition(ConditionSynced) - if cond == nil { - return corev1.ConditionUnknown, "", "" - } - return cond.Status, cond.Reason, cond.Message -} - -// SyncedConditionStatus returns the status of the synced condition. -func (j *JWTProvider) SyncedConditionStatus() corev1.ConditionStatus { - cond := j.Status.GetCondition(ConditionSynced) - if cond == nil { - return corev1.ConditionUnknown - } - return cond.Status -} - -// ToConsul converts the resource to the corresponding Consul API definition. -// Its return type is the generic ConfigEntry but a specific config entry -// type should be constructed e.g. ServiceConfigEntry. -func (j *JWTProvider) ToConsul(datacenter string) api.ConfigEntry { - return &capi.JWTProviderConfigEntry{ - Kind: j.ConsulKind(), - Name: j.ConsulName(), - JSONWebKeySet: j.Spec.JSONWebKeySet.toConsul(), - Issuer: j.Spec.Issuer, - Audiences: j.Spec.Audiences, - Locations: JWTLocations(j.Spec.Locations).toConsul(), - Forwarding: j.Spec.Forwarding.toConsul(), - ClockSkewSeconds: j.Spec.ClockSkewSeconds, - CacheConfig: j.Spec.CacheConfig.toConsul(), - Meta: meta(datacenter), - } -} - -// MatchesConsul returns true if the resource has the same fields as the Consul -// config entry. -func (j *JWTProvider) MatchesConsul(candidate api.ConfigEntry) bool { - configEntry, ok := candidate.(*capi.JWTProviderConfigEntry) - if !ok { - return false - } - // No datacenter is passed to ToConsul as we ignore the Meta field when checking for equality. - return cmp.Equal(j.ToConsul(""), configEntry, cmpopts.IgnoreFields(capi.JWTProviderConfigEntry{}, "Partition", "Namespace", "Meta", "ModifyIndex", "CreateIndex"), cmpopts.IgnoreUnexported(), cmpopts.EquateEmpty()) -} - -// Validate returns an error if the resource is invalid. -func (j *JWTProvider) Validate(consulMeta common.ConsulMeta) error { - var errs field.ErrorList - path := field.NewPath("spec") - - errs = append(errs, j.Spec.JSONWebKeySet.validate(path.Child("jsonWebKeySet"))...) - errs = append(errs, JWTLocations(j.Spec.Locations).validate(path.Child("locations"))...) - errs = append(errs, j.Spec.Forwarding.validate(path.Child("forwarding"))...) - if len(errs) > 0 { - return apierrors.NewInvalid( - schema.GroupKind{Group: ConsulHashicorpGroup, Kind: JWTProviderKubeKind}, - j.KubernetesName(), errs) - } - return nil -} - -// DefaultNamespaceFields sets Consul namespace fields on the config entry -// spec to their default values if namespaces are enabled. -func (j *JWTProvider) DefaultNamespaceFields(_ common.ConsulMeta) {} - -func countTrue(vals ...bool) int { - var result int - for _, v := range vals { - if v { - result++ - } - } - return result -} - -var _ common.ConfigEntryResource = (*JWTProvider)(nil) diff --git a/control-plane/api/v1alpha1/jwtprovider_types_test.go b/control-plane/api/v1alpha1/jwtprovider_types_test.go deleted file mode 100644 index 15a3e7a5d6..0000000000 --- a/control-plane/api/v1alpha1/jwtprovider_types_test.go +++ /dev/null @@ -1,730 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package v1alpha1 - -import ( - "testing" - "time" - - "github.com/hashicorp/consul-k8s/control-plane/api/common" - capi "github.com/hashicorp/consul/api" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// Test MatchesConsul for cases that should return true. -func TestJWTProvider_MatchesConsul(t *testing.T) { - cases := map[string]struct { - Ours JWTProvider - Theirs capi.ConfigEntry - Matches bool - }{ - "empty fields matches": { - Ours: JWTProvider{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-okta", - }, - Spec: JWTProviderSpec{}, - }, - Theirs: &capi.JWTProviderConfigEntry{ - Kind: capi.JWTProvider, - Name: "test-okta", - Namespace: "default", - CreateIndex: 1, - ModifyIndex: 2, - Meta: map[string]string{ - common.SourceKey: common.SourceValue, - common.DatacenterKey: "datacenter", - }, - }, - Matches: true, - }, - "all fields set matches": { - Ours: JWTProvider{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-okta2", - }, - Spec: JWTProviderSpec{ - JSONWebKeySet: &JSONWebKeySet{ - Local: &LocalJWKS{ - JWKS: "jwks-string", - Filename: "jwks-file", - }, - Remote: &RemoteJWKS{ - URI: "https://jwks.example.com", - RequestTimeoutMs: 567, - CacheDuration: 890, - FetchAsynchronously: true, - RetryPolicy: &JWKSRetryPolicy{ - NumRetries: 1, - RetryPolicyBackOff: &RetryPolicyBackOff{ - BaseInterval: 23, - MaxInterval: 456, - }, - }, - }, - }, - Issuer: "test-issuer", - Audiences: []string{"aud1", "aud2"}, - Locations: []*JWTLocation{ - { - Header: &JWTLocationHeader{ - Name: "jwt-header", - ValuePrefix: "my-bearer", - Forward: true, - }, - }, - { - QueryParam: &JWTLocationQueryParam{ - Name: "jwt-query-param", - }, - }, - { - Cookie: &JWTLocationCookie{ - Name: "jwt-cookie", - }, - }, - }, - Forwarding: &JWTForwardingConfig{ - HeaderName: "jwt-forward-header", - PadForwardPayloadHeader: true, - }, - ClockSkewSeconds: 357, - CacheConfig: &JWTCacheConfig{ - Size: 468, - }, - }, - }, - Theirs: &capi.JWTProviderConfigEntry{ - Kind: capi.JWTProvider, - Name: "test-okta2", - Namespace: "default", - JSONWebKeySet: &capi.JSONWebKeySet{ - Local: &capi.LocalJWKS{ - JWKS: "jwks-string", - Filename: "jwks-file", - }, - Remote: &capi.RemoteJWKS{ - URI: "https://jwks.example.com", - RequestTimeoutMs: 567, - CacheDuration: 890, - FetchAsynchronously: true, - RetryPolicy: &capi.JWKSRetryPolicy{ - NumRetries: 1, - RetryPolicyBackOff: &capi.RetryPolicyBackOff{ - BaseInterval: 23, - MaxInterval: 456, - }, - }, - }, - }, - Issuer: "test-issuer", - Audiences: []string{"aud1", "aud2"}, - Locations: []*capi.JWTLocation{ - { - Header: &capi.JWTLocationHeader{ - Name: "jwt-header", - ValuePrefix: "my-bearer", - Forward: true, - }, - }, - { - QueryParam: &capi.JWTLocationQueryParam{ - Name: "jwt-query-param", - }, - }, - { - Cookie: &capi.JWTLocationCookie{ - Name: "jwt-cookie", - }, - }, - }, - Forwarding: &capi.JWTForwardingConfig{ - HeaderName: "jwt-forward-header", - PadForwardPayloadHeader: true, - }, - ClockSkewSeconds: 357, - CacheConfig: &capi.JWTCacheConfig{ - Size: 468, - }, - }, - Matches: true, - }, - "mismatched types does not match": { - Ours: JWTProvider{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-okta3", - }, - Spec: JWTProviderSpec{}, - }, - Theirs: &capi.JWTProviderConfigEntry{}, - Matches: false, - }, - } - for name, c := range cases { - c := c - t.Run(name, func(t *testing.T) { - require.Equal(t, c.Matches, c.Ours.MatchesConsul(c.Theirs)) - }) - } -} - -func TestJWTProvider_ToConsul(t *testing.T) { - cases := map[string]struct { - Ours JWTProvider - Exp *capi.JWTProviderConfigEntry - }{ - "empty fields": { - Ours: JWTProvider{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-okta1", - }, - Spec: JWTProviderSpec{}, - }, - Exp: &capi.JWTProviderConfigEntry{ - Kind: capi.JWTProvider, - Name: "test-okta1", - Meta: map[string]string{ - common.SourceKey: common.SourceValue, - common.DatacenterKey: "datacenter", - }, - }, - }, - "every field set": { - Ours: JWTProvider{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-okta2", - }, - Spec: JWTProviderSpec{ - JSONWebKeySet: &JSONWebKeySet{ - Local: &LocalJWKS{ - JWKS: "jwks-string", - Filename: "jwks-file", - }, - Remote: &RemoteJWKS{ - URI: "https://jwks.example.com", - RequestTimeoutMs: 567, - CacheDuration: 890, - FetchAsynchronously: true, - RetryPolicy: &JWKSRetryPolicy{ - NumRetries: 1, - RetryPolicyBackOff: &RetryPolicyBackOff{ - BaseInterval: 23, - MaxInterval: 456, - }, - }, - }, - }, - Issuer: "test-issuer", - Audiences: []string{"aud1", "aud2"}, - Locations: []*JWTLocation{ - { - Header: &JWTLocationHeader{ - Name: "jwt-header", - ValuePrefix: "my-bearer", - Forward: true, - }, - }, - { - QueryParam: &JWTLocationQueryParam{ - Name: "jwt-query-param", - }, - }, - { - Cookie: &JWTLocationCookie{ - Name: "jwt-cookie", - }, - }, - }, - Forwarding: &JWTForwardingConfig{ - HeaderName: "jwt-forward-header", - PadForwardPayloadHeader: true, - }, - ClockSkewSeconds: 357, - CacheConfig: &JWTCacheConfig{ - Size: 468, - }, - }, - }, - Exp: &capi.JWTProviderConfigEntry{ - Kind: capi.JWTProvider, - Name: "test-okta2", - JSONWebKeySet: &capi.JSONWebKeySet{ - Local: &capi.LocalJWKS{ - JWKS: "jwks-string", - Filename: "jwks-file", - }, - Remote: &capi.RemoteJWKS{ - URI: "https://jwks.example.com", - RequestTimeoutMs: 567, - CacheDuration: 890, - FetchAsynchronously: true, - RetryPolicy: &capi.JWKSRetryPolicy{ - NumRetries: 1, - RetryPolicyBackOff: &capi.RetryPolicyBackOff{ - BaseInterval: 23, - MaxInterval: 456, - }, - }, - }, - }, - Issuer: "test-issuer", - Audiences: []string{"aud1", "aud2"}, - Locations: []*capi.JWTLocation{ - { - Header: &capi.JWTLocationHeader{ - Name: "jwt-header", - ValuePrefix: "my-bearer", - Forward: true, - }, - }, - { - QueryParam: &capi.JWTLocationQueryParam{ - Name: "jwt-query-param", - }, - }, - { - Cookie: &capi.JWTLocationCookie{ - Name: "jwt-cookie", - }, - }, - }, - Forwarding: &capi.JWTForwardingConfig{ - HeaderName: "jwt-forward-header", - PadForwardPayloadHeader: true, - }, - ClockSkewSeconds: 357, - CacheConfig: &capi.JWTCacheConfig{ - Size: 468, - }, - Meta: map[string]string{ - common.SourceKey: common.SourceValue, - common.DatacenterKey: "datacenter", - }, - }, - }, - } - for name, c := range cases { - t.Run(name, func(t *testing.T) { - act := c.Ours.ToConsul("datacenter") - mesh, ok := act.(*capi.JWTProviderConfigEntry) - require.True(t, ok, "could not cast") - require.Equal(t, c.Exp, mesh) - }) - } -} - -func TestJWTProvider_Validate(t *testing.T) { - cases := map[string]struct { - input *JWTProvider - expectedErrMsgs []string - consulMeta common.ConsulMeta - }{ - "valid - local jwks": { - input: &JWTProvider{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-okta1", - }, - Spec: JWTProviderSpec{ - JSONWebKeySet: &JSONWebKeySet{ - Local: &LocalJWKS{ - Filename: "jwks.txt", - }, - }, - }, - Status: Status{}, - }, - expectedErrMsgs: nil, - }, - - "valid - remote jwks": { - input: &JWTProvider{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-jwt-provider", - }, - Spec: JWTProviderSpec{ - JSONWebKeySet: &JSONWebKeySet{ - Remote: &RemoteJWKS{ - URI: "https://jwks.example.com", - FetchAsynchronously: true, - }, - }, - Locations: []*JWTLocation{ - { - Header: &JWTLocationHeader{ - Name: "Authorization", - }, - }, - }, - Forwarding: &JWTForwardingConfig{ - HeaderName: "jwt-forward-header", - }, - }, - }, - expectedErrMsgs: nil, - }, - - "valid - remote jwks with all fields": { - input: &JWTProvider{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-jwt-provider", - }, - Spec: JWTProviderSpec{ - JSONWebKeySet: &JSONWebKeySet{ - Remote: &RemoteJWKS{ - URI: "https://jwks.example.com", - RequestTimeoutMs: 5000, - CacheDuration: 10 * time.Second, - FetchAsynchronously: true, - RetryPolicy: &JWKSRetryPolicy{ - NumRetries: 3, - RetryPolicyBackOff: &RetryPolicyBackOff{ - BaseInterval: 5 * time.Second, - MaxInterval: 20 * time.Second, - }, - }, - }, - }, - Issuer: "test-issuer", - Audiences: []string{"aud1", "aud2"}, - Locations: []*JWTLocation{ - { - Header: &JWTLocationHeader{ - Name: "Authorization", - ValuePrefix: "Bearer", - Forward: true, - }, - }, - { - QueryParam: &JWTLocationQueryParam{ - Name: "access-token", - }, - }, - { - Cookie: &JWTLocationCookie{ - Name: "session-id", - }, - }, - }, - Forwarding: &JWTForwardingConfig{ - HeaderName: "jwt-forward-header", - PadForwardPayloadHeader: true, - }, - ClockSkewSeconds: 20, - CacheConfig: &JWTCacheConfig{ - Size: 30, - }, - }, - }, - expectedErrMsgs: nil, - }, - - "invalid - nil jwks": { - input: &JWTProvider{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-no-jwks", - }, - Spec: JWTProviderSpec{ - JSONWebKeySet: nil, - }, - }, - expectedErrMsgs: []string{ - `jwtprovider.consul.hashicorp.com "test-no-jwks" is invalid: spec.jsonWebKeySet: Invalid value: "null": jsonWebKeySet is required`, - }, - }, - - "invalid - empty jwks": { - input: &JWTProvider{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-no-jwks", - }, - Spec: JWTProviderSpec{ - JSONWebKeySet: &JSONWebKeySet{}, - }, - }, - expectedErrMsgs: []string{ - `jwtprovider.consul.hashicorp.com "test-no-jwks" is invalid: spec.jsonWebKeySet: Invalid value: "{}": exactly one of 'local' or 'remote' is required`, - }, - }, - - "invalid - local jwks with non-base64 string": { - input: &JWTProvider{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-jwks-base64", - }, - Spec: JWTProviderSpec{ - JSONWebKeySet: &JSONWebKeySet{ - Local: &LocalJWKS{ - JWKS: "not base64 encoded", - }, - }, - }, - }, - expectedErrMsgs: []string{ - `jwtprovider.consul.hashicorp.com "test-jwks-base64" is invalid: spec.jsonWebKeySet.local.jwks: Invalid value: "not base64 encoded": JWKS must be a valid base64-encoded string`, - }, - }, - - "invalid - both local and remote jwks set": { - input: &JWTProvider{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-jwks-local-and-remote", - }, - Spec: JWTProviderSpec{ - JSONWebKeySet: &JSONWebKeySet{ - Local: &LocalJWKS{Filename: "jwks.txt"}, - Remote: &RemoteJWKS{ - URI: "https://jwks.example.com", - }, - }, - }, - }, - expectedErrMsgs: []string{ - `jwtprovider.consul.hashicorp.com "test-jwks-local-and-remote" is invalid: spec.jsonWebKeySet: Invalid value: "{\"local\":{\"filename\":\"jwks.txt\"},\"remote\":{\"uri\":\"https://jwks.example.com\"}}": exactly one of 'local' or 'remote' is required`, - }, - }, - - "invalid - remote jwks missing uri": { - input: &JWTProvider{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-jwks-missing-uri", - }, - Spec: JWTProviderSpec{ - JSONWebKeySet: &JSONWebKeySet{ - Remote: &RemoteJWKS{ - FetchAsynchronously: true, - }, - }, - }, - }, - expectedErrMsgs: []string{ - `jwtprovider.consul.hashicorp.com "test-jwks-missing-uri" is invalid: spec.jsonWebKeySet.remote.uri: Invalid value: "": remote JWKS URI is required`, - }, - }, - - "invalid - remote jwks invalid uri": { - input: &JWTProvider{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-jwks-invalid-uri", - }, - Spec: JWTProviderSpec{ - JSONWebKeySet: &JSONWebKeySet{ - Remote: &RemoteJWKS{ - URI: "invalid-uri", - }, - }, - }, - }, - expectedErrMsgs: []string{ - `jwtprovider.consul.hashicorp.com "test-jwks-invalid-uri" is invalid: spec.jsonWebKeySet.remote.uri: Invalid value: "invalid-uri": remote JWKS URI is invalid`, - }, - }, - - "invalid - JWT location with all fields": { - input: &JWTProvider{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-jwks-all-locations", - }, - Spec: JWTProviderSpec{ - JSONWebKeySet: &JSONWebKeySet{ - Remote: &RemoteJWKS{ - URI: "https://jwks.example.com", - }, - }, - Locations: []*JWTLocation{ - { - Header: &JWTLocationHeader{ - Name: "jwt-header", - }, - QueryParam: &JWTLocationQueryParam{ - Name: "jwt-query-param", - }, - Cookie: &JWTLocationCookie{ - Name: "jwt-cookie", - }, - }, - }, - }, - }, - expectedErrMsgs: []string{ - `jwtprovider.consul.hashicorp.com "test-jwks-all-locations" is invalid: spec.locations[0]: Invalid value: "{\"header\":{\"name\":\"jwt-header\"},\"queryParam\":{\"name\":\"jwt-query-param\"},\"cookie\":{\"name\":\"jwt-cookie\"}}": exactly one of 'header', 'queryParam', or 'cookie' is required`, - }, - }, - - "invalid - JWT location with two fields": { - input: &JWTProvider{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-jwks-two-locations", - }, - Spec: JWTProviderSpec{ - JSONWebKeySet: &JSONWebKeySet{ - Remote: &RemoteJWKS{ - URI: "https://jwks.example.com", - }, - }, - Locations: []*JWTLocation{ - { - Header: &JWTLocationHeader{ - Name: "jwt-header", - }, - Cookie: &JWTLocationCookie{ - Name: "jwt-cookie", - }, - }, - }, - }, - }, - expectedErrMsgs: []string{ - `jwtprovider.consul.hashicorp.com "test-jwks-two-locations" is invalid: spec.locations[0]: Invalid value: "{\"header\":{\"name\":\"jwt-header\"},\"cookie\":{\"name\":\"jwt-cookie\"}}": exactly one of 'header', 'queryParam', or 'cookie' is required`, - }, - }, - - "invalid - remote jwks retry policy maxInterval < baseInterval": { - input: &JWTProvider{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-jwks-retry-intervals", - }, - Spec: JWTProviderSpec{ - JSONWebKeySet: &JSONWebKeySet{ - Remote: &RemoteJWKS{ - URI: "https://jwks.example.com", - RetryPolicy: &JWKSRetryPolicy{ - NumRetries: 0, - RetryPolicyBackOff: &RetryPolicyBackOff{ - BaseInterval: 100 * time.Second, - MaxInterval: 10 * time.Second, - }, - }, - }, - }, - }, - }, - expectedErrMsgs: []string{ - `jwtprovider.consul.hashicorp.com "test-jwks-retry-intervals" is invalid: spec.jsonWebKeySet.remote.retryPolicy.retryPolicyBackOff: Invalid value: "{\"baseInterval\":100000000000,\"maxInterval\":10000000000}": maxInterval should be greater or equal to baseInterval`, - }, - }, - } - - for name, testCase := range cases { - t.Run(name, func(t *testing.T) { - err := testCase.input.Validate(testCase.consulMeta) - if len(testCase.expectedErrMsgs) != 0 { - require.Error(t, err) - for _, s := range testCase.expectedErrMsgs { - require.Contains(t, err.Error(), s) - } - } else { - require.NoError(t, err) - } - }) - } - -} - -func TestJWTProvider_AddFinalizer(t *testing.T) { - jwt := &JWTProvider{} - jwt.AddFinalizer("finalizer") - require.Equal(t, []string{"finalizer"}, jwt.ObjectMeta.Finalizers) -} - -func TestJWTProvider_RemoveFinalizer(t *testing.T) { - jwt := &JWTProvider{ - ObjectMeta: metav1.ObjectMeta{ - Finalizers: []string{"f1", "f2"}, - }, - } - jwt.RemoveFinalizer("f1") - require.Equal(t, []string{"f2"}, jwt.ObjectMeta.Finalizers) -} - -func TestJWTProvider_SetSyncedCondition(t *testing.T) { - jwt := &JWTProvider{} - jwt.SetSyncedCondition(corev1.ConditionTrue, "reason", "message") - - require.Equal(t, corev1.ConditionTrue, jwt.Status.Conditions[0].Status) - require.Equal(t, "reason", jwt.Status.Conditions[0].Reason) - require.Equal(t, "message", jwt.Status.Conditions[0].Message) - now := metav1.Now() - require.True(t, jwt.Status.Conditions[0].LastTransitionTime.Before(&now)) -} - -func TestJWTProvider_SetLastSyncedTime(t *testing.T) { - jwt := &JWTProvider{} - syncedTime := metav1.NewTime(time.Now()) - jwt.SetLastSyncedTime(&syncedTime) - require.Equal(t, &syncedTime, jwt.Status.LastSyncedTime) -} - -func TestJWTProvider_GetSyncedConditionStatus(t *testing.T) { - cases := []corev1.ConditionStatus{ - corev1.ConditionUnknown, - corev1.ConditionFalse, - corev1.ConditionTrue, - } - for _, status := range cases { - t.Run(string(status), func(t *testing.T) { - jwt := &JWTProvider{ - Status: Status{ - Conditions: []Condition{{ - Type: ConditionSynced, - Status: status, - }}, - }, - } - - require.Equal(t, status, jwt.SyncedConditionStatus()) - }) - } -} - -func TestJWTProvider_GetConditionWhenStatusNil(t *testing.T) { - require.Nil(t, (&JWTProvider{}).GetCondition(ConditionSynced)) -} - -func TestJWTProvider_SyncedConditionStatusWhenStatusNil(t *testing.T) { - require.Equal(t, corev1.ConditionUnknown, (&JWTProvider{}).SyncedConditionStatus()) -} - -func TestJWTProvider_SyncedConditionWhenStatusNil(t *testing.T) { - status, reason, message := (&JWTProvider{}).SyncedCondition() - require.Equal(t, corev1.ConditionUnknown, status) - require.Equal(t, "", reason) - require.Equal(t, "", message) -} - -func TestJWTProvider_ConsulKind(t *testing.T) { - require.Equal(t, capi.JWTProvider, (&JWTProvider{}).ConsulKind()) -} - -func TestJWTProvider_KubeKind(t *testing.T) { - require.Equal(t, "jwtprovider", (&JWTProvider{}).KubeKind()) -} - -func TestJWTProvider_ConsulName(t *testing.T) { - require.Equal(t, "foo", (&JWTProvider{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}).ConsulName()) -} - -func TestJWTProvider_KubernetesName(t *testing.T) { - require.Equal(t, "foo", (&JWTProvider{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}).KubernetesName()) -} - -func TestJWTProvider_ConsulNamespace(t *testing.T) { - require.Equal(t, common.DefaultConsulNamespace, (&JWTProvider{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}}).ConsulMirroringNS()) -} - -func TestJWTProvider_ConsulGlobalResource(t *testing.T) { - require.True(t, (&JWTProvider{}).ConsulGlobalResource()) -} - -func TestJWTProvider_ObjectMeta(t *testing.T) { - meta := metav1.ObjectMeta{ - Name: "name", - Namespace: "namespace", - } - jwt := &JWTProvider{ - ObjectMeta: meta, - } - require.Equal(t, meta, jwt.GetObjectMeta()) -} diff --git a/control-plane/api/v1alpha1/jwtprovider_webhook.go b/control-plane/api/v1alpha1/jwtprovider_webhook.go deleted file mode 100644 index c434c83c01..0000000000 --- a/control-plane/api/v1alpha1/jwtprovider_webhook.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package v1alpha1 - -import ( - "context" - "net/http" - - "github.com/go-logr/logr" - "github.com/hashicorp/consul-k8s/control-plane/api/common" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" -) - -// +kubebuilder:object:generate=false - -type JWTProviderWebhook struct { - Logger logr.Logger - - // ConsulMeta contains metadata specific to the Consul installation. - ConsulMeta common.ConsulMeta - - decoder *admission.Decoder - client.Client -} - -// NOTE: The path value in the below line is the path to the webhook. -// If it is updated, run code-gen, update subcommand/controller/command.go -// and the consul-helm value for the path to the webhook. -// -// NOTE: The below line cannot be combined with any other comment. If it is it will break the code generation. -// -// +kubebuilder:webhook:verbs=create;update,path=/mutate-v1alpha1-jwtprovider,mutating=true,failurePolicy=fail,groups=consul.hashicorp.com,resources=jwtproviders,versions=v1alpha1,name=mutate-jwtprovider.consul.hashicorp.com,sideEffects=None,admissionReviewVersions=v1beta1;v1 - -func (v *JWTProviderWebhook) Handle(ctx context.Context, req admission.Request) admission.Response { - var resource JWTProvider - err := v.decoder.Decode(req, &resource) - if err != nil { - return admission.Errored(http.StatusBadRequest, err) - } - - return common.ValidateConfigEntry(ctx, req, v.Logger, v, &resource, v.ConsulMeta) -} - -func (v *JWTProviderWebhook) List(ctx context.Context) ([]common.ConfigEntryResource, error) { - var resourceList JWTProviderList - if err := v.Client.List(ctx, &resourceList); err != nil { - return nil, err - } - var entries []common.ConfigEntryResource - for _, item := range resourceList.Items { - entries = append(entries, common.ConfigEntryResource(&item)) - } - return entries, nil -} - -func (v *JWTProviderWebhook) InjectDecoder(d *admission.Decoder) error { - v.decoder = d - return nil -} diff --git a/control-plane/api/v1alpha1/mesh_types.go b/control-plane/api/v1alpha1/mesh_types.go index 162132a47a..502e567829 100644 --- a/control-plane/api/v1alpha1/mesh_types.go +++ b/control-plane/api/v1alpha1/mesh_types.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( @@ -51,9 +48,6 @@ type MeshList struct { type MeshSpec struct { // TransparentProxy controls the configuration specific to proxies in "transparent" mode. Added in v1.10.0. TransparentProxy TransparentProxyMeshConfig `json:"transparentProxy,omitempty"` - // AllowEnablingPermissiveMutualTLS must be true in order to allow setting - // MutualTLSMode=permissive in either service-defaults or proxy-defaults. - AllowEnablingPermissiveMutualTLS bool `json:"allowEnablingPermissiveMutualTLS,omitempty"` // TLS defines the TLS configuration for the service mesh. TLS *MeshTLSConfig `json:"tls,omitempty"` // HTTP defines the HTTP configuration for the service mesh. @@ -195,12 +189,11 @@ func (in *Mesh) SetLastSyncedTime(time *metav1.Time) { func (in *Mesh) ToConsul(datacenter string) capi.ConfigEntry { return &capi.MeshConfigEntry{ - TransparentProxy: in.Spec.TransparentProxy.toConsul(), - AllowEnablingPermissiveMutualTLS: in.Spec.AllowEnablingPermissiveMutualTLS, - TLS: in.Spec.TLS.toConsul(), - HTTP: in.Spec.HTTP.toConsul(), - Peering: in.Spec.Peering.toConsul(), - Meta: meta(datacenter), + TransparentProxy: in.Spec.TransparentProxy.toConsul(), + TLS: in.Spec.TLS.toConsul(), + HTTP: in.Spec.HTTP.toConsul(), + Peering: in.Spec.Peering.toConsul(), + Meta: meta(datacenter), } } diff --git a/control-plane/api/v1alpha1/mesh_types_test.go b/control-plane/api/v1alpha1/mesh_types_test.go index f2ea714f60..392c38d354 100644 --- a/control-plane/api/v1alpha1/mesh_types_test.go +++ b/control-plane/api/v1alpha1/mesh_types_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( @@ -48,7 +45,6 @@ func TestMesh_MatchesConsul(t *testing.T) { TransparentProxy: TransparentProxyMeshConfig{ MeshDestinationsOnly: true, }, - AllowEnablingPermissiveMutualTLS: true, TLS: &MeshTLSConfig{ Incoming: &MeshDirectionalTLSConfig{ TLSMinVersion: "TLSv1_0", @@ -73,7 +69,6 @@ func TestMesh_MatchesConsul(t *testing.T) { TransparentProxy: capi.TransparentProxyMeshConfig{ MeshDestinationsOnly: true, }, - AllowEnablingPermissiveMutualTLS: true, TLS: &capi.MeshTLSConfig{ Incoming: &capi.MeshDirectionalTLSConfig{ TLSMinVersion: "TLSv1_0", @@ -150,7 +145,6 @@ func TestMesh_ToConsul(t *testing.T) { TransparentProxy: TransparentProxyMeshConfig{ MeshDestinationsOnly: true, }, - AllowEnablingPermissiveMutualTLS: true, TLS: &MeshTLSConfig{ Incoming: &MeshDirectionalTLSConfig{ TLSMinVersion: "TLSv1_0", @@ -175,7 +169,6 @@ func TestMesh_ToConsul(t *testing.T) { TransparentProxy: capi.TransparentProxyMeshConfig{ MeshDestinationsOnly: true, }, - AllowEnablingPermissiveMutualTLS: true, TLS: &capi.MeshTLSConfig{ Incoming: &capi.MeshDirectionalTLSConfig{ TLSMinVersion: "TLSv1_0", diff --git a/control-plane/api/v1alpha1/mesh_webhook.go b/control-plane/api/v1alpha1/mesh_webhook.go index 1c0ea3088e..5c714c4e5f 100644 --- a/control-plane/api/v1alpha1/mesh_webhook.go +++ b/control-plane/api/v1alpha1/mesh_webhook.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/mesh_webhook_test.go b/control-plane/api/v1alpha1/mesh_webhook_test.go index 2266a6b77e..83cedb0ba4 100644 --- a/control-plane/api/v1alpha1/mesh_webhook_test.go +++ b/control-plane/api/v1alpha1/mesh_webhook_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/peeringacceptor_types.go b/control-plane/api/v1alpha1/peeringacceptor_types.go index e1ca013475..032870cb80 100644 --- a/control-plane/api/v1alpha1/peeringacceptor_types.go +++ b/control-plane/api/v1alpha1/peeringacceptor_types.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/peeringacceptor_types_test.go b/control-plane/api/v1alpha1/peeringacceptor_types_test.go index d7c05e7e2c..33d437f46a 100644 --- a/control-plane/api/v1alpha1/peeringacceptor_types_test.go +++ b/control-plane/api/v1alpha1/peeringacceptor_types_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/peeringacceptor_webhook.go b/control-plane/api/v1alpha1/peeringacceptor_webhook.go index 2bb48b3580..60367c1384 100644 --- a/control-plane/api/v1alpha1/peeringacceptor_webhook.go +++ b/control-plane/api/v1alpha1/peeringacceptor_webhook.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/peeringacceptor_webhook_test.go b/control-plane/api/v1alpha1/peeringacceptor_webhook_test.go index 251ab87c8e..02ddbca588 100644 --- a/control-plane/api/v1alpha1/peeringacceptor_webhook_test.go +++ b/control-plane/api/v1alpha1/peeringacceptor_webhook_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/peeringdialer_types.go b/control-plane/api/v1alpha1/peeringdialer_types.go index 89c16bf3ad..4ddde3fe2f 100644 --- a/control-plane/api/v1alpha1/peeringdialer_types.go +++ b/control-plane/api/v1alpha1/peeringdialer_types.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/peeringdialer_types_test.go b/control-plane/api/v1alpha1/peeringdialer_types_test.go index 69c3569acd..7e358facb8 100644 --- a/control-plane/api/v1alpha1/peeringdialer_types_test.go +++ b/control-plane/api/v1alpha1/peeringdialer_types_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/peeringdialer_webhook.go b/control-plane/api/v1alpha1/peeringdialer_webhook.go index 76c2011e4c..fc0b1c38f6 100644 --- a/control-plane/api/v1alpha1/peeringdialer_webhook.go +++ b/control-plane/api/v1alpha1/peeringdialer_webhook.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/peeringdialer_webhook_test.go b/control-plane/api/v1alpha1/peeringdialer_webhook_test.go index 42372451d1..df69923a92 100644 --- a/control-plane/api/v1alpha1/peeringdialer_webhook_test.go +++ b/control-plane/api/v1alpha1/peeringdialer_webhook_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/proxydefaults_types.go b/control-plane/api/v1alpha1/proxydefaults_types.go index 7b1529b941..215ec708ff 100644 --- a/control-plane/api/v1alpha1/proxydefaults_types.go +++ b/control-plane/api/v1alpha1/proxydefaults_types.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( @@ -67,17 +64,6 @@ type ProxyDefaultsSpec struct { // Note: This cannot be set using the CRD and should be set using annotations on the // services that are part of the mesh. TransparentProxy *TransparentProxy `json:"transparentProxy,omitempty"` - // MutualTLSMode controls whether mutual TLS is required for all incoming - // connections when transparent proxy is enabled. This can be set to - // "permissive" or "strict". "strict" is the default which requires mutual - // TLS for incoming connections. In the insecure "permissive" mode, - // connections to the sidecar proxy public listener port require mutual - // TLS, but connections to the service port do not require mutual TLS and - // are proxied to the application unmodified. Note: Intentions are not - // enforced for non-mTLS connections. To keep your services secure, we - // recommend using "strict" mode whenever possible and enabling - // "permissive" mode only when necessary. - MutualTLSMode MutualTLSMode `json:"mutualTLSMode,omitempty"` // Config is an arbitrary map of configuration values used by Connect proxies. // Any values that your proxy allows can be configured globally here. // Supports JSON config values. See https://www.consul.io/docs/connect/proxies/envoy#configuration-formatting @@ -89,15 +75,6 @@ type ProxyDefaultsSpec struct { MeshGateway MeshGateway `json:"meshGateway,omitempty"` // Expose controls the default expose path configuration for Envoy. Expose Expose `json:"expose,omitempty"` - // AccessLogs controls all envoy instances' access logging configuration. - AccessLogs *AccessLogs `json:"accessLogs,omitempty"` - // EnvoyExtensions are a list of extensions to modify Envoy proxy configuration. - EnvoyExtensions EnvoyExtensions `json:"envoyExtensions,omitempty"` - // FailoverPolicy specifies the exact mechanism used for failover. - FailoverPolicy *FailoverPolicy `json:"failoverPolicy,omitempty"` - // PrioritizeByLocality controls whether the locality of services within the - // local partition will be used to prioritize connectivity. - PrioritizeByLocality *PrioritizeByLocality `json:"prioritizeByLocality,omitempty"` } func (in *ProxyDefaults) GetObjectMeta() metav1.ObjectMeta { @@ -182,18 +159,13 @@ func (in *ProxyDefaults) SetLastSyncedTime(time *metav1.Time) { func (in *ProxyDefaults) ToConsul(datacenter string) capi.ConfigEntry { consulConfig := in.convertConfig() return &capi.ProxyConfigEntry{ - Kind: in.ConsulKind(), - Name: in.ConsulName(), - MeshGateway: in.Spec.MeshGateway.toConsul(), - Expose: in.Spec.Expose.toConsul(), - Config: consulConfig, - TransparentProxy: in.Spec.TransparentProxy.toConsul(), - MutualTLSMode: in.Spec.MutualTLSMode.toConsul(), - AccessLogs: in.Spec.AccessLogs.toConsul(), - EnvoyExtensions: in.Spec.EnvoyExtensions.toConsul(), - FailoverPolicy: in.Spec.FailoverPolicy.toConsul(), - PrioritizeByLocality: in.Spec.PrioritizeByLocality.toConsul(), - Meta: meta(datacenter), + Kind: in.ConsulKind(), + Name: in.ConsulName(), + MeshGateway: in.Spec.MeshGateway.toConsul(), + Expose: in.Spec.Expose.toConsul(), + Config: consulConfig, + TransparentProxy: in.Spec.TransparentProxy.toConsul(), + Meta: meta(datacenter), } } @@ -217,23 +189,13 @@ func (in *ProxyDefaults) Validate(_ common.ConsulMeta) error { if err := in.Spec.TransparentProxy.validate(path.Child("transparentProxy")); err != nil { allErrs = append(allErrs, err) } - if err := in.Spec.MutualTLSMode.validate(); err != nil { - allErrs = append(allErrs, field.Invalid(path.Child("mutualTLSMode"), in.Spec.MutualTLSMode, err.Error())) - } if err := in.Spec.Mode.validate(path.Child("mode")); err != nil { allErrs = append(allErrs, err) } if err := in.validateConfig(path.Child("config")); err != nil { allErrs = append(allErrs, err) } - if err := in.Spec.AccessLogs.validate(path.Child("accessLogs")); err != nil { - allErrs = append(allErrs, err) - } allErrs = append(allErrs, in.Spec.Expose.validate(path.Child("expose"))...) - allErrs = append(allErrs, in.Spec.EnvoyExtensions.validate(path.Child("envoyExtensions"))...) - allErrs = append(allErrs, in.Spec.FailoverPolicy.validate(path.Child("failoverPolicy"))...) - allErrs = append(allErrs, in.Spec.PrioritizeByLocality.validate(path.Child("prioritizeByLocality"))...) - if len(allErrs) > 0 { return apierrors.NewInvalid( schema.GroupKind{Group: ConsulHashicorpGroup, Kind: ProxyDefaultsKubeKind}, @@ -271,93 +233,7 @@ func (in *ProxyDefaults) validateConfig(path *field.Path) *field.Error { } var outConfig map[string]interface{} if err := json.Unmarshal(in.Spec.Config, &outConfig); err != nil { - return field.Invalid(path, string(in.Spec.Config), fmt.Sprintf(`must be valid map value: %s`, err)) + return field.Invalid(path, in.Spec.Config, fmt.Sprintf(`must be valid map value: %s`, err)) } return nil } - -// LogSinkType represents the destination for Envoy access logs. -// One of "file", "stderr", or "stdout". -type LogSinkType string - -const ( - DefaultLogSinkType LogSinkType = "" - FileLogSinkType LogSinkType = "file" - StdErrLogSinkType LogSinkType = "stderr" - StdOutLogSinkType LogSinkType = "stdout" -) - -// AccessLogs describes the access logging configuration for all Envoy proxies in the mesh. -type AccessLogs struct { - // Enabled turns on all access logging - Enabled bool `json:"enabled,omitempty"` - - // DisableListenerLogs turns off just listener logs for connections rejected by Envoy because they don't - // have a matching listener filter. - DisableListenerLogs bool `json:"disableListenerLogs,omitempty"` - - // Type selects the output for logs - // one of "file", "stderr". "stdout" - Type LogSinkType `json:"type,omitempty"` - - // Path is the output file to write logs for file-type logging - Path string `json:"path,omitempty"` - - // JSONFormat is a JSON-formatted string of an Envoy access log format dictionary. - // See for more info on formatting: https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#format-dictionaries - // Defining JSONFormat and TextFormat is invalid. - JSONFormat string `json:"jsonFormat,omitempty"` - - // TextFormat is a representation of Envoy access logs format. - // See for more info on formatting: https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#format-strings - // Defining JSONFormat and TextFormat is invalid. - TextFormat string `json:"textFormat,omitempty"` -} - -func (in *AccessLogs) validate(path *field.Path) *field.Error { - if in == nil { - return nil - } - - switch in.Type { - case DefaultLogSinkType, StdErrLogSinkType, StdOutLogSinkType: - // OK - case FileLogSinkType: - if in.Path == "" { - return field.Invalid(path.Child("path"), in.Path, "path must be specified when using file type access logs") - } - default: - return field.Invalid(path.Child("type"), in.Type, "invalid access log type (must be one of \"stdout\", \"stderr\", \"file\"") - } - - if in.JSONFormat != "" && in.TextFormat != "" { - return field.Invalid(path.Child("textFormat"), in.TextFormat, "cannot specify both access log jsonFormat and textFormat") - } - - if in.Type != FileLogSinkType && in.Path != "" { - return field.Invalid(path.Child("path"), in.Path, "path is only valid for file type access logs") - } - - if in.JSONFormat != "" { - msg := json.RawMessage{} - if err := json.Unmarshal([]byte(in.JSONFormat), &msg); err != nil { - return field.Invalid(path.Child("jsonFormat"), in.JSONFormat, "invalid access log json") - } - } - - return nil -} - -func (in *AccessLogs) toConsul() *capi.AccessLogsConfig { - if in == nil { - return nil - } - return &capi.AccessLogsConfig{ - Enabled: in.Enabled, - DisableListenerLogs: in.DisableListenerLogs, - JSONFormat: in.JSONFormat, - Path: in.Path, - TextFormat: in.TextFormat, - Type: capi.LogSinkType(in.Type), - } -} diff --git a/control-plane/api/v1alpha1/proxydefaults_types_test.go b/control-plane/api/v1alpha1/proxydefaults_types_test.go index 6a965fce3a..2950a3a36e 100644 --- a/control-plane/api/v1alpha1/proxydefaults_types_test.go +++ b/control-plane/api/v1alpha1/proxydefaults_types_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( @@ -74,33 +71,6 @@ func TestProxyDefaults_MatchesConsul(t *testing.T) { OutboundListenerPort: 1000, DialedDirectly: true, }, - MutualTLSMode: MutualTLSModePermissive, - AccessLogs: &AccessLogs{ - Enabled: true, - DisableListenerLogs: true, - Type: FileLogSinkType, - Path: "/var/log/envoy.logs", - TextFormat: "ITS WORKING %START_TIME%", - }, - EnvoyExtensions: EnvoyExtensions{ - EnvoyExtension{ - Name: "aws_request_signing", - Arguments: json.RawMessage(`{"AWSServiceName": "s3", "Region": "us-west-2"}`), - Required: false, - }, - EnvoyExtension{ - Name: "zipkin", - Arguments: json.RawMessage(`{"ClusterName": "zipkin_cluster", "Port": "9411", "CollectorEndpoint":"/api/v2/spans"}`), - Required: true, - }, - }, - FailoverPolicy: &FailoverPolicy{ - Mode: "sequential", - Regions: []string{"us-west-1"}, - }, - PrioritizeByLocality: &PrioritizeByLocality{ - Mode: "failover", - }, }, }, Theirs: &capi.ProxyConfigEntry{ @@ -133,40 +103,6 @@ func TestProxyDefaults_MatchesConsul(t *testing.T) { OutboundListenerPort: 1000, DialedDirectly: true, }, - MutualTLSMode: capi.MutualTLSModePermissive, - AccessLogs: &capi.AccessLogsConfig{ - Enabled: true, - DisableListenerLogs: true, - Type: capi.FileLogSinkType, - Path: "/var/log/envoy.logs", - TextFormat: "ITS WORKING %START_TIME%", - }, - EnvoyExtensions: []capi.EnvoyExtension{ - { - Name: "aws_request_signing", - Arguments: map[string]interface{}{ - "AWSServiceName": "s3", - "Region": "us-west-2", - }, - Required: false, - }, - { - Name: "zipkin", - Arguments: map[string]interface{}{ - "ClusterName": "zipkin_cluster", - "Port": "9411", - "CollectorEndpoint": "/api/v2/spans", - }, - Required: true, - }, - }, - FailoverPolicy: &capi.ServiceResolverFailoverPolicy{ - Mode: "sequential", - Regions: []string{"us-west-1"}, - }, - PrioritizeByLocality: &capi.ServiceResolverPrioritizeByLocality{ - Mode: "failover", - }, }, Matches: true, }, @@ -300,33 +236,6 @@ func TestProxyDefaults_ToConsul(t *testing.T) { OutboundListenerPort: 1000, DialedDirectly: true, }, - MutualTLSMode: MutualTLSModeStrict, - AccessLogs: &AccessLogs{ - Enabled: true, - DisableListenerLogs: true, - Type: FileLogSinkType, - Path: "/var/log/envoy.logs", - TextFormat: "ITS WORKING %START_TIME%", - }, - EnvoyExtensions: EnvoyExtensions{ - EnvoyExtension{ - Name: "aws_request_signing", - Arguments: json.RawMessage(`{"AWSServiceName": "s3", "Region": "us-west-2"}`), - Required: false, - }, - EnvoyExtension{ - Name: "zipkin", - Arguments: json.RawMessage(`{"ClusterName": "zipkin_cluster", "Port": "9411", "CollectorEndpoint":"/api/v2/spans"}`), - Required: true, - }, - }, - FailoverPolicy: &FailoverPolicy{ - Mode: "sequential", - Regions: []string{"us-west-1"}, - }, - PrioritizeByLocality: &PrioritizeByLocality{ - Mode: "none", - }, }, }, Exp: &capi.ProxyConfigEntry{ @@ -360,40 +269,6 @@ func TestProxyDefaults_ToConsul(t *testing.T) { OutboundListenerPort: 1000, DialedDirectly: true, }, - MutualTLSMode: capi.MutualTLSModeStrict, - AccessLogs: &capi.AccessLogsConfig{ - Enabled: true, - DisableListenerLogs: true, - Type: capi.FileLogSinkType, - Path: "/var/log/envoy.logs", - TextFormat: "ITS WORKING %START_TIME%", - }, - EnvoyExtensions: []capi.EnvoyExtension{ - { - Name: "aws_request_signing", - Arguments: map[string]interface{}{ - "AWSServiceName": "s3", - "Region": "us-west-2", - }, - Required: false, - }, - { - Name: "zipkin", - Arguments: map[string]interface{}{ - "ClusterName": "zipkin_cluster", - "Port": "9411", - "CollectorEndpoint": "/api/v2/spans", - }, - Required: true, - }, - }, - FailoverPolicy: &capi.ServiceResolverFailoverPolicy{ - Mode: "sequential", - Regions: []string{"us-west-1"}, - }, - PrioritizeByLocality: &capi.ServiceResolverPrioritizeByLocality{ - Mode: "none", - }, Meta: map[string]string{ common.SourceKey: common.SourceValue, common.DatacenterKey: "datacenter", @@ -418,30 +293,8 @@ func TestProxyDefaults_Validate(t *testing.T) { input *ProxyDefaults expectedErrMsg string }{ - "valid envoyExtension": { - input: &ProxyDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "global", - }, - Spec: ProxyDefaultsSpec{ - EnvoyExtensions: EnvoyExtensions{ - EnvoyExtension{ - Name: "aws_request_signing", - Arguments: json.RawMessage(`{"AWSServiceName": "s3", "Region": "us-west-2"}`), - Required: false, - }, - EnvoyExtension{ - Name: "zipkin", - Arguments: json.RawMessage(`{"ClusterName": "zipkin_cluster", "Port": "9411", "CollectorEndpoint":"/api/v2/spans"}`), - Required: true, - }, - }, - }, - }, - expectedErrMsg: "", - }, "meshgateway.mode": { - input: &ProxyDefaults{ + &ProxyDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "global", }, @@ -451,10 +304,10 @@ func TestProxyDefaults_Validate(t *testing.T) { }, }, }, - expectedErrMsg: `proxydefaults.consul.hashicorp.com "global" is invalid: spec.meshGateway.mode: Invalid value: "foobar": must be one of "remote", "local", "none", ""`, + `proxydefaults.consul.hashicorp.com "global" is invalid: spec.meshGateway.mode: Invalid value: "foobar": must be one of "remote", "local", "none", ""`, }, "expose.paths[].protocol": { - input: &ProxyDefaults{ + &ProxyDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "global", }, @@ -469,10 +322,10 @@ func TestProxyDefaults_Validate(t *testing.T) { }, }, }, - expectedErrMsg: `proxydefaults.consul.hashicorp.com "global" is invalid: spec.expose.paths[0].protocol: Invalid value: "invalid-protocol": must be one of "http", "http2"`, + `proxydefaults.consul.hashicorp.com "global" is invalid: spec.expose.paths[0].protocol: Invalid value: "invalid-protocol": must be one of "http", "http2"`, }, "expose.paths[].path": { - input: &ProxyDefaults{ + &ProxyDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "global", }, @@ -487,10 +340,10 @@ func TestProxyDefaults_Validate(t *testing.T) { }, }, }, - expectedErrMsg: `proxydefaults.consul.hashicorp.com "global" is invalid: spec.expose.paths[0].path: Invalid value: "invalid-path": must begin with a '/'`, + `proxydefaults.consul.hashicorp.com "global" is invalid: spec.expose.paths[0].path: Invalid value: "invalid-path": must begin with a '/'`, }, "transparentProxy.outboundListenerPort": { - input: &ProxyDefaults{ + &ProxyDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "global", }, @@ -500,10 +353,10 @@ func TestProxyDefaults_Validate(t *testing.T) { }, }, }, - expectedErrMsg: "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.transparentProxy.outboundListenerPort: Invalid value: 1000: use the annotation `consul.hashicorp.com/transparent-proxy-outbound-listener-port` to configure the Outbound Listener Port", + "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.transparentProxy.outboundListenerPort: Invalid value: 1000: use the annotation `consul.hashicorp.com/transparent-proxy-outbound-listener-port` to configure the Outbound Listener Port", }, "mode": { - input: &ProxyDefaults{ + &ProxyDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "global", }, @@ -511,174 +364,10 @@ func TestProxyDefaults_Validate(t *testing.T) { Mode: proxyModeRef("transparent"), }, }, - expectedErrMsg: "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.mode: Invalid value: \"transparent\": use the annotation `consul.hashicorp.com/transparent-proxy` to configure the Transparent Proxy Mode", - }, - "mutualTLSMode": { - input: &ProxyDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "global", - }, - Spec: ProxyDefaultsSpec{ - MutualTLSMode: MutualTLSMode("asdf"), - }, - }, - expectedErrMsg: `proxydefaults.consul.hashicorp.com "global" is invalid: spec.mutualTLSMode: Invalid value: "asdf": Must be one of "", "strict", or "permissive".`, - }, - "accessLogs.type": { - input: &ProxyDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "global", - }, - Spec: ProxyDefaultsSpec{ - AccessLogs: &AccessLogs{ - Type: "foo", - }, - }, - }, - expectedErrMsg: "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.accessLogs.type: Invalid value: \"foo\": invalid access log type (must be one of \"stdout\", \"stderr\", \"file\"", - }, - "accessLogs.path missing": { - input: &ProxyDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "global", - }, - Spec: ProxyDefaultsSpec{ - AccessLogs: &AccessLogs{ - Type: "file", - }, - }, - }, - expectedErrMsg: "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.accessLogs.path: Invalid value: \"\": path must be specified when using file type access logs", - }, - "accessLogs.path for wrong type": { - input: &ProxyDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "global", - }, - Spec: ProxyDefaultsSpec{ - AccessLogs: &AccessLogs{ - Path: "/var/log/envoy.logs", - }, - }, - }, - expectedErrMsg: "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.accessLogs.path: Invalid value: \"/var/log/envoy.logs\": path is only valid for file type access logs", - }, - "accessLogs.jsonFormat": { - input: &ProxyDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "global", - }, - Spec: ProxyDefaultsSpec{ - AccessLogs: &AccessLogs{ - JSONFormat: "{ \"start_time\": \"%START_TIME\"", // intentionally missing the closing brace - }, - }, - }, - expectedErrMsg: "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.accessLogs.jsonFormat: Invalid value: \"{ \\\"start_time\\\": \\\"%START_TIME\\\"\": invalid access log json", - }, - "accessLogs.textFormat": { - input: &ProxyDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "global", - }, - Spec: ProxyDefaultsSpec{ - AccessLogs: &AccessLogs{ - JSONFormat: "{ \"start_time\": \"%START_TIME\" }", - TextFormat: "MY START TIME %START_TIME", - }, - }, - }, - expectedErrMsg: "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.accessLogs.textFormat: Invalid value: \"MY START TIME %START_TIME\": cannot specify both access log jsonFormat and textFormat", - }, - "envoyExtension.arguments single empty": { - input: &ProxyDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "global", - }, - Spec: ProxyDefaultsSpec{ - EnvoyExtensions: EnvoyExtensions{ - EnvoyExtension{ - Name: "aws_request_signing", - Arguments: json.RawMessage(`{"AWSServiceName": "s3", "Region": "us-west-2"}`), - Required: false, - }, - EnvoyExtension{ - Name: "zipkin", - Arguments: nil, - Required: true, - }, - }, - }, - }, - expectedErrMsg: `proxydefaults.consul.hashicorp.com "global" is invalid: spec.envoyExtensions.envoyExtension[1].arguments: Required value: arguments must be defined`, - }, - "envoyExtension.arguments multi empty": { - input: &ProxyDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "global", - }, - Spec: ProxyDefaultsSpec{ - EnvoyExtensions: EnvoyExtensions{ - EnvoyExtension{ - Name: "aws_request_signing", - Arguments: nil, - Required: false, - }, - EnvoyExtension{ - Name: "zipkin", - Arguments: nil, - Required: true, - }, - }, - }, - }, - expectedErrMsg: `proxydefaults.consul.hashicorp.com "global" is invalid: [spec.envoyExtensions.envoyExtension[0].arguments: Required value: arguments must be defined, spec.envoyExtensions.envoyExtension[1].arguments: Required value: arguments must be defined]`, - }, - "envoyExtension.arguments invalid json": { - input: &ProxyDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "global", - }, - Spec: ProxyDefaultsSpec{ - EnvoyExtensions: EnvoyExtensions{ - EnvoyExtension{ - Name: "aws_request_signing", - Arguments: json.RawMessage(`{"SOME_INVALID_JSON"}`), - Required: false, - }, - }, - }, - }, - expectedErrMsg: `proxydefaults.consul.hashicorp.com "global" is invalid: spec.envoyExtensions.envoyExtension[0].arguments: Invalid value: "{\"SOME_INVALID_JSON\"}": must be valid map value: invalid character '}' after object key`, - }, - "failoverPolicy.mode invalid": { - input: &ProxyDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "global", - }, - Spec: ProxyDefaultsSpec{ - FailoverPolicy: &FailoverPolicy{ - Mode: "wrong-mode", - }, - }, - }, - expectedErrMsg: `proxydefaults.consul.hashicorp.com "global" is invalid: spec.failoverPolicy.mode: Invalid value: "wrong-mode": must be one of "", "sequential", "order-by-locality"`, - }, - "prioritize by locality invalid": { - input: &ProxyDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "global", - }, - Spec: ProxyDefaultsSpec{ - PrioritizeByLocality: &PrioritizeByLocality{ - Mode: "wrong-mode", - }, - }, - }, - expectedErrMsg: `proxydefaults.consul.hashicorp.com "global" is invalid: spec.prioritizeByLocality.mode: Invalid value: "wrong-mode": must be one of "", "none", "failover"`, + "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.mode: Invalid value: \"transparent\": use the annotation `consul.hashicorp.com/transparent-proxy` to configure the Transparent Proxy Mode", }, "multi-error": { - input: &ProxyDefaults{ + &ProxyDefaults{ ObjectMeta: metav1.ObjectMeta{ Name: "global", }, @@ -697,14 +386,10 @@ func TestProxyDefaults_Validate(t *testing.T) { TransparentProxy: &TransparentProxy{ OutboundListenerPort: 1000, }, - AccessLogs: &AccessLogs{ - JSONFormat: "{ \"start_time\": \"%START_TIME\" }", - TextFormat: "MY START TIME %START_TIME", - }, Mode: proxyModeRef("transparent"), }, }, - expectedErrMsg: "proxydefaults.consul.hashicorp.com \"global\" is invalid: [spec.meshGateway.mode: Invalid value: \"invalid-mode\": must be one of \"remote\", \"local\", \"none\", \"\", spec.transparentProxy.outboundListenerPort: Invalid value: 1000: use the annotation `consul.hashicorp.com/transparent-proxy-outbound-listener-port` to configure the Outbound Listener Port, spec.mode: Invalid value: \"transparent\": use the annotation `consul.hashicorp.com/transparent-proxy` to configure the Transparent Proxy Mode, spec.accessLogs.textFormat: Invalid value: \"MY START TIME %START_TIME\": cannot specify both access log jsonFormat and textFormat, spec.expose.paths[0].path: Invalid value: \"invalid-path\": must begin with a '/', spec.expose.paths[0].protocol: Invalid value: \"invalid-protocol\": must be one of \"http\", \"http2\"]", + "proxydefaults.consul.hashicorp.com \"global\" is invalid: [spec.meshGateway.mode: Invalid value: \"invalid-mode\": must be one of \"remote\", \"local\", \"none\", \"\", spec.transparentProxy.outboundListenerPort: Invalid value: 1000: use the annotation `consul.hashicorp.com/transparent-proxy-outbound-listener-port` to configure the Outbound Listener Port, spec.mode: Invalid value: \"transparent\": use the annotation `consul.hashicorp.com/transparent-proxy` to configure the Transparent Proxy Mode, spec.expose.paths[0].path: Invalid value: \"invalid-path\": must begin with a '/', spec.expose.paths[0].protocol: Invalid value: \"invalid-protocol\": must be one of \"http\", \"http2\"]", }, } for name, testCase := range cases { diff --git a/control-plane/api/v1alpha1/proxydefaults_webhook.go b/control-plane/api/v1alpha1/proxydefaults_webhook.go index ced4853b12..3873516074 100644 --- a/control-plane/api/v1alpha1/proxydefaults_webhook.go +++ b/control-plane/api/v1alpha1/proxydefaults_webhook.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/proxydefaults_webhook_test.go b/control-plane/api/v1alpha1/proxydefaults_webhook_test.go index ade806b7e3..c1aebe3e11 100644 --- a/control-plane/api/v1alpha1/proxydefaults_webhook_test.go +++ b/control-plane/api/v1alpha1/proxydefaults_webhook_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( @@ -49,7 +46,7 @@ func TestValidateProxyDefault(t *testing.T) { }, expAllow: false, // This error message is because the value "1" is valid JSON but is an invalid map - expErrMessage: "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.config: Invalid value: \"1\": must be valid map value: json: cannot unmarshal number into Go value of type map[string]interface {}", + expErrMessage: "proxydefaults.consul.hashicorp.com \"global\" is invalid: spec.config: Invalid value: json.RawMessage{0x31}: must be valid map value: json: cannot unmarshal number into Go value of type map[string]interface {}", }, "proxy default exists": { existingResources: []runtime.Object{&ProxyDefaults{ diff --git a/control-plane/api/v1alpha1/routeretryfilter_types.go b/control-plane/api/v1alpha1/routeretryfilter_types.go deleted file mode 100644 index 79fa85c608..0000000000 --- a/control-plane/api/v1alpha1/routeretryfilter_types.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func init() { - SchemeBuilder.Register(&RouteRetryFilter{}, &RouteRetryFilterList{}) -} - -const RouteRetryFilterKind = "RouteRetryFilter" - -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status - -// RouteRetryFilter is the Schema for the routeretryfilters API -// +kubebuilder:printcolumn:name="Synced",type="string",JSONPath=".status.conditions[?(@.type==\"Synced\")].status",description="The sync status of the resource with Consul" -// +kubebuilder:printcolumn:name="Last Synced",type="date",JSONPath=".status.lastSyncedTime",description="The last successful synced time of the resource with Consul" -// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="The age of the resource" -type RouteRetryFilter struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec RouteRetryFilterSpec `json:"spec,omitempty"` - Status `json:"status,omitempty"` -} - -//+kubebuilder:object:root=true - -// RouteRetryFilterList contains a list of RouteRetryFilter. -type RouteRetryFilterList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []RouteRetryFilter `json:"items"` -} - -// RouteRetryFilterSpec defines the desired state of RouteRetryFilter. -type RouteRetryFilterSpec struct { - // +kubebuilder:validation:Minimum:=0 - // +kubebuilder:validation:Optional - NumRetries *uint32 `json:"numRetries"` - // +kubebuilder:validation:Optional - RetryOn []string `json:"retryOn"` - // +kubebuilder:validation:Optional - RetryOnStatusCodes []uint32 `json:"retryOnStatusCodes"` - // +kubebuilder:validation:Optional - RetryOnConnectFailure *bool `json:"retryOnConnectFailure"` -} - -func (h *RouteRetryFilter) GetNamespace() string { - return h.Namespace -} diff --git a/control-plane/api/v1alpha1/routetimeoutfilter_types.go b/control-plane/api/v1alpha1/routetimeoutfilter_types.go deleted file mode 100644 index 59b25f62ea..0000000000 --- a/control-plane/api/v1alpha1/routetimeoutfilter_types.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "time" -) - -func init() { - SchemeBuilder.Register(&RouteTimeoutFilter{}, &RouteTimeoutFilterList{}) -} - -const RouteTimeoutFilterKind = "RouteTimeoutFilter" - -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status - -// RouteTimeoutFilter is the Schema for the httproutetimeoutfilters API -// +kubebuilder:printcolumn:name="Synced",type="string",JSONPath=".status.conditions[?(@.type==\"Synced\")].status",description="The sync status of the resource with Consul" -// +kubebuilder:printcolumn:name="Last Synced",type="date",JSONPath=".status.lastSyncedTime",description="The last successful synced time of the resource with Consul" -// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="The age of the resource" -type RouteTimeoutFilter struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec RouteTimeoutFilterSpec `json:"spec,omitempty"` - Status `json:"status,omitempty"` -} - -//+kubebuilder:object:root=true - -// RouteTimeoutFilterList contains a list of RouteTimeoutFilter. -type RouteTimeoutFilterList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []RouteTimeoutFilter `json:"items"` -} - -// RouteTimeoutFilterSpec defines the desired state of RouteTimeoutFilter. -type RouteTimeoutFilterSpec struct { - // +kubebuilder:validation:Optional - RequestTimeout time.Duration `json:"requestTimeout"` - - // +kubebuilder:validation:Optional - IdleTimeout time.Duration `json:"idleTimeout"` -} - -func (h *RouteTimeoutFilter) GetNamespace() string { - return h.Namespace -} diff --git a/control-plane/api/v1alpha1/samenessgroup_types.go b/control-plane/api/v1alpha1/samenessgroup_types.go deleted file mode 100644 index 86b02445f6..0000000000 --- a/control-plane/api/v1alpha1/samenessgroup_types.go +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package v1alpha1 - -import ( - "encoding/json" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/hashicorp/consul-k8s/control-plane/api/common" - "github.com/hashicorp/consul/api" - capi "github.com/hashicorp/consul/api" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/validation/field" -) - -const ( - SamenessGroupKubeKind string = "samenessgroup" -) - -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - -func init() { - SchemeBuilder.Register(&SamenessGroup{}, &SamenessGroupList{}) -} - -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status - -// SamenessGroup is the Schema for the samenessgroups API -// +kubebuilder:printcolumn:name="Synced",type="string",JSONPath=".status.conditions[?(@.type==\"Synced\")].status",description="The sync status of the resource with Consul" -// +kubebuilder:printcolumn:name="Last Synced",type="date",JSONPath=".status.lastSyncedTime",description="The last successful synced time of the resource with Consul" -// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="The age of the resource" -// +kubebuilder:resource:shortName="sameness-group" -type SamenessGroup struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - Spec SamenessGroupSpec `json:"spec,omitempty"` - Status `json:"status,omitempty"` -} - -//+kubebuilder:object:root=true - -// SamenessGroupList contains a list of SamenessGroup. -type SamenessGroupList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []SamenessGroup `json:"items"` -} - -// SamenessGroupSpec defines the desired state of SamenessGroup. -type SamenessGroupSpec struct { - // DefaultForFailover indicates that upstream requests to members of the given sameness group will implicitly failover between members of this sameness group. - // When DefaultForFailover is true, the local partition must be a member of the sameness group or IncludeLocal must be set to true. - DefaultForFailover bool `json:"defaultForFailover,omitempty"` - // IncludeLocal is used to include the local partition as the first member of the sameness group. - // The local partition can only be a member of a single sameness group. - IncludeLocal bool `json:"includeLocal,omitempty"` - // Members are the partitions and peers that are part of the sameness group. - // If a member of a sameness group does not exist, it will be ignored. - Members []SamenessGroupMember `json:"members,omitempty"` -} - -type SamenessGroupMember struct { - // The partitions and peers that are part of the sameness group. - // A sameness group member cannot define both peer and partition at the same time. - Partition string `json:"partition,omitempty"` - Peer string `json:"peer,omitempty"` -} - -func (in *SamenessGroup) GetObjectMeta() metav1.ObjectMeta { - return in.ObjectMeta -} - -func (in *SamenessGroup) AddFinalizer(name string) { - in.ObjectMeta.Finalizers = append(in.Finalizers(), name) -} - -func (in *SamenessGroup) RemoveFinalizer(name string) { - var newFinalizers []string - for _, oldF := range in.Finalizers() { - if oldF != name { - newFinalizers = append(newFinalizers, oldF) - } - } - in.ObjectMeta.Finalizers = newFinalizers -} - -func (in *SamenessGroup) Finalizers() []string { - return in.ObjectMeta.Finalizers -} - -func (in *SamenessGroup) ConsulKind() string { - return capi.SamenessGroup -} - -func (in *SamenessGroup) ConsulGlobalResource() bool { - return false -} - -func (in *SamenessGroup) ConsulMirroringNS() string { - return common.DefaultConsulNamespace -} - -func (in *SamenessGroup) KubeKind() string { - return SamenessGroupKubeKind -} - -func (in *SamenessGroup) ConsulName() string { - return in.ObjectMeta.Name -} - -func (in *SamenessGroup) KubernetesName() string { - return in.ObjectMeta.Name -} - -func (in *SamenessGroup) SetSyncedCondition(status corev1.ConditionStatus, reason, message string) { - in.Status.Conditions = Conditions{ - { - Type: ConditionSynced, - Status: status, - LastTransitionTime: metav1.Now(), - Reason: reason, - Message: message, - }, - } -} - -func (in *SamenessGroup) SetLastSyncedTime(time *metav1.Time) { - in.Status.LastSyncedTime = time -} - -func (in *SamenessGroup) SyncedCondition() (status corev1.ConditionStatus, reason, message string) { - cond := in.Status.GetCondition(ConditionSynced) - if cond == nil { - return corev1.ConditionUnknown, "", "" - } - return cond.Status, cond.Reason, cond.Message -} - -func (in *SamenessGroup) SyncedConditionStatus() corev1.ConditionStatus { - cond := in.Status.GetCondition(ConditionSynced) - if cond == nil { - return corev1.ConditionUnknown - } - return cond.Status -} - -func (in *SamenessGroup) ToConsul(datacenter string) api.ConfigEntry { - return &capi.SamenessGroupConfigEntry{ - Kind: in.ConsulKind(), - Name: in.ConsulName(), - DefaultForFailover: in.Spec.DefaultForFailover, - IncludeLocal: in.Spec.IncludeLocal, - Members: SamenessGroupMembers(in.Spec.Members).toConsul(), - Meta: meta(datacenter), - } -} - -func (in *SamenessGroup) MatchesConsul(candidate api.ConfigEntry) bool { - configEntry, ok := candidate.(*capi.SamenessGroupConfigEntry) - if !ok { - return false - } - return cmp.Equal(in.ToConsul(""), configEntry, cmpopts.IgnoreFields(capi.SamenessGroupConfigEntry{}, "Partition", "Meta", "ModifyIndex", "CreateIndex"), cmpopts.IgnoreUnexported(), cmpopts.EquateEmpty(), - cmp.Comparer(transparentProxyConfigComparer)) -} - -func (in *SamenessGroup) Validate(consulMeta common.ConsulMeta) error { - var allErrs field.ErrorList - path := field.NewPath("spec") - - if in == nil { - return nil - } - if in.Name == "" { - allErrs = append(allErrs, field.Invalid(path.Child("name"), in.Name, "sameness groups must have a name defined")) - } - - partition := consulMeta.Partition - includesLocal := in.Spec.IncludeLocal - - if in.ObjectMeta.Namespace != "default" && in.ObjectMeta.Namespace != "" { - allErrs = append(allErrs, field.Invalid(path.Child("name"), consulMeta.DestinationNamespace, "sameness groups must reside in the default namespace")) - } - - if len(in.Spec.Members) == 0 { - asJSON, _ := json.Marshal(in.Spec.Members) - allErrs = append(allErrs, field.Invalid(path.Child("members"), string(asJSON), "sameness groups must have at least one member")) - } - - seenMembers := make(map[SamenessGroupMember]struct{}) - for i, m := range in.Spec.Members { - if partition == m.Partition { - includesLocal = true - } - if err := m.validate(path.Child("members").Index(i)); err != nil { - allErrs = append(allErrs, err) - } - if _, ok := seenMembers[m]; ok { - asJSON, _ := json.Marshal(m) - allErrs = append(allErrs, field.Invalid(path.Child("members").Index(i), string(asJSON), "sameness group members must be unique")) - } - seenMembers[m] = struct{}{} - - } - - if !includesLocal { - allErrs = append(allErrs, field.Invalid(path.Child("members"), in.Spec.IncludeLocal, "the local partition must be a member of sameness groups")) - } - - if len(allErrs) > 0 { - return apierrors.NewInvalid( - schema.GroupKind{Group: ConsulHashicorpGroup, Kind: SamenessGroupKubeKind}, - in.KubernetesName(), allErrs) - } - - return nil -} - -// DefaultNamespaceFields has no behaviour here as sameness-groups have no namespace specific fields. -func (in *SamenessGroup) DefaultNamespaceFields(_ common.ConsulMeta) { -} - -type SamenessGroupMembers []SamenessGroupMember - -func (in SamenessGroupMembers) toConsul() []capi.SamenessGroupMember { - if in == nil { - return nil - } - - outMembers := make([]capi.SamenessGroupMember, 0, len(in)) - for _, e := range in { - consulMember := capi.SamenessGroupMember{ - Peer: e.Peer, - Partition: e.Partition, - } - outMembers = append(outMembers, consulMember) - } - return outMembers -} - -func (in *SamenessGroupMember) validate(path *field.Path) *field.Error { - asJSON, _ := json.Marshal(in) - - if in == nil { - return field.Invalid(path, string(asJSON), "sameness group member is nil") - } - if in.isEmpty() { - return field.Invalid(path, string(asJSON), "sameness group members must specify either partition or peer") - } - // We do not allow referencing peer connections in other partitions. - if in.Peer != "" && in.Partition != "" { - return field.Invalid(path, string(asJSON), "sameness group members cannot specify both partition and peer in the same entry") - } - return nil -} - -func (in *SamenessGroupMember) isEmpty() bool { - return in.Peer == "" && in.Partition == "" -} diff --git a/control-plane/api/v1alpha1/samenessgroup_types_test.go b/control-plane/api/v1alpha1/samenessgroup_types_test.go deleted file mode 100644 index 0f461701cc..0000000000 --- a/control-plane/api/v1alpha1/samenessgroup_types_test.go +++ /dev/null @@ -1,390 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package v1alpha1 - -import ( - "github.com/hashicorp/consul-k8s/control-plane/api/common" - capi "github.com/hashicorp/consul/api" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "testing" - "time" -) - -func TestSamenessGroups_ToConsul(t *testing.T) { - cases := map[string]struct { - input *SamenessGroup - expected *capi.SamenessGroupConfigEntry - }{ - "empty fields": { - &SamenessGroup{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - }, - Spec: SamenessGroupSpec{}, - }, - &capi.SamenessGroupConfigEntry{ - Name: "foo", - Kind: capi.SamenessGroup, - Meta: map[string]string{ - common.SourceKey: common.SourceValue, - common.DatacenterKey: "datacenter", - }, - }, - }, - "every field set": { - &SamenessGroup{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - }, - Spec: SamenessGroupSpec{ - DefaultForFailover: true, - IncludeLocal: true, - Members: []SamenessGroupMember{ - { - Peer: "peer2", - }, - { - Partition: "p2", - }, - }, - }, - }, - &capi.SamenessGroupConfigEntry{ - Name: "foo", - Kind: capi.SamenessGroup, - Meta: map[string]string{ - common.SourceKey: common.SourceValue, - common.DatacenterKey: "datacenter", - }, - DefaultForFailover: true, - IncludeLocal: true, - Members: []capi.SamenessGroupMember{ - { - Peer: "peer2", - }, - { - Partition: "p2", - }, - }, - }, - }, - } - for name, testCase := range cases { - t.Run(name, func(t *testing.T) { - output := testCase.input.ToConsul("datacenter") - require.Equal(t, testCase.expected, output) - }) - } -} - -func TestSamenessGroups_MatchesConsul(t *testing.T) { - cases := map[string]struct { - internal *SamenessGroup - consul capi.ConfigEntry - matches bool - }{ - "empty fields matches": { - &SamenessGroup{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-sameness-group", - }, - Spec: SamenessGroupSpec{}, - }, - &capi.SamenessGroupConfigEntry{ - Kind: capi.SamenessGroup, - Name: "my-test-sameness-group", - CreateIndex: 1, - ModifyIndex: 2, - Meta: map[string]string{ - common.SourceKey: common.SourceValue, - common.DatacenterKey: "datacenter", - }, - }, - true, - }, - "all fields populated matches": { - &SamenessGroup{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-test-sameness-group", - }, - Spec: SamenessGroupSpec{ - DefaultForFailover: true, - IncludeLocal: true, - Members: []SamenessGroupMember{ - { - Peer: "peer2", - }, - { - Partition: "p2", - }, - }, - }, - }, - &capi.SamenessGroupConfigEntry{ - Kind: capi.SamenessGroup, - Name: "my-test-sameness-group", - Meta: map[string]string{ - common.SourceKey: common.SourceValue, - common.DatacenterKey: "datacenter", - }, - DefaultForFailover: true, - IncludeLocal: true, - Members: []capi.SamenessGroupMember{ - { - Peer: "peer2", - }, - { - Partition: "p2", - }, - }, - }, - true, - }, - } - - for name, testCase := range cases { - t.Run(name, func(t *testing.T) { - require.Equal(t, testCase.matches, testCase.internal.MatchesConsul(testCase.consul)) - }) - } -} - -func TestSamenessGroups_Validate(t *testing.T) { - cases := map[string]struct { - input *SamenessGroup - partitionsEnabled bool - expectedErrMsg string - }{ - "valid": { - input: &SamenessGroup{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-sameness-group", - }, - Spec: SamenessGroupSpec{ - DefaultForFailover: true, - IncludeLocal: true, - Members: []SamenessGroupMember{ - { - Peer: "peer2", - Partition: "", - }, - { - Peer: "", - Partition: "p2", - }, - }, - }, - }, - partitionsEnabled: true, - expectedErrMsg: "", - }, - "invalid - with peer and partition both": { - input: &SamenessGroup{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-sameness-group", - }, - Spec: SamenessGroupSpec{ - DefaultForFailover: true, - IncludeLocal: true, - Members: []SamenessGroupMember{ - { - Peer: "peer2", - Partition: "p2", - }, - }, - }, - }, - partitionsEnabled: true, - expectedErrMsg: "sameness group members cannot specify both partition and peer in the same entry", - }, - "invalid - no name": { - input: &SamenessGroup{ - ObjectMeta: metav1.ObjectMeta{}, - Spec: SamenessGroupSpec{ - DefaultForFailover: true, - IncludeLocal: true, - Members: []SamenessGroupMember{ - { - Peer: "peer2", - }, - { - Partition: "p2", - }, - }, - }, - }, - partitionsEnabled: true, - expectedErrMsg: "sameness groups must have a name defined", - }, - "invalid - empty members": { - input: &SamenessGroup{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-sameness-group", - }, - Spec: SamenessGroupSpec{ - DefaultForFailover: true, - IncludeLocal: true, - Members: []SamenessGroupMember{}, - }, - }, - partitionsEnabled: true, - expectedErrMsg: "sameness groups must have at least one member", - }, - "invalid - not unique members": { - input: &SamenessGroup{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-sameness-group", - }, - Spec: SamenessGroupSpec{ - DefaultForFailover: true, - IncludeLocal: true, - Members: []SamenessGroupMember{ - { - Peer: "peer2", - }, - { - Peer: "peer2", - }, - }, - }, - }, - partitionsEnabled: true, - expectedErrMsg: "sameness group members must be unique", - }, - "invalid - not in default namespace": { - input: &SamenessGroup{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-sameness-group", - Namespace: "non-default", - }, - Spec: SamenessGroupSpec{ - DefaultForFailover: true, - IncludeLocal: true, - Members: []SamenessGroupMember{ - { - Peer: "peer2", - }, - }, - }, - }, - partitionsEnabled: true, - expectedErrMsg: "sameness groups must reside in the default namespace", - }, - } - - for name, testCase := range cases { - t.Run(name, func(t *testing.T) { - err := testCase.input.Validate(common.ConsulMeta{}) - if testCase.expectedErrMsg != "" { - require.ErrorContains(t, err, testCase.expectedErrMsg) - } else { - require.NoError(t, err) - } - }) - } -} - -func TestSamenessGroups_GetObjectMeta(t *testing.T) { - meta := metav1.ObjectMeta{ - Name: "name", - } - samenessGroups := &SamenessGroup{ - ObjectMeta: meta, - } - require.Equal(t, meta, samenessGroups.GetObjectMeta()) -} - -func TestSamenessGroups_AddFinalizer(t *testing.T) { - samenessGroups := &SamenessGroup{} - samenessGroups.AddFinalizer("finalizer") - require.Equal(t, []string{"finalizer"}, samenessGroups.ObjectMeta.Finalizers) -} - -func TestSamenessGroups_RemoveFinalizer(t *testing.T) { - samenessGroups := &SamenessGroup{ - ObjectMeta: metav1.ObjectMeta{ - Finalizers: []string{"f1", "f2"}, - }, - } - samenessGroups.RemoveFinalizer("f1") - require.Equal(t, []string{"f2"}, samenessGroups.ObjectMeta.Finalizers) -} - -func TestSamenessGroups_ConsulKind(t *testing.T) { - require.Equal(t, capi.SamenessGroup, (&SamenessGroup{}).ConsulKind()) -} - -func TestSamenessGroups_ConsulGlobalResource(t *testing.T) { - require.False(t, (&SamenessGroup{}).ConsulGlobalResource()) -} - -func TestSamenessGroups_ConsulMirroringNS(t *testing.T) { - -} - -func TestSamenessGroups_KubeKind(t *testing.T) { - require.Equal(t, "samenessgroup", (&SamenessGroup{}).KubeKind()) -} - -func TestSamenessGroups_ConsulName(t *testing.T) { - require.Equal(t, "foo", (&SamenessGroup{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}).ConsulName()) -} - -func TestSamenessGroups_KubernetesName(t *testing.T) { - require.Equal(t, "foo", (&SamenessGroup{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}).KubernetesName()) -} - -func TestSamenessGroups_SetSyncedCondition(t *testing.T) { - samenessGroups := &SamenessGroup{} - samenessGroups.SetSyncedCondition(corev1.ConditionTrue, "reason", "message") - - require.Equal(t, corev1.ConditionTrue, samenessGroups.Status.Conditions[0].Status) - require.Equal(t, "reason", samenessGroups.Status.Conditions[0].Reason) - require.Equal(t, "message", samenessGroups.Status.Conditions[0].Message) - now := metav1.Now() - require.True(t, samenessGroups.Status.Conditions[0].LastTransitionTime.Before(&now)) -} - -func TestSamenessGroups_SetLastSyncedTime(t *testing.T) { - samenessGroups := &SamenessGroup{} - syncedTime := metav1.NewTime(time.Now()) - samenessGroups.SetLastSyncedTime(&syncedTime) - - require.Equal(t, &syncedTime, samenessGroups.Status.LastSyncedTime) -} - -func TestSamenessGroups_GetSyncedConditionStatus(t *testing.T) { - cases := []corev1.ConditionStatus{ - corev1.ConditionUnknown, - corev1.ConditionFalse, - corev1.ConditionTrue, - } - for _, status := range cases { - t.Run(string(status), func(t *testing.T) { - samenessGroups := &SamenessGroup{ - Status: Status{ - Conditions: []Condition{{ - Type: ConditionSynced, - Status: status, - }}, - }, - } - - require.Equal(t, status, samenessGroups.SyncedConditionStatus()) - }) - } -} - -func TestSamenessGroups_SyncedConditionStatusWhenStatusNil(t *testing.T) { - require.Equal(t, corev1.ConditionUnknown, (&SamenessGroup{}).SyncedConditionStatus()) -} - -func TestSamenessGroups_SyncedConditionWhenStatusNil(t *testing.T) { - status, reason, message := (&SamenessGroup{}).SyncedCondition() - require.Equal(t, corev1.ConditionUnknown, status) - require.Equal(t, "", reason) - require.Equal(t, "", message) -} diff --git a/control-plane/api/v1alpha1/samenessgroup_webhook.go b/control-plane/api/v1alpha1/samenessgroup_webhook.go deleted file mode 100644 index 6c1da5cba2..0000000000 --- a/control-plane/api/v1alpha1/samenessgroup_webhook.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package v1alpha1 - -import ( - "context" - "net/http" - - "github.com/go-logr/logr" - "github.com/hashicorp/consul-k8s/control-plane/api/common" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" -) - -// +kubebuilder:object:generate=false - -type SamenessGroupWebhook struct { - Logger logr.Logger - - // ConsulMeta contains metadata specific to the Consul installation. - ConsulMeta common.ConsulMeta - - decoder *admission.Decoder - client.Client -} - -// NOTE: The path value in the below line is the path to the webhook. -// If it is updated, run code-gen, update subcommand/controller/command.go -// and the consul-helm value for the path to the webhook. -// -// NOTE: The below line cannot be combined with any other comment. If it is it will break the code generation. -// -// +kubebuilder:webhook:verbs=create;update,path=/mutate-v1alpha1-samenessgroups,mutating=true,failurePolicy=fail,groups=consul.hashicorp.com,resources=samenessgroups,versions=v1alpha1,name=mutate-samenessgroup.consul.hashicorp.com,sideEffects=None,admissionReviewVersions=v1beta1;v1 - -func (v *SamenessGroupWebhook) Handle(ctx context.Context, req admission.Request) admission.Response { - var resource SamenessGroup - err := v.decoder.Decode(req, &resource) - if err != nil { - return admission.Errored(http.StatusBadRequest, err) - } - - return common.ValidateConfigEntry(ctx, req, v.Logger, v, &resource, v.ConsulMeta) -} - -func (v *SamenessGroupWebhook) List(ctx context.Context) ([]common.ConfigEntryResource, error) { - var resourceList SamenessGroupList - if err := v.Client.List(ctx, &resourceList); err != nil { - return nil, err - } - var entries []common.ConfigEntryResource - for _, item := range resourceList.Items { - entries = append(entries, common.ConfigEntryResource(&item)) - } - return entries, nil -} - -func (v *SamenessGroupWebhook) InjectDecoder(d *admission.Decoder) error { - v.decoder = d - return nil -} diff --git a/control-plane/api/v1alpha1/servicedefaults_types.go b/control-plane/api/v1alpha1/servicedefaults_types.go index 54044cb3a8..3f1b8f1951 100644 --- a/control-plane/api/v1alpha1/servicedefaults_types.go +++ b/control-plane/api/v1alpha1/servicedefaults_types.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( @@ -11,15 +8,14 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + "github.com/hashicorp/consul-k8s/control-plane/api/common" + capi "github.com/hashicorp/consul/api" "github.com/miekg/dns" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" - - "github.com/hashicorp/consul-k8s/control-plane/api/common" - capi "github.com/hashicorp/consul/api" ) const ( @@ -74,17 +70,6 @@ type ServiceDefaultsSpec struct { // Note: This cannot be set using the CRD and should be set using annotations on the // services that are part of the mesh. TransparentProxy *TransparentProxy `json:"transparentProxy,omitempty"` - // MutualTLSMode controls whether mutual TLS is required for all incoming - // connections when transparent proxy is enabled. This can be set to - // "permissive" or "strict". "strict" is the default which requires mutual - // TLS for incoming connections. In the insecure "permissive" mode, - // connections to the sidecar proxy public listener port require mutual - // TLS, but connections to the service port do not require mutual TLS and - // are proxied to the application unmodified. Note: Intentions are not - // enforced for non-mTLS connections. To keep your services secure, we - // recommend using "strict" mode whenever possible and enabling - // "permissive" mode only when necessary. - MutualTLSMode MutualTLSMode `json:"mutualTLSMode,omitempty"` // MeshGateway controls the default mesh gateway configuration for this service. MeshGateway MeshGateway `json:"meshGateway,omitempty"` // Expose controls the default expose path configuration for Envoy. @@ -104,19 +89,13 @@ type ServiceDefaultsSpec struct { // MaxInboundConnections is the maximum number of concurrent inbound connections to // each service instance. Defaults to 0 (using consul's default) if not set. MaxInboundConnections int `json:"maxInboundConnections,omitempty"` - // LocalConnectTimeoutMs is the number of milliseconds allowed to make connections to the local application + // The number of milliseconds allowed to make connections to the local application // instance before timing out. Defaults to 5000. LocalConnectTimeoutMs int `json:"localConnectTimeoutMs,omitempty"` - // LocalRequestTimeoutMs is the timeout for HTTP requests to the local application instance in milliseconds. + // In milliseconds, the timeout for HTTP requests to the local application instance. // Applies to HTTP-based protocols only. If not specified, inherits the Envoy default for // route timeouts (15s). LocalRequestTimeoutMs int `json:"localRequestTimeoutMs,omitempty"` - // BalanceInboundConnections sets the strategy for allocating inbound connections to the service across - // proxy threads. The only supported value is exact_balance. By default, no connection balancing is used. - // Refer to the Envoy Connection Balance config for details. - BalanceInboundConnections string `json:"balanceInboundConnections,omitempty"` - // EnvoyExtensions are a list of extensions to modify Envoy proxy configuration. - EnvoyExtensions EnvoyExtensions `json:"envoyExtensions,omitempty"` } type Upstreams struct { @@ -129,14 +108,12 @@ type Upstreams struct { } type Upstream struct { - // Name is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides config entry. + // Name is only accepted within a service-defaults config entry. Name string `json:"name,omitempty"` - // Namespace is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides config entry. + // Namespace is only accepted within a service-defaults config entry. Namespace string `json:"namespace,omitempty"` - // Partition is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides config entry. + // Partition is only accepted within a service-defaults config entry. Partition string `json:"partition,omitempty"` - // Peer is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides config entry. - Peer string `json:"peer,omitempty"` // EnvoyListenerJSON is a complete override ("escape hatch") for the upstream's // listener. // Note: This escape hatch is NOT compatible with the discovery chain and @@ -291,22 +268,19 @@ func (in *ServiceDefaults) SyncedConditionStatus() corev1.ConditionStatus { // ToConsul converts the entry into it's Consul equivalent struct. func (in *ServiceDefaults) ToConsul(datacenter string) capi.ConfigEntry { return &capi.ServiceConfigEntry{ - Kind: in.ConsulKind(), - Name: in.ConsulName(), - Protocol: in.Spec.Protocol, - MeshGateway: in.Spec.MeshGateway.toConsul(), - Expose: in.Spec.Expose.toConsul(), - ExternalSNI: in.Spec.ExternalSNI, - TransparentProxy: in.Spec.TransparentProxy.toConsul(), - MutualTLSMode: in.Spec.MutualTLSMode.toConsul(), - UpstreamConfig: in.Spec.UpstreamConfig.toConsul(), - Destination: in.Spec.Destination.toConsul(), - Meta: meta(datacenter), - MaxInboundConnections: in.Spec.MaxInboundConnections, - LocalConnectTimeoutMs: in.Spec.LocalConnectTimeoutMs, - LocalRequestTimeoutMs: in.Spec.LocalRequestTimeoutMs, - BalanceInboundConnections: in.Spec.BalanceInboundConnections, - EnvoyExtensions: in.Spec.EnvoyExtensions.toConsul(), + Kind: in.ConsulKind(), + Name: in.ConsulName(), + Protocol: in.Spec.Protocol, + MeshGateway: in.Spec.MeshGateway.toConsul(), + Expose: in.Spec.Expose.toConsul(), + ExternalSNI: in.Spec.ExternalSNI, + TransparentProxy: in.Spec.TransparentProxy.toConsul(), + UpstreamConfig: in.Spec.UpstreamConfig.toConsul(), + Destination: in.Spec.Destination.toConsul(), + Meta: meta(datacenter), + MaxInboundConnections: in.Spec.MaxInboundConnections, + LocalConnectTimeoutMs: in.Spec.LocalConnectTimeoutMs, + LocalRequestTimeoutMs: in.Spec.LocalRequestTimeoutMs, } } @@ -326,9 +300,6 @@ func (in *ServiceDefaults) Validate(consulMeta common.ConsulMeta) error { if err := in.Spec.TransparentProxy.validate(path.Child("transparentProxy")); err != nil { allErrs = append(allErrs, err) } - if err := in.Spec.MutualTLSMode.validate(); err != nil { - allErrs = append(allErrs, field.Invalid(path.Child("mutualTLSMode"), in.Spec.MutualTLSMode, err.Error())) - } if err := in.Spec.Mode.validate(path.Child("mode")); err != nil { allErrs = append(allErrs, err) } @@ -348,13 +319,8 @@ func (in *ServiceDefaults) Validate(consulMeta common.ConsulMeta) error { allErrs = append(allErrs, field.Invalid(path.Child("localRequestTimeoutMs"), in.Spec.LocalRequestTimeoutMs, "LocalRequestTimeoutMs must be > 0")) } - if in.Spec.BalanceInboundConnections != "" && in.Spec.BalanceInboundConnections != "exact_balance" { - allErrs = append(allErrs, field.Invalid(path.Child("balanceInboundConnections"), in.Spec.BalanceInboundConnections, "BalanceInboundConnections must be an empty string or exact_balance")) - } - allErrs = append(allErrs, in.Spec.UpstreamConfig.validate(path.Child("upstreamConfig"), consulMeta.PartitionsEnabled)...) allErrs = append(allErrs, in.Spec.Expose.validate(path.Child("expose"))...) - allErrs = append(allErrs, in.Spec.EnvoyExtensions.validate(path.Child("envoyExtensions"))...) if len(allErrs) > 0 { return apierrors.NewInvalid( @@ -403,25 +369,10 @@ func (in *Upstream) validate(path *field.Path, kind string, partitionsEnabled bo if in.Name != "" { errs = append(errs, field.Invalid(path.Child("name"), in.Name, "upstream.name for a default upstream must be \"\"")) } - if in.Namespace != "" { - errs = append(errs, field.Invalid(path.Child("namespace"), in.Namespace, "upstream.namespace for a default upstream must be \"\"")) - } - if in.Partition != "" { - errs = append(errs, field.Invalid(path.Child("partition"), in.Partition, "upstream.partition for a default upstream must be \"\"")) - } - if in.Peer != "" { - errs = append(errs, field.Invalid(path.Child("peer"), in.Peer, "upstream.peer for a default upstream must be \"\"")) - } } else if kind == overrideUpstream { if in.Name == "" { errs = append(errs, field.Invalid(path.Child("name"), in.Name, "upstream.name for an override upstream cannot be \"\"")) } - if in.Namespace != "" && in.Peer != "" { - errs = append(errs, field.Invalid(path, in, "both namespace and peer cannot be specified.")) - } - if in.Partition != "" && in.Peer != "" { - errs = append(errs, field.Invalid(path, in, "both partition and peer cannot be specified.")) - } } if !partitionsEnabled && in.Partition != "" { errs = append(errs, field.Invalid(path.Child("partition"), in.Partition, "Consul Enterprise Admin Partitions must be enabled to set upstream.partition")) @@ -440,7 +391,6 @@ func (in *Upstream) toConsul() *capi.UpstreamConfig { Name: in.Name, Namespace: in.Namespace, Partition: in.Partition, - Peer: in.Peer, EnvoyListenerJSON: in.EnvoyListenerJSON, EnvoyClusterJSON: in.EnvoyClusterJSON, Protocol: in.Protocol, diff --git a/control-plane/api/v1alpha1/servicedefaults_types_test.go b/control-plane/api/v1alpha1/servicedefaults_types_test.go index 31a41f3f06..78ff6ce692 100644 --- a/control-plane/api/v1alpha1/servicedefaults_types_test.go +++ b/control-plane/api/v1alpha1/servicedefaults_types_test.go @@ -1,20 +1,15 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( - "encoding/json" "testing" "time" + "github.com/hashicorp/consul-k8s/control-plane/api/common" capi "github.com/hashicorp/consul/api" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/pointer" - - "github.com/hashicorp/consul-k8s/control-plane/api/common" ) func TestServiceDefaults_ToConsul(t *testing.T) { @@ -70,7 +65,6 @@ func TestServiceDefaults_ToConsul(t *testing.T) { OutboundListenerPort: 1000, DialedDirectly: true, }, - MutualTLSMode: MutualTLSModePermissive, UpstreamConfig: &Upstreams{ Defaults: &Upstream{ Name: "upstream-default", @@ -159,19 +153,6 @@ func TestServiceDefaults_ToConsul(t *testing.T) { }, }, }, - BalanceInboundConnections: "exact_balance", - EnvoyExtensions: EnvoyExtensions{ - EnvoyExtension{ - Name: "aws_request_signing", - Arguments: json.RawMessage(`{"AWSServiceName": "s3", "Region": "us-west-2"}`), - Required: false, - }, - EnvoyExtension{ - Name: "zipkin", - Arguments: json.RawMessage(`{"ClusterName": "zipkin_cluster", "Port": "9411", "CollectorEndpoint":"/api/v2/spans"}`), - Required: true, - }, - }, Destination: &ServiceDefaultsDestination{ Addresses: []string{"api.google.com"}, Port: 443, @@ -210,7 +191,6 @@ func TestServiceDefaults_ToConsul(t *testing.T) { OutboundListenerPort: 1000, DialedDirectly: true, }, - MutualTLSMode: capi.MutualTLSModePermissive, UpstreamConfig: &capi.UpstreamConfiguration{ Defaults: &capi.UpstreamConfig{ Name: "upstream-default", @@ -287,26 +267,6 @@ func TestServiceDefaults_ToConsul(t *testing.T) { }, }, }, - BalanceInboundConnections: "exact_balance", - EnvoyExtensions: []capi.EnvoyExtension{ - { - Name: "aws_request_signing", - Arguments: map[string]interface{}{ - "AWSServiceName": "s3", - "Region": "us-west-2", - }, - Required: false, - }, - { - Name: "zipkin", - Arguments: map[string]interface{}{ - "ClusterName": "zipkin_cluster", - "Port": "9411", - "CollectorEndpoint": "/api/v2/spans", - }, - Required: true, - }, - }, Destination: &capi.DestinationConfig{ Addresses: []string{"api.google.com"}, Port: 443, @@ -472,7 +432,6 @@ func TestServiceDefaults_MatchesConsul(t *testing.T) { OutboundListenerPort: 1000, DialedDirectly: true, }, - MutualTLSMode: MutualTLSModeStrict, UpstreamConfig: &Upstreams{ Defaults: &Upstream{ Name: "upstream-default", @@ -558,19 +517,6 @@ func TestServiceDefaults_MatchesConsul(t *testing.T) { }, }, }, - BalanceInboundConnections: "exact_balance", - EnvoyExtensions: EnvoyExtensions{ - EnvoyExtension{ - Name: "aws_request_signing", - Arguments: json.RawMessage(`{"AWSServiceName": "s3", "Region": "us-west-2"}`), - Required: false, - }, - EnvoyExtension{ - Name: "zipkin", - Arguments: json.RawMessage(`{"ClusterName": "zipkin_cluster", "Port": "9411", "CollectorEndpoint":"/api/v2/spans"}`), - Required: true, - }, - }, Destination: &ServiceDefaultsDestination{ Addresses: []string{"api.google.com"}, Port: 443, @@ -605,7 +551,6 @@ func TestServiceDefaults_MatchesConsul(t *testing.T) { OutboundListenerPort: 1000, DialedDirectly: true, }, - MutualTLSMode: capi.MutualTLSModeStrict, UpstreamConfig: &capi.UpstreamConfiguration{ Defaults: &capi.UpstreamConfig{ Name: "upstream-default", @@ -679,26 +624,6 @@ func TestServiceDefaults_MatchesConsul(t *testing.T) { }, }, }, - BalanceInboundConnections: "exact_balance", - EnvoyExtensions: []capi.EnvoyExtension{ - { - Name: "aws_request_signing", - Arguments: map[string]interface{}{ - "AWSServiceName": "s3", - "Region": "us-west-2", - }, - Required: false, - }, - { - Name: "zipkin", - Arguments: map[string]interface{}{ - "ClusterName": "zipkin_cluster", - "Port": "9411", - "CollectorEndpoint": "/api/v2/spans", - }, - Required: true, - }, - }, Destination: &capi.DestinationConfig{ Addresses: []string{"api.google.com"}, Port: 443, @@ -805,7 +730,6 @@ func TestServiceDefaults_Validate(t *testing.T) { MeshGateway: MeshGateway{ Mode: "remote", }, - MutualTLSMode: MutualTLSModePermissive, Expose: Expose{ Checks: false, Paths: []ExposePath{ @@ -835,39 +759,6 @@ func TestServiceDefaults_Validate(t *testing.T) { }, expectedErrMsg: "", }, - "valid - balanceInboundConnections": { - input: &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-service", - }, - Spec: ServiceDefaultsSpec{ - BalanceInboundConnections: "exact_balance", - }, - }, - expectedErrMsg: "", - }, - "valid - envoyExtension": { - input: &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-service", - }, - Spec: ServiceDefaultsSpec{ - EnvoyExtensions: EnvoyExtensions{ - EnvoyExtension{ - Name: "aws_request_signing", - Arguments: json.RawMessage(`{"AWSServiceName": "s3", "Region": "us-west-2"}`), - Required: false, - }, - EnvoyExtension{ - Name: "zipkin", - Arguments: json.RawMessage(`{"ClusterName": "zipkin_cluster", "Port": "9411", "CollectorEndpoint":"/api/v2/spans"}`), - Required: true, - }, - }, - }, - }, - expectedErrMsg: "", - }, "protocol": { input: &ServiceDefaults{ ObjectMeta: metav1.ObjectMeta{ @@ -941,17 +832,6 @@ func TestServiceDefaults_Validate(t *testing.T) { }, expectedErrMsg: "servicedefaults.consul.hashicorp.com \"my-service\" is invalid: spec.transparentProxy.outboundListenerPort: Invalid value: 1000: use the annotation `consul.hashicorp.com/transparent-proxy-outbound-listener-port` to configure the Outbound Listener Port", }, - "mutualTLSMode": { - input: &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-service", - }, - Spec: ServiceDefaultsSpec{ - MutualTLSMode: MutualTLSMode("asdf"), - }, - }, - expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.mutualTLSMode: Invalid value: "asdf": Must be one of "", "strict", or "permissive".`, - }, "mode": { input: &ServiceDefaults{ ObjectMeta: metav1.ObjectMeta{ @@ -995,21 +875,6 @@ func TestServiceDefaults_Validate(t *testing.T) { }, expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.upstreamConfig.defaults.name: Invalid value: "foobar": upstream.name for a default upstream must be ""`, }, - "upstreamConfig.defaults.namespace": { - input: &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-service", - }, - Spec: ServiceDefaultsSpec{ - UpstreamConfig: &Upstreams{ - Defaults: &Upstream{ - Namespace: "foobar", - }, - }, - }, - }, - expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.upstreamConfig.defaults.namespace: Invalid value: "foobar": upstream.namespace for a default upstream must be ""`, - }, "upstreamConfig.defaults.partition": { input: &ServiceDefaults{ ObjectMeta: metav1.ObjectMeta{ @@ -1024,22 +889,7 @@ func TestServiceDefaults_Validate(t *testing.T) { }, }, partitionsEnabled: false, - expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: [spec.upstreamConfig.defaults.partition: Invalid value: "upstream": upstream.partition for a default upstream must be "", spec.upstreamConfig.defaults.partition: Invalid value: "upstream": Consul Enterprise Admin Partitions must be enabled to set upstream.partition]`, - }, - "upstreamConfig.defaults.peer": { - input: &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-service", - }, - Spec: ServiceDefaultsSpec{ - UpstreamConfig: &Upstreams{ - Defaults: &Upstream{ - Peer: "foobar", - }, - }, - }, - }, - expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.upstreamConfig.defaults.peer: Invalid value: "foobar": upstream.peer for a default upstream must be ""`, + expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.upstreamConfig.defaults.partition: Invalid value: "upstream": Consul Enterprise Admin Partitions must be enabled to set upstream.partition`, }, "upstreamConfig.overrides.meshGateway": { input: &ServiceDefaults{ @@ -1096,44 +946,6 @@ func TestServiceDefaults_Validate(t *testing.T) { }, expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.upstreamConfig.overrides[0].partition: Invalid value: "upstream": Consul Enterprise Admin Partitions must be enabled to set upstream.partition`, }, - "upstreamConfig.overrides.partition and namespace": { - input: &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-service", - }, - Spec: ServiceDefaultsSpec{ - UpstreamConfig: &Upstreams{ - Overrides: []*Upstream{ - { - Name: "service", - Namespace: "namespace", - Peer: "peer", - }, - }, - }, - }, - }, - expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.upstreamConfig.overrides[0]: Invalid value: v1alpha1.Upstream{Name:"service", Namespace:"namespace", Partition:"", Peer:"peer", EnvoyListenerJSON:"", EnvoyClusterJSON:"", Protocol:"", ConnectTimeoutMs:0, Limits:(*v1alpha1.UpstreamLimits)(nil), PassiveHealthCheck:(*v1alpha1.PassiveHealthCheck)(nil), MeshGateway:v1alpha1.MeshGateway{Mode:""}}: both namespace and peer cannot be specified.`, - }, - "upstreamConfig.overrides.partition and peer": { - input: &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-service", - }, - Spec: ServiceDefaultsSpec{ - UpstreamConfig: &Upstreams{ - Overrides: []*Upstream{ - { - Name: "service", - Partition: "upstream", - Peer: "peer", - }, - }, - }, - }, - }, - expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: [spec.upstreamConfig.overrides[0]: Invalid value: v1alpha1.Upstream{Name:"service", Namespace:"", Partition:"upstream", Peer:"peer", EnvoyListenerJSON:"", EnvoyClusterJSON:"", Protocol:"", ConnectTimeoutMs:0, Limits:(*v1alpha1.UpstreamLimits)(nil), PassiveHealthCheck:(*v1alpha1.PassiveHealthCheck)(nil), MeshGateway:v1alpha1.MeshGateway{Mode:""}}: both partition and peer cannot be specified., spec.upstreamConfig.overrides[0].partition: Invalid value: "upstream": Consul Enterprise Admin Partitions must be enabled to set upstream.partition]`, - }, "multi-error": { input: &ServiceDefaults{ ObjectMeta: metav1.ObjectMeta{ @@ -1224,111 +1036,6 @@ func TestServiceDefaults_Validate(t *testing.T) { }, expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.destination.port: Invalid value: 0x0: invalid port number`, }, - "MaxInboundConnections (invalid value)": { - input: &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-service", - }, - Spec: ServiceDefaultsSpec{ - MaxInboundConnections: -1, - }, - }, - expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.maxinboundconnections: Invalid value: -1: MaxInboundConnections must be > 0`, - }, - "LocalConnectTimeoutMs (invalid value)": { - input: &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-service", - }, - Spec: ServiceDefaultsSpec{ - LocalConnectTimeoutMs: -1, - }, - }, - expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.localConnectTimeoutMs: Invalid value: -1: LocalConnectTimeoutMs must be > 0`, - }, - "LocalRequestTimeoutMs (invalid value)": { - input: &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-service", - }, - Spec: ServiceDefaultsSpec{ - LocalRequestTimeoutMs: -1, - }, - }, - expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.localRequestTimeoutMs: Invalid value: -1: LocalRequestTimeoutMs must be > 0`, - }, - "balanceInboundConnections (invalid value)": { - input: &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-service", - }, - Spec: ServiceDefaultsSpec{ - BalanceInboundConnections: "not_exact_balance", - }, - }, - expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.balanceInboundConnections: Invalid value: "not_exact_balance": BalanceInboundConnections must be an empty string or exact_balance`, - }, - "envoyExtension.arguments (single empty)": { - input: &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-service", - }, - Spec: ServiceDefaultsSpec{ - EnvoyExtensions: EnvoyExtensions{ - EnvoyExtension{ - Name: "aws_request_signing", - Arguments: json.RawMessage(`{"AWSServiceName": "s3", "Region": "us-west-2"}`), - Required: false, - }, - EnvoyExtension{ - Name: "zipkin", - Arguments: nil, - Required: true, - }, - }, - }, - }, - expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.envoyExtensions.envoyExtension[1].arguments: Required value: arguments must be defined`, - }, - "envoyExtension.arguments (multi empty)": { - input: &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-service", - }, - Spec: ServiceDefaultsSpec{ - EnvoyExtensions: EnvoyExtensions{ - EnvoyExtension{ - Name: "aws_request_signing", - Arguments: nil, - Required: false, - }, - EnvoyExtension{ - Name: "aws_request_signing", - Arguments: nil, - Required: false, - }, - }, - }, - }, - expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: [spec.envoyExtensions.envoyExtension[0].arguments: Required value: arguments must be defined, spec.envoyExtensions.envoyExtension[1].arguments: Required value: arguments must be defined]`, - }, - "envoyExtension.arguments (invalid json)": { - input: &ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-service", - }, - Spec: ServiceDefaultsSpec{ - EnvoyExtensions: EnvoyExtensions{ - EnvoyExtension{ - Name: "aws_request_signing", - Arguments: json.RawMessage(`{"SOME_INVALID_JSON"}`), - Required: false, - }, - }, - }, - }, - expectedErrMsg: `servicedefaults.consul.hashicorp.com "my-service" is invalid: spec.envoyExtensions.envoyExtension[0].arguments: Invalid value: "{\"SOME_INVALID_JSON\"}": must be valid map value: invalid character '}' after object key`, - }, } for name, testCase := range cases { @@ -1433,7 +1140,7 @@ func TestServiceDefaults_ConsulName(t *testing.T) { } func TestServiceDefaults_KubernetesName(t *testing.T) { - require.Equal(t, "foo", (&ServiceDefaults{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}).KubernetesName()) + require.Equal(t, "foo", (&ServiceDefaults{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}).ConsulName()) } func TestServiceDefaults_ConsulNamespace(t *testing.T) { diff --git a/control-plane/api/v1alpha1/servicedefaults_webhook.go b/control-plane/api/v1alpha1/servicedefaults_webhook.go index 124aeff5f6..f79e68bcde 100644 --- a/control-plane/api/v1alpha1/servicedefaults_webhook.go +++ b/control-plane/api/v1alpha1/servicedefaults_webhook.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/serviceintentions_types.go b/control-plane/api/v1alpha1/serviceintentions_types.go index d393f72a2d..1a41caa289 100644 --- a/control-plane/api/v1alpha1/serviceintentions_types.go +++ b/control-plane/api/v1alpha1/serviceintentions_types.go @@ -1,11 +1,7 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( "encoding/json" - "fmt" "net/http" "strings" @@ -59,8 +55,6 @@ type ServiceIntentionsSpec struct { // The order of this list does not matter, but out of convenience Consul will always store this // reverse sorted by intention precedence, as that is the order that they will be evaluated at enforcement time. Sources SourceIntentions `json:"sources,omitempty"` - // JWT specifies the configuration to validate a JSON Web Token for all incoming requests. - JWT *IntentionJWTRequirement `json:"jwt,omitempty"` } type IntentionDestination struct { @@ -84,12 +78,10 @@ type SourceIntention struct { Name string `json:"name,omitempty"` // Namespace is the namespace for the Name parameter. Namespace string `json:"namespace,omitempty"` - // Peer is the peer name for the Name parameter. + // [Experimental] Peer is the peer name for the Name parameter. Peer string `json:"peer,omitempty"` // Partition is the Admin Partition for the Name parameter. Partition string `json:"partition,omitempty"` - // SamenessGroup is the name of the sameness group, if applicable. - SamenessGroup string `json:"samenessGroup,omitempty"` // Action is required for an L4 intention, and should be set to one of // "allow" or "deny" for the action that should be taken if this intention matches a request. Action IntentionAction `json:"action,omitempty"` @@ -110,8 +102,6 @@ type IntentionPermission struct { Action IntentionAction `json:"action,omitempty"` // HTTP is a set of HTTP-specific authorization criteria. HTTP *IntentionHTTPPermission `json:"http,omitempty"` - // JWT specifies configuration to validate a JSON Web Token for incoming requests. - JWT *IntentionJWTRequirement `json:"jwt,omitempty"` } type IntentionHTTPPermission struct { @@ -146,30 +136,6 @@ type IntentionHTTPHeaderPermission struct { Invert bool `json:"invert,omitempty"` } -type IntentionJWTRequirement struct { - // Providers is a list of providers to consider when verifying a JWT. - Providers []*IntentionJWTProvider `json:"providers,omitempty"` -} - -type IntentionJWTProvider struct { - // Name is the name of the JWT provider. There MUST be a corresponding - // "jwt-provider" config entry with this name. - Name string `json:"name,omitempty"` - - // VerifyClaims is a list of additional claims to verify in a JWT's payload. - VerifyClaims []*IntentionJWTClaimVerification `json:"verifyClaims,omitempty"` -} - -type IntentionJWTClaimVerification struct { - // Path is the path to the claim in the token JSON. - Path []string `json:"path,omitempty"` - - // Value is the expected value at the given path. If the type at the path - // is a list then we verify that this value is contained in the list. If - // the type at the path is a string then we verify that this value matches. - Value string `json:"value,omitempty"` -} - // IntentionAction is the action that the intention represents. This // can be "allow" or "deny" to allowlist or denylist intentions. type IntentionAction string @@ -254,7 +220,6 @@ func (in *ServiceIntentions) ToConsul(datacenter string) api.ConfigEntry { Name: in.Spec.Destination.Name, Namespace: in.Spec.Destination.Namespace, Sources: in.Spec.Sources.toConsul(), - JWT: in.Spec.JWT.toConsul(), Meta: meta(datacenter), } } @@ -321,12 +286,10 @@ func (in *ServiceIntentions) Validate(consulMeta common.ConsulMeta) error { } else { errs = append(errs, source.Permissions.validate(path.Child("sources").Index(i))...) } - errs = append(errs, source.validate(path.Child("sources").Index(i), consulMeta.PartitionsEnabled)...) } errs = append(errs, in.validateNamespaces(consulMeta.NamespacesEnabled)...) - - errs = append(errs, in.Spec.JWT.validate(path.Child("jwt"))...) + errs = append(errs, in.validateSourcePeerAndPartitions(consulMeta.PartitionsEnabled)...) if len(errs) > 0 { return apierrors.NewInvalid( @@ -336,46 +299,6 @@ func (in *ServiceIntentions) Validate(consulMeta common.ConsulMeta) error { return nil } -func (in *SourceIntention) validate(path *field.Path, partitionsEnabled bool) field.ErrorList { - var errs field.ErrorList - - if in.Name == "" { - errs = append(errs, field.Required(path.Child("name"), "name is required.")) - } - - if strings.Contains(in.Partition, WildcardSpecifier) { - errs = append(errs, field.Invalid(path.Child("partition"), in.Partition, "partition cannot use or contain wildcard '*'")) - } - if strings.Contains(in.Peer, WildcardSpecifier) { - errs = append(errs, field.Invalid(path.Child("peer"), in.Peer, "peer cannot use or contain wildcard '*'")) - } - if strings.Contains(in.SamenessGroup, WildcardSpecifier) { - errs = append(errs, field.Invalid(path.Child("samenessgroup"), in.SamenessGroup, "samenessgroup cannot use or contain wildcard '*'")) - } - - if in.Partition != "" && !partitionsEnabled { - errs = append(errs, field.Invalid(path.Child("partition"), in.Partition, `Consul Enterprise Admin Partitions must be enabled to set source.partition`)) - } - - if in.Peer != "" && in.Partition != "" { - errs = append(errs, field.Invalid(path, *in, "cannot set peer and partition at the same time.")) - } - - if in.SamenessGroup != "" && in.Partition != "" { - errs = append(errs, field.Invalid(path, *in, "cannot set samenessgroup and partition at the same time.")) - } - - if in.SamenessGroup != "" && in.Peer != "" { - errs = append(errs, field.Invalid(path, *in, "cannot set samenessgroup and peer at the same time.")) - } - - if len(in.Description) > metaValueMaxLength { - errs = append(errs, field.Invalid(path, "", fmt.Sprintf("description exceeds maximum length %d", metaValueMaxLength))) - } - - return errs -} - // DefaultNamespaceFields sets the namespace field on spec.destination to their default values if namespaces are enabled. func (in *ServiceIntentions) DefaultNamespaceFields(consulMeta common.ConsulMeta) { // If namespaces are enabled we want to set the destination namespace field to it's @@ -404,14 +327,13 @@ func (in *SourceIntention) toConsul() *capi.SourceIntention { return nil } return &capi.SourceIntention{ - Name: in.Name, - Namespace: in.Namespace, - Partition: in.Partition, - Peer: in.Peer, - SamenessGroup: in.SamenessGroup, - Action: in.Action.toConsul(), - Permissions: in.Permissions.toConsul(), - Description: in.Description, + Name: in.Name, + Namespace: in.Namespace, + Partition: in.Partition, + Peer: in.Peer, + Action: in.Action.toConsul(), + Permissions: in.Permissions.toConsul(), + Description: in.Description, } } @@ -425,7 +347,6 @@ func (in IntentionPermissions) toConsul() []*capi.IntentionPermission { consulIntentionPermissions = append(consulIntentionPermissions, &capi.IntentionPermission{ Action: permission.Action.toConsul(), HTTP: permission.HTTP.toConsul(), - JWT: permission.JWT.toConsul(), }) } return consulIntentionPermissions @@ -461,54 +382,15 @@ func (in IntentionHTTPHeaderPermissions) toConsul() []capi.IntentionHTTPHeaderPe return headerPermissions } -func (in *IntentionJWTRequirement) toConsul() *capi.IntentionJWTRequirement { - if in == nil { - return nil - } - var providers []*capi.IntentionJWTProvider - for _, p := range in.Providers { - providers = append(providers, p.toConsul()) - } - return &capi.IntentionJWTRequirement{ - Providers: providers, - } -} - -func (in *IntentionJWTProvider) toConsul() *capi.IntentionJWTProvider { - if in == nil { - return nil - } - var claims []*capi.IntentionJWTClaimVerification - for _, c := range in.VerifyClaims { - claims = append(claims, c.toConsul()) - } - return &capi.IntentionJWTProvider{ - Name: in.Name, - VerifyClaims: claims, - } -} - -func (in *IntentionJWTClaimVerification) toConsul() *capi.IntentionJWTClaimVerification { - if in == nil { - return nil - } - return &capi.IntentionJWTClaimVerification{ - Path: in.Path, - Value: in.Value, - } -} - func (in IntentionPermissions) validate(path *field.Path) field.ErrorList { var errs field.ErrorList for i, permission := range in { - permPath := path.Child("permissions").Index(i) - if err := permission.Action.validate(permPath); err != nil { + if err := permission.Action.validate(path.Child("permissions").Index(i)); err != nil { errs = append(errs, err) } if permission.HTTP != nil { - errs = append(errs, permission.HTTP.validate(permPath)...) + errs = append(errs, permission.HTTP.validate(path.Child("permissions").Index(i))...) } - errs = append(errs, permission.JWT.validate(permPath.Child("jwt"))...) } return errs } @@ -593,6 +475,21 @@ func (in *ServiceIntentions) validateNamespaces(namespacesEnabled bool) field.Er return errs } +func (in *ServiceIntentions) validateSourcePeerAndPartitions(partitionsEnabled bool) field.ErrorList { + var errs field.ErrorList + path := field.NewPath("spec") + for i, source := range in.Spec.Sources { + if source.Partition != "" && !partitionsEnabled { + errs = append(errs, field.Invalid(path.Child("sources").Index(i).Child("partition"), source.Partition, `Consul Enterprise Admin Partitions must be enabled to set source.partition`)) + } + + if source.Peer != "" && source.Partition != "" { + errs = append(errs, field.Invalid(path.Child("sources").Index(i), source, `Both source.peer and source.partition cannot be set.`)) + } + } + return errs +} + func (in IntentionAction) validate(path *field.Path) *field.Error { actions := []string{"allow", "deny"} if !sliceContains(actions, string(in)) { @@ -611,27 +508,6 @@ func numNotEmpty(ss ...string) int { return count } -func (in *IntentionJWTRequirement) validate(path *field.Path) field.ErrorList { - var errs field.ErrorList - if in == nil { - return errs - } - - for i, p := range in.Providers { - if err := p.validate(path.Child("providers").Index(i)); err != nil { - errs = append(errs, err) - } - } - return errs -} - -func (in *IntentionJWTProvider) validate(path *field.Path) *field.Error { - if in != nil && in.Name == "" { - return field.Invalid(path.Child("name"), in.Name, "JWT provider name is required") - } - return nil -} - // sourceIntentionSortKey returns a string that can be used to sort intention // sources. func sourceIntentionSortKey(ixn *capi.SourceIntention) string { diff --git a/control-plane/api/v1alpha1/serviceintentions_types_test.go b/control-plane/api/v1alpha1/serviceintentions_types_test.go index 8d0a6d907a..01199f74d1 100644 --- a/control-plane/api/v1alpha1/serviceintentions_types_test.go +++ b/control-plane/api/v1alpha1/serviceintentions_types_test.go @@ -1,10 +1,6 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( - "strings" "testing" "time" @@ -165,37 +161,11 @@ func TestServiceIntentions_MatchesConsul(t *testing.T) { "PUT", }, }, - JWT: &IntentionJWTRequirement{ - Providers: []*IntentionJWTProvider{ - { - Name: "okta-nested", - VerifyClaims: []*IntentionJWTClaimVerification{ - { - Path: []string{"perms", "role"}, - Value: "admin-nested", - }, - }, - }, - }, - }, }, }, Description: "an L7 config", }, }, - JWT: &IntentionJWTRequirement{ - Providers: []*IntentionJWTProvider{ - { - Name: "okta", - VerifyClaims: []*IntentionJWTClaimVerification{ - { - Path: []string{"perms", "role"}, - Value: "admin", - }, - }, - }, - }, - }, }, }, Theirs: &capi.ServiceIntentionsConfigEntry{ @@ -246,37 +216,11 @@ func TestServiceIntentions_MatchesConsul(t *testing.T) { "PUT", }, }, - JWT: &capi.IntentionJWTRequirement{ - Providers: []*capi.IntentionJWTProvider{ - { - Name: "okta-nested", - VerifyClaims: []*capi.IntentionJWTClaimVerification{ - { - Path: []string{"perms", "role"}, - Value: "admin-nested", - }, - }, - }, - }, - }, }, }, Description: "an L7 config", }, }, - JWT: &capi.IntentionJWTRequirement{ - Providers: []*capi.IntentionJWTProvider{ - { - Name: "okta", - VerifyClaims: []*capi.IntentionJWTClaimVerification{ - { - Path: []string{"perms", "role"}, - Value: "admin", - }, - }, - }, - }, - }, Meta: nil, }, Matches: true, @@ -394,13 +338,6 @@ func TestServiceIntentions_ToConsul(t *testing.T) { Action: "deny", Description: "disallow access from namespace not-test", }, - { - Name: "*", - Namespace: "ns1", - SamenessGroup: "sg2", - Action: "deny", - Description: "disallow access from namespace ns1", - }, { Name: "svc-2", Namespace: "bar", @@ -428,37 +365,11 @@ func TestServiceIntentions_ToConsul(t *testing.T) { "PUT", }, }, - JWT: &IntentionJWTRequirement{ - Providers: []*IntentionJWTProvider{ - { - Name: "okta-nested", - VerifyClaims: []*IntentionJWTClaimVerification{ - { - Path: []string{"perms", "role"}, - Value: "admin-nested", - }, - }, - }, - }, - }, }, }, Description: "an L7 config", }, }, - JWT: &IntentionJWTRequirement{ - Providers: []*IntentionJWTProvider{ - { - Name: "okta", - VerifyClaims: []*IntentionJWTClaimVerification{ - { - Path: []string{"perms", "role"}, - Value: "admin", - }, - }, - }, - }, - }, }, }, Exp: &capi.ServiceIntentionsConfigEntry{ @@ -480,13 +391,6 @@ func TestServiceIntentions_ToConsul(t *testing.T) { Action: "deny", Description: "disallow access from namespace not-test", }, - { - Name: "*", - Namespace: "ns1", - SamenessGroup: "sg2", - Action: "deny", - Description: "disallow access from namespace ns1", - }, { Name: "svc-2", Namespace: "bar", @@ -514,37 +418,11 @@ func TestServiceIntentions_ToConsul(t *testing.T) { "PUT", }, }, - JWT: &capi.IntentionJWTRequirement{ - Providers: []*capi.IntentionJWTProvider{ - { - Name: "okta-nested", - VerifyClaims: []*capi.IntentionJWTClaimVerification{ - { - Path: []string{"perms", "role"}, - Value: "admin-nested", - }, - }, - }, - }, - }, }, }, Description: "an L7 config", }, }, - JWT: &capi.IntentionJWTRequirement{ - Providers: []*capi.IntentionJWTProvider{ - { - Name: "okta", - VerifyClaims: []*capi.IntentionJWTClaimVerification{ - { - Path: []string{"perms", "role"}, - Value: "admin", - }, - }, - }, - }, - }, Meta: map[string]string{ common.SourceKey: common.SourceValue, common.DatacenterKey: "datacenter", @@ -793,8 +671,6 @@ func TestServiceIntentions_DefaultNamespaceFields(t *testing.T) { } func TestServiceIntentions_Validate(t *testing.T) { - longDescription := strings.Repeat("x", metaValueMaxLength+1) - cases := map[string]struct { input *ServiceIntentions namespacesEnabled bool @@ -845,37 +721,11 @@ func TestServiceIntentions_Validate(t *testing.T) { "PUT", }, }, - JWT: &IntentionJWTRequirement{ - Providers: []*IntentionJWTProvider{ - { - Name: "okta-nested", - VerifyClaims: []*IntentionJWTClaimVerification{ - { - Path: []string{"perms", "role"}, - Value: "admin-nested", - }, - }, - }, - }, - }, }, }, Description: "an L7 config", }, }, - JWT: &IntentionJWTRequirement{ - Providers: []*IntentionJWTProvider{ - { - Name: "okta", - VerifyClaims: []*IntentionJWTClaimVerification{ - { - Path: []string{"perms", "role"}, - Value: "admin", - }, - }, - }, - }, - }, }, }, namespacesEnabled: true, @@ -1343,54 +1193,6 @@ func TestServiceIntentions_Validate(t *testing.T) { `serviceintentions.consul.hashicorp.com "does-not-matter" is invalid: spec.sources[0]: Invalid value: "{\"name\":\"svc-2\",\"namespace\":\"bar\",\"action\":\"deny\",\"permissions\":[{\"action\":\"allow\",\"http\":{\"pathExact\":\"/bar\"}}]}": action and permissions are mutually exclusive and only one of them can be specified`, }, }, - "name not specified": { - input: &ServiceIntentions{ - ObjectMeta: metav1.ObjectMeta{ - Name: "does-not-matter", - }, - Spec: ServiceIntentionsSpec{ - Destination: IntentionDestination{ - Name: "dest-service", - Namespace: "namespace", - }, - Sources: SourceIntentions{ - { - Namespace: "bar", - Action: "deny", - }, - }, - }, - }, - namespacesEnabled: true, - expectedErrMsgs: []string{ - `serviceintentions.consul.hashicorp.com "does-not-matter" is invalid: spec.sources[0].name: Required value: name is required.`, - }, - }, - "description is too long": { - input: &ServiceIntentions{ - ObjectMeta: metav1.ObjectMeta{ - Name: "does-not-matter", - }, - Spec: ServiceIntentionsSpec{ - Destination: IntentionDestination{ - Name: "dest-service", - Namespace: "namespace", - }, - Sources: SourceIntentions{ - { - Name: "foo", - Namespace: "bar", - Action: "deny", - Description: longDescription, - }, - }, - }, - }, - namespacesEnabled: true, - expectedErrMsgs: []string{ - `serviceintentions.consul.hashicorp.com "does-not-matter" is invalid: spec.sources[0]: Invalid value: "": description exceeds maximum length 512`, - }, - }, "namespaces disabled: destination namespace specified": { input: &ServiceIntentions{ ObjectMeta: metav1.ObjectMeta{ @@ -1610,71 +1412,7 @@ func TestServiceIntentions_Validate(t *testing.T) { namespacesEnabled: true, partitionsEnabled: true, expectedErrMsgs: []string{ - `cannot set peer and partition at the same time.`, - }, - }, - "single source samenessgroup and partition specified": { - input: &ServiceIntentions{ - ObjectMeta: metav1.ObjectMeta{ - Name: "does-not-matter", - }, - Spec: ServiceIntentionsSpec{ - Destination: IntentionDestination{ - Name: "dest-service", - Namespace: "namespace-a", - }, - Sources: SourceIntentions{ - { - Name: "web", - Action: "allow", - Namespace: "namespace-b", - Partition: "partition-other", - SamenessGroup: "sg2", - }, - { - Name: "db", - Action: "deny", - Namespace: "namespace-c", - }, - }, - }, - }, - namespacesEnabled: true, - partitionsEnabled: true, - expectedErrMsgs: []string{ - `cannot set samenessgroup and partition at the same time.`, - }, - }, - "single source samenessgroup and peer specified": { - input: &ServiceIntentions{ - ObjectMeta: metav1.ObjectMeta{ - Name: "does-not-matter", - }, - Spec: ServiceIntentionsSpec{ - Destination: IntentionDestination{ - Name: "dest-service", - Namespace: "namespace-a", - }, - Sources: SourceIntentions{ - { - Name: "web", - Action: "allow", - Namespace: "namespace-b", - Peer: "p2", - SamenessGroup: "sg2", - }, - { - Name: "db", - Action: "deny", - Namespace: "namespace-c", - }, - }, - }, - }, - namespacesEnabled: true, - partitionsEnabled: true, - expectedErrMsgs: []string{ - `cannot set samenessgroup and peer at the same time.`, + `spec.sources[0]: Invalid value: v1alpha1.SourceIntention{Name:"web", Namespace:"namespace-b", Peer:"peer-other", Partition:"partition-other", Action:"allow", Permissions:v1alpha1.IntentionPermissions(nil), Description:""}: Both source.peer and source.partition cannot be set.`, }, }, "multiple source peer and partition specified": { @@ -1708,108 +1446,8 @@ func TestServiceIntentions_Validate(t *testing.T) { namespacesEnabled: true, partitionsEnabled: true, expectedErrMsgs: []string{ - `spec.sources[0]: Invalid value: v1alpha1.SourceIntention{Name:"web", Namespace:"namespace-b", Peer:"peer-other", Partition:"partition-other", SamenessGroup:"", Action:"allow", Permissions:v1alpha1.IntentionPermissions(nil), Description:""}: cannot set peer and partition at the same time.`, - `spec.sources[1]: Invalid value: v1alpha1.SourceIntention{Name:"db", Namespace:"namespace-c", Peer:"peer-2", Partition:"partition-2", SamenessGroup:"", Action:"deny", Permissions:v1alpha1.IntentionPermissions(nil), Description:""}: cannot set peer and partition at the same time.`, - }, - }, - "multiple errors: wildcard peer and partition and samenessgroup specified": { - input: &ServiceIntentions{ - ObjectMeta: metav1.ObjectMeta{ - Name: "does-not-matter", - }, - Spec: ServiceIntentionsSpec{ - Destination: IntentionDestination{ - Name: "dest-service", - Namespace: "namespace-a", - }, - Sources: SourceIntentions{ - { - Name: "web", - Action: "allow", - Namespace: "namespace-b", - Partition: "*", - }, - { - Name: "db", - Action: "deny", - Namespace: "namespace-c", - Peer: "*", - }, - { - Name: "db2", - Action: "deny", - Namespace: "namespace-d", - SamenessGroup: "*", - }, - }, - }, - }, - namespacesEnabled: true, - partitionsEnabled: true, - expectedErrMsgs: []string{ - `partition cannot use or contain wildcard '*'`, - `peer cannot use or contain wildcard '*'`, - `samenessgroup cannot use or contain wildcard '*'`, - }, - }, - "invalid empty jwt provider name at top-level": { - input: &ServiceIntentions{ - ObjectMeta: metav1.ObjectMeta{ - Name: "does-not-matter", - }, - Spec: ServiceIntentionsSpec{ - Destination: IntentionDestination{ - Name: "dest-service", - }, - Sources: SourceIntentions{ - { - Name: "bar", - Action: "allow", - }, - }, - JWT: &IntentionJWTRequirement{ - Providers: []*IntentionJWTProvider{ - { - Name: "", - }, - }, - }, - }, - }, - expectedErrMsgs: []string{ - `spec.jwt.providers[0].name: Invalid value: "": JWT provider name is required`, - }, - }, - "invalid empty jwt provider name in permissions": { - input: &ServiceIntentions{ - ObjectMeta: metav1.ObjectMeta{ - Name: "does-not-matter", - }, - Spec: ServiceIntentionsSpec{ - Destination: IntentionDestination{ - Name: "dest-service", - }, - Sources: SourceIntentions{ - { - Name: "bar", - Permissions: IntentionPermissions{ - { - Action: "allow", - JWT: &IntentionJWTRequirement{ - Providers: []*IntentionJWTProvider{ - { - Name: "", - }, - }, - }, - }, - }, - }, - }, - }, - }, - expectedErrMsgs: []string{ - `spec.sources[0].permissions[0].jwt.providers[0].name: Invalid value: "": JWT provider name is required`, + `spec.sources[0]: Invalid value: v1alpha1.SourceIntention{Name:"web", Namespace:"namespace-b", Peer:"peer-other", Partition:"partition-other", Action:"allow", Permissions:v1alpha1.IntentionPermissions(nil), Description:""}: Both source.peer and source.partition cannot be set.`, + `spec.sources[1]: Invalid value: v1alpha1.SourceIntention{Name:"db", Namespace:"namespace-c", Peer:"peer-2", Partition:"partition-2", Action:"deny", Permissions:v1alpha1.IntentionPermissions(nil), Description:""}: Both source.peer and source.partition cannot be set.`, }, }, } diff --git a/control-plane/api/v1alpha1/serviceintentions_webhook.go b/control-plane/api/v1alpha1/serviceintentions_webhook.go index fef2401fb3..ddc6488690 100644 --- a/control-plane/api/v1alpha1/serviceintentions_webhook.go +++ b/control-plane/api/v1alpha1/serviceintentions_webhook.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/serviceintentions_webhook_test.go b/control-plane/api/v1alpha1/serviceintentions_webhook_test.go index 238ff7f33e..2df10ad11d 100644 --- a/control-plane/api/v1alpha1/serviceintentions_webhook_test.go +++ b/control-plane/api/v1alpha1/serviceintentions_webhook_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/serviceresolver_types.go b/control-plane/api/v1alpha1/serviceresolver_types.go index 3a8e907222..4fc637b35f 100644 --- a/control-plane/api/v1alpha1/serviceresolver_types.go +++ b/control-plane/api/v1alpha1/serviceresolver_types.go @@ -1,23 +1,17 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( "encoding/json" - "regexp" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + "github.com/hashicorp/consul-k8s/control-plane/api/common" + capi "github.com/hashicorp/consul/api" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" - - "github.com/hashicorp/consul-k8s/control-plane/api/common" - capi "github.com/hashicorp/consul/api" - "github.com/hashicorp/go-bexpr" ) const ServiceResolverKubeKind string = "serviceresolver" @@ -76,15 +70,9 @@ type ServiceResolverSpec struct { // ConnectTimeout is the timeout for establishing new network connections // to this service. ConnectTimeout metav1.Duration `json:"connectTimeout,omitempty"` - // RequestTimeout is the timeout for receiving an HTTP response from this - // service before the connection is terminated. - RequestTimeout metav1.Duration `json:"requestTimeout,omitempty"` // LoadBalancer determines the load balancing policy and configuration for services // issuing requests to this upstream service. LoadBalancer *LoadBalancer `json:"loadBalancer,omitempty"` - // PrioritizeByLocality controls whether the locality of services within the - // local partition will be used to prioritize connectivity. - PrioritizeByLocality *PrioritizeByLocality `json:"prioritizeByLocality,omitempty"` } type ServiceResolverRedirect struct { @@ -106,8 +94,6 @@ type ServiceResolverRedirect struct { // Peer is the name of the cluster peer to resolve the service from instead // of the current one. Peer string `json:"peer,omitempty"` - // SamenessGroup is the name of the sameness group to resolve the service from instead of the current one. - SamenessGroup string `json:"samenessGroup,omitempty"` } type ServiceResolverSubsetMap map[string]ServiceResolverSubset @@ -142,10 +128,6 @@ type ServiceResolverFailover struct { Datacenters []string `json:"datacenters,omitempty"` // Targets specifies a fixed list of failover targets to try during failover. Targets []ServiceResolverFailoverTarget `json:"targets,omitempty"` - // Policy specifies the exact mechanism used for failover. - Policy *FailoverPolicy `json:"policy,omitempty"` - // SamenessGroup is the name of the sameness group to try during failover. - SamenessGroup string `json:"samenessGroup,omitempty"` } type ServiceResolverFailoverTarget struct { @@ -306,17 +288,15 @@ func (in *ServiceResolver) SyncedConditionStatus() corev1.ConditionStatus { // ToConsul converts the entry into its Consul equivalent struct. func (in *ServiceResolver) ToConsul(datacenter string) capi.ConfigEntry { return &capi.ServiceResolverConfigEntry{ - Kind: in.ConsulKind(), - Name: in.ConsulName(), - DefaultSubset: in.Spec.DefaultSubset, - Subsets: in.Spec.Subsets.toConsul(), - Redirect: in.Spec.Redirect.toConsul(), - Failover: in.Spec.Failover.toConsul(), - ConnectTimeout: in.Spec.ConnectTimeout.Duration, - RequestTimeout: in.Spec.RequestTimeout.Duration, - LoadBalancer: in.Spec.LoadBalancer.toConsul(), - PrioritizeByLocality: in.Spec.PrioritizeByLocality.toConsul(), - Meta: meta(datacenter), + Kind: in.ConsulKind(), + Name: in.ConsulName(), + DefaultSubset: in.Spec.DefaultSubset, + Subsets: in.Spec.Subsets.toConsul(), + Redirect: in.Spec.Redirect.toConsul(), + Failover: in.Spec.Failover.toConsul(), + ConnectTimeout: in.Spec.ConnectTimeout.Duration, + LoadBalancer: in.Spec.LoadBalancer.toConsul(), + Meta: meta(datacenter), } } @@ -337,18 +317,14 @@ func (in *ServiceResolver) Validate(consulMeta common.ConsulMeta) error { var errs field.ErrorList path := field.NewPath("spec") - for subset, f := range in.Spec.Failover { - errs = append(errs, f.validate(path.Child("failover").Key(subset), consulMeta)...) - } - if len(in.Spec.Failover) > 0 && in.Spec.Redirect != nil { - asJSON, _ := json.Marshal(in) - errs = append(errs, field.Invalid(path, string(asJSON), "service resolver redirect and failover cannot both be set")) + for k, v := range in.Spec.Failover { + if err := v.validate(path.Child("failover").Key(k)); err != nil { + errs = append(errs, err) + } } - errs = append(errs, in.Spec.Redirect.validate(path.Child("redirect"), consulMeta)...) - errs = append(errs, in.Spec.PrioritizeByLocality.validate(path.Child("prioritizeByLocality"))...) - errs = append(errs, in.Spec.Subsets.validate(path.Child("subsets"))...) errs = append(errs, in.Spec.LoadBalancer.validate(path.Child("loadBalancer"))...) + errs = append(errs, in.validateEnterprise(consulMeta)...) if len(errs) > 0 { @@ -375,31 +351,6 @@ func (in ServiceResolverSubsetMap) toConsul() map[string]capi.ServiceResolverSub return m } -func (in ServiceResolverSubsetMap) validate(path *field.Path) field.ErrorList { - var errs field.ErrorList - if len(in) == 0 { - return nil - } - validServiceSubset := regexp.MustCompile(`^[a-z0-9]([a-z0-9-]*[a-z0-9])?$`) - - for name, subset := range in { - indexPath := path.Key(name) - - if name == "" { - errs = append(errs, field.Invalid(indexPath, name, "subset defined with empty name")) - } - if !validServiceSubset.MatchString(name) { - errs = append(errs, field.Invalid(indexPath, name, "subset name must begin or end with lower case alphanumeric characters, and contain lower case alphanumeric characters or '-' in between")) - } - if subset.Filter != "" { - if _, err := bexpr.CreateEvaluator(subset.Filter, nil); err != nil { - errs = append(errs, field.Invalid(indexPath.Child("filter"), subset.Filter, "filter for subset is not a valid expression")) - } - } - } - return errs -} - func (in ServiceResolverSubset) toConsul() capi.ServiceResolverSubset { return capi.ServiceResolverSubset{ Filter: in.Filter, @@ -418,74 +369,7 @@ func (in *ServiceResolverRedirect) toConsul() *capi.ServiceResolverRedirect { Datacenter: in.Datacenter, Partition: in.Partition, Peer: in.Peer, - SamenessGroup: in.SamenessGroup, - } -} - -func (in *ServiceResolverRedirect) validate(path *field.Path, consulMeta common.ConsulMeta) field.ErrorList { - var errs field.ErrorList - if in == nil { - return nil - } - - asJSON, _ := json.Marshal(in) - if in.isEmpty() { - errs = append(errs, field.Invalid(path, "{}", - "service resolver redirect cannot be empty")) - } - - if consulMeta.Partition != "default" && consulMeta.Partition != "" && in.Datacenter != "" { - errs = append(errs, field.Invalid(path.Child("datacenter"), in.Datacenter, - "cross-datacenter redirect is only supported in the default partition")) - } - if consulMeta.Partition != in.Partition && in.Datacenter != "" { - errs = append(errs, field.Invalid(path.Child("partition"), in.Partition, - "cross-datacenter and cross-partition redirect is not supported")) - } - - switch { - case in.SamenessGroup != "" && in.ServiceSubset != "": - errs = append(errs, field.Invalid(path, string(asJSON), - "samenessGroup cannot be set with serviceSubset")) - case in.SamenessGroup != "" && in.Partition != "": - errs = append(errs, field.Invalid(path, string(asJSON), - "partition cannot be set with samenessGroup")) - case in.SamenessGroup != "" && in.Datacenter != "": - errs = append(errs, field.Invalid(path, string(asJSON), - "samenessGroup cannot be set with datacenter")) - case in.Peer != "" && in.ServiceSubset != "": - errs = append(errs, field.Invalid(path, string(asJSON), - "peer cannot be set with serviceSubset")) - case in.Peer != "" && in.Partition != "": - errs = append(errs, field.Invalid(path, string(asJSON), - "partition cannot be set with peer")) - case in.Peer != "" && in.Datacenter != "": - errs = append(errs, field.Invalid(path, string(asJSON), - "peer cannot be set with datacenter")) - case in.Service == "": - if in.ServiceSubset != "" { - errs = append(errs, field.Invalid(path, string(asJSON), - "serviceSubset defined without service")) - } - if in.Namespace != "" { - errs = append(errs, field.Invalid(path, string(asJSON), - "namespace defined without service")) - } - if in.Partition != "" { - errs = append(errs, field.Invalid(path, string(asJSON), - "partition defined without service")) - } - if in.Peer != "" { - errs = append(errs, field.Invalid(path, string(asJSON), - "peer defined without service")) - } } - - return errs -} - -func (in *ServiceResolverRedirect) isEmpty() bool { - return in.Service == "" && in.ServiceSubset == "" && in.Namespace == "" && in.Partition == "" && in.Datacenter == "" && in.Peer == "" && in.SamenessGroup == "" } func (in ServiceResolverFailoverMap) toConsul() map[string]capi.ServiceResolverFailover { @@ -494,38 +378,23 @@ func (in ServiceResolverFailoverMap) toConsul() map[string]capi.ServiceResolverF } m := make(map[string]capi.ServiceResolverFailover) for k, v := range in { - if f := v.toConsul(); f != nil { - m[k] = *f - } + m[k] = v.toConsul() } return m } -func (in *ServiceResolverFailover) toConsul() *capi.ServiceResolverFailover { - if in == nil { - return nil - } +func (in ServiceResolverFailover) toConsul() capi.ServiceResolverFailover { var targets []capi.ServiceResolverFailoverTarget for _, target := range in.Targets { targets = append(targets, target.toConsul()) } - var policy *capi.ServiceResolverFailoverPolicy - if in.Policy != nil { - policy = &capi.ServiceResolverFailoverPolicy{ - Mode: in.Policy.Mode, - Regions: in.Policy.Regions, - } - } - - return &capi.ServiceResolverFailover{ + return capi.ServiceResolverFailover{ Service: in.Service, ServiceSubset: in.ServiceSubset, Namespace: in.Namespace, Datacenters: in.Datacenters, Targets: targets, - Policy: policy, - SamenessGroup: in.SamenessGroup, } } @@ -635,79 +504,17 @@ func (in *ServiceResolver) validateEnterprise(consulMeta common.ConsulMeta) fiel } func (in *ServiceResolverFailover) isEmpty() bool { - return in.Service == "" && in.ServiceSubset == "" && in.Namespace == "" && len(in.Datacenters) == 0 && len(in.Targets) == 0 && in.Policy == nil && in.SamenessGroup == "" + return in.Service == "" && in.ServiceSubset == "" && in.Namespace == "" && len(in.Datacenters) == 0 && len(in.Targets) == 0 } -func (in *ServiceResolverFailover) validate(path *field.Path, consulMeta common.ConsulMeta) field.ErrorList { - var errs field.ErrorList +func (in *ServiceResolverFailover) validate(path *field.Path) *field.Error { if in.isEmpty() { // NOTE: We're passing "{}" here as our value because we know that the // error is we have an empty object. - errs = append(errs, field.Invalid(path, "{}", - "service, serviceSubset, namespace, datacenters, policy, and targets cannot all be empty at once")) - } - - if consulMeta.Partition != "default" && len(in.Datacenters) != 0 { - errs = append(errs, field.Invalid(path.Child("datacenters"), in.Datacenters, - "cross-datacenter failover is only supported in the default partition")) - } - - errs = append(errs, in.Policy.validate(path.Child("policy"))...) - - asJSON, _ := json.Marshal(in) - if in.SamenessGroup != "" { - switch { - case len(in.Datacenters) > 0: - errs = append(errs, field.Invalid(path, string(asJSON), - "samenessGroup cannot be set with datacenters")) - case in.ServiceSubset != "": - errs = append(errs, field.Invalid(path, string(asJSON), - "samenessGroup cannot be set with serviceSubset")) - case len(in.Targets) > 0: - errs = append(errs, field.Invalid(path, string(asJSON), - "samenessGroup cannot be set with targets")) - } + return field.Invalid(path, "{}", + "service, serviceSubset, namespace, datacenters, and targets cannot all be empty at once") } - - if len(in.Datacenters) != 0 && len(in.Targets) != 0 { - errs = append(errs, field.Invalid(path, string(asJSON), - "targets cannot be set with datacenters")) - } - - if in.ServiceSubset != "" && len(in.Targets) != 0 { - errs = append(errs, field.Invalid(path, string(asJSON), - "targets cannot be set with serviceSubset")) - } - - if in.Service != "" && len(in.Targets) != 0 { - errs = append(errs, field.Invalid(path, string(asJSON), - "targets cannot be set with service")) - } - - for i, target := range in.Targets { - asJSON, _ := json.Marshal(target) - switch { - case target.Peer != "" && target.ServiceSubset != "": - errs = append(errs, field.Invalid(path.Child("targets").Index(i), string(asJSON), - "target.peer cannot be set with target.serviceSubset")) - case target.Peer != "" && target.Partition != "": - errs = append(errs, field.Invalid(path.Child("targets").Index(i), string(asJSON), - "target.partition cannot be set with target.peer")) - case target.Peer != "" && target.Datacenter != "": - errs = append(errs, field.Invalid(path.Child("targets").Index(i), string(asJSON), - "target.peer cannot be set with target.datacenter")) - case target.Partition != "" && target.Datacenter != "": - errs = append(errs, field.Invalid(path.Child("targets").Index(i), string(asJSON), - "target.partition cannot be set with target.datacenter")) - } - } - - for i, dc := range in.Datacenters { - if dc == "" { - errs = append(errs, field.Invalid(path.Child("datacenters").Index(i), "", "found empty datacenter")) - } - } - return errs + return nil } func (in *LoadBalancer) validate(path *field.Path) field.ErrorList { diff --git a/control-plane/api/v1alpha1/serviceresolver_types_test.go b/control-plane/api/v1alpha1/serviceresolver_types_test.go index 3fbc2b4fce..fd4fc25a60 100644 --- a/control-plane/api/v1alpha1/serviceresolver_types_test.go +++ b/control-plane/api/v1alpha1/serviceresolver_types_test.go @@ -1,10 +1,6 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( - "strings" "testing" "time" @@ -13,7 +9,6 @@ import ( "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/validation/field" ) func TestServiceResolver_MatchesConsul(t *testing.T) { @@ -66,45 +61,27 @@ func TestServiceResolver_MatchesConsul(t *testing.T) { Datacenter: "redirect_datacenter", Peer: "redirect_peer", }, - PrioritizeByLocality: &PrioritizeByLocality{ - Mode: "failover", - }, Failover: map[string]ServiceResolverFailover{ "failover1": { Service: "failover1", ServiceSubset: "failover_subset1", Namespace: "failover_namespace1", Datacenters: []string{"failover1_dc1", "failover1_dc2"}, - Policy: &FailoverPolicy{ - Mode: "sequential", - Regions: []string{"us-west-2"}, - }, - SamenessGroup: "sg2", }, "failover2": { Service: "failover2", ServiceSubset: "failover_subset2", Namespace: "failover_namespace2", Datacenters: []string{"failover2_dc1", "failover2_dc2"}, - Policy: &FailoverPolicy{ - Mode: "", - Regions: []string{"us-west-1"}, - }, - SamenessGroup: "sg3", }, "failover3": { Targets: []ServiceResolverFailoverTarget{ {Peer: "failover_peer3"}, {Partition: "failover_partition3", Namespace: "failover_namespace3"}, }, - Policy: &FailoverPolicy{ - Mode: "order-by-locality", - Regions: []string{"us-east-1"}, - }, }, }, ConnectTimeout: metav1.Duration{Duration: 1 * time.Second}, - RequestTimeout: metav1.Duration{Duration: 1 * time.Second}, LoadBalancer: &LoadBalancer{ Policy: "policy", RingHashConfig: &RingHashConfig{ @@ -151,45 +128,27 @@ func TestServiceResolver_MatchesConsul(t *testing.T) { Datacenter: "redirect_datacenter", Peer: "redirect_peer", }, - PrioritizeByLocality: &capi.ServiceResolverPrioritizeByLocality{ - Mode: "failover", - }, Failover: map[string]capi.ServiceResolverFailover{ "failover1": { Service: "failover1", ServiceSubset: "failover_subset1", Namespace: "failover_namespace1", Datacenters: []string{"failover1_dc1", "failover1_dc2"}, - Policy: &capi.ServiceResolverFailoverPolicy{ - Mode: "sequential", - Regions: []string{"us-west-2"}, - }, - SamenessGroup: "sg2", }, "failover2": { Service: "failover2", ServiceSubset: "failover_subset2", Namespace: "failover_namespace2", Datacenters: []string{"failover2_dc1", "failover2_dc2"}, - Policy: &capi.ServiceResolverFailoverPolicy{ - Mode: "", - Regions: []string{"us-west-1"}, - }, - SamenessGroup: "sg3", }, "failover3": { Targets: []capi.ServiceResolverFailoverTarget{ {Peer: "failover_peer3"}, {Partition: "failover_partition3", Namespace: "failover_namespace3"}, }, - Policy: &capi.ServiceResolverFailoverPolicy{ - Mode: "order-by-locality", - Regions: []string{"us-east-1"}, - }, }, }, ConnectTimeout: 1 * time.Second, - RequestTimeout: 1 * time.Second, LoadBalancer: &capi.LoadBalancer{ Policy: "policy", RingHashConfig: &capi.RingHashConfig{ @@ -285,45 +244,27 @@ func TestServiceResolver_ToConsul(t *testing.T) { Datacenter: "redirect_datacenter", Partition: "redirect_partition", }, - PrioritizeByLocality: &PrioritizeByLocality{ - Mode: "none", - }, Failover: map[string]ServiceResolverFailover{ "failover1": { Service: "failover1", ServiceSubset: "failover_subset1", Namespace: "failover_namespace1", Datacenters: []string{"failover1_dc1", "failover1_dc2"}, - Policy: &FailoverPolicy{ - Mode: "sequential", - Regions: []string{"us-west-2"}, - }, - SamenessGroup: "sg2", }, "failover2": { Service: "failover2", ServiceSubset: "failover_subset2", Namespace: "failover_namespace2", Datacenters: []string{"failover2_dc1", "failover2_dc2"}, - Policy: &FailoverPolicy{ - Mode: "", - Regions: []string{"us-west-1"}, - }, - SamenessGroup: "sg3", }, "failover3": { Targets: []ServiceResolverFailoverTarget{ {Peer: "failover_peer3"}, {Partition: "failover_partition3", Namespace: "failover_namespace3"}, }, - Policy: &FailoverPolicy{ - Mode: "order-by-locality", - Regions: []string{"us-east-1"}, - }, }, }, ConnectTimeout: metav1.Duration{Duration: 1 * time.Second}, - RequestTimeout: metav1.Duration{Duration: 1 * time.Second}, LoadBalancer: &LoadBalancer{ Policy: "policy", RingHashConfig: &RingHashConfig{ @@ -370,45 +311,27 @@ func TestServiceResolver_ToConsul(t *testing.T) { Datacenter: "redirect_datacenter", Partition: "redirect_partition", }, - PrioritizeByLocality: &capi.ServiceResolverPrioritizeByLocality{ - Mode: "none", - }, Failover: map[string]capi.ServiceResolverFailover{ "failover1": { Service: "failover1", ServiceSubset: "failover_subset1", Namespace: "failover_namespace1", Datacenters: []string{"failover1_dc1", "failover1_dc2"}, - Policy: &capi.ServiceResolverFailoverPolicy{ - Mode: "sequential", - Regions: []string{"us-west-2"}, - }, - SamenessGroup: "sg2", }, "failover2": { Service: "failover2", ServiceSubset: "failover_subset2", Namespace: "failover_namespace2", Datacenters: []string{"failover2_dc1", "failover2_dc2"}, - Policy: &capi.ServiceResolverFailoverPolicy{ - Mode: "", - Regions: []string{"us-west-1"}, - }, - SamenessGroup: "sg3", }, "failover3": { Targets: []capi.ServiceResolverFailoverTarget{ {Peer: "failover_peer3"}, {Partition: "failover_partition3", Namespace: "failover_namespace3"}, }, - Policy: &capi.ServiceResolverFailoverPolicy{ - Mode: "order-by-locality", - Regions: []string{"us-east-1"}, - }, }, }, ConnectTimeout: 1 * time.Second, - RequestTimeout: 1 * time.Second, LoadBalancer: &capi.LoadBalancer{ Policy: "policy", RingHashConfig: &capi.RingHashConfig{ @@ -569,15 +492,16 @@ func TestServiceResolver_Validate(t *testing.T) { Name: "foo", }, Spec: ServiceResolverSpec{ + Redirect: &ServiceResolverRedirect{ + Service: "bar", + Namespace: "namespace-a", + }, Failover: map[string]ServiceResolverFailover{ - "v1": { + "failA": { Service: "baz", Namespace: "namespace-b", }, }, - Subsets: map[string]ServiceResolverSubset{ - "v1": {Filter: "Service.Meta.version == v1"}, - }, }, }, namespacesEnabled: true, @@ -593,8 +517,10 @@ func TestServiceResolver_Validate(t *testing.T) { Redirect: &ServiceResolverRedirect{ Service: "bar", }, - Subsets: map[string]ServiceResolverSubset{ - "v1": {Filter: "Service.Meta.version == v1"}, + Failover: map[string]ServiceResolverFailover{ + "failA": { + Service: "baz", + }, }, }, }, @@ -608,15 +534,17 @@ func TestServiceResolver_Validate(t *testing.T) { Name: "foo", }, Spec: ServiceResolverSpec{ + Redirect: &ServiceResolverRedirect{ + Service: "bar", + Namespace: "namespace-a", + Partition: "other", + }, Failover: map[string]ServiceResolverFailover{ - "v1": { + "failA": { Service: "baz", Namespace: "namespace-b", }, }, - Subsets: map[string]ServiceResolverSubset{ - "v1": {Filter: "Service.Meta.version == v1"}, - }, }, }, namespacesEnabled: true, @@ -632,6 +560,11 @@ func TestServiceResolver_Validate(t *testing.T) { Redirect: &ServiceResolverRedirect{ Service: "bar", }, + Failover: map[string]ServiceResolverFailover{ + "failA": { + Service: "baz", + }, + }, }, }, namespacesEnabled: false, @@ -645,13 +578,13 @@ func TestServiceResolver_Validate(t *testing.T) { }, Spec: ServiceResolverSpec{ Failover: map[string]ServiceResolverFailover{ - "v1": { + "failA": { Service: "", ServiceSubset: "", Namespace: "", Datacenters: nil, }, - "v2": { + "failB": { Service: "", ServiceSubset: "", Namespace: "", @@ -662,32 +595,10 @@ func TestServiceResolver_Validate(t *testing.T) { }, namespacesEnabled: false, expectedErrMsgs: []string{ - "spec.failover[v1]: Invalid value: \"{}\": service, serviceSubset, namespace, datacenters, policy, and targets cannot all be empty at once", - "spec.failover[v2]: Invalid value: \"{}\": service, serviceSubset, namespace, datacenters, policy, and targets cannot all be empty at once", + "spec.failover[failA]: Invalid value: \"{}\": service, serviceSubset, namespace, datacenters, and targets cannot all be empty at once", + "spec.failover[failB]: Invalid value: \"{}\": service, serviceSubset, namespace, datacenters, and targets cannot all be empty at once", }, }, - "service resolver redirect and failover cannot both be set": { - input: &ServiceResolver{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - }, - Spec: ServiceResolverSpec{ - Redirect: &ServiceResolverRedirect{ - Service: "bar", - Namespace: "namespace-a", - }, - Failover: map[string]ServiceResolverFailover{ - "failA": { - Service: "baz", - Namespace: "namespace-b", - }, - }, - }, - }, - namespacesEnabled: true, - partitionsEnabled: false, - expectedErrMsgs: []string{"service resolver redirect and failover cannot both be set"}, - }, "hashPolicy.field invalid": { input: &ServiceResolver{ ObjectMeta: metav1.ObjectMeta{ @@ -761,19 +672,11 @@ func TestServiceResolver_Validate(t *testing.T) { }, }, }, - Subsets: map[string]ServiceResolverSubset{ - "": { - Filter: "random string", - }, - }, }, }, namespacesEnabled: false, expectedErrMsgs: []string{ - `spec.loadBalancer.hashPolicies[0]: Invalid value: "{\"field\":\"header\",\"sourceIP\":true}": cannot set both field and sourceIP`, - `subset defined with empty name`, - `subset name must begin or end with lower case alphanumeric characters, and contain lower case alphanumeric characters or '-' in between`, - `filter for subset is not a valid expression`, + `serviceresolver.consul.hashicorp.com "foo" is invalid: spec.loadBalancer.hashPolicies[0]: Invalid value: "{\"field\":\"header\",\"sourceIP\":true}": cannot set both field and sourceIP`, }, }, "hashPolicy nothing set is valid": { @@ -824,7 +727,6 @@ func TestServiceResolver_Validate(t *testing.T) { }, Spec: ServiceResolverSpec{ Redirect: &ServiceResolverRedirect{ - Service: "bar", Namespace: "namespace-a", }, }, @@ -841,7 +743,6 @@ func TestServiceResolver_Validate(t *testing.T) { }, Spec: ServiceResolverSpec{ Redirect: &ServiceResolverRedirect{ - Service: "bar", Namespace: "namespace-a", Partition: "other", }, @@ -860,19 +761,14 @@ func TestServiceResolver_Validate(t *testing.T) { }, Spec: ServiceResolverSpec{ Failover: map[string]ServiceResolverFailover{ - "v1": { + "failA": { Namespace: "namespace-a", }, }, - Subsets: map[string]ServiceResolverSubset{ - "v1": { - Filter: "Service.Meta.version == v1", - }, - }, }, }, expectedErrMsgs: []string{ - "serviceresolver.consul.hashicorp.com \"foo\" is invalid: spec.failover[v1].namespace: Invalid value: \"namespace-a\": Consul Enterprise namespaces must be enabled to set failover.namespace", + "serviceresolver.consul.hashicorp.com \"foo\" is invalid: spec.failover[failA].namespace: Invalid value: \"namespace-a\": Consul Enterprise namespaces must be enabled to set failover.namespace", }, namespacesEnabled: false, }, @@ -898,22 +794,6 @@ func TestServiceResolver_Validate(t *testing.T) { "spec.failover[failB].namespace: Invalid value: \"namespace-b\": Consul Enterprise namespaces must be enabled to set failover.namespace", }, }, - "prioritize by locality invalid": { - input: &ServiceResolver{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - }, - Spec: ServiceResolverSpec{ - PrioritizeByLocality: &PrioritizeByLocality{ - Mode: "bad", - }, - }, - }, - namespacesEnabled: false, - expectedErrMsgs: []string{ - "serviceresolver.consul.hashicorp.com \"foo\" is invalid: spec.prioritizeByLocality.mode: Invalid value: \"bad\": must be one of \"\", \"none\", \"failover\"", - }, - }, } for name, testCase := range cases { t.Run(name, func(t *testing.T) { @@ -929,497 +809,3 @@ func TestServiceResolver_Validate(t *testing.T) { }) } } - -func TestServiceResolverRedirect_ToConsul(t *testing.T) { - cases := map[string]struct { - Ours *ServiceResolverRedirect - Exp *capi.ServiceResolverRedirect - }{ - "nil": { - Ours: nil, - Exp: nil, - }, - "empty fields": { - Ours: &ServiceResolverRedirect{}, - Exp: &capi.ServiceResolverRedirect{}, - }, - "every field set": { - Ours: &ServiceResolverRedirect{ - Service: "foo", - ServiceSubset: "v1", - Namespace: "ns1", - Datacenter: "dc1", - Partition: "default", - Peer: "peer1", - SamenessGroup: "sg1", - }, - Exp: &capi.ServiceResolverRedirect{ - Service: "foo", - ServiceSubset: "v1", - Namespace: "ns1", - Datacenter: "dc1", - Partition: "default", - Peer: "peer1", - SamenessGroup: "sg1", - }, - }, - } - for name, c := range cases { - t.Run(name, func(t *testing.T) { - actual := c.Ours.toConsul() - require.Equal(t, c.Exp, actual) - }) - } -} - -func TestServiceResolverRedirect_Validate(t *testing.T) { - cases := map[string]struct { - input *ServiceResolverRedirect - consulMeta common.ConsulMeta - expectedErrMsgs []string - }{ - "empty redirect": { - input: &ServiceResolverRedirect{}, - consulMeta: common.ConsulMeta{}, - expectedErrMsgs: []string{ - "service resolver redirect cannot be empty", - }, - }, - "cross-datacenter redirect is only supported in the default partition": { - input: &ServiceResolverRedirect{ - Datacenter: "dc2", - Partition: "p2", - Service: "foo", - }, - consulMeta: common.ConsulMeta{ - Partition: "p2", - PartitionsEnabled: true, - }, - expectedErrMsgs: []string{ - "cross-datacenter redirect is only supported in the default partition", - }, - }, - "cross-datacenter and cross-partition redirect is not supported": { - input: &ServiceResolverRedirect{ - Partition: "p1", - Datacenter: "dc2", - Service: "foo", - }, - consulMeta: common.ConsulMeta{ - Partition: "default", - PartitionsEnabled: true, - }, - expectedErrMsgs: []string{ - "cross-datacenter and cross-partition redirect is not supported", - }, - }, - "samenessGroup cannot be set with serviceSubset": { - input: &ServiceResolverRedirect{ - Service: "foo", - ServiceSubset: "v1", - SamenessGroup: "sg2", - }, - expectedErrMsgs: []string{ - "samenessGroup cannot be set with serviceSubset", - }, - }, - "samenessGroup cannot be set with partition": { - input: &ServiceResolverRedirect{ - Partition: "default", - Service: "foo", - SamenessGroup: "sg2", - }, - consulMeta: common.ConsulMeta{ - Partition: "default", - PartitionsEnabled: true, - }, - expectedErrMsgs: []string{ - "partition cannot be set with samenessGroup", - }, - }, - "samenessGroup cannot be set with datacenter": { - input: &ServiceResolverRedirect{ - Datacenter: "dc2", - Service: "foo", - SamenessGroup: "sg2", - }, - consulMeta: common.ConsulMeta{ - Partition: "default", - PartitionsEnabled: true, - }, - expectedErrMsgs: []string{ - "cross-datacenter and cross-partition redirect is not supported", - "samenessGroup cannot be set with datacenter", - }, - }, - "peer cannot be set with serviceSubset": { - input: &ServiceResolverRedirect{ - Peer: "p2", - Service: "foo", - ServiceSubset: "v1", - }, - consulMeta: common.ConsulMeta{ - Partition: "default", - PartitionsEnabled: true, - }, - expectedErrMsgs: []string{ - "peer cannot be set with serviceSubset", - }, - }, - "partition cannot be set with peer": { - input: &ServiceResolverRedirect{ - Partition: "default", - Peer: "p2", - Service: "foo", - }, - consulMeta: common.ConsulMeta{ - Partition: "default", - PartitionsEnabled: true, - }, - expectedErrMsgs: []string{ - "partition cannot be set with peer", - }, - }, - "peer cannot be set with datacenter": { - input: &ServiceResolverRedirect{ - Peer: "p2", - Service: "foo", - Datacenter: "dc2", - }, - consulMeta: common.ConsulMeta{ - Partition: "default", - PartitionsEnabled: true, - }, - expectedErrMsgs: []string{ - "peer cannot be set with datacenter", - "cross-datacenter and cross-partition redirect is not supported", - }, - }, - "serviceSubset defined without service": { - input: &ServiceResolverRedirect{ - ServiceSubset: "v1", - }, - consulMeta: common.ConsulMeta{ - PartitionsEnabled: true, - }, - expectedErrMsgs: []string{ - "serviceSubset defined without service", - }, - }, - "namespace defined without service": { - input: &ServiceResolverRedirect{ - Namespace: "ns1", - }, - consulMeta: common.ConsulMeta{ - PartitionsEnabled: true, - }, - expectedErrMsgs: []string{ - "namespace defined without service", - }, - }, - "partition defined without service": { - input: &ServiceResolverRedirect{ - Partition: "default", - }, - consulMeta: common.ConsulMeta{ - Partition: "default", - PartitionsEnabled: true, - }, - expectedErrMsgs: []string{ - "partition defined without service", - }, - }, - "peer defined without service": { - input: &ServiceResolverRedirect{ - Peer: "p2", - }, - consulMeta: common.ConsulMeta{ - PartitionsEnabled: true, - }, - expectedErrMsgs: []string{ - "peer defined without service", - }, - }, - } - - path := field.NewPath("spec.redirect") - for name, testCase := range cases { - t.Run(name, func(t *testing.T) { - errList := testCase.input.validate(path, testCase.consulMeta) - compareErrorLists(t, testCase.expectedErrMsgs, errList) - }) - } -} - -func compareErrorLists(t *testing.T, expectedErrMsgs []string, errList field.ErrorList) { - if len(expectedErrMsgs) != 0 { - require.Equal(t, len(expectedErrMsgs), len(errList)) - for _, m := range expectedErrMsgs { - found := false - for _, e := range errList { - errMsg := e.ErrorBody() - if strings.Contains(errMsg, m) { - found = true - break - } - } - require.Equal(t, true, found) - } - } else { - require.Equal(t, 0, len(errList)) - } -} - -func TestServiceResolverFailover_ToConsul(t *testing.T) { - cases := map[string]struct { - Ours *ServiceResolverFailover - Exp *capi.ServiceResolverFailover - }{ - "nil": { - Ours: nil, - Exp: nil, - }, - "empty fields": { - Ours: &ServiceResolverFailover{}, - Exp: &capi.ServiceResolverFailover{}, - }, - "every field set": { - Ours: &ServiceResolverFailover{ - Service: "foo", - ServiceSubset: "v1", - Namespace: "ns1", - Datacenters: []string{"dc1"}, - Targets: []ServiceResolverFailoverTarget{ - { - Peer: "p2", - }, - }, - Policy: &FailoverPolicy{ - Mode: "sequential", - Regions: []string{"us-west-2"}, - }, - SamenessGroup: "sg1", - }, - Exp: &capi.ServiceResolverFailover{ - Service: "foo", - ServiceSubset: "v1", - Namespace: "ns1", - Datacenters: []string{"dc1"}, - Targets: []capi.ServiceResolverFailoverTarget{ - { - Peer: "p2", - }, - }, - Policy: &capi.ServiceResolverFailoverPolicy{ - Mode: "sequential", - Regions: []string{"us-west-2"}, - }, - SamenessGroup: "sg1", - }, - }, - } - for name, c := range cases { - t.Run(name, func(t *testing.T) { - actual := c.Ours.toConsul() - require.Equal(t, c.Exp, actual) - }) - } -} - -func TestServiceResolverFailover_Validate(t *testing.T) { - cases := map[string]struct { - input *ServiceResolverFailover - consulMeta common.ConsulMeta - expectedErrMsgs []string - }{ - "empty failover": { - input: &ServiceResolverFailover{}, - consulMeta: common.ConsulMeta{}, - expectedErrMsgs: []string{ - "service, serviceSubset, namespace, datacenters, policy, and targets cannot all be empty at once", - }, - }, - "cross-datacenter failover is only supported in the default partition": { - input: &ServiceResolverFailover{ - Datacenters: []string{"dc2"}, - Service: "foo", - }, - consulMeta: common.ConsulMeta{ - Partition: "p2", - PartitionsEnabled: true, - }, - expectedErrMsgs: []string{ - "cross-datacenter failover is only supported in the default partition", - }, - }, - "samenessGroup cannot be set with datacenters": { - input: &ServiceResolverFailover{ - Service: "foo", - Datacenters: []string{"dc2"}, - SamenessGroup: "sg2", - }, - consulMeta: common.ConsulMeta{ - Partition: "default", - PartitionsEnabled: true, - }, - expectedErrMsgs: []string{ - "samenessGroup cannot be set with datacenters", - }, - }, - "samenessGroup cannot be set with serviceSubset": { - input: &ServiceResolverFailover{ - ServiceSubset: "v1", - Service: "foo", - SamenessGroup: "sg2", - }, - consulMeta: common.ConsulMeta{ - Partition: "default", - PartitionsEnabled: true, - }, - expectedErrMsgs: []string{ - "samenessGroup cannot be set with serviceSubset", - }, - }, - "samenessGroup cannot be set with targets": { - input: &ServiceResolverFailover{ - Targets: []ServiceResolverFailoverTarget{ - { - Peer: "p2", - }, - }, - SamenessGroup: "sg2", - }, - consulMeta: common.ConsulMeta{ - Partition: "default", - PartitionsEnabled: true, - }, - expectedErrMsgs: []string{ - "samenessGroup cannot be set with targets", - }, - }, - "targets cannot be set with datacenters": { - input: &ServiceResolverFailover{ - Targets: []ServiceResolverFailoverTarget{ - { - Peer: "p2", - }, - }, - Datacenters: []string{"dc1"}, - }, - consulMeta: common.ConsulMeta{ - Partition: "default", - PartitionsEnabled: true, - }, - expectedErrMsgs: []string{ - "targets cannot be set with datacenters", - }, - }, - "targets cannot be set with serviceSubset or service": { - input: &ServiceResolverFailover{ - Targets: []ServiceResolverFailoverTarget{ - { - Peer: "p2", - }, - }, - ServiceSubset: "v1", - Service: "foo", - }, - consulMeta: common.ConsulMeta{ - Partition: "default", - PartitionsEnabled: true, - }, - expectedErrMsgs: []string{ - "targets cannot be set with serviceSubset", - "targets cannot be set with service", - }, - }, - "target.peer cannot be set with target.serviceSubset": { - input: &ServiceResolverFailover{ - Targets: []ServiceResolverFailoverTarget{ - { - Peer: "p2", - ServiceSubset: "v1", - }, - }, - }, - consulMeta: common.ConsulMeta{ - Partition: "default", - PartitionsEnabled: true, - }, - expectedErrMsgs: []string{ - "target.peer cannot be set with target.serviceSubset", - }, - }, - "target.partition cannot be set with target.peer": { - input: &ServiceResolverFailover{ - Targets: []ServiceResolverFailoverTarget{ - { - Peer: "p2", - Partition: "partition2", - }, - }, - }, - consulMeta: common.ConsulMeta{ - Partition: "default", - PartitionsEnabled: true, - }, - expectedErrMsgs: []string{ - "target.partition cannot be set with target.peer", - }, - }, - "target.peer cannot be set with target.datacenter": { - input: &ServiceResolverFailover{ - Targets: []ServiceResolverFailoverTarget{ - { - Peer: "p2", - Datacenter: "dc2", - }, - }, - }, - consulMeta: common.ConsulMeta{ - Partition: "default", - PartitionsEnabled: true, - }, - expectedErrMsgs: []string{ - "target.peer cannot be set with target.datacenter", - }, - }, - "target.partition cannot be set with target.datacenter": { - input: &ServiceResolverFailover{ - Targets: []ServiceResolverFailoverTarget{ - { - Partition: "p2", - Datacenter: "dc2", - }, - }, - }, - consulMeta: common.ConsulMeta{ - Partition: "default", - PartitionsEnabled: true, - }, - expectedErrMsgs: []string{ - "target.partition cannot be set with target.datacenter", - }, - }, - "found empty datacenter": { - input: &ServiceResolverFailover{ - Datacenters: []string{""}, - }, - consulMeta: common.ConsulMeta{ - Partition: "default", - PartitionsEnabled: true, - }, - expectedErrMsgs: []string{ - "found empty datacenter", - }, - }, - } - - path := field.NewPath("spec.redirect") - for name, testCase := range cases { - t.Run(name, func(t *testing.T) { - errList := testCase.input.validate(path, testCase.consulMeta) - compareErrorLists(t, testCase.expectedErrMsgs, errList) - }) - } -} diff --git a/control-plane/api/v1alpha1/serviceresolver_webhook.go b/control-plane/api/v1alpha1/serviceresolver_webhook.go index 88996e2f8d..ca5f9d9482 100644 --- a/control-plane/api/v1alpha1/serviceresolver_webhook.go +++ b/control-plane/api/v1alpha1/serviceresolver_webhook.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/servicerouter_types.go b/control-plane/api/v1alpha1/servicerouter_types.go index 43e7353bf5..931d5ccb3a 100644 --- a/control-plane/api/v1alpha1/servicerouter_types.go +++ b/control-plane/api/v1alpha1/servicerouter_types.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/servicerouter_types_test.go b/control-plane/api/v1alpha1/servicerouter_types_test.go index 653bdc26c1..3110922210 100644 --- a/control-plane/api/v1alpha1/servicerouter_types_test.go +++ b/control-plane/api/v1alpha1/servicerouter_types_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/servicerouter_webhook.go b/control-plane/api/v1alpha1/servicerouter_webhook.go index cdcc3ba439..f6837fcf7b 100644 --- a/control-plane/api/v1alpha1/servicerouter_webhook.go +++ b/control-plane/api/v1alpha1/servicerouter_webhook.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/servicesplitter_types.go b/control-plane/api/v1alpha1/servicesplitter_types.go index d94dbb7120..b61b1a320b 100644 --- a/control-plane/api/v1alpha1/servicesplitter_types.go +++ b/control-plane/api/v1alpha1/servicesplitter_types.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/servicesplitter_types_test.go b/control-plane/api/v1alpha1/servicesplitter_types_test.go index e2ade99f1c..48e9eeac54 100644 --- a/control-plane/api/v1alpha1/servicesplitter_types_test.go +++ b/control-plane/api/v1alpha1/servicesplitter_types_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/servicesplitter_webhook.go b/control-plane/api/v1alpha1/servicesplitter_webhook.go index 42dbbbf54a..c0020c88b8 100644 --- a/control-plane/api/v1alpha1/servicesplitter_webhook.go +++ b/control-plane/api/v1alpha1/servicesplitter_webhook.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/shared_types.go b/control-plane/api/v1alpha1/shared_types.go index 148376a393..9b884cf476 100644 --- a/control-plane/api/v1alpha1/shared_types.go +++ b/control-plane/api/v1alpha1/shared_types.go @@ -1,10 +1,6 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( - "encoding/json" "fmt" "strings" @@ -16,9 +12,6 @@ import ( // This file contains structs that are shared between multiple config entries. -// metaValueMaxLength is the maximum allowed string length of a metadata value. -const metaValueMaxLength = 512 - type MeshGatewayMode string // Expose describes HTTP paths to expose through Envoy outside of Connect. @@ -58,35 +51,6 @@ type TransparentProxy struct { DialedDirectly bool `json:"dialedDirectly,omitempty"` } -type MutualTLSMode string - -const ( - // MutualTLSModeDefault represents no specific mode and should - // be used to indicate that a different layer of the configuration - // chain should take precedence. - MutualTLSModeDefault MutualTLSMode = "" - - // MutualTLSModeStrict requires mTLS for incoming traffic. - MutualTLSModeStrict MutualTLSMode = "strict" - - // MutualTLSModePermissive allows incoming non-mTLS traffic. - MutualTLSModePermissive MutualTLSMode = "permissive" -) - -func (m MutualTLSMode) validate() error { - switch m { - case MutualTLSModeDefault, MutualTLSModeStrict, MutualTLSModePermissive: - return nil - } - return fmt.Errorf("Must be one of %q, %q, or %q.", - MutualTLSModeDefault, MutualTLSModeStrict, MutualTLSModePermissive, - ) -} - -func (m MutualTLSMode) toConsul() capi.MutualTLSMode { - return capi.MutualTLSMode(m) -} - // MeshGateway controls how Mesh Gateways are used for upstream Connect // services. type MeshGateway struct { @@ -115,19 +79,6 @@ type HTTPHeaderModifiers struct { Remove []string `json:"remove,omitempty"` } -// EnvoyExtension has configuration for an extension that patches Envoy resources. -type EnvoyExtension struct { - Name string `json:"name,omitempty"` - Required bool `json:"required,omitempty"` - // +kubebuilder:validation:Type=object - // +kubebuilder:validation:Schemaless - // +kubebuilder:pruning:PreserveUnknownFields - Arguments json.RawMessage `json:"arguments,omitempty"` -} - -// EnvoyExtensions represents a list of the EnvoyExtension configuration. -type EnvoyExtensions []EnvoyExtension - func (in MeshGateway) toConsul() capi.MeshGatewayConfig { mode := capi.MeshGatewayMode(in.Mode) switch mode { @@ -225,122 +176,6 @@ func (in *HTTPHeaderModifiers) toConsul() *capi.HTTPHeaderModifiers { } } -func (in EnvoyExtensions) toConsul() []capi.EnvoyExtension { - if in == nil { - return nil - } - - outConfig := make([]capi.EnvoyExtension, 0) - - for _, e := range in { - consulExtension := capi.EnvoyExtension{ - Name: e.Name, - Required: e.Required, - } - - // We already validate that arguments is present - var args map[string]interface{} - _ = json.Unmarshal(e.Arguments, &args) - consulExtension.Arguments = args - outConfig = append(outConfig, consulExtension) - } - - return outConfig -} - -func (in EnvoyExtensions) validate(path *field.Path) field.ErrorList { - if len(in) == 0 { - return nil - } - - var errs field.ErrorList - for i, e := range in { - if err := e.validate(path.Child("envoyExtension").Index(i)); err != nil { - errs = append(errs, err) - } - } - - return errs -} - -func (in EnvoyExtension) validate(path *field.Path) *field.Error { - // Validate that the arguments are not nil - if in.Arguments == nil { - err := field.Required(path.Child("arguments"), "arguments must be defined") - return err - } - // Validate that the arguments are valid json - var outConfig map[string]interface{} - if err := json.Unmarshal(in.Arguments, &outConfig); err != nil { - return field.Invalid(path.Child("arguments"), string(in.Arguments), fmt.Sprintf(`must be valid map value: %s`, err)) - } - return nil -} - -// FailoverPolicy specifies the exact mechanism used for failover. -type FailoverPolicy struct { - // Mode specifies the type of failover that will be performed. Valid values are - // "sequential", "" (equivalent to "sequential") and "order-by-locality". - Mode string `json:"mode,omitempty"` - // Regions is the ordered list of the regions of the failover targets. - // Valid values can be "us-west-1", "us-west-2", and so on. - Regions []string `json:"regions,omitempty"` -} - -func (in *FailoverPolicy) toConsul() *capi.ServiceResolverFailoverPolicy { - if in == nil { - return nil - } - - return &capi.ServiceResolverFailoverPolicy{ - Mode: in.Mode, - Regions: in.Regions, - } -} - -func (in *FailoverPolicy) validate(path *field.Path) field.ErrorList { - var errs field.ErrorList - if in == nil { - return nil - } - modes := []string{"", "sequential", "order-by-locality"} - if !sliceContains(modes, in.Mode) { - errs = append(errs, field.Invalid(path.Child("mode"), in.Mode, notInSliceMessage(modes))) - } - return errs -} - -// PrioritizeByLocality controls whether the locality of services within the -// local partition will be used to prioritize connectivity. -type PrioritizeByLocality struct { - // Mode specifies the type of prioritization that will be performed - // when selecting nodes in the local partition. - // Valid values are: "" (default "none"), "none", and "failover". - Mode string `json:"mode,omitempty"` -} - -func (in *PrioritizeByLocality) toConsul() *capi.ServiceResolverPrioritizeByLocality { - if in == nil { - return nil - } - - return &capi.ServiceResolverPrioritizeByLocality{ - Mode: in.Mode, - } -} - -func (in *PrioritizeByLocality) validate(path *field.Path) field.ErrorList { - var errs field.ErrorList - if in == nil { - return nil - } - modes := []string{"", "none", "failover"} - if !sliceContains(modes, in.Mode) { - errs = append(errs, field.Invalid(path.Child("mode"), in.Mode, notInSliceMessage(modes))) - } - return errs -} - func notInSliceMessage(slice []string) string { return fmt.Sprintf(`must be one of "%s"`, strings.Join(slice, `", "`)) } diff --git a/control-plane/api/v1alpha1/status.go b/control-plane/api/v1alpha1/status.go index 0e11bf930b..d7cd0e0b78 100644 --- a/control-plane/api/v1alpha1/status.go +++ b/control-plane/api/v1alpha1/status.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/terminatinggateway_types.go b/control-plane/api/v1alpha1/terminatinggateway_types.go index cf453160ff..6e708b5d44 100644 --- a/control-plane/api/v1alpha1/terminatinggateway_types.go +++ b/control-plane/api/v1alpha1/terminatinggateway_types.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/terminatinggateway_types_test.go b/control-plane/api/v1alpha1/terminatinggateway_types_test.go index 2daf93c6a4..9d8ba9948d 100644 --- a/control-plane/api/v1alpha1/terminatinggateway_types_test.go +++ b/control-plane/api/v1alpha1/terminatinggateway_types_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/terminatinggateway_webhook.go b/control-plane/api/v1alpha1/terminatinggateway_webhook.go index 481a1a1f15..b0427b87ca 100644 --- a/control-plane/api/v1alpha1/terminatinggateway_webhook.go +++ b/control-plane/api/v1alpha1/terminatinggateway_webhook.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package v1alpha1 import ( diff --git a/control-plane/api/v1alpha1/zz_generated.deepcopy.go b/control-plane/api/v1alpha1/zz_generated.deepcopy.go index ccd69c4281..9131d5aeef 100644 --- a/control-plane/api/v1alpha1/zz_generated.deepcopy.go +++ b/control-plane/api/v1alpha1/zz_generated.deepcopy.go @@ -7,26 +7,10 @@ package v1alpha1 import ( "encoding/json" - "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *AccessLogs) DeepCopyInto(out *AccessLogs) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AccessLogs. -func (in *AccessLogs) DeepCopy() *AccessLogs { - if in == nil { - return nil - } - out := new(AccessLogs) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Condition) DeepCopyInto(out *Condition) { *out = *in @@ -64,146 +48,6 @@ func (in Conditions) DeepCopy() Conditions { return *out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ControlPlaneRequestLimit) DeepCopyInto(out *ControlPlaneRequestLimit) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControlPlaneRequestLimit. -func (in *ControlPlaneRequestLimit) DeepCopy() *ControlPlaneRequestLimit { - if in == nil { - return nil - } - out := new(ControlPlaneRequestLimit) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ControlPlaneRequestLimit) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ControlPlaneRequestLimitList) DeepCopyInto(out *ControlPlaneRequestLimitList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]ControlPlaneRequestLimit, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControlPlaneRequestLimitList. -func (in *ControlPlaneRequestLimitList) DeepCopy() *ControlPlaneRequestLimitList { - if in == nil { - return nil - } - out := new(ControlPlaneRequestLimitList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ControlPlaneRequestLimitList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ControlPlaneRequestLimitSpec) DeepCopyInto(out *ControlPlaneRequestLimitSpec) { - *out = *in - out.ReadWriteRatesConfig = in.ReadWriteRatesConfig - if in.ACL != nil { - in, out := &in.ACL, &out.ACL - *out = new(ReadWriteRatesConfig) - **out = **in - } - if in.Catalog != nil { - in, out := &in.Catalog, &out.Catalog - *out = new(ReadWriteRatesConfig) - **out = **in - } - if in.ConfigEntry != nil { - in, out := &in.ConfigEntry, &out.ConfigEntry - *out = new(ReadWriteRatesConfig) - **out = **in - } - if in.ConnectCA != nil { - in, out := &in.ConnectCA, &out.ConnectCA - *out = new(ReadWriteRatesConfig) - **out = **in - } - if in.Coordinate != nil { - in, out := &in.Coordinate, &out.Coordinate - *out = new(ReadWriteRatesConfig) - **out = **in - } - if in.DiscoveryChain != nil { - in, out := &in.DiscoveryChain, &out.DiscoveryChain - *out = new(ReadWriteRatesConfig) - **out = **in - } - if in.Health != nil { - in, out := &in.Health, &out.Health - *out = new(ReadWriteRatesConfig) - **out = **in - } - if in.Intention != nil { - in, out := &in.Intention, &out.Intention - *out = new(ReadWriteRatesConfig) - **out = **in - } - if in.KV != nil { - in, out := &in.KV, &out.KV - *out = new(ReadWriteRatesConfig) - **out = **in - } - if in.Tenancy != nil { - in, out := &in.Tenancy, &out.Tenancy - *out = new(ReadWriteRatesConfig) - **out = **in - } - if in.PreparedQuery != nil { - in, out := &in.PreparedQuery, &out.PreparedQuery - *out = new(ReadWriteRatesConfig) - **out = **in - } - if in.Session != nil { - in, out := &in.Session, &out.Session - *out = new(ReadWriteRatesConfig) - **out = **in - } - if in.Txn != nil { - in, out := &in.Txn, &out.Txn - *out = new(ReadWriteRatesConfig) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControlPlaneRequestLimitSpec. -func (in *ControlPlaneRequestLimitSpec) DeepCopy() *ControlPlaneRequestLimitSpec { - if in == nil { - return nil - } - out := new(ControlPlaneRequestLimitSpec) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CookieConfig) DeepCopyInto(out *CookieConfig) { *out = *in @@ -220,97 +64,6 @@ func (in *CookieConfig) DeepCopy() *CookieConfig { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CopyAnnotationsSpec) DeepCopyInto(out *CopyAnnotationsSpec) { - *out = *in - if in.Service != nil { - in, out := &in.Service, &out.Service - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CopyAnnotationsSpec. -func (in *CopyAnnotationsSpec) DeepCopy() *CopyAnnotationsSpec { - if in == nil { - return nil - } - out := new(CopyAnnotationsSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DeploymentSpec) DeepCopyInto(out *DeploymentSpec) { - *out = *in - if in.DefaultInstances != nil { - in, out := &in.DefaultInstances, &out.DefaultInstances - *out = new(int32) - **out = **in - } - if in.MaxInstances != nil { - in, out := &in.MaxInstances, &out.MaxInstances - *out = new(int32) - **out = **in - } - if in.MinInstances != nil { - in, out := &in.MinInstances, &out.MinInstances - *out = new(int32) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentSpec. -func (in *DeploymentSpec) DeepCopy() *DeploymentSpec { - if in == nil { - return nil - } - out := new(DeploymentSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyExtension) DeepCopyInto(out *EnvoyExtension) { - *out = *in - if in.Arguments != nil { - in, out := &in.Arguments, &out.Arguments - *out = make(json.RawMessage, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyExtension. -func (in *EnvoyExtension) DeepCopy() *EnvoyExtension { - if in == nil { - return nil - } - out := new(EnvoyExtension) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in EnvoyExtensions) DeepCopyInto(out *EnvoyExtensions) { - { - in := &in - *out = make(EnvoyExtensions, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyExtensions. -func (in EnvoyExtensions) DeepCopy() EnvoyExtensions { - if in == nil { - return nil - } - out := new(EnvoyExtensions) - in.DeepCopyInto(out) - return *out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExportedService) DeepCopyInto(out *ExportedService) { *out = *in @@ -447,120 +200,6 @@ func (in *ExposePath) DeepCopy() *ExposePath { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *FailoverPolicy) DeepCopyInto(out *FailoverPolicy) { - *out = *in - if in.Regions != nil { - in, out := &in.Regions, &out.Regions - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FailoverPolicy. -func (in *FailoverPolicy) DeepCopy() *FailoverPolicy { - if in == nil { - return nil - } - out := new(FailoverPolicy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *GatewayClassConfig) DeepCopyInto(out *GatewayClassConfig) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayClassConfig. -func (in *GatewayClassConfig) DeepCopy() *GatewayClassConfig { - if in == nil { - return nil - } - out := new(GatewayClassConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *GatewayClassConfig) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *GatewayClassConfigList) DeepCopyInto(out *GatewayClassConfigList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]GatewayClassConfig, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayClassConfigList. -func (in *GatewayClassConfigList) DeepCopy() *GatewayClassConfigList { - if in == nil { - return nil - } - out := new(GatewayClassConfigList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *GatewayClassConfigList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *GatewayClassConfigSpec) DeepCopyInto(out *GatewayClassConfigSpec) { - *out = *in - if in.ServiceType != nil { - in, out := &in.ServiceType, &out.ServiceType - *out = new(v1.ServiceType) - **out = **in - } - if in.NodeSelector != nil { - in, out := &in.NodeSelector, &out.NodeSelector - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Tolerations != nil { - in, out := &in.Tolerations, &out.Tolerations - *out = make([]v1.Toleration, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - in.DeploymentSpec.DeepCopyInto(&out.DeploymentSpec) - in.CopyAnnotations.DeepCopyInto(&out.CopyAnnotations) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayClassConfigSpec. -func (in *GatewayClassConfigSpec) DeepCopy() *GatewayClassConfigSpec { - if in == nil { - return nil - } - out := new(GatewayClassConfigSpec) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GatewayServiceTLSConfig) DeepCopyInto(out *GatewayServiceTLSConfig) { *out = *in @@ -930,111 +569,34 @@ func (in *IntentionHTTPPermission) DeepCopy() *IntentionHTTPPermission { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *IntentionJWTClaimVerification) DeepCopyInto(out *IntentionJWTClaimVerification) { +func (in *IntentionPermission) DeepCopyInto(out *IntentionPermission) { *out = *in - if in.Path != nil { - in, out := &in.Path, &out.Path - *out = make([]string, len(*in)) - copy(*out, *in) + if in.HTTP != nil { + in, out := &in.HTTP, &out.HTTP + *out = new(IntentionHTTPPermission) + (*in).DeepCopyInto(*out) } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntentionJWTClaimVerification. -func (in *IntentionJWTClaimVerification) DeepCopy() *IntentionJWTClaimVerification { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntentionPermission. +func (in *IntentionPermission) DeepCopy() *IntentionPermission { if in == nil { return nil } - out := new(IntentionJWTClaimVerification) + out := new(IntentionPermission) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *IntentionJWTProvider) DeepCopyInto(out *IntentionJWTProvider) { - *out = *in - if in.VerifyClaims != nil { - in, out := &in.VerifyClaims, &out.VerifyClaims - *out = make([]*IntentionJWTClaimVerification, len(*in)) +func (in IntentionPermissions) DeepCopyInto(out *IntentionPermissions) { + { + in := &in + *out = make(IntentionPermissions, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(IntentionJWTClaimVerification) - (*in).DeepCopyInto(*out) - } - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntentionJWTProvider. -func (in *IntentionJWTProvider) DeepCopy() *IntentionJWTProvider { - if in == nil { - return nil - } - out := new(IntentionJWTProvider) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *IntentionJWTRequirement) DeepCopyInto(out *IntentionJWTRequirement) { - *out = *in - if in.Providers != nil { - in, out := &in.Providers, &out.Providers - *out = make([]*IntentionJWTProvider, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(IntentionJWTProvider) - (*in).DeepCopyInto(*out) - } - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntentionJWTRequirement. -func (in *IntentionJWTRequirement) DeepCopy() *IntentionJWTRequirement { - if in == nil { - return nil - } - out := new(IntentionJWTRequirement) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *IntentionPermission) DeepCopyInto(out *IntentionPermission) { - *out = *in - if in.HTTP != nil { - in, out := &in.HTTP, &out.HTTP - *out = new(IntentionHTTPPermission) - (*in).DeepCopyInto(*out) - } - if in.JWT != nil { - in, out := &in.JWT, &out.JWT - *out = new(IntentionJWTRequirement) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntentionPermission. -func (in *IntentionPermission) DeepCopy() *IntentionPermission { - if in == nil { - return nil - } - out := new(IntentionPermission) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in IntentionPermissions) DeepCopyInto(out *IntentionPermissions) { - { - in := &in - *out = make(IntentionPermissions, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(IntentionPermission) + *out = new(IntentionPermission) (*in).DeepCopyInto(*out) } } @@ -1043,782 +605,78 @@ func (in IntentionPermissions) DeepCopyInto(out *IntentionPermissions) { // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntentionPermissions. func (in IntentionPermissions) DeepCopy() IntentionPermissions { - if in == nil { - return nil - } - out := new(IntentionPermissions) - in.DeepCopyInto(out) - return *out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *JSONWebKeySet) DeepCopyInto(out *JSONWebKeySet) { - *out = *in - if in.Local != nil { - in, out := &in.Local, &out.Local - *out = new(LocalJWKS) - **out = **in - } - if in.Remote != nil { - in, out := &in.Remote, &out.Remote - *out = new(RemoteJWKS) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JSONWebKeySet. -func (in *JSONWebKeySet) DeepCopy() *JSONWebKeySet { - if in == nil { - return nil - } - out := new(JSONWebKeySet) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *JWKSRetryPolicy) DeepCopyInto(out *JWKSRetryPolicy) { - *out = *in - if in.RetryPolicyBackOff != nil { - in, out := &in.RetryPolicyBackOff, &out.RetryPolicyBackOff - *out = new(RetryPolicyBackOff) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWKSRetryPolicy. -func (in *JWKSRetryPolicy) DeepCopy() *JWKSRetryPolicy { - if in == nil { - return nil - } - out := new(JWKSRetryPolicy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *JWTCacheConfig) DeepCopyInto(out *JWTCacheConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWTCacheConfig. -func (in *JWTCacheConfig) DeepCopy() *JWTCacheConfig { - if in == nil { - return nil - } - out := new(JWTCacheConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *JWTForwardingConfig) DeepCopyInto(out *JWTForwardingConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWTForwardingConfig. -func (in *JWTForwardingConfig) DeepCopy() *JWTForwardingConfig { - if in == nil { - return nil - } - out := new(JWTForwardingConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *JWTLocation) DeepCopyInto(out *JWTLocation) { - *out = *in - if in.Header != nil { - in, out := &in.Header, &out.Header - *out = new(JWTLocationHeader) - **out = **in - } - if in.QueryParam != nil { - in, out := &in.QueryParam, &out.QueryParam - *out = new(JWTLocationQueryParam) - **out = **in - } - if in.Cookie != nil { - in, out := &in.Cookie, &out.Cookie - *out = new(JWTLocationCookie) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWTLocation. -func (in *JWTLocation) DeepCopy() *JWTLocation { - if in == nil { - return nil - } - out := new(JWTLocation) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *JWTLocationCookie) DeepCopyInto(out *JWTLocationCookie) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWTLocationCookie. -func (in *JWTLocationCookie) DeepCopy() *JWTLocationCookie { - if in == nil { - return nil - } - out := new(JWTLocationCookie) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *JWTLocationHeader) DeepCopyInto(out *JWTLocationHeader) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWTLocationHeader. -func (in *JWTLocationHeader) DeepCopy() *JWTLocationHeader { - if in == nil { - return nil - } - out := new(JWTLocationHeader) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *JWTLocationQueryParam) DeepCopyInto(out *JWTLocationQueryParam) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWTLocationQueryParam. -func (in *JWTLocationQueryParam) DeepCopy() *JWTLocationQueryParam { - if in == nil { - return nil - } - out := new(JWTLocationQueryParam) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in JWTLocations) DeepCopyInto(out *JWTLocations) { - { - in := &in - *out = make(JWTLocations, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(JWTLocation) - (*in).DeepCopyInto(*out) - } - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWTLocations. -func (in JWTLocations) DeepCopy() JWTLocations { - if in == nil { - return nil - } - out := new(JWTLocations) - in.DeepCopyInto(out) - return *out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *JWTProvider) DeepCopyInto(out *JWTProvider) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWTProvider. -func (in *JWTProvider) DeepCopy() *JWTProvider { - if in == nil { - return nil - } - out := new(JWTProvider) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *JWTProvider) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *JWTProviderList) DeepCopyInto(out *JWTProviderList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]JWTProvider, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWTProviderList. -func (in *JWTProviderList) DeepCopy() *JWTProviderList { - if in == nil { - return nil - } - out := new(JWTProviderList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *JWTProviderList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *JWTProviderSpec) DeepCopyInto(out *JWTProviderSpec) { - *out = *in - if in.JSONWebKeySet != nil { - in, out := &in.JSONWebKeySet, &out.JSONWebKeySet - *out = new(JSONWebKeySet) - (*in).DeepCopyInto(*out) - } - if in.Audiences != nil { - in, out := &in.Audiences, &out.Audiences - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Locations != nil { - in, out := &in.Locations, &out.Locations - *out = make([]*JWTLocation, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(JWTLocation) - (*in).DeepCopyInto(*out) - } - } - } - if in.Forwarding != nil { - in, out := &in.Forwarding, &out.Forwarding - *out = new(JWTForwardingConfig) - **out = **in - } - if in.CacheConfig != nil { - in, out := &in.CacheConfig, &out.CacheConfig - *out = new(JWTCacheConfig) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWTProviderSpec. -func (in *JWTProviderSpec) DeepCopy() *JWTProviderSpec { - if in == nil { - return nil - } - out := new(JWTProviderSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *LeastRequestConfig) DeepCopyInto(out *LeastRequestConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LeastRequestConfig. -func (in *LeastRequestConfig) DeepCopy() *LeastRequestConfig { - if in == nil { - return nil - } - out := new(LeastRequestConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *LinkedService) DeepCopyInto(out *LinkedService) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LinkedService. -func (in *LinkedService) DeepCopy() *LinkedService { - if in == nil { - return nil - } - out := new(LinkedService) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *LoadBalancer) DeepCopyInto(out *LoadBalancer) { - *out = *in - if in.RingHashConfig != nil { - in, out := &in.RingHashConfig, &out.RingHashConfig - *out = new(RingHashConfig) - **out = **in - } - if in.LeastRequestConfig != nil { - in, out := &in.LeastRequestConfig, &out.LeastRequestConfig - *out = new(LeastRequestConfig) - **out = **in - } - if in.HashPolicies != nil { - in, out := &in.HashPolicies, &out.HashPolicies - *out = make([]HashPolicy, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoadBalancer. -func (in *LoadBalancer) DeepCopy() *LoadBalancer { - if in == nil { - return nil - } - out := new(LoadBalancer) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *LocalJWKS) DeepCopyInto(out *LocalJWKS) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalJWKS. -func (in *LocalJWKS) DeepCopy() *LocalJWKS { - if in == nil { - return nil - } - out := new(LocalJWKS) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Mesh) DeepCopyInto(out *Mesh) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Mesh. -func (in *Mesh) DeepCopy() *Mesh { - if in == nil { - return nil - } - out := new(Mesh) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Mesh) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MeshDirectionalTLSConfig) DeepCopyInto(out *MeshDirectionalTLSConfig) { - *out = *in - if in.CipherSuites != nil { - in, out := &in.CipherSuites, &out.CipherSuites - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MeshDirectionalTLSConfig. -func (in *MeshDirectionalTLSConfig) DeepCopy() *MeshDirectionalTLSConfig { - if in == nil { - return nil - } - out := new(MeshDirectionalTLSConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MeshGateway) DeepCopyInto(out *MeshGateway) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MeshGateway. -func (in *MeshGateway) DeepCopy() *MeshGateway { - if in == nil { - return nil - } - out := new(MeshGateway) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MeshHTTPConfig) DeepCopyInto(out *MeshHTTPConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MeshHTTPConfig. -func (in *MeshHTTPConfig) DeepCopy() *MeshHTTPConfig { - if in == nil { - return nil - } - out := new(MeshHTTPConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MeshList) DeepCopyInto(out *MeshList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Mesh, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MeshList. -func (in *MeshList) DeepCopy() *MeshList { - if in == nil { - return nil - } - out := new(MeshList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *MeshList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MeshService) DeepCopyInto(out *MeshService) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MeshService. -func (in *MeshService) DeepCopy() *MeshService { - if in == nil { - return nil - } - out := new(MeshService) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *MeshService) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MeshServiceList) DeepCopyInto(out *MeshServiceList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]MeshService, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MeshServiceList. -func (in *MeshServiceList) DeepCopy() *MeshServiceList { - if in == nil { - return nil - } - out := new(MeshServiceList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *MeshServiceList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MeshServiceSpec) DeepCopyInto(out *MeshServiceSpec) { - *out = *in - if in.Peer != nil { - in, out := &in.Peer, &out.Peer - *out = new(string) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MeshServiceSpec. -func (in *MeshServiceSpec) DeepCopy() *MeshServiceSpec { - if in == nil { - return nil - } - out := new(MeshServiceSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MeshSpec) DeepCopyInto(out *MeshSpec) { - *out = *in - out.TransparentProxy = in.TransparentProxy - if in.TLS != nil { - in, out := &in.TLS, &out.TLS - *out = new(MeshTLSConfig) - (*in).DeepCopyInto(*out) - } - if in.HTTP != nil { - in, out := &in.HTTP, &out.HTTP - *out = new(MeshHTTPConfig) - **out = **in - } - if in.Peering != nil { - in, out := &in.Peering, &out.Peering - *out = new(PeeringMeshConfig) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MeshSpec. -func (in *MeshSpec) DeepCopy() *MeshSpec { - if in == nil { - return nil - } - out := new(MeshSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MeshTLSConfig) DeepCopyInto(out *MeshTLSConfig) { - *out = *in - if in.Incoming != nil { - in, out := &in.Incoming, &out.Incoming - *out = new(MeshDirectionalTLSConfig) - (*in).DeepCopyInto(*out) - } - if in.Outgoing != nil { - in, out := &in.Outgoing, &out.Outgoing - *out = new(MeshDirectionalTLSConfig) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MeshTLSConfig. -func (in *MeshTLSConfig) DeepCopy() *MeshTLSConfig { - if in == nil { - return nil - } - out := new(MeshTLSConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PassiveHealthCheck) DeepCopyInto(out *PassiveHealthCheck) { - *out = *in - out.Interval = in.Interval - if in.EnforcingConsecutive5xx != nil { - in, out := &in.EnforcingConsecutive5xx, &out.EnforcingConsecutive5xx - *out = new(uint32) - **out = **in - } - if in.MaxEjectionPercent != nil { - in, out := &in.MaxEjectionPercent, &out.MaxEjectionPercent - *out = new(uint32) - **out = **in - } - if in.BaseEjectionTime != nil { - in, out := &in.BaseEjectionTime, &out.BaseEjectionTime - *out = new(metav1.Duration) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PassiveHealthCheck. -func (in *PassiveHealthCheck) DeepCopy() *PassiveHealthCheck { - if in == nil { - return nil - } - out := new(PassiveHealthCheck) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Peer) DeepCopyInto(out *Peer) { - *out = *in - if in.Secret != nil { - in, out := &in.Secret, &out.Secret - *out = new(Secret) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Peer. -func (in *Peer) DeepCopy() *Peer { - if in == nil { - return nil - } - out := new(Peer) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PeeringAcceptor) DeepCopyInto(out *PeeringAcceptor) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PeeringAcceptor. -func (in *PeeringAcceptor) DeepCopy() *PeeringAcceptor { - if in == nil { - return nil - } - out := new(PeeringAcceptor) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PeeringAcceptor) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PeeringAcceptorList) DeepCopyInto(out *PeeringAcceptorList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]PeeringAcceptor, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } + if in == nil { + return nil } + out := new(IntentionPermissions) + in.DeepCopyInto(out) + return *out } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PeeringAcceptorList. -func (in *PeeringAcceptorList) DeepCopy() *PeeringAcceptorList { +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LeastRequestConfig) DeepCopyInto(out *LeastRequestConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LeastRequestConfig. +func (in *LeastRequestConfig) DeepCopy() *LeastRequestConfig { if in == nil { return nil } - out := new(PeeringAcceptorList) + out := new(LeastRequestConfig) in.DeepCopyInto(out) return out } -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PeeringAcceptorList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PeeringAcceptorSpec) DeepCopyInto(out *PeeringAcceptorSpec) { +func (in *LinkedService) DeepCopyInto(out *LinkedService) { *out = *in - if in.Peer != nil { - in, out := &in.Peer, &out.Peer - *out = new(Peer) - (*in).DeepCopyInto(*out) - } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PeeringAcceptorSpec. -func (in *PeeringAcceptorSpec) DeepCopy() *PeeringAcceptorSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LinkedService. +func (in *LinkedService) DeepCopy() *LinkedService { if in == nil { return nil } - out := new(PeeringAcceptorSpec) + out := new(LinkedService) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PeeringAcceptorStatus) DeepCopyInto(out *PeeringAcceptorStatus) { +func (in *LoadBalancer) DeepCopyInto(out *LoadBalancer) { *out = *in - if in.LatestPeeringVersion != nil { - in, out := &in.LatestPeeringVersion, &out.LatestPeeringVersion - *out = new(uint64) + if in.RingHashConfig != nil { + in, out := &in.RingHashConfig, &out.RingHashConfig + *out = new(RingHashConfig) **out = **in } - if in.SecretRef != nil { - in, out := &in.SecretRef, &out.SecretRef - *out = new(SecretRefStatus) + if in.LeastRequestConfig != nil { + in, out := &in.LeastRequestConfig, &out.LeastRequestConfig + *out = new(LeastRequestConfig) **out = **in } - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make(Conditions, len(*in)) + if in.HashPolicies != nil { + in, out := &in.HashPolicies, &out.HashPolicies + *out = make([]HashPolicy, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } - if in.LastSyncedTime != nil { - in, out := &in.LastSyncedTime, &out.LastSyncedTime - *out = (*in).DeepCopy() - } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PeeringAcceptorStatus. -func (in *PeeringAcceptorStatus) DeepCopy() *PeeringAcceptorStatus { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoadBalancer. +func (in *LoadBalancer) DeepCopy() *LoadBalancer { if in == nil { return nil } - out := new(PeeringAcceptorStatus) + out := new(LoadBalancer) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PeeringDialer) DeepCopyInto(out *PeeringDialer) { +func (in *Mesh) DeepCopyInto(out *Mesh) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) @@ -1826,50 +684,18 @@ func (in *PeeringDialer) DeepCopyInto(out *PeeringDialer) { in.Status.DeepCopyInto(&out.Status) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PeeringDialer. -func (in *PeeringDialer) DeepCopy() *PeeringDialer { - if in == nil { - return nil - } - out := new(PeeringDialer) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PeeringDialer) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PeeringDialerList) DeepCopyInto(out *PeeringDialerList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]PeeringDialer, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PeeringDialerList. -func (in *PeeringDialerList) DeepCopy() *PeeringDialerList { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Mesh. +func (in *Mesh) DeepCopy() *Mesh { if in == nil { return nil } - out := new(PeeringDialerList) + out := new(Mesh) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PeeringDialerList) DeepCopyObject() runtime.Object { +func (in *Mesh) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -1877,271 +703,196 @@ func (in *PeeringDialerList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PeeringDialerSpec) DeepCopyInto(out *PeeringDialerSpec) { - *out = *in - if in.Peer != nil { - in, out := &in.Peer, &out.Peer - *out = new(Peer) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PeeringDialerSpec. -func (in *PeeringDialerSpec) DeepCopy() *PeeringDialerSpec { - if in == nil { - return nil - } - out := new(PeeringDialerSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PeeringDialerStatus) DeepCopyInto(out *PeeringDialerStatus) { +func (in *MeshDirectionalTLSConfig) DeepCopyInto(out *MeshDirectionalTLSConfig) { *out = *in - if in.LatestPeeringVersion != nil { - in, out := &in.LatestPeeringVersion, &out.LatestPeeringVersion - *out = new(uint64) - **out = **in - } - if in.SecretRef != nil { - in, out := &in.SecretRef, &out.SecretRef - *out = new(SecretRefStatus) - **out = **in - } - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make(Conditions, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.LastSyncedTime != nil { - in, out := &in.LastSyncedTime, &out.LastSyncedTime - *out = (*in).DeepCopy() - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PeeringDialerStatus. -func (in *PeeringDialerStatus) DeepCopy() *PeeringDialerStatus { - if in == nil { - return nil + if in.CipherSuites != nil { + in, out := &in.CipherSuites, &out.CipherSuites + *out = make([]string, len(*in)) + copy(*out, *in) } - out := new(PeeringDialerStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PeeringMeshConfig) DeepCopyInto(out *PeeringMeshConfig) { - *out = *in } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PeeringMeshConfig. -func (in *PeeringMeshConfig) DeepCopy() *PeeringMeshConfig { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MeshDirectionalTLSConfig. +func (in *MeshDirectionalTLSConfig) DeepCopy() *MeshDirectionalTLSConfig { if in == nil { return nil } - out := new(PeeringMeshConfig) + out := new(MeshDirectionalTLSConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PrioritizeByLocality) DeepCopyInto(out *PrioritizeByLocality) { +func (in *MeshGateway) DeepCopyInto(out *MeshGateway) { *out = *in } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrioritizeByLocality. -func (in *PrioritizeByLocality) DeepCopy() *PrioritizeByLocality { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MeshGateway. +func (in *MeshGateway) DeepCopy() *MeshGateway { if in == nil { return nil } - out := new(PrioritizeByLocality) + out := new(MeshGateway) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProxyDefaults) DeepCopyInto(out *ProxyDefaults) { +func (in *MeshHTTPConfig) DeepCopyInto(out *MeshHTTPConfig) { *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyDefaults. -func (in *ProxyDefaults) DeepCopy() *ProxyDefaults { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MeshHTTPConfig. +func (in *MeshHTTPConfig) DeepCopy() *MeshHTTPConfig { if in == nil { return nil } - out := new(ProxyDefaults) + out := new(MeshHTTPConfig) in.DeepCopyInto(out) return out } -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ProxyDefaults) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProxyDefaultsList) DeepCopyInto(out *ProxyDefaultsList) { +func (in *MeshList) DeepCopyInto(out *MeshList) { *out = *in out.TypeMeta = in.TypeMeta in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items - *out = make([]ProxyDefaults, len(*in)) + *out = make([]Mesh, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyDefaultsList. -func (in *ProxyDefaultsList) DeepCopy() *ProxyDefaultsList { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MeshList. +func (in *MeshList) DeepCopy() *MeshList { if in == nil { return nil } - out := new(ProxyDefaultsList) + out := new(MeshList) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ProxyDefaultsList) DeepCopyObject() runtime.Object { +func (in *MeshList) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProxyDefaultsSpec) DeepCopyInto(out *ProxyDefaultsSpec) { - *out = *in - if in.Mode != nil { - in, out := &in.Mode, &out.Mode - *out = new(ProxyMode) - **out = **in - } - if in.TransparentProxy != nil { - in, out := &in.TransparentProxy, &out.TransparentProxy - *out = new(TransparentProxy) - **out = **in - } - if in.Config != nil { - in, out := &in.Config, &out.Config - *out = make(json.RawMessage, len(*in)) - copy(*out, *in) - } - out.MeshGateway = in.MeshGateway - in.Expose.DeepCopyInto(&out.Expose) - if in.AccessLogs != nil { - in, out := &in.AccessLogs, &out.AccessLogs - *out = new(AccessLogs) - **out = **in - } - if in.EnvoyExtensions != nil { - in, out := &in.EnvoyExtensions, &out.EnvoyExtensions - *out = make(EnvoyExtensions, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.FailoverPolicy != nil { - in, out := &in.FailoverPolicy, &out.FailoverPolicy - *out = new(FailoverPolicy) - (*in).DeepCopyInto(*out) - } - if in.PrioritizeByLocality != nil { - in, out := &in.PrioritizeByLocality, &out.PrioritizeByLocality - *out = new(PrioritizeByLocality) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyDefaultsSpec. -func (in *ProxyDefaultsSpec) DeepCopy() *ProxyDefaultsSpec { - if in == nil { - return nil - } - out := new(ProxyDefaultsSpec) - in.DeepCopyInto(out) - return out + } + return nil } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ReadWriteRatesConfig) DeepCopyInto(out *ReadWriteRatesConfig) { +func (in *MeshSpec) DeepCopyInto(out *MeshSpec) { *out = *in + out.TransparentProxy = in.TransparentProxy + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(MeshTLSConfig) + (*in).DeepCopyInto(*out) + } + if in.HTTP != nil { + in, out := &in.HTTP, &out.HTTP + *out = new(MeshHTTPConfig) + **out = **in + } + if in.Peering != nil { + in, out := &in.Peering, &out.Peering + *out = new(PeeringMeshConfig) + **out = **in + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReadWriteRatesConfig. -func (in *ReadWriteRatesConfig) DeepCopy() *ReadWriteRatesConfig { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MeshSpec. +func (in *MeshSpec) DeepCopy() *MeshSpec { if in == nil { return nil } - out := new(ReadWriteRatesConfig) + out := new(MeshSpec) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RemoteJWKS) DeepCopyInto(out *RemoteJWKS) { +func (in *MeshTLSConfig) DeepCopyInto(out *MeshTLSConfig) { *out = *in - if in.RetryPolicy != nil { - in, out := &in.RetryPolicy, &out.RetryPolicy - *out = new(JWKSRetryPolicy) + if in.Incoming != nil { + in, out := &in.Incoming, &out.Incoming + *out = new(MeshDirectionalTLSConfig) + (*in).DeepCopyInto(*out) + } + if in.Outgoing != nil { + in, out := &in.Outgoing, &out.Outgoing + *out = new(MeshDirectionalTLSConfig) (*in).DeepCopyInto(*out) } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemoteJWKS. -func (in *RemoteJWKS) DeepCopy() *RemoteJWKS { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MeshTLSConfig. +func (in *MeshTLSConfig) DeepCopy() *MeshTLSConfig { if in == nil { return nil } - out := new(RemoteJWKS) + out := new(MeshTLSConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RetryPolicyBackOff) DeepCopyInto(out *RetryPolicyBackOff) { +func (in *PassiveHealthCheck) DeepCopyInto(out *PassiveHealthCheck) { *out = *in + out.Interval = in.Interval + if in.EnforcingConsecutive5xx != nil { + in, out := &in.EnforcingConsecutive5xx, &out.EnforcingConsecutive5xx + *out = new(uint32) + **out = **in + } + if in.MaxEjectionPercent != nil { + in, out := &in.MaxEjectionPercent, &out.MaxEjectionPercent + *out = new(uint32) + **out = **in + } + if in.BaseEjectionTime != nil { + in, out := &in.BaseEjectionTime, &out.BaseEjectionTime + *out = new(v1.Duration) + **out = **in + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RetryPolicyBackOff. -func (in *RetryPolicyBackOff) DeepCopy() *RetryPolicyBackOff { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PassiveHealthCheck. +func (in *PassiveHealthCheck) DeepCopy() *PassiveHealthCheck { if in == nil { return nil } - out := new(RetryPolicyBackOff) + out := new(PassiveHealthCheck) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RingHashConfig) DeepCopyInto(out *RingHashConfig) { +func (in *Peer) DeepCopyInto(out *Peer) { *out = *in + if in.Secret != nil { + in, out := &in.Secret, &out.Secret + *out = new(Secret) + **out = **in + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RingHashConfig. -func (in *RingHashConfig) DeepCopy() *RingHashConfig { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Peer. +func (in *Peer) DeepCopy() *Peer { if in == nil { return nil } - out := new(RingHashConfig) + out := new(Peer) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RouteRetryFilter) DeepCopyInto(out *RouteRetryFilter) { +func (in *PeeringAcceptor) DeepCopyInto(out *PeeringAcceptor) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) @@ -2149,18 +900,18 @@ func (in *RouteRetryFilter) DeepCopyInto(out *RouteRetryFilter) { in.Status.DeepCopyInto(&out.Status) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteRetryFilter. -func (in *RouteRetryFilter) DeepCopy() *RouteRetryFilter { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PeeringAcceptor. +func (in *PeeringAcceptor) DeepCopy() *PeeringAcceptor { if in == nil { return nil } - out := new(RouteRetryFilter) + out := new(PeeringAcceptor) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RouteRetryFilter) DeepCopyObject() runtime.Object { +func (in *PeeringAcceptor) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -2168,31 +919,31 @@ func (in *RouteRetryFilter) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RouteRetryFilterList) DeepCopyInto(out *RouteRetryFilterList) { +func (in *PeeringAcceptorList) DeepCopyInto(out *PeeringAcceptorList) { *out = *in out.TypeMeta = in.TypeMeta in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items - *out = make([]RouteRetryFilter, len(*in)) + *out = make([]PeeringAcceptor, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteRetryFilterList. -func (in *RouteRetryFilterList) DeepCopy() *RouteRetryFilterList { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PeeringAcceptorList. +func (in *PeeringAcceptorList) DeepCopy() *PeeringAcceptorList { if in == nil { return nil } - out := new(RouteRetryFilterList) + out := new(PeeringAcceptorList) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RouteRetryFilterList) DeepCopyObject() runtime.Object { +func (in *PeeringAcceptorList) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -2200,61 +951,82 @@ func (in *RouteRetryFilterList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RouteRetryFilterSpec) DeepCopyInto(out *RouteRetryFilterSpec) { +func (in *PeeringAcceptorSpec) DeepCopyInto(out *PeeringAcceptorSpec) { *out = *in - if in.NumRetries != nil { - in, out := &in.NumRetries, &out.NumRetries - *out = new(uint32) - **out = **in + if in.Peer != nil { + in, out := &in.Peer, &out.Peer + *out = new(Peer) + (*in).DeepCopyInto(*out) } - if in.RetryOn != nil { - in, out := &in.RetryOn, &out.RetryOn - *out = make([]string, len(*in)) - copy(*out, *in) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PeeringAcceptorSpec. +func (in *PeeringAcceptorSpec) DeepCopy() *PeeringAcceptorSpec { + if in == nil { + return nil } - if in.RetryOnStatusCodes != nil { - in, out := &in.RetryOnStatusCodes, &out.RetryOnStatusCodes - *out = make([]uint32, len(*in)) - copy(*out, *in) + out := new(PeeringAcceptorSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PeeringAcceptorStatus) DeepCopyInto(out *PeeringAcceptorStatus) { + *out = *in + if in.LatestPeeringVersion != nil { + in, out := &in.LatestPeeringVersion, &out.LatestPeeringVersion + *out = new(uint64) + **out = **in } - if in.RetryOnConnectFailure != nil { - in, out := &in.RetryOnConnectFailure, &out.RetryOnConnectFailure - *out = new(bool) + if in.SecretRef != nil { + in, out := &in.SecretRef, &out.SecretRef + *out = new(SecretRefStatus) **out = **in } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.LastSyncedTime != nil { + in, out := &in.LastSyncedTime, &out.LastSyncedTime + *out = (*in).DeepCopy() + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteRetryFilterSpec. -func (in *RouteRetryFilterSpec) DeepCopy() *RouteRetryFilterSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PeeringAcceptorStatus. +func (in *PeeringAcceptorStatus) DeepCopy() *PeeringAcceptorStatus { if in == nil { return nil } - out := new(RouteRetryFilterSpec) + out := new(PeeringAcceptorStatus) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RouteTimeoutFilter) DeepCopyInto(out *RouteTimeoutFilter) { +func (in *PeeringDialer) DeepCopyInto(out *PeeringDialer) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec + in.Spec.DeepCopyInto(&out.Spec) in.Status.DeepCopyInto(&out.Status) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteTimeoutFilter. -func (in *RouteTimeoutFilter) DeepCopy() *RouteTimeoutFilter { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PeeringDialer. +func (in *PeeringDialer) DeepCopy() *PeeringDialer { if in == nil { return nil } - out := new(RouteTimeoutFilter) + out := new(PeeringDialer) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RouteTimeoutFilter) DeepCopyObject() runtime.Object { +func (in *PeeringDialer) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -2262,31 +1034,31 @@ func (in *RouteTimeoutFilter) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RouteTimeoutFilterList) DeepCopyInto(out *RouteTimeoutFilterList) { +func (in *PeeringDialerList) DeepCopyInto(out *PeeringDialerList) { *out = *in out.TypeMeta = in.TypeMeta in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items - *out = make([]RouteTimeoutFilter, len(*in)) + *out = make([]PeeringDialer, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteTimeoutFilterList. -func (in *RouteTimeoutFilterList) DeepCopy() *RouteTimeoutFilterList { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PeeringDialerList. +func (in *PeeringDialerList) DeepCopy() *PeeringDialerList { if in == nil { return nil } - out := new(RouteTimeoutFilterList) + out := new(PeeringDialerList) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RouteTimeoutFilterList) DeepCopyObject() runtime.Object { +func (in *PeeringDialerList) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -2294,22 +1066,78 @@ func (in *RouteTimeoutFilterList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RouteTimeoutFilterSpec) DeepCopyInto(out *RouteTimeoutFilterSpec) { +func (in *PeeringDialerSpec) DeepCopyInto(out *PeeringDialerSpec) { + *out = *in + if in.Peer != nil { + in, out := &in.Peer, &out.Peer + *out = new(Peer) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PeeringDialerSpec. +func (in *PeeringDialerSpec) DeepCopy() *PeeringDialerSpec { + if in == nil { + return nil + } + out := new(PeeringDialerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PeeringDialerStatus) DeepCopyInto(out *PeeringDialerStatus) { + *out = *in + if in.LatestPeeringVersion != nil { + in, out := &in.LatestPeeringVersion, &out.LatestPeeringVersion + *out = new(uint64) + **out = **in + } + if in.SecretRef != nil { + in, out := &in.SecretRef, &out.SecretRef + *out = new(SecretRefStatus) + **out = **in + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.LastSyncedTime != nil { + in, out := &in.LastSyncedTime, &out.LastSyncedTime + *out = (*in).DeepCopy() + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PeeringDialerStatus. +func (in *PeeringDialerStatus) DeepCopy() *PeeringDialerStatus { + if in == nil { + return nil + } + out := new(PeeringDialerStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PeeringMeshConfig) DeepCopyInto(out *PeeringMeshConfig) { *out = *in } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteTimeoutFilterSpec. -func (in *RouteTimeoutFilterSpec) DeepCopy() *RouteTimeoutFilterSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PeeringMeshConfig. +func (in *PeeringMeshConfig) DeepCopy() *PeeringMeshConfig { if in == nil { return nil } - out := new(RouteTimeoutFilterSpec) + out := new(PeeringMeshConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SamenessGroup) DeepCopyInto(out *SamenessGroup) { +func (in *ProxyDefaults) DeepCopyInto(out *ProxyDefaults) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) @@ -2317,18 +1145,18 @@ func (in *SamenessGroup) DeepCopyInto(out *SamenessGroup) { in.Status.DeepCopyInto(&out.Status) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SamenessGroup. -func (in *SamenessGroup) DeepCopy() *SamenessGroup { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyDefaults. +func (in *ProxyDefaults) DeepCopy() *ProxyDefaults { if in == nil { return nil } - out := new(SamenessGroup) + out := new(ProxyDefaults) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *SamenessGroup) DeepCopyObject() runtime.Object { +func (in *ProxyDefaults) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -2336,31 +1164,31 @@ func (in *SamenessGroup) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SamenessGroupList) DeepCopyInto(out *SamenessGroupList) { +func (in *ProxyDefaultsList) DeepCopyInto(out *ProxyDefaultsList) { *out = *in out.TypeMeta = in.TypeMeta in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items - *out = make([]SamenessGroup, len(*in)) + *out = make([]ProxyDefaults, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SamenessGroupList. -func (in *SamenessGroupList) DeepCopy() *SamenessGroupList { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyDefaultsList. +func (in *ProxyDefaultsList) DeepCopy() *ProxyDefaultsList { if in == nil { return nil } - out := new(SamenessGroupList) + out := new(ProxyDefaultsList) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *SamenessGroupList) DeepCopyObject() runtime.Object { +func (in *ProxyDefaultsList) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -2368,55 +1196,48 @@ func (in *SamenessGroupList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SamenessGroupMember) DeepCopyInto(out *SamenessGroupMember) { +func (in *ProxyDefaultsSpec) DeepCopyInto(out *ProxyDefaultsSpec) { *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SamenessGroupMember. -func (in *SamenessGroupMember) DeepCopy() *SamenessGroupMember { - if in == nil { - return nil + if in.Mode != nil { + in, out := &in.Mode, &out.Mode + *out = new(ProxyMode) + **out = **in } - out := new(SamenessGroupMember) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in SamenessGroupMembers) DeepCopyInto(out *SamenessGroupMembers) { - { - in := &in - *out = make(SamenessGroupMembers, len(*in)) + if in.TransparentProxy != nil { + in, out := &in.TransparentProxy, &out.TransparentProxy + *out = new(TransparentProxy) + **out = **in + } + if in.Config != nil { + in, out := &in.Config, &out.Config + *out = make(json.RawMessage, len(*in)) copy(*out, *in) } + out.MeshGateway = in.MeshGateway + in.Expose.DeepCopyInto(&out.Expose) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SamenessGroupMembers. -func (in SamenessGroupMembers) DeepCopy() SamenessGroupMembers { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyDefaultsSpec. +func (in *ProxyDefaultsSpec) DeepCopy() *ProxyDefaultsSpec { if in == nil { return nil } - out := new(SamenessGroupMembers) + out := new(ProxyDefaultsSpec) in.DeepCopyInto(out) - return *out + return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SamenessGroupSpec) DeepCopyInto(out *SamenessGroupSpec) { +func (in *RingHashConfig) DeepCopyInto(out *RingHashConfig) { *out = *in - if in.Members != nil { - in, out := &in.Members, &out.Members - *out = make([]SamenessGroupMember, len(*in)) - copy(*out, *in) - } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SamenessGroupSpec. -func (in *SamenessGroupSpec) DeepCopy() *SamenessGroupSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RingHashConfig. +func (in *RingHashConfig) DeepCopy() *RingHashConfig { if in == nil { return nil } - out := new(SamenessGroupSpec) + out := new(RingHashConfig) in.DeepCopyInto(out) return out } @@ -2571,13 +1392,6 @@ func (in *ServiceDefaultsSpec) DeepCopyInto(out *ServiceDefaultsSpec) { *out = new(ServiceDefaultsDestination) (*in).DeepCopyInto(*out) } - if in.EnvoyExtensions != nil { - in, out := &in.EnvoyExtensions, &out.EnvoyExtensions - *out = make(EnvoyExtensions, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceDefaultsSpec. @@ -2664,11 +1478,6 @@ func (in *ServiceIntentionsSpec) DeepCopyInto(out *ServiceIntentionsSpec) { } } } - if in.JWT != nil { - in, out := &in.JWT, &out.JWT - *out = new(IntentionJWTRequirement) - (*in).DeepCopyInto(*out) - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceIntentionsSpec. @@ -2721,11 +1530,6 @@ func (in *ServiceResolverFailover) DeepCopyInto(out *ServiceResolverFailover) { *out = make([]ServiceResolverFailoverTarget, len(*in)) copy(*out, *in) } - if in.Policy != nil { - in, out := &in.Policy, &out.Policy - *out = new(FailoverPolicy) - (*in).DeepCopyInto(*out) - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceResolverFailover. @@ -2844,17 +1648,11 @@ func (in *ServiceResolverSpec) DeepCopyInto(out *ServiceResolverSpec) { } } out.ConnectTimeout = in.ConnectTimeout - out.RequestTimeout = in.RequestTimeout if in.LoadBalancer != nil { in, out := &in.LoadBalancer, &out.LoadBalancer *out = new(LoadBalancer) (*in).DeepCopyInto(*out) } - if in.PrioritizeByLocality != nil { - in, out := &in.PrioritizeByLocality, &out.PrioritizeByLocality - *out = new(PrioritizeByLocality) - **out = **in - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceResolverSpec. diff --git a/control-plane/build-support/controller/README.md b/control-plane/build-support/controller/README.md new file mode 100644 index 0000000000..0d24937531 --- /dev/null +++ b/control-plane/build-support/controller/README.md @@ -0,0 +1,5 @@ +## Overview + +`boilerplate.go.txt` is a file required by `operator-sdk` when it performs code-generation. + +It's contents provide the headers to the generated files but as we do not require headers for the files we generate, it has been left intentionally blank. diff --git a/control-plane/build-support/controller/boilerplate.go.txt b/control-plane/build-support/controller/boilerplate.go.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/control-plane/build-support/functions/00-vars.sh b/control-plane/build-support/functions/00-vars.sh index 484344703c..1f03013c32 100644 --- a/control-plane/build-support/functions/00-vars.sh +++ b/control-plane/build-support/functions/00-vars.sh @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - # GPG Key ID to use for publically released builds HASHICORP_GPG_KEY="348FFC4C" diff --git a/control-plane/build-support/functions/10-util.sh b/control-plane/build-support/functions/10-util.sh index 3bc87124d9..2706c69be8 100644 --- a/control-plane/build-support/functions/10-util.sh +++ b/control-plane/build-support/functions/10-util.sh @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - function err { if test "${COLORIZE}" -eq 1; then tput bold @@ -619,6 +616,7 @@ function update_version_helm { local vfile="$1/values.yaml" local cfile="$1/Chart.yaml" local version="$2" + local consul_version="$5" local prerelease="$3" local full_version="$2" local full_consul_version="$5" diff --git a/control-plane/build-support/functions/20-build.sh b/control-plane/build-support/functions/20-build.sh index dac626b88f..ddde7b6acf 100644 --- a/control-plane/build-support/functions/20-build.sh +++ b/control-plane/build-support/functions/20-build.sh @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - function refresh_docker_images { # Arguments: # $1 - Path to top level Consul source @@ -180,7 +177,7 @@ function build_consul_local { # * - error # # Note: - # The GOLDFLAGS, GOEXPERIMENT, and GOTAGS environment variables will be used if set + # The GOLDFLAGS and GOTAGS environment variables will be used if set # If the CONSUL_DEV environment var is truthy only the local platform/architecture is built. # If the XC_OS or the XC_ARCH environment vars are present then only those platforms/architectures # will be built. Otherwise all supported platform/architectures are built @@ -188,14 +185,6 @@ function build_consul_local { # build with go install. # The GOXPARALLEL environment variable is used if set - if [ "${GOTAGS:-}" == "fips" ]; then - CGO_ENABLED=1 - else - CGO_ENABLED=0 - fi - - echo "GOEXPERIMENT: $GOEXPERIMENT, GOTAGS: $GOTAGS CGO_ENABLED: $CGO_ENABLED" >> ~/debug.txt - if ! test -d "$1" then err "ERROR: '$1' is not a directory. build_consul must be called with the path to the top level source as the first argument'" @@ -250,7 +239,7 @@ function build_consul_local { then status "Using gox for concurrent compilation" - CGO_ENABLED=${CGO_ENABLED} GOEXPERIMENT=${GOEXPERIMENT} gox \ + CGO_ENABLED=0 gox \ -os="${build_os}" \ -arch="${build_arch}" \ -ldflags="${GOLDFLAGS}" \ @@ -298,7 +287,7 @@ function build_consul_local { else OS_BIN_EXTENSION="" fi - CGO_ENABLED=${CGO_ENABLED} GOEXPERIMENT=${GOEXPERIMENT} GOOS=${os} GOARCH=${arch} go build -ldflags "${GOLDFLAGS}" -tags "${GOTAGS}" -o "${outdir}/${bin_name}" + CGO_ENABLED=0 GOOS=${os} GOARCH=${arch} go build -ldflags "${GOLDFLAGS}" -tags "${GOTAGS}" -o "${outdir}/${bin_name}" if test $? -ne 0 then err "ERROR: Failed to build Consul for ${osarch}" diff --git a/control-plane/build-support/functions/40-publish.sh b/control-plane/build-support/functions/40-publish.sh index aae9a5f719..975c835bc0 100644 --- a/control-plane/build-support/functions/40-publish.sh +++ b/control-plane/build-support/functions/40-publish.sh @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - function hashicorp_release { # Arguments: # $1 - Path to directory containing all of the release artifacts diff --git a/control-plane/build-support/scripts/build-local.sh b/control-plane/build-support/scripts/build-local.sh index 7325e025b7..95d18e0ba6 100755 --- a/control-plane/build-support/scripts/build-local.sh +++ b/control-plane/build-support/scripts/build-local.sh @@ -1,7 +1,4 @@ #!/bin/bash -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - SCRIPT_NAME="$(basename ${BASH_SOURCE[0]})" pushd $(dirname ${BASH_SOURCE[0]}) > /dev/null SCRIPT_DIR=$(pwd) @@ -35,8 +32,6 @@ Options: -a | --arch ARCH Space separated string of architectures to build. - --fips FIPS Whether to use FIPS cryptography. - -h | --help Print this help text. EOF } @@ -96,11 +91,6 @@ function main { build_arch="$2" shift 2 ;; - --fips ) - GOTAGS="fips" - GOEXPERIMENT="boringcrypto" - shift 1 - ;; * ) err_usage "ERROR: Unknown argument: '$1'" return 1 diff --git a/control-plane/build-support/scripts/consul-enterprise-version.sh b/control-plane/build-support/scripts/consul-enterprise-version.sh index 37df85dfc5..24adb6a793 100755 --- a/control-plane/build-support/scripts/consul-enterprise-version.sh +++ b/control-plane/build-support/scripts/consul-enterprise-version.sh @@ -10,4 +10,5 @@ elif [[ !"${VERSION}" == *"hashicorp/consul:"* ]]; then VERSION=$(echo ${VERSION} | sed "s/consul:/consul-enterprise:/g" | sed "s/$/-ent/g") fi + echo "${VERSION}" diff --git a/control-plane/build-support/scripts/functions.sh b/control-plane/build-support/scripts/functions.sh index 590666eb7d..0301c0d3a1 100644 --- a/control-plane/build-support/scripts/functions.sh +++ b/control-plane/build-support/scripts/functions.sh @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - # # NOTE: This file is meant to be sourced from other bash scripts/shells # diff --git a/control-plane/build-support/scripts/terraformfmtcheck.sh b/control-plane/build-support/scripts/terraformfmtcheck.sh index 8608a88e30..0f962a1c1b 100755 --- a/control-plane/build-support/scripts/terraformfmtcheck.sh +++ b/control-plane/build-support/scripts/terraformfmtcheck.sh @@ -1,7 +1,4 @@ #!/usr/bin/env bash -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - # Check terraform fmt echo "==> Checking that code complies with terraform fmt requirements..." diff --git a/control-plane/build-support/scripts/version.sh b/control-plane/build-support/scripts/version.sh index fce325e03c..f91b0c3917 100755 --- a/control-plane/build-support/scripts/version.sh +++ b/control-plane/build-support/scripts/version.sh @@ -1,7 +1,4 @@ #!/usr/bin/env bash -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - version_file=$1 version=$(awk '$1 == "Version" && $2 == "=" { gsub(/"/, "", $3); print $3 }' < "${version_file}") diff --git a/control-plane/catalog/to-consul/annotation.go b/control-plane/catalog/to-consul/annotation.go index edca70b60c..5df5ab71f4 100644 --- a/control-plane/catalog/to-consul/annotation.go +++ b/control-plane/catalog/to-consul/annotation.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package catalog const ( diff --git a/control-plane/catalog/to-consul/resource.go b/control-plane/catalog/to-consul/resource.go index 2d29d6c15a..8d6fa9b82b 100644 --- a/control-plane/catalog/to-consul/resource.go +++ b/control-plane/catalog/to-consul/resource.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package catalog import ( @@ -512,7 +509,7 @@ func (t *ServiceResource) generateRegistrations(key string) { r.Service.ID = serviceID(r.Service.Service, ip) r.Service.Address = ip // Adding information about service weight. - // Overrides the existing weight if present. + // Overrides the existing weight if present if weight, ok := svc.Annotations[annotationServiceWeight]; ok && weight != "" { weightI, err := getServiceWeight(weight) if err == nil { @@ -561,7 +558,7 @@ func (t *ServiceResource) generateRegistrations(key string) { r.Service.Address = addr // Adding information about service weight. - // Overrides the existing weight if present. + // Overrides the existing weight if present if weight, ok := svc.Annotations[annotationServiceWeight]; ok && weight != "" { weightI, err := getServiceWeight(weight) if err == nil { @@ -1028,7 +1025,7 @@ func consulHealthCheckID(k8sNS string, serviceID string) string { // Calculates the passing service weight. func getServiceWeight(weight string) (int, error) { - // error validation if the input param is a number. + // error validation if the input param is a number weightI, err := strconv.Atoi(weight) if err != nil { return -1, err diff --git a/control-plane/catalog/to-consul/resource_test.go b/control-plane/catalog/to-consul/resource_test.go index 3b8fb78497..680c052823 100644 --- a/control-plane/catalog/to-consul/resource_test.go +++ b/control-plane/catalog/to-consul/resource_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package catalog import ( diff --git a/control-plane/catalog/to-consul/service_id.go b/control-plane/catalog/to-consul/service_id.go index 8300871b73..1aa3071497 100644 --- a/control-plane/catalog/to-consul/service_id.go +++ b/control-plane/catalog/to-consul/service_id.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package catalog import ( diff --git a/control-plane/catalog/to-consul/syncer.go b/control-plane/catalog/to-consul/syncer.go index 9f1df18ba6..19e0aaca6f 100644 --- a/control-plane/catalog/to-consul/syncer.go +++ b/control-plane/catalog/to-consul/syncer.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package catalog import ( diff --git a/control-plane/catalog/to-consul/syncer_ent_test.go b/control-plane/catalog/to-consul/syncer_ent_test.go index 5dfb158d23..fbe2cbd494 100644 --- a/control-plane/catalog/to-consul/syncer_ent_test.go +++ b/control-plane/catalog/to-consul/syncer_ent_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - //go:build enterprise package catalog diff --git a/control-plane/catalog/to-consul/syncer_test.go b/control-plane/catalog/to-consul/syncer_test.go index 3fae7a3d16..d8d9b0f402 100644 --- a/control-plane/catalog/to-consul/syncer_test.go +++ b/control-plane/catalog/to-consul/syncer_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package catalog import ( diff --git a/control-plane/catalog/to-consul/testing.go b/control-plane/catalog/to-consul/testing.go index 5f19017cbe..e6541c6ba1 100644 --- a/control-plane/catalog/to-consul/testing.go +++ b/control-plane/catalog/to-consul/testing.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package catalog import ( diff --git a/control-plane/catalog/to-k8s/sink.go b/control-plane/catalog/to-k8s/sink.go index 6e201253df..fa8821989e 100644 --- a/control-plane/catalog/to-k8s/sink.go +++ b/control-plane/catalog/to-k8s/sink.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package catalog import ( diff --git a/control-plane/catalog/to-k8s/sink_test.go b/control-plane/catalog/to-k8s/sink_test.go index cfba502268..fbce7bbaaf 100644 --- a/control-plane/catalog/to-k8s/sink_test.go +++ b/control-plane/catalog/to-k8s/sink_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package catalog import ( diff --git a/control-plane/catalog/to-k8s/source.go b/control-plane/catalog/to-k8s/source.go index ab34089d8e..5a384e760a 100644 --- a/control-plane/catalog/to-k8s/source.go +++ b/control-plane/catalog/to-k8s/source.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package catalog import ( diff --git a/control-plane/catalog/to-k8s/source_test.go b/control-plane/catalog/to-k8s/source_test.go index 66afb4b608..ca00a1e954 100644 --- a/control-plane/catalog/to-k8s/source_test.go +++ b/control-plane/catalog/to-k8s/source_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package catalog import ( diff --git a/control-plane/catalog/to-k8s/testing.go b/control-plane/catalog/to-k8s/testing.go index 1eb731a17f..d7181bdbea 100644 --- a/control-plane/catalog/to-k8s/testing.go +++ b/control-plane/catalog/to-k8s/testing.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package catalog import ( diff --git a/control-plane/cni/config/config.go b/control-plane/cni/config/config.go index a9dafd0ab7..f22d3ff79b 100644 --- a/control-plane/cni/config/config.go +++ b/control-plane/cni/config/config.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package config const ( diff --git a/control-plane/cni/go.mod b/control-plane/cni/go.mod index e8fcc980ef..d1e0f62f2c 100644 --- a/control-plane/cni/go.mod +++ b/control-plane/cni/go.mod @@ -3,9 +3,9 @@ module github.com/hashicorp/consul-k8s/control-plane/cni require ( github.com/containernetworking/cni v1.1.1 github.com/containernetworking/plugins v1.1.1 - github.com/hashicorp/consul/sdk v0.13.1 - github.com/hashicorp/go-hclog v1.2.1 - github.com/stretchr/testify v1.7.2 + github.com/hashicorp/consul/sdk v0.13.0 + github.com/hashicorp/go-hclog v0.16.1 + github.com/stretchr/testify v1.7.1 k8s.io/api v0.22.2 k8s.io/apimachinery v0.22.2 k8s.io/client-go v0.22.2 @@ -14,7 +14,7 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect - github.com/fatih/color v1.13.0 // indirect + github.com/fatih/color v1.12.0 // indirect github.com/go-logr/logr v0.4.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.2 // indirect @@ -23,8 +23,8 @@ require ( github.com/googleapis/gnostic v0.5.5 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/json-iterator/go v1.1.11 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-colorable v0.1.8 // indirect + github.com/mattn/go-isatty v0.0.13 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.1 // indirect github.com/pkg/errors v0.9.1 // indirect @@ -37,10 +37,10 @@ require ( golang.org/x/text v0.11.0 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.27.1 // indirect + google.golang.org/protobuf v1.26.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect k8s.io/klog/v2 v2.9.0 // indirect k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a // indirect @@ -50,4 +50,4 @@ require ( replace github.com/hashicorp/consul/sdk => github.com/hashicorp/consul/sdk v0.4.1-0.20221021205723-cc843c4be892 -go 1.20 +go 1.19 diff --git a/control-plane/cni/go.sum b/control-plane/cni/go.sum index 8f4c0668ea..727657541e 100644 --- a/control-plane/cni/go.sum +++ b/control-plane/cni/go.sum @@ -57,8 +57,8 @@ github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCv github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= +github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -135,8 +135,8 @@ github.com/hashicorp/consul/sdk v0.4.1-0.20221021205723-cc843c4be892 h1:jw0NwPmN github.com/hashicorp/consul/sdk v0.4.1-0.20221021205723-cc843c4be892/go.mod h1:yPkX5Q6CsxTFMjQQDJwzeNmUUF5NUGGbrDsv9wTb8cw= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.2.1 h1:YQsLlGDJgwhXFpucSPyVbCBviQtjlHv3jLTlp8YmtEw= -github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v0.16.1 h1:IVQwpTGNRRIHafnTs2dQLIk4ENtneRIEEJWOVDqz99o= +github.com/hashicorp/go-hclog v0.16.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -162,15 +162,14 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= +github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -218,8 +217,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -335,10 +334,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -451,9 +447,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -474,9 +469,8 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/control-plane/cni/main.go b/control-plane/cni/main.go index e35f5ad811..7b05b5c6cd 100644 --- a/control-plane/cni/main.go +++ b/control-plane/cni/main.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package main import ( diff --git a/control-plane/cni/main_test.go b/control-plane/cni/main_test.go index 7c289a9825..740e15c646 100644 --- a/control-plane/cni/main_test.go +++ b/control-plane/cni/main_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package main import ( diff --git a/control-plane/commands.go b/control-plane/commands.go index e2bcb0f693..ec3b7ca612 100644 --- a/control-plane/commands.go +++ b/control-plane/commands.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package main import ( @@ -11,9 +8,6 @@ import ( cmdConsulLogout "github.com/hashicorp/consul-k8s/control-plane/subcommand/consul-logout" cmdCreateFederationSecret "github.com/hashicorp/consul-k8s/control-plane/subcommand/create-federation-secret" cmdDeleteCompletedJob "github.com/hashicorp/consul-k8s/control-plane/subcommand/delete-completed-job" - cmdFetchServerRegion "github.com/hashicorp/consul-k8s/control-plane/subcommand/fetch-server-region" - cmdGatewayCleanup "github.com/hashicorp/consul-k8s/control-plane/subcommand/gateway-cleanup" - cmdGatewayResources "github.com/hashicorp/consul-k8s/control-plane/subcommand/gateway-resources" cmdGetConsulClientCA "github.com/hashicorp/consul-k8s/control-plane/subcommand/get-consul-client-ca" cmdGossipEncryptionAutogenerate "github.com/hashicorp/consul-k8s/control-plane/subcommand/gossip-encryption-autogenerate" cmdInjectConnect "github.com/hashicorp/consul-k8s/control-plane/subcommand/inject-connect" @@ -51,14 +45,6 @@ func init() { return &cmdConsulLogout.Command{UI: ui}, nil }, - "gateway-cleanup": func() (cli.Command, error) { - return &cmdGatewayCleanup.Command{UI: ui}, nil - }, - - "gateway-resources": func() (cli.Command, error) { - return &cmdGatewayResources.Command{UI: ui}, nil - }, - "server-acl-init": func() (cli.Command, error) { return &cmdServerACLInit.Command{UI: ui}, nil }, @@ -101,9 +87,6 @@ func init() { "install-cni": func() (cli.Command, error) { return &cmdInstallCNI.Command{UI: ui}, nil }, - "fetch-server-region": func() (cli.Command, error) { - return &cmdFetchServerRegion.Command{UI: ui}, nil - }, } } diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_controlplanerequestlimits.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_controlplanerequestlimits.yaml deleted file mode 100644 index 11da54e9ac..0000000000 --- a/control-plane/config/crd/bases/consul.hashicorp.com_controlplanerequestlimits.yaml +++ /dev/null @@ -1,198 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: controlplanerequestlimits.consul.hashicorp.com -spec: - group: consul.hashicorp.com - names: - kind: ControlPlaneRequestLimit - listKind: ControlPlaneRequestLimitList - plural: controlplanerequestlimits - singular: controlplanerequestlimit - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The sync status of the resource with Consul - jsonPath: .status.conditions[?(@.type=="Synced")].status - name: Synced - type: string - - description: The age of the resource - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: ControlPlaneRequestLimit is the Schema for the controlplanerequestlimits - API. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: ControlPlaneRequestLimitSpec defines the desired state of - ControlPlaneRequestLimit. - properties: - acl: - properties: - readRate: - type: number - writeRate: - type: number - type: object - catalog: - properties: - readRate: - type: number - writeRate: - type: number - type: object - configEntry: - properties: - readRate: - type: number - writeRate: - type: number - type: object - connectCA: - properties: - readRate: - type: number - writeRate: - type: number - type: object - coordinate: - properties: - readRate: - type: number - writeRate: - type: number - type: object - discoveryChain: - properties: - readRate: - type: number - writeRate: - type: number - type: object - health: - properties: - readRate: - type: number - writeRate: - type: number - type: object - intention: - properties: - readRate: - type: number - writeRate: - type: number - type: object - kv: - properties: - readRate: - type: number - writeRate: - type: number - type: object - mode: - type: string - perparedQuery: - properties: - readRate: - type: number - writeRate: - type: number - type: object - readRate: - type: number - session: - properties: - readRate: - type: number - writeRate: - type: number - type: object - tenancy: - properties: - readRate: - type: number - writeRate: - type: number - type: object - txn: - properties: - readRate: - type: number - writeRate: - type: number - type: object - writeRate: - type: number - type: object - status: - properties: - conditions: - description: Conditions indicate the latest available observations - of a resource's current state. - items: - description: 'Conditions define a readiness condition for a Consul - resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' - properties: - lastTransitionTime: - description: LastTransitionTime is the last time the condition - transitioned from one status to another. - format: date-time - type: string - message: - description: A human readable message indicating details about - the transition. - type: string - reason: - description: The reason for the condition's last transition. - type: string - status: - description: Status of the condition, one of True, False, Unknown. - type: string - type: - description: Type of condition. - type: string - required: - - status - - type - type: object - type: array - lastSyncedTime: - description: LastSyncedTime is the last time the resource successfully - synced with Consul. - format: date-time - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml index 0b6b969856..da1a66fd74 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -72,12 +69,8 @@ spec: the service to. type: string peer: - description: Peer is the name of the peer to export the - service to. - type: string - samenessGroup: - description: SamenessGroup is the name of the sameness - group to export the service to. + description: '[Experimental] Peer is the name of the peer + to export the service to.' type: string type: object type: array diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml deleted file mode 100644 index e60e4a1cfa..0000000000 --- a/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml +++ /dev/null @@ -1,146 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: gatewayclassconfigs.consul.hashicorp.com -spec: - group: consul.hashicorp.com - names: - kind: GatewayClassConfig - listKind: GatewayClassConfigList - plural: gatewayclassconfigs - singular: gatewayclassconfig - scope: Cluster - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: GatewayClassConfig defines the values that may be set on a GatewayClass - for Consul API Gateway. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of GatewayClassConfig. - properties: - copyAnnotations: - description: Annotation Information to copy to services or deployments - properties: - service: - description: List of annotations to copy to the gateway service. - items: - type: string - type: array - type: object - deployment: - description: Deployment defines the deployment configuration for the - gateway. - properties: - defaultInstances: - default: 1 - description: Number of gateway instances that should be deployed - by default - format: int32 - maximum: 8 - minimum: 1 - type: integer - maxInstances: - default: 8 - description: Max allowed number of gateway instances - format: int32 - maximum: 8 - minimum: 1 - type: integer - minInstances: - default: 1 - description: Minimum allowed number of gateway instances - format: int32 - maximum: 8 - minimum: 1 - type: integer - type: object - nodeSelector: - additionalProperties: - type: string - description: 'NodeSelector is a selector which must be true for the - pod to fit on a node. Selector which must match a node''s labels - for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' - type: object - podSecurityPolicy: - description: The name of an existing Kubernetes PodSecurityPolicy - to bind to the managed ServiceAccount if ACLs are managed. - type: string - serviceType: - description: Service Type string describes ingress methods for a service - enum: - - ClusterIP - - NodePort - - LoadBalancer - type: string - tolerations: - description: 'Tolerations allow the scheduler to schedule nodes with - matching taints. More Info: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/' - items: - description: The pod this Toleration is attached to tolerates any - taint that matches the triple using the matching - operator . - properties: - effect: - description: Effect indicates the taint effect to match. Empty - means match all taint effects. When specified, allowed values - are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: Key is the taint key that the toleration applies - to. Empty means match all taint keys. If the key is empty, - operator must be Exists; this combination means to match all - values and all keys. - type: string - operator: - description: Operator represents a key's relationship to the - value. Valid operators are Exists and Equal. Defaults to Equal. - Exists is equivalent to wildcard for value, so that a pod - can tolerate all taints of a particular category. - type: string - tolerationSeconds: - description: TolerationSeconds represents the period of time - the toleration (which must be of effect NoExecute, otherwise - this field is ignored) tolerates the taint. By default, it - is not set, which means tolerate the taint forever (do not - evict). Zero and negative values will be treated as 0 (evict - immediately) by the system. - format: int64 - type: integer - value: - description: Value is the taint value the toleration matches - to. If the operator is Exists, the value should be empty, - otherwise just a regular string. - type: string - type: object - type: array - type: object - type: object - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml index fd8ebc86ff..16ac322090 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml deleted file mode 100644 index 2e8ac24330..0000000000 --- a/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml +++ /dev/null @@ -1,260 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: jwtproviders.consul.hashicorp.com -spec: - group: consul.hashicorp.com - names: - kind: JWTProvider - listKind: JWTProviderList - plural: jwtproviders - singular: jwtprovider - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: JWTProvider is the Schema for the jwtproviders API. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: JWTProviderSpec defines the desired state of JWTProvider - properties: - audiences: - description: Audiences is the set of audiences the JWT is allowed - to access. If specified, all JWTs verified with this provider must - address at least one of these to be considered valid. - items: - type: string - type: array - cacheConfig: - description: CacheConfig defines configuration for caching the validation - result for previously seen JWTs. Caching results can speed up verification - when individual tokens are expected to be handled multiple times. - properties: - size: - description: "Size specifies the maximum number of JWT verification - results to cache. \n Defaults to 0, meaning that JWT caching - is disabled." - type: integer - type: object - clockSkewSeconds: - description: "ClockSkewSeconds specifies the maximum allowable time - difference from clock skew when validating the \"exp\" (Expiration) - and \"nbf\" (Not Before) claims. \n Default value is 30 seconds." - type: integer - forwarding: - description: Forwarding defines rules for forwarding verified JWTs - to the backend. - properties: - headerName: - description: "HeaderName is a header name to use when forwarding - a verified JWT to the backend. The verified JWT could have been - extracted from any location (query param, header, or cookie). - \n The header value will be base64-URL-encoded, and will not - be padded unless PadForwardPayloadHeader is true." - type: string - padForwardPayloadHeader: - description: "PadForwardPayloadHeader determines whether padding - should be added to the base64 encoded token forwarded with ForwardPayloadHeader. - \n Default value is false." - type: boolean - type: object - issuer: - description: Issuer is the entity that must have issued the JWT. This - value must match the "iss" claim of the token. - type: string - jsonWebKeySet: - description: JSONWebKeySet defines a JSON Web Key Set, its location - on disk, or the means with which to fetch a key set from a remote - server. - properties: - local: - description: Local specifies a local source for the key set. - properties: - filename: - description: Filename configures a location on disk where - the JWKS can be found. If specified, the file must be present - on the disk of ALL proxies with intentions referencing this - provider. - type: string - jwks: - description: JWKS contains a base64 encoded JWKS. - type: string - type: object - remote: - description: Remote specifies how to fetch a key set from a remote - server. - properties: - cacheDuration: - description: "CacheDuration is the duration after which cached - keys should be expired. \n Default value is 5 minutes." - format: int64 - type: integer - fetchAsynchronously: - description: "FetchAsynchronously indicates that the JWKS - should be fetched when a client request arrives. Client - requests will be paused until the JWKS is fetched. If false, - the proxy listener will wait for the JWKS to be fetched - before being activated. \n Default value is false." - type: boolean - requestTimeoutMs: - description: RequestTimeoutMs is the number of milliseconds - to time out when making a request for the JWKS. - type: integer - retryPolicy: - description: "RetryPolicy defines a retry policy for fetching - JWKS. \n There is no retry by default." - properties: - numRetries: - description: "NumRetries is the number of times to retry - fetching the JWKS. The retry strategy uses jittered - exponential backoff with a base interval of 1s and max - of 10s. \n Default value is 0." - type: integer - retryPolicyBackOff: - description: "Backoff policy \n Defaults to Envoy's backoff - policy" - properties: - baseInterval: - description: "BaseInterval to be used for the next - back off computation \n The default value from envoy - is 1s" - format: int64 - type: integer - maxInterval: - description: "MaxInternal to be used to specify the - maximum interval between retries. Optional but should - be greater or equal to BaseInterval. \n Defaults - to 10 times BaseInterval" - format: int64 - type: integer - type: object - type: object - uri: - description: URI is the URI of the server to query for the - JWKS. - type: string - type: object - type: object - locations: - description: 'Locations where the JWT will be present in requests. - Envoy will check all of these locations to extract a JWT. If no - locations are specified Envoy will default to: 1. Authorization - header with Bearer schema: "Authorization: Bearer " 2. accessToken - query parameter.' - items: - description: "JWTLocation is a location where the JWT could be present - in requests. \n Only one of Header, QueryParam, or Cookie can - be specified." - properties: - cookie: - description: Cookie defines how to extract a JWT from an HTTP - request cookie. - properties: - name: - description: Name is the name of the cookie containing the - token. - type: string - type: object - header: - description: Header defines how to extract a JWT from an HTTP - request header. - properties: - forward: - description: "Forward defines whether the header with the - JWT should be forwarded after the token has been verified. - If false, the header will not be forwarded to the backend. - \n Default value is false." - type: boolean - name: - description: Name is the name of the header containing the - token. - type: string - valuePrefix: - description: 'ValuePrefix is an optional prefix that precedes - the token in the header value. For example, "Bearer " - is a standard value prefix for a header named "Authorization", - but the prefix is not part of the token itself: "Authorization: - Bearer "' - type: string - type: object - queryParam: - description: QueryParam defines how to extract a JWT from an - HTTP request query parameter. - properties: - name: - description: Name is the name of the query param containing - the token. - type: string - type: object - type: object - type: array - type: object - status: - properties: - conditions: - description: Conditions indicate the latest available observations - of a resource's current state. - items: - description: 'Conditions define a readiness condition for a Consul - resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' - properties: - lastTransitionTime: - description: LastTransitionTime is the last time the condition - transitioned from one status to another. - format: date-time - type: string - message: - description: A human readable message indicating details about - the transition. - type: string - reason: - description: The reason for the condition's last transition. - type: string - status: - description: Status of the condition, one of True, False, Unknown. - type: string - type: - description: Type of condition. - type: string - required: - - status - - type - type: object - type: array - lastSyncedTime: - description: LastSyncedTime is the last time the resource successfully - synced with Consul. - format: date-time - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml index adbb12bba6..7ad173afbf 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -51,11 +48,6 @@ spec: spec: description: MeshSpec defines the desired state of Mesh. properties: - allowEnablingPermissiveMutualTLS: - description: AllowEnablingPermissiveMutualTLS must be true in order - to allow setting MutualTLSMode=permissive in either service-defaults - or proxy-defaults. - type: boolean http: description: HTTP defines the HTTP configuration for the service mesh. properties: diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml deleted file mode 100644 index 04f8f493e7..0000000000 --- a/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: meshservices.consul.hashicorp.com -spec: - group: consul.hashicorp.com - names: - kind: MeshService - listKind: MeshServiceList - plural: meshservices - singular: meshservice - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: MeshService holds a reference to an externally managed Consul - Service Mesh service. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of MeshService. - properties: - name: - description: Name holds the service name for a Consul service. - type: string - peer: - description: Peer optionally specifies the name of the peer exporting - the Consul service. If not specified, the Consul service is assumed - to be in the local datacenter. - type: string - type: object - type: object - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml index 50df179f04..e782ef472f 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml index 01e4363f14..d5103252a5 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml index 25e61a51ee..6b9628cd74 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -53,60 +50,12 @@ spec: spec: description: ProxyDefaultsSpec defines the desired state of ProxyDefaults. properties: - accessLogs: - description: AccessLogs controls all envoy instances' access logging - configuration. - properties: - disableListenerLogs: - description: DisableListenerLogs turns off just listener logs - for connections rejected by Envoy because they don't have a - matching listener filter. - type: boolean - enabled: - description: Enabled turns on all access logging - type: boolean - jsonFormat: - description: 'JSONFormat is a JSON-formatted string of an Envoy - access log format dictionary. See for more info on formatting: - https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#format-dictionaries - Defining JSONFormat and TextFormat is invalid.' - type: string - path: - description: Path is the output file to write logs for file-type - logging - type: string - textFormat: - description: 'TextFormat is a representation of Envoy access logs - format. See for more info on formatting: https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#format-strings - Defining JSONFormat and TextFormat is invalid.' - type: string - type: - description: Type selects the output for logs one of "file", "stderr". - "stdout" - type: string - type: object config: description: Config is an arbitrary map of configuration values used by Connect proxies. Any values that your proxy allows can be configured globally here. Supports JSON config values. See https://www.consul.io/docs/connect/proxies/envoy#configuration-formatting type: object x-kubernetes-preserve-unknown-fields: true - envoyExtensions: - description: EnvoyExtensions are a list of extensions to modify Envoy - proxy configuration. - items: - description: EnvoyExtension has configuration for an extension that - patches Envoy resources. - properties: - arguments: - type: object - x-kubernetes-preserve-unknown-fields: true - name: - type: string - required: - type: boolean - type: object - type: array expose: description: Expose controls the default expose path configuration for Envoy. @@ -139,32 +88,6 @@ spec: type: object type: array type: object - failoverPolicy: - description: FailoverPolicy specifies the exact mechanism used for - failover. - properties: - mode: - description: Mode specifies the type of failover that will be - performed. Valid values are "sequential", "" (equivalent to - "sequential") and "order-by-locality". - type: string - regions: - description: Regions is the ordered list of the regions of the - failover targets. Valid values can be "us-west-1", "us-west-2", - and so on. - items: - type: string - type: array - type: object - prioritizeByLocality: - description: PrioritizeByLocality contains the configuration for - locality aware routing. - properties: - mode: - description: Mode specifies the behavior of PrioritizeByLocality - routing. Valid values are "", "none", and "failover". - type: string - type: object meshGateway: description: MeshGateway controls the default mesh gateway configuration for this service. @@ -185,18 +108,6 @@ spec: CRD and should be set using annotations on the services that are part of the mesh.' type: string - mutualTLSMode: - description: 'MutualTLSMode controls whether mutual TLS is required - for all incoming connections when transparent proxy is enabled. - This can be set to "permissive" or "strict". "strict" is the default - which requires mutual TLS for incoming connections. In the insecure - "permissive" mode, connections to the sidecar proxy public listener - port require mutual TLS, but connections to the service port do - not require mutual TLS and are proxied to the application unmodified. - Note: Intentions are not enforced for non-mTLS connections. To keep - your services secure, we recommend using "strict" mode whenever - possible and enabling "permissive" mode only when necessary.' - type: string transparentProxy: description: 'TransparentProxy controls configuration specific to proxies in transparent mode. Note: This cannot be set using the diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_routeretryfilters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_routeretryfilters.yaml deleted file mode 100644 index 5928864ac5..0000000000 --- a/control-plane/config/crd/bases/consul.hashicorp.com_routeretryfilters.yaml +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null - name: routeretryfilters.consul.hashicorp.com -spec: - group: consul.hashicorp.com - names: - kind: RouteRetryFilter - listKind: RouteRetryFilterList - plural: routeretryfilters - singular: routeretryfilter - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The sync status of the resource with Consul - jsonPath: .status.conditions[?(@.type=="Synced")].status - name: Synced - type: string - - description: The last successful synced time of the resource with Consul - jsonPath: .status.lastSyncedTime - name: Last Synced - type: date - - description: The age of the resource - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: RouteRetryFilter is the Schema for the routeretryfilters API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: RouteRetryFilterSpec defines the desired state of RouteRetryFilter - properties: - numRetries: - format: int32 - minimum: 0 - type: integer - retryOn: - items: - type: string - type: array - retryOnConnectFailure: - type: boolean - retryOnStatusCodes: - items: - format: int32 - type: integer - type: array - type: object - status: - properties: - conditions: - description: Conditions indicate the latest available observations - of a resource's current state. - items: - description: 'Conditions define a readiness condition for a Consul - resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' - properties: - lastTransitionTime: - description: LastTransitionTime is the last time the condition - transitioned from one status to another. - format: date-time - type: string - message: - description: A human readable message indicating details about - the transition. - type: string - reason: - description: The reason for the condition's last transition. - type: string - status: - description: Status of the condition, one of True, False, Unknown. - type: string - type: - description: Type of condition. - type: string - required: - - status - - type - type: object - type: array - lastSyncedTime: - description: LastSyncedTime is the last time the resource successfully - synced with Consul. - format: date-time - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_routetimeoutfilters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_routetimeoutfilters.yaml deleted file mode 100644 index e671ecd9b9..0000000000 --- a/control-plane/config/crd/bases/consul.hashicorp.com_routetimeoutfilters.yaml +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null - name: routetimeoutfilters.consul.hashicorp.com -spec: - group: consul.hashicorp.com - names: - kind: RouteTimeoutFilter - listKind: RouteTimeoutFilterList - plural: routetimeoutfilters - singular: routetimeoutfilter - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The sync status of the resource with Consul - jsonPath: .status.conditions[?(@.type=="Synced")].status - name: Synced - type: string - - description: The last successful synced time of the resource with Consul - jsonPath: .status.lastSyncedTime - name: Last Synced - type: date - - description: The age of the resource - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: RouteTimeoutFilter is the Schema for the httproutetimeoutfilters - API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: RouteTimeoutFilterSpec defines the desired state of RouteTimeoutFilter - properties: - idleTimeout: - description: A Duration represents the elapsed time between two instants - as an int64 nanosecond count. The representation limits the largest - representable duration to approximately 290 years. - format: int64 - type: integer - requestTimeout: - description: A Duration represents the elapsed time between two instants - as an int64 nanosecond count. The representation limits the largest - representable duration to approximately 290 years. - format: int64 - type: integer - type: object - status: - properties: - conditions: - description: Conditions indicate the latest available observations - of a resource's current state. - items: - description: 'Conditions define a readiness condition for a Consul - resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' - properties: - lastTransitionTime: - description: LastTransitionTime is the last time the condition - transitioned from one status to another. - format: date-time - type: string - message: - description: A human readable message indicating details about - the transition. - type: string - reason: - description: The reason for the condition's last transition. - type: string - status: - description: Status of the condition, one of True, False, Unknown. - type: string - type: - description: Type of condition. - type: string - required: - - status - - type - type: object - type: array - lastSyncedTime: - description: LastSyncedTime is the last time the resource successfully - synced with Consul. - format: date-time - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml deleted file mode 100644 index c71a211f63..0000000000 --- a/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: samenessgroups.consul.hashicorp.com -spec: - group: consul.hashicorp.com - names: - kind: SamenessGroup - listKind: SamenessGroupList - plural: samenessgroups - shortNames: - - sameness-group - singular: samenessgroup - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The sync status of the resource with Consul - jsonPath: .status.conditions[?(@.type=="Synced")].status - name: Synced - type: string - - description: The last successful synced time of the resource with Consul - jsonPath: .status.lastSyncedTime - name: Last Synced - type: date - - description: The age of the resource - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: SamenessGroup is the Schema for the samenessgroups API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: SamenessGroupSpec defines the desired state of SamenessGroup. - properties: - defaultForFailover: - description: DefaultForFailover indicates that upstream requests to - members of the given sameness group will implicitly failover between - members of this sameness group. When DefaultForFailover is true, - the local partition must be a member of the sameness group or IncludeLocal - must be set to true. - type: boolean - includeLocal: - description: IncludeLocal is used to include the local partition as - the first member of the sameness group. The local partition can - only be a member of a single sameness group. - type: boolean - members: - description: Members are the partitions and peers that are part of - the sameness group. If a member of a sameness group does not exist, - it will be ignored. - items: - properties: - partition: - description: The partitions and peers that are part of the sameness - group. A sameness group member cannot define both peer and - partition at the same time. - type: string - peer: - type: string - type: object - type: array - type: object - status: - properties: - conditions: - description: Conditions indicate the latest available observations - of a resource's current state. - items: - description: 'Conditions define a readiness condition for a Consul - resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' - properties: - lastTransitionTime: - description: LastTransitionTime is the last time the condition - transitioned from one status to another. - format: date-time - type: string - message: - description: A human readable message indicating details about - the transition. - type: string - reason: - description: The reason for the condition's last transition. - type: string - status: - description: Status of the condition, one of True, False, Unknown. - type: string - type: - description: Type of condition. - type: string - required: - - status - - type - type: object - type: array - lastSyncedTime: - description: LastSyncedTime is the last time the resource successfully - synced with Consul. - format: date-time - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml index 5a2c7a58fd..7324f0a296 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -53,12 +50,6 @@ spec: spec: description: ServiceDefaultsSpec defines the desired state of ServiceDefaults. properties: - balanceInboundConnections: - description: BalanceInboundConnections sets the strategy for allocating - inbound connections to the service across proxy threads. The only - supported value is exact_balance. By default, no connection balancing - is used. Refer to the Envoy Connection Balance config for details. - type: string destination: description: Destination is an address(es)/port combination that represents an endpoint outside the mesh. This is only valid when the mesh is @@ -78,22 +69,6 @@ spec: format: int32 type: integer type: object - envoyExtensions: - description: EnvoyExtensions are a list of extensions to modify Envoy - proxy configuration. - items: - description: EnvoyExtension has configuration for an extension that - patches Envoy resources. - properties: - arguments: - type: object - x-kubernetes-preserve-unknown-fields: true - name: - type: string - required: - type: boolean - type: object - type: array expose: description: Expose controls the default expose path configuration for Envoy. @@ -132,15 +107,15 @@ spec: with an external system. type: string localConnectTimeoutMs: - description: LocalConnectTimeoutMs is the number of milliseconds allowed - to make connections to the local application instance before timing - out. Defaults to 5000. + description: The number of milliseconds allowed to make connections + to the local application instance before timing out. Defaults to + 5000. type: integer localRequestTimeoutMs: - description: LocalRequestTimeoutMs is the timeout for HTTP requests - to the local application instance in milliseconds. Applies to HTTP-based - protocols only. If not specified, inherits the Envoy default for - route timeouts (15s). + description: In milliseconds, the timeout for HTTP requests to the + local application instance. Applies to HTTP-based protocols only. + If not specified, inherits the Envoy default for route timeouts + (15s). type: integer maxInboundConnections: description: MaxInboundConnections is the maximum number of concurrent @@ -167,18 +142,6 @@ spec: CRD and should be set using annotations on the services that are part of the mesh.' type: string - mutualTLSMode: - description: 'MutualTLSMode controls whether mutual TLS is required - for all incoming connections when transparent proxy is enabled. - This can be set to "permissive" or "strict". "strict" is the default - which requires mutual TLS for incoming connections. In the insecure - "permissive" mode, connections to the sidecar proxy public listener - port require mutual TLS, but connections to the service port do - not require mutual TLS and are proxied to the application unmodified. - Note: Intentions are not enforced for non-mTLS connections. To keep - your services secure, we recommend using "strict" mode whenever - possible and enabling "permissive" mode only when necessary.' - type: string protocol: description: Protocol sets the protocol of the service. This is used by Connect proxies for things like observability features and to @@ -266,15 +229,15 @@ spec: type: string type: object name: - description: Name is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides + description: Name is only accepted within a service-defaults config entry. type: string namespace: - description: Namespace is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides + description: Namespace is only accepted within a service-defaults config entry. type: string partition: - description: Partition is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides + description: Partition is only accepted within a service-defaults config entry. type: string passiveHealthCheck: @@ -313,10 +276,6 @@ spec: format: int32 type: integer type: object - peer: - description: Peer is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides - config entry. - type: string protocol: description: Protocol describes the upstream's service protocol. Valid values are "tcp", "http" and "grpc". Anything else @@ -383,15 +342,15 @@ spec: type: string type: object name: - description: Name is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides + description: Name is only accepted within a service-defaults config entry. type: string namespace: - description: Namespace is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides + description: Namespace is only accepted within a service-defaults config entry. type: string partition: - description: Partition is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides + description: Partition is only accepted within a service-defaults config entry. type: string passiveHealthCheck: @@ -432,10 +391,6 @@ spec: format: int32 type: integer type: object - peer: - description: Peer is only accepted within service ServiceDefaultsSpec.UpstreamConfig.Overrides - config entry. - type: string protocol: description: Protocol describes the upstream's service protocol. Valid values are "tcp", "http" and "grpc". Anything else diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml index a4efd6e958..a0cc7a6343 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -70,43 +67,6 @@ spec: have intentions defined. type: string type: object - jwt: - description: JWT specifies the configuration to validate a JSON Web - Token for all incoming requests. - properties: - providers: - description: Providers is a list of providers to consider when - verifying a JWT. - items: - properties: - name: - description: Name is the name of the JWT provider. There - MUST be a corresponding "jwt-provider" config entry with - this name. - type: string - verifyClaims: - description: VerifyClaims is a list of additional claims - to verify in a JWT's payload. - items: - properties: - path: - description: Path is the path to the claim in the - token JSON. - items: - type: string - type: array - value: - description: Value is the expected value at the given - path. If the type at the path is a list then we - verify that this value is contained in the list. - If the type at the path is a string then we verify - that this value matches. - type: string - type: object - type: array - type: object - type: array - type: object sources: description: Sources is the list of all intention sources and the authorization granted to those sources. The order of this list does @@ -135,7 +95,8 @@ spec: description: Partition is the Admin Partition for the Name parameter. type: string peer: - description: Peer is the peer name for the Name parameter. + description: '[Experimental] Peer is the peer name for the Name + parameter.' type: string permissions: description: Permissions is the list of all additional L7 attributes @@ -216,50 +177,8 @@ spec: match on the HTTP request path. type: string type: object - jwt: - description: JWT specifies configuration to validate a - JSON Web Token for incoming requests. - properties: - providers: - description: Providers is a list of providers to consider - when verifying a JWT. - items: - properties: - name: - description: Name is the name of the JWT provider. - There MUST be a corresponding "jwt-provider" - config entry with this name. - type: string - verifyClaims: - description: VerifyClaims is a list of additional - claims to verify in a JWT's payload. - items: - properties: - path: - description: Path is the path to the claim - in the token JSON. - items: - type: string - type: array - value: - description: Value is the expected value - at the given path. If the type at the - path is a list then we verify that this - value is contained in the list. If the - type at the path is a string then we - verify that this value matches. - type: string - type: object - type: array - type: object - type: array - type: object type: object type: array - samenessGroup: - description: SamenessGroup is the name of the sameness group, - if applicable. - type: string type: object type: array type: object diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml index 0146eca982..a84fc0bd88 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -75,26 +72,6 @@ spec: service from to form the failover group of instances. If empty the current namespace is used. type: string - policy: - description: Policy specifies the exact mechanism used for failover. - properties: - mode: - description: Mode specifies the type of failover that will - be performed. Valid values are "sequential", "" (equivalent - to "sequential") and "order-by-locality". - type: string - regions: - description: Regions is the ordered list of the regions - of the failover targets. Valid values can be "us-west-1", - "us-west-2", and so on. - items: - type: string - type: array - type: object - samenessGroup: - description: SamenessGroup is the name of the sameness group - to try during failover. - type: string service: description: Service is the service to resolve instead of the default as the failover group of instances during failover. @@ -223,16 +200,6 @@ spec: type: integer type: object type: object - prioritizeByLocality: - description: PrioritizeByLocality controls whether the locality of - services within the local partition will be used to prioritize connectivity. - properties: - mode: - description: 'Mode specifies the type of prioritization that will - be performed when selecting nodes in the local partition. Valid - values are: "" (default "none"), "none", and "failover".' - type: string - type: object redirect: description: Redirect when configured, all attempts to resolve the service this resolver defines will be substituted for the supplied @@ -258,10 +225,6 @@ spec: description: Peer is the name of the cluster peer to resolve the service from instead of the current one. type: string - samenessGroup: - description: SamenessGroup is the name of the sameness group to - resolve the service from instead of the current one. - type: string service: description: Service is a service to resolve instead of the current service. @@ -272,10 +235,6 @@ spec: If empty the default subset is used. type: string type: object - requestTimeout: - description: RequestTimeout is the timeout for receiving an HTTP response - from this service before the connection is terminated. - type: string subsets: additionalProperties: properties: diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml index 31f5ee2924..8b55692bd2 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml index aa2b592c94..df8bbbfbdf 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml index b465cd9494..8e6c449ef8 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/control-plane/config/crd/external/gatewayclasses.gateway.networking.k8s.io.yaml b/control-plane/config/crd/external/gatewayclasses.gateway.networking.k8s.io.yaml deleted file mode 100644 index 044c7af939..0000000000 --- a/control-plane/config/crd/external/gatewayclasses.gateway.networking.k8s.io.yaml +++ /dev/null @@ -1,323 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 - gateway.networking.k8s.io/bundle-version: v0.6.2 - gateway.networking.k8s.io/channel: experimental - creationTimestamp: null - name: gatewayclasses.gateway.networking.k8s.io -spec: - group: gateway.networking.k8s.io - names: - categories: - - gateway-api - kind: GatewayClass - listKind: GatewayClassList - plural: gatewayclasses - shortNames: - - gc - singular: gatewayclass - scope: Cluster - versions: - - additionalPrinterColumns: - - jsonPath: .spec.controllerName - name: Controller - type: string - - jsonPath: .status.conditions[?(@.type=="Accepted")].status - name: Accepted - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .spec.description - name: Description - priority: 1 - type: string - deprecated: true - deprecationWarning: The v1alpha2 version of GatewayClass has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1. - name: v1alpha2 - schema: - openAPIV3Schema: - description: "GatewayClass describes a class of Gateways available to the user for creating Gateway resources. \n It is recommended that this resource be used as a template for Gateways. This means that a Gateway is based on the state of the GatewayClass at the time it was created and changes to the GatewayClass or associated parameters are not propagated down to existing Gateways. This recommendation is intended to limit the blast radius of changes to GatewayClass or associated parameters. If implementations choose to propagate GatewayClass changes to existing Gateways, that MUST be clearly documented by the implementation. \n Whenever one or more Gateways are using a GatewayClass, implementations MUST add the `gateway-exists-finalizer.gateway.networking.k8s.io` finalizer on the associated GatewayClass. This ensures that a GatewayClass associated with a Gateway is not deleted while in use. \n GatewayClass is a Cluster level resource." - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of GatewayClass. - properties: - controllerName: - description: "ControllerName is the name of the controller that is managing Gateways of this class. The value of this field MUST be a domain prefixed path. \n Example: \"example.net/gateway-controller\". \n This field is not mutable and cannot be empty. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - description: - description: Description helps describe a GatewayClass with more details. - maxLength: 64 - type: string - parametersRef: - description: "ParametersRef is a reference to a resource that contains the configuration parameters corresponding to the GatewayClass. This is optional if the controller does not require any additional configuration. \n ParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap, or an implementation-specific custom resource. The resource can be cluster-scoped or namespace-scoped. \n If the referent cannot be found, the GatewayClass's \"InvalidParameters\" status condition will be true. \n Support: Implementation-specific" - properties: - group: - description: Group is the group of the referent. - 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. - 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 - namespace: - description: Namespace is the namespace of the referent. This field is required when referring to a Namespace-scoped resource and MUST be unset when referring to a Cluster-scoped resource. - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - group - - kind - - name - type: object - required: - - controllerName - type: object - status: - default: - conditions: - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: Waiting - status: Unknown - type: Accepted - description: Status defines the current state of GatewayClass. - properties: - conditions: - default: - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: Pending - status: Unknown - type: Accepted - description: "Conditions is the current status from the controller for this GatewayClass. \n Controllers should prefer to publish conditions using values of GatewayClassConditionType for the type of each Condition." - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - type: object - required: - - spec - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .spec.controllerName - name: Controller - type: string - - jsonPath: .status.conditions[?(@.type=="Accepted")].status - name: Accepted - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .spec.description - name: Description - priority: 1 - type: string - name: v1beta1 - schema: - openAPIV3Schema: - description: "GatewayClass describes a class of Gateways available to the user for creating Gateway resources. \n It is recommended that this resource be used as a template for Gateways. This means that a Gateway is based on the state of the GatewayClass at the time it was created and changes to the GatewayClass or associated parameters are not propagated down to existing Gateways. This recommendation is intended to limit the blast radius of changes to GatewayClass or associated parameters. If implementations choose to propagate GatewayClass changes to existing Gateways, that MUST be clearly documented by the implementation. \n Whenever one or more Gateways are using a GatewayClass, implementations MUST add the `gateway-exists-finalizer.gateway.networking.k8s.io` finalizer on the associated GatewayClass. This ensures that a GatewayClass associated with a Gateway is not deleted while in use. \n GatewayClass is a Cluster level resource." - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of GatewayClass. - properties: - controllerName: - description: "ControllerName is the name of the controller that is managing Gateways of this class. The value of this field MUST be a domain prefixed path. \n Example: \"example.net/gateway-controller\". \n This field is not mutable and cannot be empty. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - description: - description: Description helps describe a GatewayClass with more details. - maxLength: 64 - type: string - parametersRef: - description: "ParametersRef is a reference to a resource that contains the configuration parameters corresponding to the GatewayClass. This is optional if the controller does not require any additional configuration. \n ParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap, or an implementation-specific custom resource. The resource can be cluster-scoped or namespace-scoped. \n If the referent cannot be found, the GatewayClass's \"InvalidParameters\" status condition will be true. \n Support: Implementation-specific" - properties: - group: - description: Group is the group of the referent. - 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. - 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 - namespace: - description: Namespace is the namespace of the referent. This field is required when referring to a Namespace-scoped resource and MUST be unset when referring to a Cluster-scoped resource. - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - group - - kind - - name - type: object - required: - - controllerName - type: object - status: - default: - conditions: - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: Waiting - status: Unknown - type: Accepted - description: Status defines the current state of GatewayClass. - properties: - conditions: - default: - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: Pending - status: Unknown - type: Accepted - description: "Conditions is the current status from the controller for this GatewayClass. \n Controllers should prefer to publish conditions using values of GatewayClassConditionType for the type of each Condition." - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/external/gateways.gateway.networking.k8s.io.yaml b/control-plane/config/crd/external/gateways.gateway.networking.k8s.io.yaml deleted file mode 100644 index b7a7c8a7d1..0000000000 --- a/control-plane/config/crd/external/gateways.gateway.networking.k8s.io.yaml +++ /dev/null @@ -1,877 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 - gateway.networking.k8s.io/bundle-version: v0.6.2 - gateway.networking.k8s.io/channel: experimental - creationTimestamp: null - name: gateways.gateway.networking.k8s.io -spec: - group: gateway.networking.k8s.io - names: - categories: - - gateway-api - kind: Gateway - listKind: GatewayList - plural: gateways - shortNames: - - gtw - singular: gateway - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.gatewayClassName - name: Class - type: string - - jsonPath: .status.addresses[*].value - name: Address - type: string - - jsonPath: .status.conditions[?(@.type=="Programmed")].status - name: Programmed - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - deprecated: true - deprecationWarning: The v1alpha2 version of Gateway has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1. - name: v1alpha2 - schema: - openAPIV3Schema: - description: Gateway represents an instance of a service-traffic handling infrastructure by binding Listeners to a set of IP addresses. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of Gateway. - properties: - addresses: - description: "Addresses requested for this Gateway. This is optional and behavior can depend on the implementation. If a value is set in the spec and the requested address is invalid or unavailable, the implementation MUST indicate this in the associated entry in GatewayStatus.Addresses. \n The Addresses field represents a request for the address(es) on the \"outside of the Gateway\", that traffic bound for this Gateway will use. This could be the IP address or hostname of an external load balancer or other networking infrastructure, or some other address that traffic will be sent to. \n The .listener.hostname field is used to route traffic that has already arrived at the Gateway to the correct in-cluster destination. \n If no Addresses are specified, the implementation MAY schedule the Gateway in an implementation-specific manner, assigning an appropriate set of Addresses. \n The implementation MUST bind all Listeners to every GatewayAddress that it assigns to the Gateway and add a corresponding entry in GatewayStatus.Addresses. \n Support: Extended" - items: - description: GatewayAddress describes an address that can be bound to a Gateway. - properties: - type: - default: IPAddress - description: Type of the address. - maxLength: 253 - minLength: 1 - pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - value: - description: "Value of the address. The validity of the values will depend on the type and support by the controller. \n Examples: `1.2.3.4`, `128::1`, `my-ip-address`." - maxLength: 253 - minLength: 1 - type: string - required: - - value - type: object - maxItems: 16 - type: array - gatewayClassName: - description: GatewayClassName used for this Gateway. This is the name of a GatewayClass resource. - maxLength: 253 - minLength: 1 - type: string - listeners: - description: "Listeners associated with this Gateway. Listeners define logical endpoints that are bound on this Gateway's addresses. At least one Listener MUST be specified. \n Each listener in a Gateway must have a unique combination of Hostname, Port, and Protocol. \n An implementation MAY group Listeners by Port and then collapse each group of Listeners into a single Listener if the implementation determines that the Listeners in the group are \"compatible\". An implementation MAY also group together and collapse compatible Listeners belonging to different Gateways. \n For example, an implementation might consider Listeners to be compatible with each other if all of the following conditions are met: \n 1. Either each Listener within the group specifies the \"HTTP\" Protocol or each Listener within the group specifies either the \"HTTPS\" or \"TLS\" Protocol. \n 2. Each Listener within the group specifies a Hostname that is unique within the group. \n 3. As a special case, one Listener within a group may omit Hostname, in which case this Listener matches when no other Listener matches. \n If the implementation does collapse compatible Listeners, the hostname provided in the incoming client request MUST be matched to a Listener to find the correct set of Routes. The incoming hostname MUST be matched using the Hostname field for each Listener in order of most to least specific. That is, exact matches must be processed before wildcard matches. \n If this field specifies multiple Listeners that have the same Port value but are not compatible, the implementation must raise a \"Conflicted\" condition in the Listener status. \n Support: Core" - items: - description: Listener embodies the concept of a logical endpoint where a Gateway accepts network connections. - properties: - allowedRoutes: - default: - namespaces: - from: Same - description: "AllowedRoutes defines the types of routes that MAY be attached to a Listener and the trusted namespaces where those Route resources MAY be present. \n Although a client request may match multiple route rules, only one rule may ultimately receive the request. Matching precedence MUST be determined in order of the following criteria: \n * The most specific match as defined by the Route type. * The oldest Route based on creation timestamp. For example, a Route with a creation timestamp of \"2020-09-08 01:02:03\" is given precedence over a Route with a creation timestamp of \"2020-09-08 01:02:04\". * If everything else is equivalent, the Route appearing first in alphabetical order (namespace/name) should be given precedence. For example, foo/bar is given precedence over foo/baz. \n All valid rules within a Route attached to this Listener should be implemented. Invalid Route rules can be ignored (sometimes that will mean the full Route). If a Route rule transitions from valid to invalid, support for that Route rule should be dropped to ensure consistency. For example, even if a filter specified by a Route rule is invalid, the rest of the rules within that Route should still be supported. \n Support: Core" - properties: - kinds: - description: "Kinds specifies the groups and kinds of Routes that are allowed to bind to this Gateway Listener. When unspecified or empty, the kinds of Routes selected are determined using the Listener protocol. \n A RouteGroupKind MUST correspond to kinds of Routes that are compatible with the application protocol specified in the Listener's Protocol field. If an implementation does not support or recognize this resource type, it MUST set the \"ResolvedRefs\" condition to False for this Listener with the \"InvalidRouteKinds\" reason. \n Support: Core" - items: - description: RouteGroupKind indicates the group and kind of a Route resource. - properties: - group: - default: gateway.networking.k8s.io - description: Group is the group of the Route. - 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 the kind of the Route. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - required: - - kind - type: object - maxItems: 8 - type: array - namespaces: - default: - from: Same - description: "Namespaces indicates namespaces from which Routes may be attached to this Listener. This is restricted to the namespace of this Gateway by default. \n Support: Core" - properties: - from: - default: Same - description: "From indicates where Routes will be selected for this Gateway. Possible values are: * All: Routes in all namespaces may be used by this Gateway. * Selector: Routes in namespaces selected by the selector may be used by this Gateway. * Same: Only Routes in the same namespace may be used by this Gateway. \n Support: Core" - enum: - - All - - Selector - - Same - type: string - selector: - description: "Selector must be specified when From is set to \"Selector\". In that case, only Routes in Namespaces matching this Selector will be selected by this Gateway. This field is ignored for other values of \"From\". \n Support: Core" - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - type: object - type: object - hostname: - description: "Hostname specifies the virtual hostname to match for protocol types that define this concept. When unspecified, all hostnames are matched. This field is ignored for protocols that don't require hostname based matching. \n Implementations MUST apply Hostname matching appropriately for each of the following protocols: \n * TLS: The Listener Hostname MUST match the SNI. * HTTP: The Listener Hostname MUST match the Host header of the request. * HTTPS: The Listener Hostname SHOULD match at both the TLS and HTTP protocol layers as described above. If an implementation does not ensure that both the SNI and Host header match the Listener hostname, it MUST clearly document that. \n For HTTPRoute and TLSRoute resources, there is an interaction with the `spec.hostnames` array. When both listener and route specify hostnames, there MUST be an intersection between the values for a Route to be accepted. For more information, refer to the Route specific Hostnames documentation. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - name: - description: "Name is the name of the Listener. This name MUST be unique within a Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - port: - description: "Port is the network port. Multiple listeners may use the same port, subject to the Listener compatibility rules. \n Support: Core" - format: int32 - maximum: 65535 - minimum: 1 - type: integer - protocol: - description: "Protocol specifies the network protocol this listener expects to receive. \n Support: Core" - maxLength: 255 - minLength: 1 - pattern: ^[a-zA-Z0-9]([-a-zSA-Z0-9]*[a-zA-Z0-9])?$|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9]+$ - type: string - tls: - description: "TLS is the TLS configuration for the Listener. This field is required if the Protocol field is \"HTTPS\" or \"TLS\". It is invalid to set this field if the Protocol field is \"HTTP\", \"TCP\", or \"UDP\". \n The association of SNIs to Certificate defined in GatewayTLSConfig is defined based on the Hostname field for this listener. \n The GatewayClass MUST use the longest matching SNI out of all available certificates for any TLS handshake. \n Support: Core" - properties: - certificateRefs: - description: "CertificateRefs contains a series of references to Kubernetes objects that contains TLS certificates and private keys. These certificates are used to establish a TLS handshake for requests that match the hostname of the associated listener. \n A single CertificateRef to a Kubernetes Secret has \"Core\" support. Implementations MAY choose to support attaching multiple certificates to a Listener, but this behavior is implementation-specific. \n References to a resource in different namespace are invalid UNLESS there is a ReferenceGrant in the target namespace that allows the certificate to be attached. If a ReferenceGrant does not allow this reference, the \"ResolvedRefs\" condition MUST be set to False for this listener with the \"RefNotPermitted\" reason. \n This field is required to have at least one element when the mode is set to \"Terminate\" (default) and is optional otherwise. \n CertificateRefs can reference to standard Kubernetes resources, i.e. Secret, or implementation-specific custom resources. \n Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls \n Support: Implementation-specific (More than one reference or other resource types)" - items: - description: "SecretObjectReference identifies an API object including its namespace, defaulting to Secret. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. \n References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object." - properties: - group: - default: "" - 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: - default: Secret - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - name - type: object - maxItems: 64 - type: array - mode: - default: Terminate - description: "Mode defines the TLS behavior for the TLS session initiated by the client. There are two possible modes: \n - Terminate: The TLS session between the downstream client and the Gateway is terminated at the Gateway. This mode requires certificateRefs to be set and contain at least one element. - Passthrough: The TLS session is NOT terminated by the Gateway. This implies that the Gateway can't decipher the TLS stream except for the ClientHello message of the TLS protocol. CertificateRefs field is ignored in this mode. \n Support: Core" - enum: - - Terminate - - Passthrough - type: string - options: - additionalProperties: - description: AnnotationValue is the value of an annotation in Gateway API. This is used for validation of maps such as TLS options. This roughly matches Kubernetes annotation validation, although the length validation in that case is based on the entire size of the annotations struct. - maxLength: 4096 - minLength: 0 - type: string - description: "Options are a list of key/value pairs to enable extended TLS configuration for each implementation. For example, configuring the minimum TLS version or supported cipher suites. \n A set of common keys MAY be defined by the API in the future. To avoid any ambiguity, implementation-specific definitions MUST use domain-prefixed names, such as `example.com/my-custom-option`. Un-prefixed names are reserved for key names defined by Gateway API. \n Support: Implementation-specific" - maxProperties: 16 - type: object - type: object - required: - - name - - port - - protocol - type: object - maxItems: 64 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - required: - - gatewayClassName - - listeners - type: object - status: - default: - conditions: - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: NotReconciled - status: Unknown - type: Accepted - description: Status defines the current state of Gateway. - properties: - addresses: - description: Addresses lists the IP addresses that have actually been bound to the Gateway. These addresses may differ from the addresses in the Spec, e.g. if the Gateway automatically assigns an address from a reserved pool. - items: - description: GatewayAddress describes an address that can be bound to a Gateway. - properties: - type: - default: IPAddress - description: Type of the address. - maxLength: 253 - minLength: 1 - pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - value: - description: "Value of the address. The validity of the values will depend on the type and support by the controller. \n Examples: `1.2.3.4`, `128::1`, `my-ip-address`." - maxLength: 253 - minLength: 1 - type: string - required: - - value - type: object - maxItems: 16 - type: array - conditions: - default: - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: Pending - status: Unknown - type: Accepted - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: Pending - status: Unknown - type: Programmed - description: "Conditions describe the current conditions of the Gateway. \n Implementations should prefer to express Gateway conditions using the `GatewayConditionType` and `GatewayConditionReason` constants so that operators and tools can converge on a common vocabulary to describe Gateway state. \n Known condition types are: \n * \"Accepted\" * \"Ready\"" - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - listeners: - description: Listeners provide status for each unique listener port defined in the Spec. - items: - description: ListenerStatus is the status associated with a Listener. - properties: - attachedRoutes: - description: AttachedRoutes represents the total number of Routes that have been successfully attached to this Listener. - format: int32 - type: integer - conditions: - description: Conditions describe the current condition of this listener. - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - name: - description: Name is the name of the Listener that this status corresponds to. - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - supportedKinds: - description: "SupportedKinds is the list indicating the Kinds supported by this listener. This MUST represent the kinds an implementation supports for that Listener configuration. \n If kinds are specified in Spec that are not supported, they MUST NOT appear in this list and an implementation MUST set the \"ResolvedRefs\" condition to \"False\" with the \"InvalidRouteKinds\" reason. If both valid and invalid Route kinds are specified, the implementation MUST reference the valid Route kinds that have been specified." - items: - description: RouteGroupKind indicates the group and kind of a Route resource. - properties: - group: - default: gateway.networking.k8s.io - description: Group is the group of the Route. - 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 the kind of the Route. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - required: - - kind - type: object - maxItems: 8 - type: array - required: - - attachedRoutes - - conditions - - name - - supportedKinds - type: object - maxItems: 64 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - required: - - spec - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .spec.gatewayClassName - name: Class - type: string - - jsonPath: .status.addresses[*].value - name: Address - type: string - - jsonPath: .status.conditions[?(@.type=="Programmed")].status - name: Programmed - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: Gateway represents an instance of a service-traffic handling infrastructure by binding Listeners to a set of IP addresses. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of Gateway. - properties: - addresses: - description: "Addresses requested for this Gateway. This is optional and behavior can depend on the implementation. If a value is set in the spec and the requested address is invalid or unavailable, the implementation MUST indicate this in the associated entry in GatewayStatus.Addresses. \n The Addresses field represents a request for the address(es) on the \"outside of the Gateway\", that traffic bound for this Gateway will use. This could be the IP address or hostname of an external load balancer or other networking infrastructure, or some other address that traffic will be sent to. \n The .listener.hostname field is used to route traffic that has already arrived at the Gateway to the correct in-cluster destination. \n If no Addresses are specified, the implementation MAY schedule the Gateway in an implementation-specific manner, assigning an appropriate set of Addresses. \n The implementation MUST bind all Listeners to every GatewayAddress that it assigns to the Gateway and add a corresponding entry in GatewayStatus.Addresses. \n Support: Extended" - items: - description: GatewayAddress describes an address that can be bound to a Gateway. - properties: - type: - default: IPAddress - description: Type of the address. - maxLength: 253 - minLength: 1 - pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - value: - description: "Value of the address. The validity of the values will depend on the type and support by the controller. \n Examples: `1.2.3.4`, `128::1`, `my-ip-address`." - maxLength: 253 - minLength: 1 - type: string - required: - - value - type: object - maxItems: 16 - type: array - gatewayClassName: - description: GatewayClassName used for this Gateway. This is the name of a GatewayClass resource. - maxLength: 253 - minLength: 1 - type: string - listeners: - description: "Listeners associated with this Gateway. Listeners define logical endpoints that are bound on this Gateway's addresses. At least one Listener MUST be specified. \n Each listener in a Gateway must have a unique combination of Hostname, Port, and Protocol. \n An implementation MAY group Listeners by Port and then collapse each group of Listeners into a single Listener if the implementation determines that the Listeners in the group are \"compatible\". An implementation MAY also group together and collapse compatible Listeners belonging to different Gateways. \n For example, an implementation might consider Listeners to be compatible with each other if all of the following conditions are met: \n 1. Either each Listener within the group specifies the \"HTTP\" Protocol or each Listener within the group specifies either the \"HTTPS\" or \"TLS\" Protocol. \n 2. Each Listener within the group specifies a Hostname that is unique within the group. \n 3. As a special case, one Listener within a group may omit Hostname, in which case this Listener matches when no other Listener matches. \n If the implementation does collapse compatible Listeners, the hostname provided in the incoming client request MUST be matched to a Listener to find the correct set of Routes. The incoming hostname MUST be matched using the Hostname field for each Listener in order of most to least specific. That is, exact matches must be processed before wildcard matches. \n If this field specifies multiple Listeners that have the same Port value but are not compatible, the implementation must raise a \"Conflicted\" condition in the Listener status. \n Support: Core" - items: - description: Listener embodies the concept of a logical endpoint where a Gateway accepts network connections. - properties: - allowedRoutes: - default: - namespaces: - from: Same - description: "AllowedRoutes defines the types of routes that MAY be attached to a Listener and the trusted namespaces where those Route resources MAY be present. \n Although a client request may match multiple route rules, only one rule may ultimately receive the request. Matching precedence MUST be determined in order of the following criteria: \n * The most specific match as defined by the Route type. * The oldest Route based on creation timestamp. For example, a Route with a creation timestamp of \"2020-09-08 01:02:03\" is given precedence over a Route with a creation timestamp of \"2020-09-08 01:02:04\". * If everything else is equivalent, the Route appearing first in alphabetical order (namespace/name) should be given precedence. For example, foo/bar is given precedence over foo/baz. \n All valid rules within a Route attached to this Listener should be implemented. Invalid Route rules can be ignored (sometimes that will mean the full Route). If a Route rule transitions from valid to invalid, support for that Route rule should be dropped to ensure consistency. For example, even if a filter specified by a Route rule is invalid, the rest of the rules within that Route should still be supported. \n Support: Core" - properties: - kinds: - description: "Kinds specifies the groups and kinds of Routes that are allowed to bind to this Gateway Listener. When unspecified or empty, the kinds of Routes selected are determined using the Listener protocol. \n A RouteGroupKind MUST correspond to kinds of Routes that are compatible with the application protocol specified in the Listener's Protocol field. If an implementation does not support or recognize this resource type, it MUST set the \"ResolvedRefs\" condition to False for this Listener with the \"InvalidRouteKinds\" reason. \n Support: Core" - items: - description: RouteGroupKind indicates the group and kind of a Route resource. - properties: - group: - default: gateway.networking.k8s.io - description: Group is the group of the Route. - 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 the kind of the Route. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - required: - - kind - type: object - maxItems: 8 - type: array - namespaces: - default: - from: Same - description: "Namespaces indicates namespaces from which Routes may be attached to this Listener. This is restricted to the namespace of this Gateway by default. \n Support: Core" - properties: - from: - default: Same - description: "From indicates where Routes will be selected for this Gateway. Possible values are: * All: Routes in all namespaces may be used by this Gateway. * Selector: Routes in namespaces selected by the selector may be used by this Gateway. * Same: Only Routes in the same namespace may be used by this Gateway. \n Support: Core" - enum: - - All - - Selector - - Same - type: string - selector: - description: "Selector must be specified when From is set to \"Selector\". In that case, only Routes in Namespaces matching this Selector will be selected by this Gateway. This field is ignored for other values of \"From\". \n Support: Core" - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - type: object - type: object - hostname: - description: "Hostname specifies the virtual hostname to match for protocol types that define this concept. When unspecified, all hostnames are matched. This field is ignored for protocols that don't require hostname based matching. \n Implementations MUST apply Hostname matching appropriately for each of the following protocols: \n * TLS: The Listener Hostname MUST match the SNI. * HTTP: The Listener Hostname MUST match the Host header of the request. * HTTPS: The Listener Hostname SHOULD match at both the TLS and HTTP protocol layers as described above. If an implementation does not ensure that both the SNI and Host header match the Listener hostname, it MUST clearly document that. \n For HTTPRoute and TLSRoute resources, there is an interaction with the `spec.hostnames` array. When both listener and route specify hostnames, there MUST be an intersection between the values for a Route to be accepted. For more information, refer to the Route specific Hostnames documentation. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - name: - description: "Name is the name of the Listener. This name MUST be unique within a Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - port: - description: "Port is the network port. Multiple listeners may use the same port, subject to the Listener compatibility rules. \n Support: Core" - format: int32 - maximum: 65535 - minimum: 1 - type: integer - protocol: - description: "Protocol specifies the network protocol this listener expects to receive. \n Support: Core" - maxLength: 255 - minLength: 1 - pattern: ^[a-zA-Z0-9]([-a-zSA-Z0-9]*[a-zA-Z0-9])?$|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9]+$ - type: string - tls: - description: "TLS is the TLS configuration for the Listener. This field is required if the Protocol field is \"HTTPS\" or \"TLS\". It is invalid to set this field if the Protocol field is \"HTTP\", \"TCP\", or \"UDP\". \n The association of SNIs to Certificate defined in GatewayTLSConfig is defined based on the Hostname field for this listener. \n The GatewayClass MUST use the longest matching SNI out of all available certificates for any TLS handshake. \n Support: Core" - properties: - certificateRefs: - description: "CertificateRefs contains a series of references to Kubernetes objects that contains TLS certificates and private keys. These certificates are used to establish a TLS handshake for requests that match the hostname of the associated listener. \n A single CertificateRef to a Kubernetes Secret has \"Core\" support. Implementations MAY choose to support attaching multiple certificates to a Listener, but this behavior is implementation-specific. \n References to a resource in different namespace are invalid UNLESS there is a ReferenceGrant in the target namespace that allows the certificate to be attached. If a ReferenceGrant does not allow this reference, the \"ResolvedRefs\" condition MUST be set to False for this listener with the \"RefNotPermitted\" reason. \n This field is required to have at least one element when the mode is set to \"Terminate\" (default) and is optional otherwise. \n CertificateRefs can reference to standard Kubernetes resources, i.e. Secret, or implementation-specific custom resources. \n Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls \n Support: Implementation-specific (More than one reference or other resource types)" - items: - description: "SecretObjectReference identifies an API object including its namespace, defaulting to Secret. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. \n References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object." - properties: - group: - default: "" - 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: - default: Secret - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - name - type: object - maxItems: 64 - type: array - mode: - default: Terminate - description: "Mode defines the TLS behavior for the TLS session initiated by the client. There are two possible modes: \n - Terminate: The TLS session between the downstream client and the Gateway is terminated at the Gateway. This mode requires certificateRefs to be set and contain at least one element. - Passthrough: The TLS session is NOT terminated by the Gateway. This implies that the Gateway can't decipher the TLS stream except for the ClientHello message of the TLS protocol. CertificateRefs field is ignored in this mode. \n Support: Core" - enum: - - Terminate - - Passthrough - type: string - options: - additionalProperties: - description: AnnotationValue is the value of an annotation in Gateway API. This is used for validation of maps such as TLS options. This roughly matches Kubernetes annotation validation, although the length validation in that case is based on the entire size of the annotations struct. - maxLength: 4096 - minLength: 0 - type: string - description: "Options are a list of key/value pairs to enable extended TLS configuration for each implementation. For example, configuring the minimum TLS version or supported cipher suites. \n A set of common keys MAY be defined by the API in the future. To avoid any ambiguity, implementation-specific definitions MUST use domain-prefixed names, such as `example.com/my-custom-option`. Un-prefixed names are reserved for key names defined by Gateway API. \n Support: Implementation-specific" - maxProperties: 16 - type: object - type: object - required: - - name - - port - - protocol - type: object - maxItems: 64 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - required: - - gatewayClassName - - listeners - type: object - status: - default: - conditions: - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: NotReconciled - status: Unknown - type: Accepted - description: Status defines the current state of Gateway. - properties: - addresses: - description: Addresses lists the IP addresses that have actually been bound to the Gateway. These addresses may differ from the addresses in the Spec, e.g. if the Gateway automatically assigns an address from a reserved pool. - items: - description: GatewayAddress describes an address that can be bound to a Gateway. - properties: - type: - default: IPAddress - description: Type of the address. - maxLength: 253 - minLength: 1 - pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - value: - description: "Value of the address. The validity of the values will depend on the type and support by the controller. \n Examples: `1.2.3.4`, `128::1`, `my-ip-address`." - maxLength: 253 - minLength: 1 - type: string - required: - - value - type: object - maxItems: 16 - type: array - conditions: - default: - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: Pending - status: Unknown - type: Accepted - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: Pending - status: Unknown - type: Programmed - description: "Conditions describe the current conditions of the Gateway. \n Implementations should prefer to express Gateway conditions using the `GatewayConditionType` and `GatewayConditionReason` constants so that operators and tools can converge on a common vocabulary to describe Gateway state. \n Known condition types are: \n * \"Accepted\" * \"Ready\"" - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - listeners: - description: Listeners provide status for each unique listener port defined in the Spec. - items: - description: ListenerStatus is the status associated with a Listener. - properties: - attachedRoutes: - description: AttachedRoutes represents the total number of Routes that have been successfully attached to this Listener. - format: int32 - type: integer - conditions: - description: Conditions describe the current condition of this listener. - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - name: - description: Name is the name of the Listener that this status corresponds to. - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - supportedKinds: - description: "SupportedKinds is the list indicating the Kinds supported by this listener. This MUST represent the kinds an implementation supports for that Listener configuration. \n If kinds are specified in Spec that are not supported, they MUST NOT appear in this list and an implementation MUST set the \"ResolvedRefs\" condition to \"False\" with the \"InvalidRouteKinds\" reason. If both valid and invalid Route kinds are specified, the implementation MUST reference the valid Route kinds that have been specified." - items: - description: RouteGroupKind indicates the group and kind of a Route resource. - properties: - group: - default: gateway.networking.k8s.io - description: Group is the group of the Route. - 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 the kind of the Route. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - required: - - kind - type: object - maxItems: 8 - type: array - required: - - attachedRoutes - - conditions - - name - - supportedKinds - type: object - maxItems: 64 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/external/grpcroutes.gateway.networking.k8s.io.yaml b/control-plane/config/crd/external/grpcroutes.gateway.networking.k8s.io.yaml deleted file mode 100644 index 8f3ab6d385..0000000000 --- a/control-plane/config/crd/external/grpcroutes.gateway.networking.k8s.io.yaml +++ /dev/null @@ -1,761 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 - gateway.networking.k8s.io/bundle-version: v0.6.2 - gateway.networking.k8s.io/channel: experimental - creationTimestamp: null - name: grpcroutes.gateway.networking.k8s.io -spec: - group: gateway.networking.k8s.io - names: - categories: - - gateway-api - kind: GRPCRoute - listKind: GRPCRouteList - plural: grpcroutes - singular: grpcroute - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.hostnames - name: Hostnames - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha2 - schema: - openAPIV3Schema: - description: "GRPCRoute provides a way to route gRPC requests. This includes the capability to match requests by hostname, gRPC service, gRPC method, or HTTP/2 header. Filters can be used to specify additional processing steps. Backends specify where matching requests will be routed. \n GRPCRoute falls under extended support within the Gateway API. Within the following specification, the word \"MUST\" indicates that an implementation supporting GRPCRoute must conform to the indicated requirement, but an implementation not supporting this route type need not follow the requirement unless explicitly indicated. \n Implementations supporting `GRPCRoute` with the `HTTPS` `ProtocolType` MUST accept HTTP/2 connections without an initial upgrade from HTTP/1.1, i.e. via ALPN. If the implementation does not support this, then it MUST set the \"Accepted\" condition to \"False\" for the affected listener with a reason of \"UnsupportedProtocol\". Implementations MAY also accept HTTP/2 connections with an upgrade from HTTP/1. \n Implementations supporting `GRPCRoute` with the `HTTP` `ProtocolType` MUST support HTTP/2 over cleartext TCP (h2c, https://www.rfc-editor.org/rfc/rfc7540#section-3.1) without an initial upgrade from HTTP/1.1, i.e. with prior knowledge (https://www.rfc-editor.org/rfc/rfc7540#section-3.4). If the implementation does not support this, then it MUST set the \"Accepted\" condition to \"False\" for the affected listener with a reason of \"UnsupportedProtocol\". Implementations MAY also accept HTTP/2 connections with an upgrade from HTTP/1, i.e. without prior knowledge. \n Support: Extended" - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of GRPCRoute. - properties: - hostnames: - description: "Hostnames defines a set of hostnames to match against the GRPC Host header to select a GRPCRoute to process the request. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label MUST appear by itself as the first label. \n If a hostname is specified by both the Listener and GRPCRoute, there MUST be at least one intersecting hostname for the GRPCRoute to be attached to the Listener. For example: \n * A Listener with `test.example.com` as the hostname matches GRPCRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches GRPCRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `test.example.com` and `*.example.com` would both match. On the other hand, `example.com` and `test.example.net` would not match. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n If both the Listener and GRPCRoute have specified hostnames, any GRPCRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the GRPCRoute specified `test.example.com` and `test.example.net`, `test.example.net` MUST NOT be considered for a match. \n If both the Listener and GRPCRoute have specified hostnames, and none match with the criteria above, then the GRPCRoute MUST NOT be accepted by the implementation. The implementation MUST raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. \n If a Route (A) of type HTTPRoute or GRPCRoute is attached to a Listener and that listener already has another Route (B) of the other type attached and the intersection of the hostnames of A and B is non-empty, then the implementation MUST accept exactly one of these two routes, determined by the following criteria, in order: \n * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n The rejected Route MUST raise an 'Accepted' condition with a status of 'False' in the corresponding RouteParentStatus. \n Support: Core" - items: - description: "Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n Hostname can be \"precise\" which is a domain name without the terminating dot of a network host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). \n Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed." - maxLength: 253 - minLength: 1 - pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - maxItems: 16 - type: array - parentRefs: - description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." - items: - description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - 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. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - maxItems: 32 - type: array - rules: - default: - - matches: - - method: - type: Exact - description: Rules are a list of GRPC matchers, filters and actions. - items: - description: GRPCRouteRule defines the semantics for matching an gRPC request based on conditions (matches), processing it (filters), and forwarding the request to an API object (backendRefs). - properties: - backendRefs: - description: "BackendRefs defines the backend(s) where matching requests should be sent. \n Failure behavior here depends on how many BackendRefs are specified and how many are invalid. \n If *all* entries in BackendRefs are invalid, and there are also no filters specified in this route rule, *all* traffic which matches this rule MUST receive an `UNAVAILABLE` status. \n See the GRPCBackendRef definition for the rules about what makes a single GRPCBackendRef invalid. \n When a GRPCBackendRef is invalid, `UNAVAILABLE` statuses MUST be returned for requests that would have otherwise been routed to an invalid backend. If multiple backends are specified, and some are invalid, the proportion of requests that would otherwise have been routed to an invalid backend MUST receive an `UNAVAILABLE` status. \n For example, if two backends are specified with equal weights, and one is invalid, 50 percent of traffic MUST receive an `UNAVAILABLE` status. Implementations may choose how that 50 percent is determined. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Core" - items: - description: GRPCBackendRef defines how a GRPCRoute forwards a gRPC request. - properties: - filters: - description: "Filters defined at this level MUST be executed if and only if the request is being forwarded to the backend defined here. \n Support: Implementation-specific (For broader support of filters, use the Filters field in GRPCRouteRule.)" - items: - description: GRPCRouteFilter defines processing steps that must be completed during the request or response lifecycle. GRPCRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. - properties: - extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" - 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 - requestHeaderModifier: - description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" - properties: - add: - description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" - items: - type: string - maxItems: 16 - type: array - set: - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - requestMirror: - description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" - properties: - backendRef: - description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" - properties: - group: - default: "" - 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: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name - type: object - required: - - backendRef - type: object - responseHeaderModifier: - description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " - properties: - add: - description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" - items: - type: string - maxItems: 16 - type: array - set: - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: - description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations supporting GRPCRoute MUST support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` MUST be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n " - enum: - - ResponseHeaderModifier - - RequestHeaderModifier - - RequestMirror - - ExtensionRef - type: string - required: - - type - type: object - maxItems: 16 - type: array - group: - default: "" - 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: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - weight: - default: 1 - description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support for this field varies based on the context where used." - format: int32 - maximum: 1000000 - minimum: 0 - type: integer - required: - - name - type: object - maxItems: 16 - type: array - filters: - description: "Filters define the filters that are applied to requests that match this rule. \n The effects of ordering of multiple behaviors are currently unspecified. This can change in the future based on feedback during the alpha stage. \n Conformance-levels at this level are defined based on the type of filter: \n - ALL core filters MUST be supported by all implementations that support GRPCRoute. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. \n Specifying a core filter multiple times has unspecified or implementation-specific conformance. Support: Core" - items: - description: GRPCRouteFilter defines processing steps that must be completed during the request or response lifecycle. GRPCRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. - properties: - extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" - 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 - requestHeaderModifier: - description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" - properties: - add: - description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" - items: - type: string - maxItems: 16 - type: array - set: - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - requestMirror: - description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" - properties: - backendRef: - description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" - properties: - group: - default: "" - 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: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name - type: object - required: - - backendRef - type: object - responseHeaderModifier: - description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " - properties: - add: - description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" - items: - type: string - maxItems: 16 - type: array - set: - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: - description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations supporting GRPCRoute MUST support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` MUST be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n " - enum: - - ResponseHeaderModifier - - RequestHeaderModifier - - RequestMirror - - ExtensionRef - type: string - required: - - type - type: object - maxItems: 16 - type: array - matches: - default: - - method: - type: Exact - description: "Matches define conditions used for matching the rule against incoming gRPC requests. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied. \n For example, take the following matches configuration: \n ``` matches: - method: service: foo.bar headers: values: version: 2 - method: service: foo.bar.v2 ``` \n For a request to match against this rule, it MUST satisfy EITHER of the two conditions: \n - service of foo.bar AND contains the header `version: 2` - service of foo.bar.v2 \n See the documentation for GRPCRouteMatch on how to specify multiple match conditions to be ANDed together. \n If no matches are specified, the implementation MUST match every gRPC request. \n Proxy or Load Balancer routing configuration generated from GRPCRoutes MUST prioritize rules based on the following criteria, continuing on ties. Merging MUST not be done between GRPCRoutes and HTTPRoutes. Precedence MUST be given to the rule with the largest number of: \n * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. * Characters in a matching service. * Characters in a matching method. * Header matches. \n If ties still exist across multiple Routes, matching precedence MUST be determined in order of the following criteria, continuing on ties: \n * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n If ties still exist within the Route that has been given precedence, matching precedence MUST be granted to the first matching rule meeting the above criteria." - items: - description: "GRPCRouteMatch defines the predicate used to match requests to a given action. Multiple match types are ANDed together, i.e. the match will evaluate to true only if all conditions are satisfied. \n For example, the match below will match a gRPC request only if its service is `foo` AND it contains the `version: v1` header: \n ``` matches: - method: type: Exact service: \"foo\" headers: - name: \"version\" value \"v1\" \n ```" - properties: - headers: - description: Headers specifies gRPC request header matchers. Multiple match values are ANDed together, meaning, a request MUST match all the specified headers to select the route. - items: - description: GRPCHeaderMatch describes how to select a gRPC route by matching gRPC request headers. - properties: - name: - description: "Name is the name of the gRPC Header to be matched. \n If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: Type specifies how to match against the value of the header. - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of the gRPC Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - method: - default: - type: Exact - description: Method specifies a gRPC request service/method matcher. If this field is not specified, all services and methods will match. - properties: - method: - description: "Value of the method to match against. If left empty or omitted, will match all services. \n At least one of Service and Method MUST be a non-empty string. \n A GRPC Method must be a valid Protobuf Method (https://protobuf.com/docs/language-spec#methods)." - maxLength: 1024 - pattern: ^[A-Za-z_][A-Za-z_0-9]*$ - type: string - service: - description: "Value of the service to match against. If left empty or omitted, will match any service. \n At least one of Service and Method MUST be a non-empty string. \n A GRPC Service must be a valid Protobuf Type Name (https://protobuf.com/docs/language-spec#type-references)." - maxLength: 1024 - pattern: ^(?i)\.?[a-z_][a-z_0-9]*(\.[a-z_][a-z_0-9]*)*$ - type: string - type: - default: Exact - description: "Type specifies how to match against the service and/or method. Support: Core (Exact with service and method specified) \n Support: Implementation-specific (Exact with method specified but no service specified) \n Support: Implementation-specific (RegularExpression)" - enum: - - Exact - - RegularExpression - type: string - type: object - type: object - maxItems: 8 - type: array - type: object - maxItems: 16 - type: array - type: object - status: - description: Status defines the current state of GRPCRoute. - properties: - parents: - description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." - items: - description: RouteParentStatus describes the status of a route with respect to an associated Parent. - properties: - conditions: - description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - controllerName: - description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/gateway-controller\". \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - parentRef: - description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - 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. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - required: - - controllerName - - parentRef - type: object - maxItems: 32 - type: array - required: - - parents - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/external/httproutes.gateway.networking.k8s.io.yaml b/control-plane/config/crd/external/httproutes.gateway.networking.k8s.io.yaml deleted file mode 100644 index b455d788de..0000000000 --- a/control-plane/config/crd/external/httproutes.gateway.networking.k8s.io.yaml +++ /dev/null @@ -1,1909 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 - gateway.networking.k8s.io/bundle-version: v0.6.2 - gateway.networking.k8s.io/channel: experimental - creationTimestamp: null - name: httproutes.gateway.networking.k8s.io -spec: - group: gateway.networking.k8s.io - names: - categories: - - gateway-api - kind: HTTPRoute - listKind: HTTPRouteList - plural: httproutes - singular: httproute - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.hostnames - name: Hostnames - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - deprecated: true - deprecationWarning: The v1alpha2 version of HTTPRoute has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1. - name: v1alpha2 - schema: - openAPIV3Schema: - description: HTTPRoute provides a way to route HTTP requests. This includes the capability to match requests by hostname, path, header, or query param. Filters can be used to specify additional processing steps. Backends specify where matching requests should be routed. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of HTTPRoute. - properties: - hostnames: - description: "Hostnames defines a set of hostname that should match against the HTTP Host header to select a HTTPRoute to process the request. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n If a hostname is specified by both the Listener and HTTPRoute, there must be at least one intersecting hostname for the HTTPRoute to be attached to the Listener. For example: \n * A Listener with `test.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `*.example.com`, `test.example.com`, and `foo.test.example.com` would all match. On the other hand, `example.com` and `test.example.net` would not match. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n If both the Listener and HTTPRoute have specified hostnames, any HTTPRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the HTTPRoute specified `test.example.com` and `test.example.net`, `test.example.net` must not be considered for a match. \n If both the Listener and HTTPRoute have specified hostnames, and none match with the criteria above, then the HTTPRoute is not accepted. The implementation must raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. \n In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. overlapping wildcard matching and exact matching hostnames), precedence must be given to rules from the HTTPRoute with the largest number of: \n * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. \n If ties exist across multiple Routes, the matching precedence rules for HTTPRouteMatches takes over. \n Support: Core" - items: - description: "Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n Hostname can be \"precise\" which is a domain name without the terminating dot of a network host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). \n Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed." - maxLength: 253 - minLength: 1 - pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - maxItems: 16 - type: array - parentRefs: - description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." - items: - description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - 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. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - maxItems: 32 - type: array - rules: - default: - - matches: - - path: - type: PathPrefix - value: / - description: Rules are a list of HTTP matchers, filters and actions. - items: - description: HTTPRouteRule defines semantics for matching an HTTP request based on conditions (matches), processing it (filters), and forwarding the request to an API object (backendRefs). - properties: - backendRefs: - description: "BackendRefs defines the backend(s) where matching requests should be sent. \n Failure behavior here depends on how many BackendRefs are specified and how many are invalid. \n If *all* entries in BackendRefs are invalid, and there are also no filters specified in this route rule, *all* traffic which matches this rule MUST receive a 500 status code. \n See the HTTPBackendRef definition for the rules about what makes a single HTTPBackendRef invalid. \n When a HTTPBackendRef is invalid, 500 status codes MUST be returned for requests that would have otherwise been routed to an invalid backend. If multiple backends are specified, and some are invalid, the proportion of requests that would otherwise have been routed to an invalid backend MUST receive a 500 status code. \n For example, if two backends are specified with equal weights, and one is invalid, 50 percent of traffic must receive a 500. Implementations may choose how that 50 percent is determined. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Core" - items: - description: HTTPBackendRef defines how a HTTPRoute should forward an HTTP request. - properties: - filters: - description: "Filters defined at this level should be executed if and only if the request is being forwarded to the backend defined here. \n Support: Implementation-specific (For broader support of filters, use the Filters field in HTTPRouteRule.)" - items: - description: HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. - properties: - extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" - 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 - requestHeaderModifier: - description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" - properties: - add: - description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" - items: - type: string - maxItems: 16 - type: array - set: - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - requestMirror: - description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" - properties: - backendRef: - description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" - properties: - group: - default: "" - 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: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name - type: object - required: - - backendRef - type: object - requestRedirect: - description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n Support: Core" - properties: - hostname: - description: "Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname of the request is used. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - path: - description: "Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. \n Support: Extended \n " - properties: - replaceFullPath: - description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " - maxLength: 1024 - type: string - replacePrefixMatch: - description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " - maxLength: 1024 - type: string - type: - description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - port: - description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n Support: Extended" - format: int32 - maximum: 65535 - minimum: 1 - type: integer - scheme: - description: "Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" - enum: - - http - - https - type: string - statusCode: - default: 302 - description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" - enum: - - 301 - - 302 - type: integer - type: object - responseHeaderModifier: - description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " - properties: - add: - description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" - items: - type: string - maxItems: 16 - type: array - set: - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: - description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - RequestHeaderModifier - - ResponseHeaderModifier - - RequestMirror - - RequestRedirect - - URLRewrite - - ExtensionRef - type: string - urlRewrite: - description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " - properties: - hostname: - description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - path: - description: "Path defines a path rewrite. \n Support: Extended \n " - properties: - replaceFullPath: - description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " - maxLength: 1024 - type: string - replacePrefixMatch: - description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " - maxLength: 1024 - type: string - type: - description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - type: object - required: - - type - type: object - maxItems: 16 - type: array - group: - default: "" - 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: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - weight: - default: 1 - description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support for this field varies based on the context where used." - format: int32 - maximum: 1000000 - minimum: 0 - type: integer - required: - - name - type: object - maxItems: 16 - type: array - filters: - description: "Filters define the filters that are applied to requests that match this rule. \n The effects of ordering of multiple behaviors are currently unspecified. This can change in the future based on feedback during the alpha stage. \n Conformance-levels at this level are defined based on the type of filter: \n - ALL core filters MUST be supported by all implementations. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. \n Specifying a core filter multiple times has unspecified or implementation-specific conformance. \n All filters are expected to be compatible with each other except for the URLRewrite and RequestRedirect filters, which may not be combined. If an implementation can not support other combinations of filters, they must clearly document that limitation. In all cases where incompatible or unsupported filters are specified, implementations MUST add a warning condition to status. \n Support: Core" - items: - description: HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. - properties: - extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" - 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 - requestHeaderModifier: - description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" - properties: - add: - description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" - items: - type: string - maxItems: 16 - type: array - set: - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - requestMirror: - description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" - properties: - backendRef: - description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" - properties: - group: - default: "" - 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: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name - type: object - required: - - backendRef - type: object - requestRedirect: - description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n Support: Core" - properties: - hostname: - description: "Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname of the request is used. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - path: - description: "Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. \n Support: Extended \n " - properties: - replaceFullPath: - description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " - maxLength: 1024 - type: string - replacePrefixMatch: - description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " - maxLength: 1024 - type: string - type: - description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - port: - description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n Support: Extended" - format: int32 - maximum: 65535 - minimum: 1 - type: integer - scheme: - description: "Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" - enum: - - http - - https - type: string - statusCode: - default: 302 - description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" - enum: - - 301 - - 302 - type: integer - type: object - responseHeaderModifier: - description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " - properties: - add: - description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" - items: - type: string - maxItems: 16 - type: array - set: - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: - description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - RequestHeaderModifier - - ResponseHeaderModifier - - RequestMirror - - RequestRedirect - - URLRewrite - - ExtensionRef - type: string - urlRewrite: - description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " - properties: - hostname: - description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - path: - description: "Path defines a path rewrite. \n Support: Extended \n " - properties: - replaceFullPath: - description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " - maxLength: 1024 - type: string - replacePrefixMatch: - description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " - maxLength: 1024 - type: string - type: - description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - type: object - required: - - type - type: object - maxItems: 16 - type: array - matches: - default: - - path: - type: PathPrefix - value: / - description: "Matches define conditions used for matching the rule against incoming HTTP requests. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied. \n For example, take the following matches configuration: \n ``` matches: - path: value: \"/foo\" headers: - name: \"version\" value: \"v2\" - path: value: \"/v2/foo\" ``` \n For a request to match against this rule, a request must satisfy EITHER of the two conditions: \n - path prefixed with `/foo` AND contains the header `version: v2` - path prefix of `/v2/foo` \n See the documentation for HTTPRouteMatch on how to specify multiple match conditions that should be ANDed together. \n If no matches are specified, the default is a prefix path match on \"/\", which has the effect of matching every HTTP request. \n Proxy or Load Balancer routing configuration generated from HTTPRoutes MUST prioritize matches based on the following criteria, continuing on ties. Across all rules specified on applicable Routes, precedence must be given to the match with the largest number of: \n * Characters in a matching path. * Header matches. * Query param matches. \n If ties still exist across multiple Routes, matching precedence MUST be determined in order of the following criteria, continuing on ties: \n * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n If ties still exist within an HTTPRoute, matching precedence MUST be granted to the FIRST matching rule (in list order) with a match meeting the above criteria. \n When no rules matching a request have been successfully attached to the parent a request is coming from, a HTTP 404 status code MUST be returned." - items: - description: "HTTPRouteMatch defines the predicate used to match requests to a given action. Multiple match types are ANDed together, i.e. the match will evaluate to true only if all conditions are satisfied. \n For example, the match below will match a HTTP request only if its path starts with `/foo` AND it contains the `version: v1` header: \n ``` match: \n \tpath: \t value: \"/foo\" \theaders: \t- name: \"version\" \t value \"v1\" \n ```" - properties: - headers: - description: Headers specifies HTTP request header matchers. Multiple match values are ANDed together, meaning, a request must match all the specified headers to select the route. - items: - description: HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request headers. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent. \n When a header is repeated in an HTTP request, it is implementation-specific behavior as to how this is represented. Generally, proxies should follow the guidance from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding processing a repeated header, with special handling for \"Set-Cookie\"." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: "Type specifies how to match against the value of the header. \n Support: Core (Exact) \n Support: Implementation-specific (RegularExpression) \n Since RegularExpression HeaderMatchType has implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect." - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - method: - description: "Method specifies HTTP method matcher. When specified, this route will be matched only if the request has the specified method. \n Support: Extended" - enum: - - GET - - HEAD - - POST - - PUT - - DELETE - - CONNECT - - OPTIONS - - TRACE - - PATCH - type: string - path: - default: - type: PathPrefix - value: / - description: Path specifies a HTTP request path matcher. If this field is not specified, a default prefix match on the "/" path is provided. - properties: - type: - default: PathPrefix - description: "Type specifies how to match against the path Value. \n Support: Core (Exact, PathPrefix) \n Support: Implementation-specific (RegularExpression)" - enum: - - Exact - - PathPrefix - - RegularExpression - type: string - value: - default: / - description: Value of the HTTP path to match against. - maxLength: 1024 - type: string - type: object - queryParams: - description: "QueryParams specifies HTTP query parameter matchers. Multiple match values are ANDed together, meaning, a request must match all the specified query parameters to select the route. \n Support: Extended" - items: - description: HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP query parameters. - properties: - name: - description: "Name is the name of the HTTP query param to be matched. This must be an exact string match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). \n If multiple entries specify equivalent query param names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent query param name MUST be ignored. \n If a query param is repeated in an HTTP request, the behavior is purposely left undefined, since different data planes have different capabilities. However, it is *recommended* that implementations should match against the first value of the param if the data plane supports it, as this behavior is expected in other load balancing contexts outside of the Gateway API. \n Users SHOULD NOT route traffic based on repeated query params to guard themselves against potential differences in the implementations." - maxLength: 256 - minLength: 1 - type: string - type: - default: Exact - description: "Type specifies how to match against the value of the query parameter. \n Support: Extended (Exact) \n Support: Implementation-specific (RegularExpression) \n Since RegularExpression QueryParamMatchType has Implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect." - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP query param to be matched. - maxLength: 1024 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - maxItems: 8 - type: array - type: object - maxItems: 16 - type: array - type: object - status: - description: Status defines the current state of HTTPRoute. - properties: - parents: - description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." - items: - description: RouteParentStatus describes the status of a route with respect to an associated Parent. - properties: - conditions: - description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - controllerName: - description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/gateway-controller\". \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - parentRef: - description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - 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. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - required: - - controllerName - - parentRef - type: object - maxItems: 32 - type: array - required: - - parents - type: object - required: - - spec - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .spec.hostnames - name: Hostnames - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: HTTPRoute provides a way to route HTTP requests. This includes the capability to match requests by hostname, path, header, or query param. Filters can be used to specify additional processing steps. Backends specify where matching requests should be routed. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of HTTPRoute. - properties: - hostnames: - description: "Hostnames defines a set of hostname that should match against the HTTP Host header to select a HTTPRoute to process the request. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n If a hostname is specified by both the Listener and HTTPRoute, there must be at least one intersecting hostname for the HTTPRoute to be attached to the Listener. For example: \n * A Listener with `test.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `*.example.com`, `test.example.com`, and `foo.test.example.com` would all match. On the other hand, `example.com` and `test.example.net` would not match. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n If both the Listener and HTTPRoute have specified hostnames, any HTTPRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the HTTPRoute specified `test.example.com` and `test.example.net`, `test.example.net` must not be considered for a match. \n If both the Listener and HTTPRoute have specified hostnames, and none match with the criteria above, then the HTTPRoute is not accepted. The implementation must raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. \n In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. overlapping wildcard matching and exact matching hostnames), precedence must be given to rules from the HTTPRoute with the largest number of: \n * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. \n If ties exist across multiple Routes, the matching precedence rules for HTTPRouteMatches takes over. \n Support: Core" - items: - description: "Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n Hostname can be \"precise\" which is a domain name without the terminating dot of a network host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). \n Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed." - maxLength: 253 - minLength: 1 - pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - maxItems: 16 - type: array - parentRefs: - description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." - items: - description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - 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. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - maxItems: 32 - type: array - rules: - default: - - matches: - - path: - type: PathPrefix - value: / - description: Rules are a list of HTTP matchers, filters and actions. - items: - description: HTTPRouteRule defines semantics for matching an HTTP request based on conditions (matches), processing it (filters), and forwarding the request to an API object (backendRefs). - properties: - backendRefs: - description: "BackendRefs defines the backend(s) where matching requests should be sent. \n Failure behavior here depends on how many BackendRefs are specified and how many are invalid. \n If *all* entries in BackendRefs are invalid, and there are also no filters specified in this route rule, *all* traffic which matches this rule MUST receive a 500 status code. \n See the HTTPBackendRef definition for the rules about what makes a single HTTPBackendRef invalid. \n When a HTTPBackendRef is invalid, 500 status codes MUST be returned for requests that would have otherwise been routed to an invalid backend. If multiple backends are specified, and some are invalid, the proportion of requests that would otherwise have been routed to an invalid backend MUST receive a 500 status code. \n For example, if two backends are specified with equal weights, and one is invalid, 50 percent of traffic must receive a 500. Implementations may choose how that 50 percent is determined. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Core" - items: - description: HTTPBackendRef defines how a HTTPRoute should forward an HTTP request. - properties: - filters: - description: "Filters defined at this level should be executed if and only if the request is being forwarded to the backend defined here. \n Support: Implementation-specific (For broader support of filters, use the Filters field in HTTPRouteRule.)" - items: - description: HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. - properties: - extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" - 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 - requestHeaderModifier: - description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" - properties: - add: - description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" - items: - type: string - maxItems: 16 - type: array - set: - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - requestMirror: - description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" - properties: - backendRef: - description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" - properties: - group: - default: "" - 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: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name - type: object - required: - - backendRef - type: object - requestRedirect: - description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n Support: Core" - properties: - hostname: - description: "Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname of the request is used. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - path: - description: "Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. \n Support: Extended \n " - properties: - replaceFullPath: - description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " - maxLength: 1024 - type: string - replacePrefixMatch: - description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " - maxLength: 1024 - type: string - type: - description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - port: - description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n Support: Extended" - format: int32 - maximum: 65535 - minimum: 1 - type: integer - scheme: - description: "Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" - enum: - - http - - https - type: string - statusCode: - default: 302 - description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" - enum: - - 301 - - 302 - type: integer - type: object - responseHeaderModifier: - description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " - properties: - add: - description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" - items: - type: string - maxItems: 16 - type: array - set: - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: - description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - RequestHeaderModifier - - ResponseHeaderModifier - - RequestMirror - - RequestRedirect - - URLRewrite - - ExtensionRef - type: string - urlRewrite: - description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " - properties: - hostname: - description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - path: - description: "Path defines a path rewrite. \n Support: Extended \n " - properties: - replaceFullPath: - description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " - maxLength: 1024 - type: string - replacePrefixMatch: - description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " - maxLength: 1024 - type: string - type: - description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - type: object - required: - - type - type: object - maxItems: 16 - type: array - group: - default: "" - 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: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - weight: - default: 1 - description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support for this field varies based on the context where used." - format: int32 - maximum: 1000000 - minimum: 0 - type: integer - required: - - name - type: object - maxItems: 16 - type: array - filters: - description: "Filters define the filters that are applied to requests that match this rule. \n The effects of ordering of multiple behaviors are currently unspecified. This can change in the future based on feedback during the alpha stage. \n Conformance-levels at this level are defined based on the type of filter: \n - ALL core filters MUST be supported by all implementations. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. \n Specifying a core filter multiple times has unspecified or implementation-specific conformance. \n All filters are expected to be compatible with each other except for the URLRewrite and RequestRedirect filters, which may not be combined. If an implementation can not support other combinations of filters, they must clearly document that limitation. In all cases where incompatible or unsupported filters are specified, implementations MUST add a warning condition to status. \n Support: Core" - items: - description: HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. - properties: - extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" - 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 - requestHeaderModifier: - description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" - properties: - add: - description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" - items: - type: string - maxItems: 16 - type: array - set: - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - requestMirror: - description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" - properties: - backendRef: - description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" - properties: - group: - default: "" - 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: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name - type: object - required: - - backendRef - type: object - requestRedirect: - description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n Support: Core" - properties: - hostname: - description: "Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname of the request is used. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - path: - description: "Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. \n Support: Extended \n " - properties: - replaceFullPath: - description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " - maxLength: 1024 - type: string - replacePrefixMatch: - description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " - maxLength: 1024 - type: string - type: - description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - port: - description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n Support: Extended" - format: int32 - maximum: 65535 - minimum: 1 - type: integer - scheme: - description: "Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" - enum: - - http - - https - type: string - statusCode: - default: 302 - description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" - enum: - - 301 - - 302 - type: integer - type: object - responseHeaderModifier: - description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " - properties: - add: - description: "Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar" - items: - type: string - maxItems: 16 - type: array - set: - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: - description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - RequestHeaderModifier - - ResponseHeaderModifier - - RequestMirror - - RequestRedirect - - URLRewrite - - ExtensionRef - type: string - urlRewrite: - description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " - properties: - hostname: - description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - path: - description: "Path defines a path rewrite. \n Support: Extended \n " - properties: - replaceFullPath: - description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " - maxLength: 1024 - type: string - replacePrefixMatch: - description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " - maxLength: 1024 - type: string - type: - description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - type: object - required: - - type - type: object - maxItems: 16 - type: array - matches: - default: - - path: - type: PathPrefix - value: / - description: "Matches define conditions used for matching the rule against incoming HTTP requests. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied. \n For example, take the following matches configuration: \n ``` matches: - path: value: \"/foo\" headers: - name: \"version\" value: \"v2\" - path: value: \"/v2/foo\" ``` \n For a request to match against this rule, a request must satisfy EITHER of the two conditions: \n - path prefixed with `/foo` AND contains the header `version: v2` - path prefix of `/v2/foo` \n See the documentation for HTTPRouteMatch on how to specify multiple match conditions that should be ANDed together. \n If no matches are specified, the default is a prefix path match on \"/\", which has the effect of matching every HTTP request. \n Proxy or Load Balancer routing configuration generated from HTTPRoutes MUST prioritize matches based on the following criteria, continuing on ties. Across all rules specified on applicable Routes, precedence must be given to the match with the largest number of: \n * Characters in a matching path. * Header matches. * Query param matches. \n If ties still exist across multiple Routes, matching precedence MUST be determined in order of the following criteria, continuing on ties: \n * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n If ties still exist within an HTTPRoute, matching precedence MUST be granted to the FIRST matching rule (in list order) with a match meeting the above criteria. \n When no rules matching a request have been successfully attached to the parent a request is coming from, a HTTP 404 status code MUST be returned." - items: - description: "HTTPRouteMatch defines the predicate used to match requests to a given action. Multiple match types are ANDed together, i.e. the match will evaluate to true only if all conditions are satisfied. \n For example, the match below will match a HTTP request only if its path starts with `/foo` AND it contains the `version: v1` header: \n ``` match: \n \tpath: \t value: \"/foo\" \theaders: \t- name: \"version\" \t value \"v1\" \n ```" - properties: - headers: - description: Headers specifies HTTP request header matchers. Multiple match values are ANDed together, meaning, a request must match all the specified headers to select the route. - items: - description: HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request headers. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent. \n When a header is repeated in an HTTP request, it is implementation-specific behavior as to how this is represented. Generally, proxies should follow the guidance from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding processing a repeated header, with special handling for \"Set-Cookie\"." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: "Type specifies how to match against the value of the header. \n Support: Core (Exact) \n Support: Implementation-specific (RegularExpression) \n Since RegularExpression HeaderMatchType has implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect." - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - method: - description: "Method specifies HTTP method matcher. When specified, this route will be matched only if the request has the specified method. \n Support: Extended" - enum: - - GET - - HEAD - - POST - - PUT - - DELETE - - CONNECT - - OPTIONS - - TRACE - - PATCH - type: string - path: - default: - type: PathPrefix - value: / - description: Path specifies a HTTP request path matcher. If this field is not specified, a default prefix match on the "/" path is provided. - properties: - type: - default: PathPrefix - description: "Type specifies how to match against the path Value. \n Support: Core (Exact, PathPrefix) \n Support: Implementation-specific (RegularExpression)" - enum: - - Exact - - PathPrefix - - RegularExpression - type: string - value: - default: / - description: Value of the HTTP path to match against. - maxLength: 1024 - type: string - type: object - queryParams: - description: "QueryParams specifies HTTP query parameter matchers. Multiple match values are ANDed together, meaning, a request must match all the specified query parameters to select the route. \n Support: Extended" - items: - description: HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP query parameters. - properties: - name: - description: "Name is the name of the HTTP query param to be matched. This must be an exact string match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). \n If multiple entries specify equivalent query param names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent query param name MUST be ignored. \n If a query param is repeated in an HTTP request, the behavior is purposely left undefined, since different data planes have different capabilities. However, it is *recommended* that implementations should match against the first value of the param if the data plane supports it, as this behavior is expected in other load balancing contexts outside of the Gateway API. \n Users SHOULD NOT route traffic based on repeated query params to guard themselves against potential differences in the implementations." - maxLength: 256 - minLength: 1 - type: string - type: - default: Exact - description: "Type specifies how to match against the value of the query parameter. \n Support: Extended (Exact) \n Support: Implementation-specific (RegularExpression) \n Since RegularExpression QueryParamMatchType has Implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect." - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP query param to be matched. - maxLength: 1024 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - maxItems: 8 - type: array - type: object - maxItems: 16 - type: array - type: object - status: - description: Status defines the current state of HTTPRoute. - properties: - parents: - description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." - items: - description: RouteParentStatus describes the status of a route with respect to an associated Parent. - properties: - conditions: - description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - controllerName: - description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/gateway-controller\". \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - parentRef: - description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - 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. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - required: - - controllerName - - parentRef - type: object - maxItems: 32 - type: array - required: - - parents - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/external/kustomization.yaml b/control-plane/config/crd/external/kustomization.yaml deleted file mode 100644 index a1a0e349ff..0000000000 --- a/control-plane/config/crd/external/kustomization.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -# This file is Helm ignored. It is only used for the `make generate-external-crds` command. - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -resources: - - github.com/kubernetes-sigs/gateway-api/config/crd/experimental?ref=v0.6.2 diff --git a/control-plane/config/crd/external/referencegrants.gateway.networking.k8s.io.yaml b/control-plane/config/crd/external/referencegrants.gateway.networking.k8s.io.yaml deleted file mode 100644 index cd39b9c12a..0000000000 --- a/control-plane/config/crd/external/referencegrants.gateway.networking.k8s.io.yaml +++ /dev/null @@ -1,203 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 - gateway.networking.k8s.io/bundle-version: v0.6.2 - gateway.networking.k8s.io/channel: experimental - creationTimestamp: null - name: referencegrants.gateway.networking.k8s.io -spec: - group: gateway.networking.k8s.io - names: - categories: - - gateway-api - kind: ReferenceGrant - listKind: ReferenceGrantList - plural: referencegrants - shortNames: - - refgrant - singular: referencegrant - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha2 - schema: - openAPIV3Schema: - description: "ReferenceGrant identifies kinds of resources in other namespaces that are trusted to reference the specified kinds of resources in the same namespace as the policy. \n Each ReferenceGrant can be used to represent a unique trust relationship. Additional Reference Grants can be used to add to the set of trusted sources of inbound references for the namespace they are defined within. \n All cross-namespace references in Gateway API (with the exception of cross-namespace Gateway-route attachment) require a ReferenceGrant. \n ReferenceGrant is a form of runtime verification allowing users to assert which cross-namespace object references are permitted. Implementations that support ReferenceGrant MUST NOT permit cross-namespace references which have no grant, and MUST respond to the removal of a grant by revoking the access that the grant allowed. \n Support: Core" - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of ReferenceGrant. - properties: - from: - description: "From describes the trusted namespaces and kinds that can reference the resources described in \"To\". Each entry in this list MUST be considered to be an additional place that references can be valid from, or to put this another way, entries MUST be combined using OR. \n Support: Core" - items: - description: ReferenceGrantFrom describes trusted namespaces and kinds. - properties: - group: - description: "Group is the group of the referent. When empty, the Kubernetes core API group is inferred. \n Support: Core" - 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 the kind of the referent. Although implementations may support additional resources, the following types are part of the \"Core\" support level for this field. \n When used to permit a SecretObjectReference: \n * Gateway \n When used to permit a BackendObjectReference: \n * GRPCRoute * HTTPRoute * TCPRoute * TLSRoute * UDPRoute" - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - namespace: - description: "Namespace is the namespace of the referent. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - group - - kind - - namespace - type: object - maxItems: 16 - minItems: 1 - type: array - to: - description: "To describes the resources that may be referenced by the resources described in \"From\". Each entry in this list MUST be considered to be an additional place that references can be valid to, or to put this another way, entries MUST be combined using OR. \n Support: Core" - items: - description: ReferenceGrantTo describes what Kinds are allowed as targets of the references. - properties: - group: - description: "Group is the group of the referent. When empty, the Kubernetes core API group is inferred. \n Support: Core" - 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 the kind of the referent. Although implementations may support additional resources, the following types are part of the \"Core\" support level for this field: \n * Secret when used to permit a SecretObjectReference * Service when used to permit a BackendObjectReference" - 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. When unspecified, this policy refers to all resources of the specified Group and Kind in the local namespace. - maxLength: 253 - minLength: 1 - type: string - required: - - group - - kind - type: object - maxItems: 16 - minItems: 1 - type: array - required: - - from - - to - type: object - type: object - served: true - storage: true - subresources: {} - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: "ReferenceGrant identifies kinds of resources in other namespaces that are trusted to reference the specified kinds of resources in the same namespace as the policy. \n Each ReferenceGrant can be used to represent a unique trust relationship. Additional Reference Grants can be used to add to the set of trusted sources of inbound references for the namespace they are defined within. \n All cross-namespace references in Gateway API (with the exception of cross-namespace Gateway-route attachment) require a ReferenceGrant. \n ReferenceGrant is a form of runtime verification allowing users to assert which cross-namespace object references are permitted. Implementations that support ReferenceGrant MUST NOT permit cross-namespace references which have no grant, and MUST respond to the removal of a grant by revoking the access that the grant allowed. \n Support: Core" - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of ReferenceGrant. - properties: - from: - description: "From describes the trusted namespaces and kinds that can reference the resources described in \"To\". Each entry in this list MUST be considered to be an additional place that references can be valid from, or to put this another way, entries MUST be combined using OR. \n Support: Core" - items: - description: ReferenceGrantFrom describes trusted namespaces and kinds. - properties: - group: - description: "Group is the group of the referent. When empty, the Kubernetes core API group is inferred. \n Support: Core" - 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 the kind of the referent. Although implementations may support additional resources, the following types are part of the \"Core\" support level for this field. \n When used to permit a SecretObjectReference: \n * Gateway \n When used to permit a BackendObjectReference: \n * GRPCRoute * HTTPRoute * TCPRoute * TLSRoute * UDPRoute" - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - namespace: - description: "Namespace is the namespace of the referent. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - group - - kind - - namespace - type: object - maxItems: 16 - minItems: 1 - type: array - to: - description: "To describes the resources that may be referenced by the resources described in \"From\". Each entry in this list MUST be considered to be an additional place that references can be valid to, or to put this another way, entries MUST be combined using OR. \n Support: Core" - items: - description: ReferenceGrantTo describes what Kinds are allowed as targets of the references. - properties: - group: - description: "Group is the group of the referent. When empty, the Kubernetes core API group is inferred. \n Support: Core" - 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 the kind of the referent. Although implementations may support additional resources, the following types are part of the \"Core\" support level for this field: \n * Secret when used to permit a SecretObjectReference * Service when used to permit a BackendObjectReference" - 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. When unspecified, this policy refers to all resources of the specified Group and Kind in the local namespace. - maxLength: 253 - minLength: 1 - type: string - required: - - group - - kind - type: object - maxItems: 16 - minItems: 1 - type: array - required: - - from - - to - type: object - type: object - served: true - storage: false - subresources: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/external/tcproutes.gateway.networking.k8s.io.yaml b/control-plane/config/crd/external/tcproutes.gateway.networking.k8s.io.yaml deleted file mode 100644 index 906b442d31..0000000000 --- a/control-plane/config/crd/external/tcproutes.gateway.networking.k8s.io.yaml +++ /dev/null @@ -1,276 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 - gateway.networking.k8s.io/bundle-version: v0.6.2 - gateway.networking.k8s.io/channel: experimental - creationTimestamp: null - name: tcproutes.gateway.networking.k8s.io -spec: - group: gateway.networking.k8s.io - names: - categories: - - gateway-api - kind: TCPRoute - listKind: TCPRouteList - plural: tcproutes - singular: tcproute - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha2 - schema: - openAPIV3Schema: - description: TCPRoute provides a way to route TCP requests. When combined with a Gateway listener, it can be used to forward connections on the port specified by the listener to a set of backends specified by the TCPRoute. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of TCPRoute. - properties: - parentRefs: - description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." - items: - description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - 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. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - maxItems: 32 - type: array - rules: - description: Rules are a list of TCP matchers and actions. - items: - description: TCPRouteRule is the configuration for a given rule. - properties: - backendRefs: - description: "BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a non-existent resource or a Service with no endpoints), the underlying implementation MUST actively reject connection attempts to this backend. Connection rejections must respect weight; if an invalid backend is requested to have 80% of connections, then 80% of connections must be rejected instead. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Extended" - items: - description: "BackendRef defines how a Route should forward a request to a Kubernetes resource. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details." - properties: - group: - default: "" - 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: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - weight: - default: 1 - description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support for this field varies based on the context where used." - format: int32 - maximum: 1000000 - minimum: 0 - type: integer - required: - - name - type: object - maxItems: 16 - minItems: 1 - type: array - type: object - maxItems: 16 - minItems: 1 - type: array - required: - - rules - type: object - status: - description: Status defines the current state of TCPRoute. - properties: - parents: - description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." - items: - description: RouteParentStatus describes the status of a route with respect to an associated Parent. - properties: - conditions: - description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - controllerName: - description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/gateway-controller\". \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - parentRef: - description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - 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. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - required: - - controllerName - - parentRef - type: object - maxItems: 32 - type: array - required: - - parents - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/external/tlsroutes.gateway.networking.k8s.io.yaml b/control-plane/config/crd/external/tlsroutes.gateway.networking.k8s.io.yaml deleted file mode 100644 index 2e22b04ef0..0000000000 --- a/control-plane/config/crd/external/tlsroutes.gateway.networking.k8s.io.yaml +++ /dev/null @@ -1,286 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 - gateway.networking.k8s.io/bundle-version: v0.6.2 - gateway.networking.k8s.io/channel: experimental - creationTimestamp: null - name: tlsroutes.gateway.networking.k8s.io -spec: - group: gateway.networking.k8s.io - names: - categories: - - gateway-api - kind: TLSRoute - listKind: TLSRouteList - plural: tlsroutes - singular: tlsroute - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha2 - schema: - openAPIV3Schema: - description: "The TLSRoute resource is similar to TCPRoute, but can be configured to match against TLS-specific metadata. This allows more flexibility in matching streams for a given TLS listener. \n If you need to forward traffic to a single target for a TLS listener, you could choose to use a TCPRoute with a TLS listener." - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of TLSRoute. - properties: - hostnames: - description: "Hostnames defines a set of SNI names that should match against the SNI attribute of TLS ClientHello message in TLS handshake. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed in SNI names per RFC 6066. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n If a hostname is specified by both the Listener and TLSRoute, there must be at least one intersecting hostname for the TLSRoute to be attached to the Listener. For example: \n * A Listener with `test.example.com` as the hostname matches TLSRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches TLSRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `test.example.com` and `*.example.com` would both match. On the other hand, `example.com` and `test.example.net` would not match. \n If both the Listener and TLSRoute have specified hostnames, any TLSRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the TLSRoute specified `test.example.com` and `test.example.net`, `test.example.net` must not be considered for a match. \n If both the Listener and TLSRoute have specified hostnames, and none match with the criteria above, then the TLSRoute is not accepted. The implementation must raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. \n Support: Core" - items: - description: "Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n Hostname can be \"precise\" which is a domain name without the terminating dot of a network host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). \n Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed." - maxLength: 253 - minLength: 1 - pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - maxItems: 16 - type: array - parentRefs: - description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." - items: - description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - 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. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - maxItems: 32 - type: array - rules: - description: Rules are a list of TLS matchers and actions. - items: - description: TLSRouteRule is the configuration for a given rule. - properties: - backendRefs: - description: "BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a non-existent resource or a Service with no endpoints), the rule performs no forwarding; if no filters are specified that would result in a response being sent, the underlying implementation must actively reject request attempts to this backend, by rejecting the connection or returning a 500 status code. Request rejections must respect weight; if an invalid backend is requested to have 80% of requests, then 80% of requests must be rejected instead. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Extended" - items: - description: "BackendRef defines how a Route should forward a request to a Kubernetes resource. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details." - properties: - group: - default: "" - 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: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - weight: - default: 1 - description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support for this field varies based on the context where used." - format: int32 - maximum: 1000000 - minimum: 0 - type: integer - required: - - name - type: object - maxItems: 16 - minItems: 1 - type: array - type: object - maxItems: 16 - minItems: 1 - type: array - required: - - rules - type: object - status: - description: Status defines the current state of TLSRoute. - properties: - parents: - description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." - items: - description: RouteParentStatus describes the status of a route with respect to an associated Parent. - properties: - conditions: - description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - controllerName: - description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/gateway-controller\". \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - parentRef: - description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - 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. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - required: - - controllerName - - parentRef - type: object - maxItems: 32 - type: array - required: - - parents - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/external/udproutes.gateway.networking.k8s.io.yaml b/control-plane/config/crd/external/udproutes.gateway.networking.k8s.io.yaml deleted file mode 100644 index 19b03dcd0b..0000000000 --- a/control-plane/config/crd/external/udproutes.gateway.networking.k8s.io.yaml +++ /dev/null @@ -1,276 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 - gateway.networking.k8s.io/bundle-version: v0.6.2 - gateway.networking.k8s.io/channel: experimental - creationTimestamp: null - name: udproutes.gateway.networking.k8s.io -spec: - group: gateway.networking.k8s.io - names: - categories: - - gateway-api - kind: UDPRoute - listKind: UDPRouteList - plural: udproutes - singular: udproute - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha2 - schema: - openAPIV3Schema: - description: UDPRoute provides a way to route UDP traffic. When combined with a Gateway listener, it can be used to forward traffic on the port specified by the listener to a set of backends specified by the UDPRoute. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of UDPRoute. - properties: - parentRefs: - description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." - items: - description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - 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. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - maxItems: 32 - type: array - rules: - description: Rules are a list of UDP matchers and actions. - items: - description: UDPRouteRule is the configuration for a given rule. - properties: - backendRefs: - description: "BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a non-existent resource or a Service with no endpoints), the underlying implementation MUST actively reject connection attempts to this backend. Packet drops must respect weight; if an invalid backend is requested to have 80% of the packets, then 80% of packets must be dropped instead. \n Support: Core for Kubernetes Service Support: Implementation-specific for any other resource \n Support for weight: Extended" - items: - description: "BackendRef defines how a Route should forward a request to a Kubernetes resource. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details." - properties: - group: - default: "" - 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: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - 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 - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - weight: - default: 1 - description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support for this field varies based on the context where used." - format: int32 - maximum: 1000000 - minimum: 0 - type: integer - required: - - name - type: object - maxItems: 16 - minItems: 1 - type: array - type: object - maxItems: 16 - minItems: 1 - type: array - required: - - rules - type: object - status: - description: Status defines the current state of UDPRoute. - properties: - parents: - description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." - items: - description: RouteParentStatus describes the status of a route with respect to an associated Parent. - properties: - conditions: - description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - controllerName: - description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/gateway-controller\". \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - parentRef: - description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - 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. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - required: - - controllerName - - parentRef - type: object - maxItems: 32 - type: array - required: - - parents - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/kustomization.yaml b/control-plane/config/crd/kustomization.yaml deleted file mode 100644 index cbb41bc60c..0000000000 --- a/control-plane/config/crd/kustomization.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -# This kustomization.yaml is not intended to be run by itself, -# since it depends on service name and namespace that are out of this kustomize package. -# It should be run by config/default -resources: -- bases/consul.hashicorp.com_controlplanerequestlimits.yaml -#+kubebuilder:scaffold:crdkustomizeresource - -patches: -# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. -# patches here are for enabling the conversion webhook for each CRD -#- patches/webhook_in_controlplanerequestlimits.yaml -#+kubebuilder:scaffold:crdkustomizewebhookpatch - -# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. -# patches here are for enabling the CA injection for each CRD -#- patches/cainjection_in_controlplanerequestlimits.yaml -#+kubebuilder:scaffold:crdkustomizecainjectionpatch - -# the following config is for teaching kustomize how to do kustomization for CRDs. -configurations: -- kustomizeconfig.yaml diff --git a/control-plane/config/crd/kustomizeconfig.yaml b/control-plane/config/crd/kustomizeconfig.yaml deleted file mode 100644 index 5d1332c4bf..0000000000 --- a/control-plane/config/crd/kustomizeconfig.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -# This file is for teaching kustomize how to substitute name and namespace reference in CRD -nameReference: -- kind: Service - version: v1 - fieldSpecs: - - kind: CustomResourceDefinition - group: apiextensions.k8s.io - path: spec/conversion/webhookClientConfig/service/name - -namespace: -- kind: CustomResourceDefinition - group: apiextensions.k8s.io - path: spec/conversion/webhookClientConfig/service/namespace - create: false - -varReference: -- path: metadata/annotations diff --git a/control-plane/config/rbac/role.yaml b/control-plane/config/rbac/role.yaml index 7f90780e02..8daf050ec0 100644 --- a/control-plane/config/rbac/role.yaml +++ b/control-plane/config/rbac/role.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -26,26 +23,6 @@ rules: - secrets/status verbs: - get -- apiGroups: - - consul.hashicorp.com - resources: - - controlplanerequestlimits - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - consul.hashicorp.com - resources: - - controlplanerequestlimits/status - verbs: - - get - - patch - - update - apiGroups: - consul.hashicorp.com resources: @@ -86,26 +63,6 @@ rules: - get - patch - update -- apiGroups: - - consul.hashicorp.com - resources: - - jwtproviders - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - consul.hashicorp.com - resources: - - jwtproviders/status - verbs: - - get - - patch - - update - apiGroups: - consul.hashicorp.com resources: @@ -186,26 +143,6 @@ rules: - get - patch - update -- apiGroups: - - consul.hashicorp.com - resources: - - samenessgroups - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - consul.hashicorp.com - resources: - - samenessgroups/status - verbs: - - get - - patch - - update - apiGroups: - consul.hashicorp.com resources: diff --git a/control-plane/config/webhook/manifests.yaml b/control-plane/config/webhook/manifests.yaml index 0861f9253a..c1ffb5e0fe 100644 --- a/control-plane/config/webhook/manifests.yaml +++ b/control-plane/config/webhook/manifests.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - --- apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration @@ -8,27 +5,6 @@ metadata: creationTimestamp: null name: mutating-webhook-configuration webhooks: -- admissionReviewVersions: - - v1beta1 - - v1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /mutate-v1alpha1-controlplanerequestlimits - failurePolicy: Fail - name: mutate-controlplanerequestlimits.consul.hashicorp.com - rules: - - apiGroups: - - consul.hashicorp.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - controlplanerequestlimits - sideEffects: None - admissionReviewVersions: - v1beta1 - v1 @@ -71,27 +47,6 @@ webhooks: resources: - ingressgateways sideEffects: None -- admissionReviewVersions: - - v1beta1 - - v1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /mutate-v1alpha1-jwtprovider - failurePolicy: Fail - name: mutate-jwtprovider.consul.hashicorp.com - rules: - - apiGroups: - - consul.hashicorp.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - jwtproviders - sideEffects: None - admissionReviewVersions: - v1beta1 - v1 @@ -176,27 +131,6 @@ webhooks: resources: - proxydefaults sideEffects: None -- admissionReviewVersions: - - v1beta1 - - v1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /mutate-v1alpha1-samenessgroups - failurePolicy: Fail - name: mutate-samenessgroup.consul.hashicorp.com - rules: - - apiGroups: - - consul.hashicorp.com - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - samenessgroups - sideEffects: None - admissionReviewVersions: - v1beta1 - v1 diff --git a/control-plane/connect-inject/common/common.go b/control-plane/connect-inject/common/common.go index a99d9fd12e..0ebf829666 100644 --- a/control-plane/connect-inject/common/common.go +++ b/control-plane/connect-inject/common/common.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package common import ( diff --git a/control-plane/connect-inject/common/common_test.go b/control-plane/connect-inject/common/common_test.go index 79a9294fe2..155f6b892d 100644 --- a/control-plane/connect-inject/common/common_test.go +++ b/control-plane/connect-inject/common/common_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package common import ( diff --git a/control-plane/connect-inject/constants/annotations_and_labels.go b/control-plane/connect-inject/constants/annotations_and_labels.go index 4efcc24c74..8767de33b3 100644 --- a/control-plane/connect-inject/constants/annotations_and_labels.go +++ b/control-plane/connect-inject/constants/annotations_and_labels.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package constants const ( diff --git a/control-plane/connect-inject/constants/constants.go b/control-plane/connect-inject/constants/constants.go index ca6fe23606..673a64eabc 100644 --- a/control-plane/connect-inject/constants/constants.go +++ b/control-plane/connect-inject/constants/constants.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package constants const ( @@ -16,15 +13,6 @@ const ( // MetaKeyKubeNS is the meta key name for Kubernetes namespace used for the Consul services. MetaKeyKubeNS = "k8s-namespace" - // MetaKeyKubeName is the meta key name for Kubernetes object name used for a Consul object. - MetaKeyKubeName = "k8s-name" - - // MetaKeyDatacenter is the datacenter that this object was registered from. - MetaKeyDatacenter = "datacenter" - - // MetaKeyKubeServiceName is the meta key name for Kubernetes service name used for the Consul services. - MetaKeyKubeServiceName = "k8s-service-name" - // MetaKeyPodName is the meta key name for Kubernetes pod name used for the Consul services. MetaKeyPodName = "pod-name" diff --git a/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks.go b/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks.go index c71ee9ba55..f54fb71d11 100644 --- a/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks.go +++ b/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package endpoints import ( diff --git a/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks_test.go b/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks_test.go index 36ad222d68..7f4978c133 100644 --- a/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks_test.go +++ b/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package endpoints import ( diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go index 3f139c2662..b971406f68 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go @@ -1,5 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 package endpoints import ( @@ -44,10 +42,9 @@ const ( terminatingGateway = "terminating-gateway" ingressGateway = "ingress-gateway" - kubernetesSuccessReasonMsg = "Kubernetes health checks passing" - envoyPrometheusBindAddr = "envoy_prometheus_bind_addr" - envoyTelemetryCollectorBindSocketDir = "envoy_telemetry_collector_bind_socket_dir" - defaultNS = "default" + kubernetesSuccessReasonMsg = "Kubernetes health checks passing" + envoyPrometheusBindAddr = "envoy_prometheus_bind_addr" + defaultNS = "default" // clusterIPTaggedAddressName is the key for the tagged address to store the service's cluster IP and service port // in Consul. Note: This value should not be changed without a corresponding change in Consul. @@ -120,10 +117,6 @@ type Controller struct { // to Consul client agents. EnableAutoEncrypt bool - // EnableTelemetryCollector controls whether the proxy service should be registered - // with config to enable telemetry forwarding. - EnableTelemetryCollector bool - MetricsConfig metrics.Config Log logr.Logger @@ -298,14 +291,6 @@ func (r *Controller) registerServicesAndHealthCheck(apiClient *api.Client, pod c return err } - // Add manual ip to the VIP table - r.Log.Info("adding manual ip to virtual ip table in Consul", "name", serviceRegistration.Service.Service, - "id", serviceRegistration.ID) - err = assignServiceVirtualIP(r.Context, apiClient, serviceRegistration.Service) - if err != nil { - r.Log.Error(err, "failed to add ip to virtual ip table", "name", serviceRegistration.Service.Service) - } - // Register the proxy service instance with Consul. r.Log.Info("registering proxy service with Consul", "name", proxyServiceRegistration.Service.Service) _, err = apiClient.Catalog().Register(proxyServiceRegistration, nil) @@ -317,20 +302,6 @@ func (r *Controller) registerServicesAndHealthCheck(apiClient *api.Client, pod c return nil } -func parseLocality(node corev1.Node) *api.Locality { - region := node.Labels[corev1.LabelTopologyRegion] - zone := node.Labels[corev1.LabelTopologyZone] - - if region == "" { - return nil - } - - return &api.Locality{ - Region: region, - Zone: zone, - } -} - // registerGateway creates Consul registrations for the Connect Gateways and registers them with Consul. // It also upserts a Kubernetes health check for the service based on whether the endpoint address is ready. func (r *Controller) registerGateway(apiClient *api.Client, pod corev1.Pod, serviceEndpoints corev1.Endpoints, healthStatus string, endpointAddressMap map[string]bool) error { @@ -418,11 +389,6 @@ func (r *Controller) createServiceRegistrations(pod corev1.Pod, serviceEndpoints } } - var node corev1.Node - // Ignore errors because we don't want failures to block running services. - _ = r.Client.Get(context.Background(), types.NamespacedName{Name: pod.Spec.NodeName, Namespace: pod.Namespace}, &node) - locality := parseLocality(node) - // We only want that annotation to be present when explicitly overriding the consul svc name // Otherwise, the Consul service name should equal the Kubernetes Service name. // The service name in Consul defaults to the Endpoints object name, and is overridden by the pod @@ -458,7 +424,6 @@ func (r *Controller) createServiceRegistrations(pod corev1.Pod, serviceEndpoints Meta: meta, Namespace: consulNS, Tags: tags, - Locality: locality, } serviceRegistration := &api.CatalogRegistration{ Node: common.ConsulNodeNameFromK8sNode(pod.Spec.NodeName), @@ -506,10 +471,6 @@ func (r *Controller) createServiceRegistrations(pod corev1.Pod, serviceEndpoints proxyConfig.Config[envoyPrometheusBindAddr] = prometheusScrapeListener } - if r.EnableTelemetryCollector && proxyConfig.Config != nil { - proxyConfig.Config[envoyTelemetryCollectorBindSocketDir] = "/consul/connect-inject" - } - if consulServicePort > 0 { proxyConfig.LocalServiceAddress = "127.0.0.1" proxyConfig.LocalServicePort = consulServicePort @@ -535,8 +496,6 @@ func (r *Controller) createServiceRegistrations(pod corev1.Pod, serviceEndpoints Namespace: consulNS, Proxy: proxyConfig, Tags: tags, - // Sidecar locality (not proxied service locality) is used for locality-aware routing. - Locality: locality, } // A user can enable/disable tproxy for an entire namespace. @@ -692,9 +651,6 @@ func (r *Controller) createGatewayRegistrations(pod corev1.Pod, serviceEndpoints ID: pod.Name, Address: pod.Status.PodIP, Meta: meta, - Proxy: &api.AgentServiceConnectProxyConfig{ - Config: map[string]interface{}{}, - }, } gatewayServiceName, ok := pod.Annotations[constants.AnnotationGatewayConsulServiceName] @@ -794,10 +750,6 @@ func (r *Controller) createGatewayRegistrations(pod corev1.Pod, serviceEndpoints } } - if r.EnableTelemetryCollector && service.Proxy != nil && service.Proxy.Config != nil { - service.Proxy.Config[envoyTelemetryCollectorBindSocketDir] = "/consul/service" - } - serviceRegistration := &api.CatalogRegistration{ Node: common.ConsulNodeNameFromK8sNode(pod.Spec.NodeName), Address: pod.Status.HostIP, @@ -1319,26 +1271,6 @@ func (r *Controller) appendNodeMeta(registration *api.CatalogRegistration) { } } -// assignServiceVirtualIPs manually assigns the ClusterIP to the virtual IP table so that transparent proxy routing works. -func assignServiceVirtualIP(ctx context.Context, apiClient *api.Client, svc *api.AgentService) error { - ip := svc.TaggedAddresses[clusterIPTaggedAddressName].Address - if ip == "" { - return nil - } - - _, _, err := apiClient.Internal().AssignServiceVirtualIP(ctx, svc.Service, []string{ip}, &api.WriteOptions{Namespace: svc.Namespace, Partition: svc.Partition}) - if err != nil { - // Maintain backwards compatibility with older versions of Consul that do not support the VIP improvements. Tproxy - // will not work 100% correctly but the mesh will still work - if strings.Contains(err.Error(), "404") { - return fmt.Errorf("failed to add ip for service %s to virtual ip table. Please upgrade Consul to version 1.16 or higher", svc.Service) - } else { - return err - } - } - return nil -} - // hasBeenInjected checks the value of the status annotation and returns true if the Pod has been injected. func hasBeenInjected(pod corev1.Pod) bool { if anno, ok := pod.Annotations[constants.KeyInjectStatus]; ok && anno == constants.Injected { diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go index e0c7f034b1..12fabdf13d 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - //go:build enterprise package endpoints @@ -11,7 +8,7 @@ import ( "testing" mapset "github.com/deckarep/golang-set" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" @@ -226,7 +223,7 @@ func TestReconcileCreateEndpointWithNamespaces(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -487,7 +484,7 @@ func TestReconcileCreateGatewayWithNamespaces(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -1494,7 +1491,7 @@ func TestReconcileUpdateEndpointWithNamespaces(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -1564,7 +1561,7 @@ func TestReconcileUpdateEndpointWithNamespaces(t *testing.T) { // Read the token from Consul. token, _, err := consulClient.ACL().TokenRead(tokenID, nil) if deregisteredServices.Contains(serviceID) { - require.Contains(t, err.Error(), "ACL not found") + require.EqualError(t, err, "Unexpected response code: 403 (ACL not found)") } else { require.NoError(t, err, "token should exist for service instance: "+serviceID) require.NotNil(t, token) @@ -1780,7 +1777,7 @@ func TestReconcileDeleteEndpointWithNamespaces(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -1822,7 +1819,7 @@ func TestReconcileDeleteEndpointWithNamespaces(t *testing.T) { if tt.enableACLs { _, _, err = consulClient.ACL().TokenRead(token.AccessorID, nil) - require.Contains(t, err.Error(), "ACL not found") + require.EqualError(t, err, "Unexpected response code: 403 (ACL not found)") } }) } @@ -2074,7 +2071,7 @@ func TestReconcileDeleteGatewayWithNamespaces(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -2107,7 +2104,7 @@ func TestReconcileDeleteGatewayWithNamespaces(t *testing.T) { if tt.enableACLs { _, _, err = consulClient.ACL().TokenRead(token.AccessorID, nil) - require.Contains(t, err.Error(), "ACL not found") + require.EqualError(t, err, "Unexpected response code: 403 (ACL not found)") } }) } diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go index 2cec69dd0a..e6694d6718 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package endpoints import ( @@ -999,7 +996,6 @@ func TestReconcileCreateEndpoint(t *testing.T) { expectedProxySvcInstances []*api.CatalogService expectedHealthChecks []*api.HealthCheck metricsEnabled bool - telemetryCollectorDisabled bool nodeMeta map[string]string expErr string }{ @@ -1082,7 +1078,6 @@ func TestReconcileCreateEndpoint(t *testing.T) { DestinationServiceID: "pod1-service-created", LocalServiceAddress: "", LocalServicePort: 0, - Config: map[string]any{"envoy_telemetry_collector_bind_socket_dir": string("/consul/connect-inject")}, }, ServiceMeta: map[string]string{constants.MetaKeyPodName: "pod1", metaKeyKubeServiceName: "service-created", constants.MetaKeyKubeNS: "default", metaKeyManagedBy: constants.ManagedByValue, metaKeySyntheticNode: "true"}, ServiceTags: []string{}, @@ -1168,9 +1163,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { Port: 443, }, }, - ServiceProxy: &api.AgentServiceConnectProxyConfig{ - Config: map[string]any{"envoy_telemetry_collector_bind_socket_dir": string("/consul/service")}, - }, + ServiceProxy: &api.AgentServiceConnectProxyConfig{}, NodeMeta: map[string]string{ "synthetic-node": "true", "test-node": "true", @@ -1223,80 +1216,6 @@ func TestReconcileCreateEndpoint(t *testing.T) { } return []runtime.Object{gateway, endpoint} }, - expectedConsulSvcInstances: []*api.CatalogService{ - { - ServiceID: "mesh-gateway", - ServiceName: "mesh-gateway", - ServiceAddress: "1.2.3.4", - ServicePort: 8443, - ServiceMeta: map[string]string{constants.MetaKeyPodName: "mesh-gateway", metaKeyKubeServiceName: "mesh-gateway", constants.MetaKeyKubeNS: "default", metaKeyManagedBy: constants.ManagedByValue, metaKeySyntheticNode: "true"}, - ServiceTags: []string{}, - ServiceTaggedAddresses: map[string]api.ServiceAddress{ - "lan": { - Address: "1.2.3.4", - Port: 8443, - }, - "wan": { - Address: "2.3.4.5", - Port: 443, - }, - }, - ServiceProxy: &api.AgentServiceConnectProxyConfig{ - Config: map[string]interface{}{ - "envoy_prometheus_bind_addr": "1.2.3.4:20200", - "envoy_telemetry_collector_bind_socket_dir": "/consul/service", - }, - }, - }, - }, - expectedHealthChecks: []*api.HealthCheck{ - { - CheckID: "default/mesh-gateway", - ServiceName: "mesh-gateway", - ServiceID: "mesh-gateway", - Name: consulKubernetesCheckName, - Status: api.HealthPassing, - Output: kubernetesSuccessReasonMsg, - Type: consulKubernetesCheckType, - }, - }, - metricsEnabled: true, - }, - { - name: "Mesh_Gateway_with_Metrics_enabled_and_telemetry_collector_disabled", - svcName: "mesh-gateway", - consulSvcName: "mesh-gateway", - telemetryCollectorDisabled: true, - k8sObjects: func() []runtime.Object { - gateway := createGatewayPod("mesh-gateway", "1.2.3.4", map[string]string{ - constants.AnnotationGatewayConsulServiceName: "mesh-gateway", - constants.AnnotationGatewayWANSource: "Static", - constants.AnnotationGatewayWANAddress: "2.3.4.5", - constants.AnnotationGatewayWANPort: "443", - constants.AnnotationMeshGatewayContainerPort: "8443", - constants.AnnotationGatewayKind: meshGateway}) - endpoint := &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Name: "mesh-gateway", - Namespace: "default", - }, - Subsets: []corev1.EndpointSubset{ - { - Addresses: []corev1.EndpointAddress{ - { - IP: "1.2.3.4", - TargetRef: &corev1.ObjectReference{ - Kind: "Pod", - Name: "mesh-gateway", - Namespace: "default", - }, - }, - }, - }, - }, - } - return []runtime.Object{gateway, endpoint} - }, expectedConsulSvcInstances: []*api.CatalogService{ { ServiceID: "mesh-gateway", @@ -1379,10 +1298,8 @@ func TestReconcileCreateEndpoint(t *testing.T) { metaKeyManagedBy: constants.ManagedByValue, metaKeySyntheticNode: "true", }, - ServiceTags: []string{}, - ServiceProxy: &api.AgentServiceConnectProxyConfig{ - Config: map[string]any{"envoy_telemetry_collector_bind_socket_dir": string("/consul/service")}, - }, + ServiceTags: []string{}, + ServiceProxy: &api.AgentServiceConnectProxyConfig{}, }, }, expectedHealthChecks: []*api.HealthCheck{ @@ -1445,8 +1362,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { ServiceTags: []string{}, ServiceProxy: &api.AgentServiceConnectProxyConfig{ Config: map[string]interface{}{ - "envoy_prometheus_bind_addr": "1.2.3.4:20200", - "envoy_telemetry_collector_bind_socket_dir": "/consul/service", + "envoy_prometheus_bind_addr": "1.2.3.4:20200", }, }, }, @@ -1546,7 +1462,6 @@ func TestReconcileCreateEndpoint(t *testing.T) { "address": "0.0.0.0", }, }, - "envoy_telemetry_collector_bind_socket_dir": "/consul/service", }, }, }, @@ -1647,8 +1562,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { "address": "0.0.0.0", }, }, - "envoy_prometheus_bind_addr": "1.2.3.4:20200", - "envoy_telemetry_collector_bind_socket_dir": "/consul/service", + "envoy_prometheus_bind_addr": "1.2.3.4:20200", }, }, }, @@ -1733,7 +1647,6 @@ func TestReconcileCreateEndpoint(t *testing.T) { DestinationServiceID: "pod1-service-created", LocalServiceAddress: "", LocalServicePort: 0, - Config: map[string]any{"envoy_telemetry_collector_bind_socket_dir": string("/consul/connect-inject")}, }, ServiceMeta: map[string]string{constants.MetaKeyPodName: "pod1", metaKeyKubeServiceName: "service-created", constants.MetaKeyKubeNS: "default", metaKeyManagedBy: constants.ManagedByValue, metaKeySyntheticNode: "true"}, ServiceTags: []string{}, @@ -1748,7 +1661,6 @@ func TestReconcileCreateEndpoint(t *testing.T) { DestinationServiceID: "pod2-service-created", LocalServiceAddress: "", LocalServicePort: 0, - Config: map[string]any{"envoy_telemetry_collector_bind_socket_dir": string("/consul/connect-inject")}, }, ServiceMeta: map[string]string{constants.MetaKeyPodName: "pod2", metaKeyKubeServiceName: "service-created", constants.MetaKeyKubeNS: "default", metaKeyManagedBy: constants.ManagedByValue, metaKeySyntheticNode: "true"}, ServiceTags: []string{}, @@ -1874,7 +1786,6 @@ func TestReconcileCreateEndpoint(t *testing.T) { DestinationServiceID: "pod1-service-created", LocalServiceAddress: "", LocalServicePort: 0, - Config: map[string]any{"envoy_telemetry_collector_bind_socket_dir": string("/consul/connect-inject")}, }, ServiceMeta: map[string]string{constants.MetaKeyPodName: "pod1", metaKeyKubeServiceName: "service-created", constants.MetaKeyKubeNS: "default", metaKeyManagedBy: constants.ManagedByValue, metaKeySyntheticNode: "true"}, ServiceTags: []string{}, @@ -1889,7 +1800,6 @@ func TestReconcileCreateEndpoint(t *testing.T) { DestinationServiceID: "pod2-service-created", LocalServiceAddress: "", LocalServicePort: 0, - Config: map[string]any{"envoy_telemetry_collector_bind_socket_dir": string("/consul/connect-inject")}, }, ServiceMeta: map[string]string{constants.MetaKeyPodName: "pod2", metaKeyKubeServiceName: "service-created", constants.MetaKeyKubeNS: "default", metaKeyManagedBy: constants.ManagedByValue, metaKeySyntheticNode: "true"}, ServiceTags: []string{}, @@ -1941,17 +1851,6 @@ func TestReconcileCreateEndpoint(t *testing.T) { pod1.Annotations[constants.AnnotationUpstreams] = "upstream1:1234" pod1.Annotations[constants.AnnotationEnableMetrics] = "true" pod1.Annotations[constants.AnnotationPrometheusScrapePort] = "12345" - pod1.Spec.NodeName = "my-node" - node := &corev1.Node{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-node", - Namespace: "default", - Labels: map[string]string{ - corev1.LabelTopologyRegion: "us-west-1", - corev1.LabelTopologyZone: "us-west-1a", - }, - }, - } endpoint := &corev1.Endpoints{ ObjectMeta: metav1.ObjectMeta{ Name: "service-created", @@ -1972,7 +1871,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { }, }, } - return []runtime.Object{pod1, node, endpoint} + return []runtime.Object{pod1, endpoint} }, expectedConsulSvcInstances: []*api.CatalogService{ { @@ -1992,10 +1891,6 @@ func TestReconcileCreateEndpoint(t *testing.T) { }, ServiceTags: []string{"abc,123", "pod1"}, ServiceProxy: &api.AgentServiceConnectProxyConfig{}, - ServiceLocality: &api.Locality{ - Region: "us-west-1", - Zone: "us-west-1a", - }, }, }, expectedProxySvcInstances: []*api.CatalogService{ @@ -2017,14 +1912,9 @@ func TestReconcileCreateEndpoint(t *testing.T) { }, }, Config: map[string]interface{}{ - "envoy_prometheus_bind_addr": "0.0.0.0:12345", - "envoy_telemetry_collector_bind_socket_dir": "/consul/connect-inject", + "envoy_prometheus_bind_addr": "0.0.0.0:12345", }, }, - ServiceLocality: &api.Locality{ - Region: "us-west-1", - Zone: "us-west-1a", - }, ServiceMeta: map[string]string{ "name": "abc", "version": "2", @@ -2123,7 +2013,6 @@ func TestReconcileCreateEndpoint(t *testing.T) { DestinationServiceID: "pod1-service-created", LocalServiceAddress: "", LocalServicePort: 0, - Config: map[string]any{"envoy_telemetry_collector_bind_socket_dir": string("/consul/connect-inject")}, }, ServiceMeta: map[string]string{constants.MetaKeyPodName: "pod1", metaKeyKubeServiceName: "service-created", constants.MetaKeyKubeNS: "default", metaKeyManagedBy: constants.ManagedByValue, metaKeySyntheticNode: "true"}, ServiceTags: []string{}, @@ -2183,9 +2072,6 @@ func TestReconcileCreateEndpoint(t *testing.T) { EnableGatewayMetrics: true, } } - - ep.EnableTelemetryCollector = !tt.telemetryCollectorDisabled - namespacedName := types.NamespacedName{ Namespace: "default", Name: tt.svcName, @@ -2212,7 +2098,6 @@ func TestReconcileCreateEndpoint(t *testing.T) { require.Equal(t, tt.expectedConsulSvcInstances[i].ServicePort, instance.ServicePort) require.Equal(t, tt.expectedConsulSvcInstances[i].ServiceMeta, instance.ServiceMeta) require.Equal(t, tt.expectedConsulSvcInstances[i].ServiceTags, instance.ServiceTags) - require.Equal(t, tt.expectedConsulSvcInstances[i].ServiceLocality, instance.ServiceLocality) require.Equal(t, tt.expectedConsulSvcInstances[i].ServiceTaggedAddresses, instance.ServiceTaggedAddresses) require.Equal(t, tt.expectedConsulSvcInstances[i].ServiceProxy, instance.ServiceProxy) if tt.nodeMeta != nil { @@ -2229,7 +2114,6 @@ func TestReconcileCreateEndpoint(t *testing.T) { require.Equal(t, tt.expectedProxySvcInstances[i].ServicePort, instance.ServicePort) require.Equal(t, tt.expectedProxySvcInstances[i].ServiceMeta, instance.ServiceMeta) require.Equal(t, tt.expectedProxySvcInstances[i].ServiceTags, instance.ServiceTags) - require.Equal(t, tt.expectedProxySvcInstances[i].ServiceLocality, instance.ServiceLocality) if tt.nodeMeta != nil { require.Equal(t, tt.expectedProxySvcInstances[i].NodeMeta, instance.NodeMeta) } @@ -2260,36 +2144,6 @@ func TestReconcileCreateEndpoint(t *testing.T) { } } -func TestParseLocality(t *testing.T) { - t.Run("no labels", func(t *testing.T) { - n := corev1.Node{} - require.Nil(t, parseLocality(n)) - }) - - t.Run("zone only", func(t *testing.T) { - n := corev1.Node{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - corev1.LabelTopologyZone: "us-west-1a", - }, - }, - } - require.Nil(t, parseLocality(n)) - }) - - t.Run("everything", func(t *testing.T) { - n := corev1.Node{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - corev1.LabelTopologyRegion: "us-west-1", - corev1.LabelTopologyZone: "us-west-1a", - }, - }, - } - require.Equal(t, &api.Locality{Region: "us-west-1", Zone: "us-west-1a"}, parseLocality(n)) - }) -} - // Tests updating an Endpoints object. // - Tests updates via the register codepath: // - When an address in an Endpoint is updated, that the corresponding service instance in Consul is updated. @@ -3690,7 +3544,7 @@ func TestReconcileUpdateEndpoint(t *testing.T) { // Read the token from Consul. token, _, err := consulClient.ACL().TokenRead(tokenID, nil) if deregisteredServices.Contains(sID) { - require.Contains(t, err.Error(), "ACL not found") + require.EqualError(t, err, "Unexpected response code: 403 (ACL not found)") } else { require.NoError(t, err, "token should exist for service instance: "+sID) require.NotNil(t, token) @@ -4291,7 +4145,7 @@ func TestReconcileDeleteEndpoint(t *testing.T) { if tt.enableACLs { _, _, err = consulClient.ACL().TokenRead(token.AccessorID, nil) - require.Contains(t, err.Error(), "ACL not found") + require.EqualError(t, err, "Unexpected response code: 403 (ACL not found)") } }) } @@ -6674,54 +6528,3 @@ func createGatewayPod(name, ip string, annotations map[string]string) *corev1.Po } return pod } - -func TestReconcileAssignServiceVirtualIP(t *testing.T) { - t.Parallel() - ctx := context.Background() - cases := []struct { - name string - service *api.AgentService - expectErr bool - }{ - { - name: "valid service", - service: &api.AgentService{ - ID: "", - Service: "foo", - Port: 80, - Address: "1.2.3.4", - TaggedAddresses: map[string]api.ServiceAddress{ - "virtual": { - Address: "1.2.3.4", - Port: 80, - }, - }, - Meta: map[string]string{constants.MetaKeyKubeNS: "default"}, - }, - expectErr: false, - }, - { - name: "service missing IP should not error", - service: &api.AgentService{ - ID: "", - Service: "bar", - Meta: map[string]string{constants.MetaKeyKubeNS: "default"}, - }, - expectErr: false, - }, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - - // Create test consulServer server. - testClient := test.TestServerWithMockConnMgrWatcher(t, nil) - apiClient := testClient.APIClient - err := assignServiceVirtualIP(ctx, apiClient, c.service) - if err != nil { - require.True(t, c.expectErr) - } else { - require.False(t, c.expectErr) - } - }) - } -} diff --git a/control-plane/connect-inject/controllers/peering/peering_acceptor_controller.go b/control-plane/connect-inject/controllers/peering/peering_acceptor_controller.go index 3a1251aae6..044de55998 100644 --- a/control-plane/connect-inject/controllers/peering/peering_acceptor_controller.go +++ b/control-plane/connect-inject/controllers/peering/peering_acceptor_controller.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package peering import ( diff --git a/control-plane/connect-inject/controllers/peering/peering_acceptor_controller_test.go b/control-plane/connect-inject/controllers/peering/peering_acceptor_controller_test.go index 601be1a833..30db612602 100644 --- a/control-plane/connect-inject/controllers/peering/peering_acceptor_controller_test.go +++ b/control-plane/connect-inject/controllers/peering/peering_acceptor_controller_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package peering import ( diff --git a/control-plane/connect-inject/controllers/peering/peering_dialer_controller.go b/control-plane/connect-inject/controllers/peering/peering_dialer_controller.go index 37de792cb6..98646b1654 100644 --- a/control-plane/connect-inject/controllers/peering/peering_dialer_controller.go +++ b/control-plane/connect-inject/controllers/peering/peering_dialer_controller.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package peering import ( diff --git a/control-plane/connect-inject/controllers/peering/peering_dialer_controller_test.go b/control-plane/connect-inject/controllers/peering/peering_dialer_controller_test.go index c142cd9eee..3bc959ab0a 100644 --- a/control-plane/connect-inject/controllers/peering/peering_dialer_controller_test.go +++ b/control-plane/connect-inject/controllers/peering/peering_dialer_controller_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package peering import ( diff --git a/control-plane/connect-inject/metrics/metrics_configuration.go b/control-plane/connect-inject/metrics/metrics_configuration.go index 6f9c29c85b..09f01a4c87 100644 --- a/control-plane/connect-inject/metrics/metrics_configuration.go +++ b/control-plane/connect-inject/metrics/metrics_configuration.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package metrics import ( diff --git a/control-plane/connect-inject/metrics/metrics_configuration_test.go b/control-plane/connect-inject/metrics/metrics_configuration_test.go index 12045e28d1..ea232576b5 100644 --- a/control-plane/connect-inject/metrics/metrics_configuration_test.go +++ b/control-plane/connect-inject/metrics/metrics_configuration_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package metrics import ( diff --git a/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go b/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go index ad2e07fffa..c1fae3eff9 100644 --- a/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go +++ b/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package webhook import ( diff --git a/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go b/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go index d83b094d99..a65f49228d 100644 --- a/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go +++ b/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package webhook import ( diff --git a/control-plane/connect-inject/webhook/container_env.go b/control-plane/connect-inject/webhook/container_env.go index 2b2d7f5471..7c65dcd77c 100644 --- a/control-plane/connect-inject/webhook/container_env.go +++ b/control-plane/connect-inject/webhook/container_env.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package webhook import ( diff --git a/control-plane/connect-inject/webhook/container_env_test.go b/control-plane/connect-inject/webhook/container_env_test.go index 50d38832c0..62200e7bbd 100644 --- a/control-plane/connect-inject/webhook/container_env_test.go +++ b/control-plane/connect-inject/webhook/container_env_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package webhook import ( diff --git a/control-plane/connect-inject/webhook/container_init.go b/control-plane/connect-inject/webhook/container_init.go index 88962f771e..db13485489 100644 --- a/control-plane/connect-inject/webhook/container_init.go +++ b/control-plane/connect-inject/webhook/container_init.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package webhook import ( diff --git a/control-plane/connect-inject/webhook/container_init_test.go b/control-plane/connect-inject/webhook/container_init_test.go index fa2a95dbf9..5f14f44bc2 100644 --- a/control-plane/connect-inject/webhook/container_init_test.go +++ b/control-plane/connect-inject/webhook/container_init_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package webhook import ( diff --git a/control-plane/connect-inject/webhook/container_volume.go b/control-plane/connect-inject/webhook/container_volume.go index 76ba461c7b..b33567469b 100644 --- a/control-plane/connect-inject/webhook/container_volume.go +++ b/control-plane/connect-inject/webhook/container_volume.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package webhook import ( diff --git a/control-plane/connect-inject/webhook/dns.go b/control-plane/connect-inject/webhook/dns.go index 3f73928ece..ed4e95703b 100644 --- a/control-plane/connect-inject/webhook/dns.go +++ b/control-plane/connect-inject/webhook/dns.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package webhook import ( diff --git a/control-plane/connect-inject/webhook/dns_test.go b/control-plane/connect-inject/webhook/dns_test.go index e8d718557e..d6392c5317 100644 --- a/control-plane/connect-inject/webhook/dns_test.go +++ b/control-plane/connect-inject/webhook/dns_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package webhook import ( diff --git a/control-plane/connect-inject/webhook/health_checks_test.go b/control-plane/connect-inject/webhook/health_checks_test.go index 53e2781509..9279d8f140 100644 --- a/control-plane/connect-inject/webhook/health_checks_test.go +++ b/control-plane/connect-inject/webhook/health_checks_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package webhook import ( diff --git a/control-plane/connect-inject/webhook/heath_checks.go b/control-plane/connect-inject/webhook/heath_checks.go index 8986f9e985..42d6da08e1 100644 --- a/control-plane/connect-inject/webhook/heath_checks.go +++ b/control-plane/connect-inject/webhook/heath_checks.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package webhook import ( diff --git a/control-plane/connect-inject/webhook/mesh_webhook.go b/control-plane/connect-inject/webhook/mesh_webhook.go index d97bca6646..c8d412184b 100644 --- a/control-plane/connect-inject/webhook/mesh_webhook.go +++ b/control-plane/connect-inject/webhook/mesh_webhook.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package webhook import ( diff --git a/control-plane/connect-inject/webhook/mesh_webhook_ent_test.go b/control-plane/connect-inject/webhook/mesh_webhook_ent_test.go index 860694dcef..3f6b668c6b 100644 --- a/control-plane/connect-inject/webhook/mesh_webhook_ent_test.go +++ b/control-plane/connect-inject/webhook/mesh_webhook_ent_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - //go:build enterprise package webhook @@ -9,8 +6,8 @@ import ( "context" "testing" - "github.com/deckarep/golang-set" - logrtest "github.com/go-logr/logr/testing" + mapset "github.com/deckarep/golang-set" + logrtest "github.com/go-logr/logr/testr" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" "github.com/hashicorp/consul-k8s/control-plane/helper/test" "github.com/hashicorp/consul/api" @@ -52,7 +49,7 @@ func TestHandler_MutateWithNamespaces(t *testing.T) { { Name: "single destination namespace 'default' from k8s 'default'", Webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -74,7 +71,7 @@ func TestHandler_MutateWithNamespaces(t *testing.T) { { Name: "single destination namespace 'default' from k8s 'non-default'", Webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -96,7 +93,7 @@ func TestHandler_MutateWithNamespaces(t *testing.T) { { Name: "single destination namespace 'dest' from k8s 'default'", Webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -118,7 +115,7 @@ func TestHandler_MutateWithNamespaces(t *testing.T) { { Name: "single destination namespace 'dest' from k8s 'non-default'", Webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -140,7 +137,7 @@ func TestHandler_MutateWithNamespaces(t *testing.T) { { Name: "mirroring from k8s 'default'", Webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -163,7 +160,7 @@ func TestHandler_MutateWithNamespaces(t *testing.T) { { Name: "mirroring from k8s 'dest'", Webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -186,7 +183,7 @@ func TestHandler_MutateWithNamespaces(t *testing.T) { { Name: "mirroring with prefix from k8s 'default'", Webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -210,7 +207,7 @@ func TestHandler_MutateWithNamespaces(t *testing.T) { { Name: "mirroring with prefix from k8s 'dest'", Webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -298,7 +295,7 @@ func TestHandler_MutateWithNamespaces_ACLs(t *testing.T) { { Name: "acls + single destination namespace 'default' from k8s 'default'", Webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -321,7 +318,7 @@ func TestHandler_MutateWithNamespaces_ACLs(t *testing.T) { { Name: "acls + single destination namespace 'default' from k8s 'non-default'", Webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -344,7 +341,7 @@ func TestHandler_MutateWithNamespaces_ACLs(t *testing.T) { { Name: "acls + single destination namespace 'dest' from k8s 'default'", Webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -367,7 +364,7 @@ func TestHandler_MutateWithNamespaces_ACLs(t *testing.T) { { Name: "acls + single destination namespace 'dest' from k8s 'non-default'", Webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -390,7 +387,7 @@ func TestHandler_MutateWithNamespaces_ACLs(t *testing.T) { { Name: "acls + mirroring from k8s 'default'", Webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -414,7 +411,7 @@ func TestHandler_MutateWithNamespaces_ACLs(t *testing.T) { { Name: "acls + mirroring from k8s 'dest'", Webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -438,7 +435,7 @@ func TestHandler_MutateWithNamespaces_ACLs(t *testing.T) { { Name: "acls + mirroring with prefix from k8s 'default'", Webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -463,7 +460,7 @@ func TestHandler_MutateWithNamespaces_ACLs(t *testing.T) { { Name: "acls + mirroring with prefix from k8s 'dest'", Webhook: MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -603,7 +600,7 @@ func TestHandler_MutateWithNamespaces_Annotation(t *testing.T) { require.NoError(t, err) webhook := MeshWebhook{ - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, diff --git a/control-plane/connect-inject/webhook/mesh_webhook_test.go b/control-plane/connect-inject/webhook/mesh_webhook_test.go index 946933f7d7..9761551b85 100644 --- a/control-plane/connect-inject/webhook/mesh_webhook_test.go +++ b/control-plane/connect-inject/webhook/mesh_webhook_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package webhook import ( diff --git a/control-plane/connect-inject/webhook/redirect_traffic.go b/control-plane/connect-inject/webhook/redirect_traffic.go index b0cbefeeaa..8ae8fce068 100644 --- a/control-plane/connect-inject/webhook/redirect_traffic.go +++ b/control-plane/connect-inject/webhook/redirect_traffic.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package webhook import ( diff --git a/control-plane/connect-inject/webhook/redirect_traffic_test.go b/control-plane/connect-inject/webhook/redirect_traffic_test.go index bfa8ad8f2a..38aad20578 100644 --- a/control-plane/connect-inject/webhook/redirect_traffic_test.go +++ b/control-plane/connect-inject/webhook/redirect_traffic_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package webhook import ( diff --git a/control-plane/consul/consul.go b/control-plane/consul/consul.go index 3cf916ffbf..bb46308ff8 100644 --- a/control-plane/consul/consul.go +++ b/control-plane/consul/consul.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package consul import ( diff --git a/control-plane/consul/consul_test.go b/control-plane/consul/consul_test.go index 04afe08033..02430b0f48 100644 --- a/control-plane/consul/consul_test.go +++ b/control-plane/consul/consul_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package consul import ( diff --git a/control-plane/controllers/configentry_controller.go b/control-plane/controller/configentry_controller.go similarity index 84% rename from control-plane/controllers/configentry_controller.go rename to control-plane/controller/configentry_controller.go index 133afc0a1c..7782d5e9a9 100644 --- a/control-plane/controllers/configentry_controller.go +++ b/control-plane/controller/configentry_controller.go @@ -1,7 +1,4 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers +package controller import ( "context" @@ -199,7 +196,6 @@ func (r *ConfigEntryController) ReconcileEntry(ctx context.Context, crdCtrl Cont return r.syncFailed(ctx, logger, crdCtrl, configEntry, ConsulAgentError, fmt.Errorf("writing config entry to consul: %w", err)) } - logger.Info("config entry created", "request-time", writeMeta.RequestTime) return r.syncSuccessful(ctx, crdCtrl, configEntry) } @@ -268,15 +264,6 @@ func (r *ConfigEntryController) ReconcileEntry(ctx context.Context, crdCtrl Cont return r.syncSuccessful(ctx, crdCtrl, configEntry) } - // For resolvers and splitters, we need to set the ClusterIP of the matching service to Consul so that transparent - // proxy works correctly. Do not fail the reconcile if assigning the virtual IP returns an error. - if needsVirtualIPAssignment(r.DatacenterName, configEntry) { - err = assignServiceVirtualIP(ctx, logger, consulClient, crdCtrl, req.NamespacedName, configEntry, r.DatacenterName) - if err != nil { - logger.Error(err, "failed assigning service virtual ip") - } - } - return ctrl.Result{}, nil } @@ -391,75 +378,6 @@ func (r *ConfigEntryController) nonMatchingMigrationError(kubeEntry common.Confi return fmt.Errorf("migration failed: Kubernetes resource does not match existing Consul config entry: consul=%s, kube=%s", consulJSON, kubeJSON) } -// needsVirtualIPAssignment checks to see if a configEntry type needs to be assigned a virtual IP. -func needsVirtualIPAssignment(datacenterName string, configEntry common.ConfigEntryResource) bool { - switch configEntry.KubeKind() { - case common.ServiceResolver: - return true - case common.ServiceRouter: - return true - case common.ServiceSplitter: - return true - case common.ServiceDefaults: - return true - case common.ServiceIntentions: - entry := configEntry.ToConsul(datacenterName) - intention, ok := entry.(*capi.ServiceIntentionsConfigEntry) - if !ok { - return false - } - // We should not persist virtual ips if the destination is a wildcard - // in any form, since that would target multiple services. - return !strings.Contains(intention.Name, "*") && - !strings.Contains(intention.Namespace, "*") && - !strings.Contains(intention.Partition, "*") - } - return false -} - -// assignServiceVirtualIPs manually sends the ClusterIP for a matching service for ServiceRouter or ServiceSplitter -// CRDs to Consul so that it can be added to the virtual IP table. The assignment is skipped if the matching service -// does not exist or if an older version of Consul is being used. Endpoints Controller, on service registration, also -// manually sends a ClusterIP when a service is created. This increases the chance of a real IP ending up in the -// discovery chain. -func assignServiceVirtualIP(ctx context.Context, logger logr.Logger, consulClient *capi.Client, crdCtrl Controller, namespacedName types.NamespacedName, configEntry common.ConfigEntryResource, datacenter string) error { - service := corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: configEntry.KubernetesName(), - Namespace: namespacedName.Namespace, - }, - } - if err := crdCtrl.Get(ctx, namespacedName, &service); err != nil { - // It is non-fatal if the service does not exist. The ClusterIP will get added when the service is registered in - // the endpoints controller - if k8serr.IsNotFound(err) { - return nil - } - // Something is really wrong with the service - return err - } - - consulType := configEntry.ToConsul(datacenter) - wo := &capi.WriteOptions{ - Namespace: consulType.GetNamespace(), - Partition: consulType.GetPartition(), - } - - logger.Info("adding manual ip to virtual ip table in Consul", "name", service.Name) - _, _, err := consulClient.Internal().AssignServiceVirtualIP(ctx, consulType.GetName(), []string{service.Spec.ClusterIP}, wo) - if err != nil { - // Maintain backwards compatibility with older versions of Consul that do not support the manual VIP improvements. With the older version, the mesh - // will still work. - if isNotFoundErr(err) { - logger.Error(err, "failed to add ip to virtual ip table. Please upgrade Consul to version 1.16 or higher", "name", service.Name) - return nil - } else { - return err - } - } - return nil -} - func isNotFoundErr(err error) bool { return err != nil && strings.Contains(err.Error(), "404") } diff --git a/control-plane/controller/configentry_controller_ent_test.go b/control-plane/controller/configentry_controller_ent_test.go new file mode 100644 index 0000000000..aa59ddaceb --- /dev/null +++ b/control-plane/controller/configentry_controller_ent_test.go @@ -0,0 +1,759 @@ +//go:build enterprise + +package controller_test + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/go-logr/logr" + logrtest "github.com/go-logr/logr/testr" + "github.com/hashicorp/consul-k8s/control-plane/api/common" + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul-k8s/control-plane/controller" + "github.com/hashicorp/consul-k8s/control-plane/helper/test" + capi "github.com/hashicorp/consul/api" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +// NOTE: We're not testing each controller type here because that's done in +// the OSS tests and it would result in too many permutations. Instead +// we're only testing with the ServiceDefaults and ProxyDefaults controller which will exercise +// all the namespaces code for config entries that are namespaced and those that +// exist in the global namespace. + +func TestConfigEntryController_createsConfigEntry_consulNamespaces(tt *testing.T) { + tt.Parallel() + + cases := map[string]struct { + Mirror bool + MirrorPrefix string + SourceKubeNS string + DestConsulNS string + ExpConsulNS string + }{ + "SourceKubeNS=default, DestConsulNS=default": { + SourceKubeNS: "default", + DestConsulNS: "default", + ExpConsulNS: "default", + }, + "SourceKubeNS=kube, DestConsulNS=default": { + SourceKubeNS: "kube", + DestConsulNS: "default", + ExpConsulNS: "default", + }, + "SourceKubeNS=default, DestConsulNS=other": { + SourceKubeNS: "default", + DestConsulNS: "other", + ExpConsulNS: "other", + }, + "SourceKubeNS=kube, DestConsulNS=other": { + SourceKubeNS: "kube", + DestConsulNS: "other", + ExpConsulNS: "other", + }, + "SourceKubeNS=default, Mirror=true": { + SourceKubeNS: "default", + Mirror: true, + ExpConsulNS: "default", + }, + "SourceKubeNS=kube, Mirror=true": { + SourceKubeNS: "kube", + Mirror: true, + ExpConsulNS: "kube", + }, + "SourceKubeNS=default, Mirror=true, Prefix=prefix": { + SourceKubeNS: "default", + Mirror: true, + MirrorPrefix: "prefix-", + ExpConsulNS: "prefix-default", + }, + } + + for name, c := range cases { + configEntryKinds := map[string]struct { + ConsulKind string + ConsulNamespace string + KubeResource common.ConfigEntryResource + GetController func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *controller.ConfigEntryController) reconcile.Reconciler + AssertValidConfig func(entry capi.ConfigEntry) bool + }{ + "namespaced": { + ConsulKind: capi.ServiceDefaults, + KubeResource: &v1alpha1.ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: c.SourceKubeNS, + }, + Spec: v1alpha1.ServiceDefaultsSpec{ + Protocol: "http", + }, + }, + GetController: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *controller.ConfigEntryController) reconcile.Reconciler { + return &controller.ServiceDefaultsController{ + Client: client, + Log: logger, + Scheme: scheme, + ConfigEntryController: cont, + } + }, + AssertValidConfig: func(cfg capi.ConfigEntry) bool { + configEntry, ok := cfg.(*capi.ServiceConfigEntry) + if !ok { + return false + } + return configEntry.Protocol == "http" + }, + ConsulNamespace: c.ExpConsulNS, + }, + "global": { + ConsulKind: capi.ProxyDefaults, + KubeResource: &v1alpha1.ProxyDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.Global, + Namespace: c.SourceKubeNS, + }, + Spec: v1alpha1.ProxyDefaultsSpec{ + MeshGateway: v1alpha1.MeshGateway{ + Mode: "remote", + }, + }, + }, + GetController: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *controller.ConfigEntryController) reconcile.Reconciler { + return &controller.ProxyDefaultsController{ + Client: client, + Log: logger, + Scheme: scheme, + ConfigEntryController: cont, + } + }, + AssertValidConfig: func(cfg capi.ConfigEntry) bool { + configEntry, ok := cfg.(*capi.ProxyConfigEntry) + if !ok { + return false + } + return configEntry.MeshGateway.Mode == capi.MeshGatewayModeRemote + }, + ConsulNamespace: common.DefaultConsulNamespace, + }, + "intentions": { + ConsulKind: capi.ServiceIntentions, + KubeResource: &v1alpha1.ServiceIntentions{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: c.SourceKubeNS, + }, + Spec: v1alpha1.ServiceIntentionsSpec{ + Destination: v1alpha1.IntentionDestination{ + Name: "test", + Namespace: c.ExpConsulNS, + }, + Sources: v1alpha1.SourceIntentions{ + &v1alpha1.SourceIntention{ + Name: "baz", + Namespace: "bar", + Action: "allow", + }, + }, + }, + }, + GetController: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *controller.ConfigEntryController) reconcile.Reconciler { + return &controller.ServiceIntentionsController{ + Client: client, + Log: logger, + Scheme: scheme, + ConfigEntryController: cont, + } + }, + AssertValidConfig: func(cfg capi.ConfigEntry) bool { + configEntry, ok := cfg.(*capi.ServiceIntentionsConfigEntry) + if !ok { + return false + } + return configEntry.Sources[0].Action == capi.IntentionActionAllow + }, + ConsulNamespace: c.ExpConsulNS, + }, + } + + for kind, in := range configEntryKinds { + tt.Run(fmt.Sprintf("%s : %s", name, kind), func(t *testing.T) { + req := require.New(t) + s := runtime.NewScheme() + s.AddKnownTypes(v1alpha1.GroupVersion, in.KubeResource) + ctx := context.Background() + + testClient := test.TestServerWithMockConnMgrWatcher(t, nil) + testClient.TestServer.WaitForServiceIntentions(t) + consulClient := testClient.APIClient + + fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(in.KubeResource).Build() + + r := in.GetController( + fakeClient, + logrtest.New(t), + s, + &controller.ConfigEntryController{ + ConsulClientConfig: testClient.Cfg, + ConsulServerConnMgr: testClient.Watcher, + EnableConsulNamespaces: true, + EnableNSMirroring: c.Mirror, + NSMirroringPrefix: c.MirrorPrefix, + ConsulDestinationNamespace: c.DestConsulNS, + }, + ) + + resp, err := r.Reconcile(ctx, ctrl.Request{ + NamespacedName: types.NamespacedName{ + Namespace: c.SourceKubeNS, + Name: in.KubeResource.KubernetesName(), + }, + }) + req.NoError(err) + req.False(resp.Requeue) + + cfg, _, err := consulClient.ConfigEntries().Get(in.ConsulKind, in.KubeResource.ConsulName(), &capi.QueryOptions{ + Namespace: in.ConsulNamespace, + }) + req.NoError(err) + + result := in.AssertValidConfig(cfg) + req.True(result) + + // Check that the status is "synced". + err = fakeClient.Get(ctx, types.NamespacedName{ + Namespace: c.SourceKubeNS, + Name: in.KubeResource.KubernetesName(), + }, in.KubeResource) + req.NoError(err) + conditionSynced := in.KubeResource.SyncedConditionStatus() + req.Equal(conditionSynced, corev1.ConditionTrue) + }) + } + } +} + +func TestConfigEntryController_updatesConfigEntry_consulNamespaces(tt *testing.T) { + tt.Parallel() + + cases := map[string]struct { + Mirror bool + MirrorPrefix string + SourceKubeNS string + DestConsulNS string + ExpConsulNS string + }{ + "SourceKubeNS=default, DestConsulNS=default": { + SourceKubeNS: "default", + DestConsulNS: "default", + ExpConsulNS: "default", + }, + "SourceKubeNS=kube, DestConsulNS=default": { + SourceKubeNS: "kube", + DestConsulNS: "default", + ExpConsulNS: "default", + }, + "SourceKubeNS=default, DestConsulNS=other": { + SourceKubeNS: "default", + DestConsulNS: "other", + ExpConsulNS: "other", + }, + "SourceKubeNS=kube, DestConsulNS=other": { + SourceKubeNS: "kube", + DestConsulNS: "other", + ExpConsulNS: "other", + }, + "SourceKubeNS=default, Mirror=true": { + SourceKubeNS: "default", + Mirror: true, + ExpConsulNS: "default", + }, + "SourceKubeNS=kube, Mirror=true": { + SourceKubeNS: "kube", + Mirror: true, + ExpConsulNS: "kube", + }, + "SourceKubeNS=default, Mirror=true, Prefix=prefix": { + SourceKubeNS: "default", + Mirror: true, + MirrorPrefix: "prefix-", + ExpConsulNS: "prefix-default", + }, + } + + for name, c := range cases { + configEntryKinds := map[string]struct { + ConsulKind string + ConsulNamespace string + KubeResource common.ConfigEntryResource + GetControllerFunc func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *controller.ConfigEntryController) reconcile.Reconciler + AssertValidConfigFunc func(entry capi.ConfigEntry) bool + WriteConfigEntryFunc func(consulClient *capi.Client, namespace string) error + UpdateResourceFunc func(client client.Client, ctx context.Context, in common.ConfigEntryResource) error + }{ + "namespaced": { + ConsulKind: capi.ServiceDefaults, + KubeResource: &v1alpha1.ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: c.SourceKubeNS, + Finalizers: []string{controller.FinalizerName}, + }, + Spec: v1alpha1.ServiceDefaultsSpec{ + Protocol: "http", + }, + }, + ConsulNamespace: c.ExpConsulNS, + GetControllerFunc: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *controller.ConfigEntryController) reconcile.Reconciler { + return &controller.ServiceDefaultsController{ + Client: client, + Log: logger, + Scheme: scheme, + ConfigEntryController: cont, + } + }, + WriteConfigEntryFunc: func(consulClient *capi.Client, namespace string) error { + _, _, err := consulClient.ConfigEntries().Set(&capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "foo", + Protocol: "http", + }, &capi.WriteOptions{Namespace: namespace}) + return err + }, + UpdateResourceFunc: func(client client.Client, ctx context.Context, in common.ConfigEntryResource) error { + svcDefault := in.(*v1alpha1.ServiceDefaults) + svcDefault.Spec.Protocol = "tcp" + return client.Update(ctx, svcDefault) + }, + AssertValidConfigFunc: func(cfg capi.ConfigEntry) bool { + configEntry, ok := cfg.(*capi.ServiceConfigEntry) + if !ok { + return false + } + return configEntry.Protocol == "tcp" + }, + }, + "global": { + ConsulKind: capi.ProxyDefaults, + KubeResource: &v1alpha1.ProxyDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.Global, + Namespace: c.SourceKubeNS, + Finalizers: []string{controller.FinalizerName}, + }, + Spec: v1alpha1.ProxyDefaultsSpec{ + MeshGateway: v1alpha1.MeshGateway{ + Mode: "remote", + }, + }, + }, + ConsulNamespace: common.DefaultConsulNamespace, + GetControllerFunc: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *controller.ConfigEntryController) reconcile.Reconciler { + return &controller.ProxyDefaultsController{ + Client: client, + Log: logger, + Scheme: scheme, + ConfigEntryController: cont, + } + }, + WriteConfigEntryFunc: func(consulClient *capi.Client, namespace string) error { + _, _, err := consulClient.ConfigEntries().Set(&capi.ProxyConfigEntry{ + Kind: capi.ProxyDefaults, + Name: common.Global, + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeRemote, + }, + }, &capi.WriteOptions{Namespace: namespace}) + return err + }, + UpdateResourceFunc: func(client client.Client, ctx context.Context, in common.ConfigEntryResource) error { + proxyDefaults := in.(*v1alpha1.ProxyDefaults) + proxyDefaults.Spec.MeshGateway.Mode = "local" + return client.Update(ctx, proxyDefaults) + }, + AssertValidConfigFunc: func(cfg capi.ConfigEntry) bool { + configEntry, ok := cfg.(*capi.ProxyConfigEntry) + if !ok { + return false + } + return configEntry.MeshGateway.Mode == capi.MeshGatewayModeLocal + }, + }, + "intentions": { + ConsulKind: capi.ServiceIntentions, + KubeResource: &v1alpha1.ServiceIntentions{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: c.SourceKubeNS, + Finalizers: []string{controller.FinalizerName}, + }, + Spec: v1alpha1.ServiceIntentionsSpec{ + Destination: v1alpha1.IntentionDestination{ + Name: "foo", + Namespace: c.ExpConsulNS, + }, + Sources: v1alpha1.SourceIntentions{ + &v1alpha1.SourceIntention{ + Name: "bar", + Namespace: "baz", + Action: "deny", + }, + }, + }, + }, + ConsulNamespace: c.ExpConsulNS, + GetControllerFunc: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *controller.ConfigEntryController) reconcile.Reconciler { + return &controller.ServiceIntentionsController{ + Client: client, + Log: logger, + Scheme: scheme, + ConfigEntryController: cont, + } + }, + WriteConfigEntryFunc: func(consulClient *capi.Client, namespace string) error { + _, _, err := consulClient.ConfigEntries().Set(&capi.ServiceIntentionsConfigEntry{ + Kind: capi.ServiceIntentions, + Name: "foo", + Sources: []*capi.SourceIntention{ + { + Name: "bar", + Namespace: "baz", + Action: capi.IntentionActionDeny, + }, + }, + }, &capi.WriteOptions{Namespace: namespace}) + return err + }, + UpdateResourceFunc: func(client client.Client, ctx context.Context, in common.ConfigEntryResource) error { + svcIntention := in.(*v1alpha1.ServiceIntentions) + svcIntention.Spec.Sources[0].Action = "allow" + return client.Update(ctx, svcIntention) + }, + AssertValidConfigFunc: func(cfg capi.ConfigEntry) bool { + configEntry, ok := cfg.(*capi.ServiceIntentionsConfigEntry) + if !ok { + return false + } + return configEntry.Sources[0].Action == capi.IntentionActionAllow + }, + }, + } + for kind, in := range configEntryKinds { + tt.Run(fmt.Sprintf("%s : %s", name, kind), func(t *testing.T) { + req := require.New(t) + s := runtime.NewScheme() + s.AddKnownTypes(v1alpha1.GroupVersion, in.KubeResource) + ctx := context.Background() + + testClient := test.TestServerWithMockConnMgrWatcher(t, nil) + testClient.TestServer.WaitForServiceIntentions(t) + consulClient := testClient.APIClient + + fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(in.KubeResource).Build() + + r := in.GetControllerFunc( + fakeClient, + logrtest.New(t), + s, + &controller.ConfigEntryController{ + ConsulClientConfig: testClient.Cfg, + ConsulServerConnMgr: testClient.Watcher, + EnableConsulNamespaces: true, + EnableNSMirroring: c.Mirror, + NSMirroringPrefix: c.MirrorPrefix, + ConsulDestinationNamespace: c.DestConsulNS, + }, + ) + + // We haven't run reconcile yet so ensure it's created in Consul. + { + if in.ConsulNamespace != "default" { + _, _, err := consulClient.Namespaces().Create(&capi.Namespace{ + Name: in.ConsulNamespace, + }, nil) + req.NoError(err) + } + + err := in.WriteConfigEntryFunc(consulClient, in.ConsulNamespace) + req.NoError(err) + } + + // Now update it. + { + // First get it so we have the latest revision number. + err := fakeClient.Get(ctx, types.NamespacedName{ + Namespace: c.SourceKubeNS, + Name: in.KubeResource.KubernetesName(), + }, in.KubeResource) + req.NoError(err) + + // Update the resource. + err = in.UpdateResourceFunc(fakeClient, ctx, in.KubeResource) + req.NoError(err) + + resp, err := r.Reconcile(ctx, ctrl.Request{ + NamespacedName: types.NamespacedName{ + Namespace: c.SourceKubeNS, + Name: in.KubeResource.KubernetesName(), + }, + }) + req.NoError(err) + req.False(resp.Requeue) + + cfg, _, err := consulClient.ConfigEntries().Get(in.ConsulKind, in.KubeResource.ConsulName(), &capi.QueryOptions{ + Namespace: in.ConsulNamespace, + }) + req.NoError(err) + req.True(in.AssertValidConfigFunc(cfg)) + } + }) + } + } +} + +func TestConfigEntryController_deletesConfigEntry_consulNamespaces(tt *testing.T) { + tt.Parallel() + + cases := map[string]struct { + Mirror bool + MirrorPrefix string + SourceKubeNS string + DestConsulNS string + ExpConsulNS string + }{ + "SourceKubeNS=default, DestConsulNS=default": { + SourceKubeNS: "default", + DestConsulNS: "default", + ExpConsulNS: "default", + }, + "SourceKubeNS=kube, DestConsulNS=default": { + SourceKubeNS: "kube", + DestConsulNS: "default", + ExpConsulNS: "default", + }, + "SourceKubeNS=default, DestConsulNS=other": { + SourceKubeNS: "default", + DestConsulNS: "other", + ExpConsulNS: "other", + }, + "SourceKubeNS=kube, DestConsulNS=other": { + SourceKubeNS: "kube", + DestConsulNS: "other", + ExpConsulNS: "other", + }, + "SourceKubeNS=default, Mirror=true": { + SourceKubeNS: "default", + Mirror: true, + ExpConsulNS: "default", + }, + "SourceKubeNS=kube, Mirror=true": { + SourceKubeNS: "kube", + Mirror: true, + ExpConsulNS: "kube", + }, + "SourceKubeNS=default, Mirror=true, Prefix=prefix": { + SourceKubeNS: "default", + Mirror: true, + MirrorPrefix: "prefix-", + ExpConsulNS: "prefix-default", + }, + } + + for name, c := range cases { + configEntryKinds := map[string]struct { + ConsulKind string + ConsulNamespace string + KubeResource common.ConfigEntryResource + GetControllerFunc func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *controller.ConfigEntryController) reconcile.Reconciler + WriteConfigEntryFunc func(consulClient *capi.Client, namespace string) error + }{ + "namespaced": { + ConsulKind: capi.ServiceDefaults, + // Create it with the deletion timestamp set to mimic that it's already + // been marked for deletion. + KubeResource: &v1alpha1.ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: c.SourceKubeNS, + Finalizers: []string{controller.FinalizerName}, + DeletionTimestamp: &metav1.Time{Time: time.Now()}, + }, + Spec: v1alpha1.ServiceDefaultsSpec{ + Protocol: "http", + }, + }, + ConsulNamespace: c.ExpConsulNS, + GetControllerFunc: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *controller.ConfigEntryController) reconcile.Reconciler { + return &controller.ServiceDefaultsController{ + Client: client, + Log: logger, + Scheme: scheme, + ConfigEntryController: cont, + } + }, + WriteConfigEntryFunc: func(consulClient *capi.Client, namespace string) error { + _, _, err := consulClient.ConfigEntries().Set(&capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: "foo", + Protocol: "http", + }, &capi.WriteOptions{Namespace: namespace}) + return err + }, + }, + "global": { + ConsulKind: capi.ProxyDefaults, + // Create it with the deletion timestamp set to mimic that it's already + // been marked for deletion. + KubeResource: &v1alpha1.ProxyDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.Global, + Namespace: c.SourceKubeNS, + Finalizers: []string{controller.FinalizerName}, + DeletionTimestamp: &metav1.Time{Time: time.Now()}, + }, + Spec: v1alpha1.ProxyDefaultsSpec{ + MeshGateway: v1alpha1.MeshGateway{ + Mode: "remote", + }, + }, + }, + ConsulNamespace: common.DefaultConsulNamespace, + GetControllerFunc: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *controller.ConfigEntryController) reconcile.Reconciler { + return &controller.ProxyDefaultsController{ + Client: client, + Log: logger, + Scheme: scheme, + ConfigEntryController: cont, + } + }, + WriteConfigEntryFunc: func(consulClient *capi.Client, namespace string) error { + _, _, err := consulClient.ConfigEntries().Set(&capi.ProxyConfigEntry{ + Kind: capi.ProxyDefaults, + Name: common.Global, + MeshGateway: capi.MeshGatewayConfig{ + Mode: capi.MeshGatewayModeRemote, + }, + }, &capi.WriteOptions{Namespace: namespace}) + return err + }, + }, + "intentions": { + ConsulKind: capi.ServiceIntentions, + // Create it with the deletion timestamp set to mimic that it's already + // been marked for deletion. + KubeResource: &v1alpha1.ServiceIntentions{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: c.SourceKubeNS, + Finalizers: []string{controller.FinalizerName}, + DeletionTimestamp: &metav1.Time{Time: time.Now()}, + }, + Spec: v1alpha1.ServiceIntentionsSpec{ + Destination: v1alpha1.IntentionDestination{ + Name: "test", + Namespace: c.ExpConsulNS, + }, + Sources: v1alpha1.SourceIntentions{ + &v1alpha1.SourceIntention{ + Name: "bar", + Namespace: "baz", + Action: "deny", + }, + }, + }, + }, + ConsulNamespace: c.ExpConsulNS, + GetControllerFunc: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *controller.ConfigEntryController) reconcile.Reconciler { + return &controller.ServiceIntentionsController{ + Client: client, + Log: logger, + Scheme: scheme, + ConfigEntryController: cont, + } + }, + WriteConfigEntryFunc: func(consulClient *capi.Client, namespace string) error { + _, _, err := consulClient.ConfigEntries().Set(&capi.ServiceIntentionsConfigEntry{ + Kind: capi.ServiceIntentions, + Name: "test", + Sources: []*capi.SourceIntention{ + { + Name: "bar", + Namespace: "baz", + Action: capi.IntentionActionDeny, + }, + }, + }, &capi.WriteOptions{Namespace: namespace}) + return err + }, + }, + } + for kind, in := range configEntryKinds { + tt.Run(fmt.Sprintf("%s : %s", name, kind), func(t *testing.T) { + req := require.New(t) + + s := runtime.NewScheme() + s.AddKnownTypes(v1alpha1.GroupVersion, in.KubeResource) + + testClient := test.TestServerWithMockConnMgrWatcher(t, nil) + testClient.TestServer.WaitForServiceIntentions(t) + consulClient := testClient.APIClient + + fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(in.KubeResource).Build() + + r := in.GetControllerFunc( + fakeClient, + logrtest.New(t), + s, + &controller.ConfigEntryController{ + ConsulClientConfig: testClient.Cfg, + ConsulServerConnMgr: testClient.Watcher, + EnableConsulNamespaces: true, + EnableNSMirroring: c.Mirror, + NSMirroringPrefix: c.MirrorPrefix, + ConsulDestinationNamespace: c.DestConsulNS, + }, + ) + + // We haven't run reconcile yet so ensure it's created in Consul. + { + if in.ConsulNamespace != "default" { + _, _, err := consulClient.Namespaces().Create(&capi.Namespace{ + Name: in.ConsulNamespace, + }, nil) + req.NoError(err) + } + + err := in.WriteConfigEntryFunc(consulClient, in.ConsulNamespace) + req.NoError(err) + } + + // Now run reconcile. It's marked for deletion so this should delete it. + { + resp, err := r.Reconcile(context.Background(), ctrl.Request{ + NamespacedName: types.NamespacedName{ + Namespace: c.SourceKubeNS, + Name: in.KubeResource.KubernetesName(), + }, + }) + req.NoError(err) + req.False(resp.Requeue) + + _, _, err = consulClient.ConfigEntries().Get(in.ConsulKind, in.KubeResource.ConsulName(), &capi.QueryOptions{ + Namespace: in.ConsulNamespace, + }) + req.EqualError(err, fmt.Sprintf(`Unexpected response code: 404 (Config entry not found for "%s" / "%s")`, in.ConsulKind, in.KubeResource.ConsulName())) + } + }) + } + } +} diff --git a/control-plane/controllers/configentry_controller_test.go b/control-plane/controller/configentry_controller_test.go similarity index 85% rename from control-plane/controllers/configentry_controller_test.go rename to control-plane/controller/configentry_controller_test.go index 071d67ca6f..3b8ba9e561 100644 --- a/control-plane/controllers/configentry_controller_test.go +++ b/control-plane/controller/configentry_controller_test.go @@ -1,7 +1,4 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers +package controller import ( "context" @@ -22,7 +19,6 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -432,50 +428,6 @@ func TestConfigEntryControllers_createsConfigEntry(t *testing.T) { require.Equal(t, "sni", resource.Services[0].SNI) }, }, - { - kubeKind: "JWTProvider", - consulKind: capi.JWTProvider, - configEntryResource: &v1alpha1.JWTProvider{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-jwt-provider", - Namespace: kubeNS, - }, - Spec: v1alpha1.JWTProviderSpec{ - JSONWebKeySet: &v1alpha1.JSONWebKeySet{ - Local: &v1alpha1.LocalJWKS{ - Filename: "jwks.txt", - }, - }, - Issuer: "test-issuer", - }, - }, - reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) testReconciler { - return &JWTProviderController{ - Client: client, - Log: logger, - ConfigEntryController: &ConfigEntryController{ - ConsulClientConfig: cfg, - ConsulServerConnMgr: watcher, - DatacenterName: datacenterName, - }, - } - }, - compare: func(t *testing.T, consulEntry capi.ConfigEntry) { - jwt, ok := consulEntry.(*capi.JWTProviderConfigEntry) - require.True(t, ok, "cast error") - require.Equal(t, capi.JWTProvider, jwt.Kind) - require.Equal(t, "test-jwt-provider", jwt.Name) - require.Equal(t, - &capi.JSONWebKeySet{ - Local: &capi.LocalJWKS{ - Filename: "jwks.txt", - }, - }, - jwt.JSONWebKeySet, - ) - require.Equal(t, "test-issuer", jwt.Issuer) - }, - }, } for _, c := range cases { @@ -953,56 +905,6 @@ func TestConfigEntryControllers_updatesConfigEntry(t *testing.T) { require.Equal(t, "new-sni", resource.Services[0].SNI) }, }, - { - kubeKind: "JWTProvider", - consulKind: capi.JWTProvider, - configEntryResource: &v1alpha1.JWTProvider{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-jwt-provider", - Namespace: kubeNS, - }, - Spec: v1alpha1.JWTProviderSpec{ - JSONWebKeySet: &v1alpha1.JSONWebKeySet{ - Local: &v1alpha1.LocalJWKS{ - Filename: "jwks.txt", - }, - }, - Issuer: "test-issuer", - }, - }, - reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) testReconciler { - return &JWTProviderController{ - Client: client, - Log: logger, - ConfigEntryController: &ConfigEntryController{ - ConsulClientConfig: cfg, - ConsulServerConnMgr: watcher, - DatacenterName: datacenterName, - }, - } - }, - updateF: func(resource common.ConfigEntryResource) { - jwt := resource.(*v1alpha1.JWTProvider) - jwt.Spec.Issuer = "test-updated-issuer" - jwt.Spec.Audiences = []string{"aud1"} - }, - compare: func(t *testing.T, consulEntry capi.ConfigEntry) { - jwt, ok := consulEntry.(*capi.JWTProviderConfigEntry) - require.True(t, ok, "cast error") - require.Equal(t, capi.JWTProvider, jwt.Kind) - require.Equal(t, "test-jwt-provider", jwt.Name) - require.Equal(t, - &capi.JSONWebKeySet{ - Local: &capi.LocalJWKS{ - Filename: "jwks.txt", - }, - }, - jwt.JSONWebKeySet, - ) - require.Equal(t, "test-updated-issuer", jwt.Issuer) - require.Equal(t, []string{"aud1"}, jwt.Audiences) - }, - }, } for _, c := range cases { @@ -1404,37 +1306,6 @@ func TestConfigEntryControllers_deletesConfigEntry(t *testing.T) { } }, }, - { - kubeKind: "JWTProvider", - consulKind: capi.JWTProvider, - configEntryResourceWithDeletion: &v1alpha1.JWTProvider{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.Global, - Namespace: kubeNS, - DeletionTimestamp: &metav1.Time{Time: time.Now()}, - Finalizers: []string{FinalizerName}, - }, - Spec: v1alpha1.JWTProviderSpec{ - JSONWebKeySet: &v1alpha1.JSONWebKeySet{ - Local: &v1alpha1.LocalJWKS{ - Filename: "jwks.txt", - }, - }, - Issuer: "test-issuer", - }, - }, - reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) testReconciler { - return &JWTProviderController{ - Client: client, - Log: logger, - ConfigEntryController: &ConfigEntryController{ - ConsulClientConfig: cfg, - ConsulServerConnMgr: watcher, - DatacenterName: datacenterName, - }, - } - }, - }, } for _, c := range cases { @@ -2017,235 +1888,3 @@ func TestConfigEntryController_Migration(t *testing.T) { }) } } - -func TestConfigEntryControllers_assignServiceVirtualIP(t *testing.T) { - t.Parallel() - kubeNS := "default" - - cases := []struct { - name string - kubeKind string - consulKind string - consulPrereqs []capi.ConfigEntry - configEntryResource common.ConfigEntryResource - service corev1.Service - reconciler func(client.Client, *consul.Config, consul.ServerConnectionManager, logr.Logger) Controller - expectErr bool - }{ - { - name: "ServiceResolver no error and vip should be assigned", - kubeKind: "ServiceResolver", - consulKind: capi.ServiceRouter, - configEntryResource: &v1alpha1.ServiceRouter{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: kubeNS, - }, - Spec: v1alpha1.ServiceRouterSpec{ - Routes: []v1alpha1.ServiceRoute{ - { - Match: &v1alpha1.ServiceRouteMatch{ - HTTP: &v1alpha1.ServiceRouteHTTPMatch{ - PathPrefix: "/admin", - }, - }, - }, - }, - }, - }, - service: corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: kubeNS, - }, - Spec: corev1.ServiceSpec{ - ClusterIP: "10.0.0.1", - Ports: []corev1.ServicePort{ - { - Port: 8081, - }, - }, - }, - }, - reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) Controller { - return &ServiceRouterController{ - Client: client, - Log: logger, - ConfigEntryController: &ConfigEntryController{ - ConsulClientConfig: cfg, - ConsulServerConnMgr: watcher, - DatacenterName: datacenterName, - }, - } - }, - expectErr: false, - }, - { - name: "ServiceRouter no error and vip should be assigned", - kubeKind: "ServiceRouter", - consulKind: capi.ServiceRouter, - consulPrereqs: []capi.ConfigEntry{ - &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "bar", - Protocol: "http", - }, - }, - configEntryResource: &v1alpha1.ServiceRouter{ - ObjectMeta: metav1.ObjectMeta{ - Name: "bar", - Namespace: kubeNS, - }, - Spec: v1alpha1.ServiceRouterSpec{ - Routes: []v1alpha1.ServiceRoute{ - { - Match: &v1alpha1.ServiceRouteMatch{ - HTTP: &v1alpha1.ServiceRouteHTTPMatch{ - PathPrefix: "/admin", - }, - }, - }, - }, - }, - }, - service: corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "bar", - Namespace: kubeNS, - }, - Spec: corev1.ServiceSpec{ - ClusterIP: "10.0.0.2", - Ports: []corev1.ServicePort{ - { - Port: 8081, - }, - }, - }, - }, - reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) Controller { - return &ServiceRouterController{ - Client: client, - Log: logger, - ConfigEntryController: &ConfigEntryController{ - ConsulClientConfig: cfg, - ConsulServerConnMgr: watcher, - DatacenterName: datacenterName, - }, - } - }, - expectErr: false, - }, - { - name: "ServiceRouter should fail because service does not have a valid IP address", - kubeKind: "ServiceRouter", - consulKind: capi.ServiceRouter, - consulPrereqs: []capi.ConfigEntry{ - &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "bar", - Protocol: "http", - }, - }, - configEntryResource: &v1alpha1.ServiceRouter{ - ObjectMeta: metav1.ObjectMeta{ - Name: "bar", - Namespace: kubeNS, - }, - Spec: v1alpha1.ServiceRouterSpec{ - Routes: []v1alpha1.ServiceRoute{ - { - Match: &v1alpha1.ServiceRouteMatch{ - HTTP: &v1alpha1.ServiceRouteHTTPMatch{ - PathPrefix: "/admin", - }, - }, - }, - }, - }, - }, - service: corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "bar", - Namespace: kubeNS, - }, - }, - reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) Controller { - return &ServiceRouterController{ - Client: client, - Log: logger, - ConfigEntryController: &ConfigEntryController{ - ConsulClientConfig: cfg, - ConsulServerConnMgr: watcher, - DatacenterName: datacenterName, - }, - } - }, - expectErr: true, - }, - { - name: "ServiceSplitter no error because a matching service does not exist", - kubeKind: "ServiceSplitter", - consulKind: capi.ServiceSplitter, - consulPrereqs: []capi.ConfigEntry{ - &capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "foo", - Protocol: "http", - }, - }, - configEntryResource: &v1alpha1.ServiceSplitter{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: kubeNS, - }, - Spec: v1alpha1.ServiceSplitterSpec{ - Splits: []v1alpha1.ServiceSplit{ - { - Weight: 100, - }, - }, - }, - }, - reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) Controller { - return &ServiceSplitterController{ - Client: client, - Log: logger, - ConfigEntryController: &ConfigEntryController{ - ConsulClientConfig: cfg, - ConsulServerConnMgr: watcher, - DatacenterName: datacenterName, - }, - } - }, - expectErr: false, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - ctx := context.Background() - - s := runtime.NewScheme() - s.AddKnownTypes(v1alpha1.GroupVersion, c.configEntryResource) - s.AddKnownTypes(schema.GroupVersion{Group: "", Version: "v1"}, &corev1.Service{}) - fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(&c.service, c.configEntryResource).Build() - - testClient := test.TestServerWithMockConnMgrWatcher(t, nil) - testClient.TestServer.WaitForLeader(t) - consulClient := testClient.APIClient - - ctrl := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.New(t)) - namespacedName := types.NamespacedName{ - Namespace: kubeNS, - Name: c.configEntryResource.KubernetesName(), - } - - err := assignServiceVirtualIP(ctx, ctrl.Logger(namespacedName), consulClient, ctrl, namespacedName, c.configEntryResource, "dc1") - if err != nil { - require.True(t, c.expectErr) - } else { - require.False(t, c.expectErr) - } - }) - } -} diff --git a/control-plane/controllers/exportedservices_controller.go b/control-plane/controller/exportedservices_controller.go similarity index 94% rename from control-plane/controllers/exportedservices_controller.go rename to control-plane/controller/exportedservices_controller.go index 2e82ed0eae..9466360207 100644 --- a/control-plane/controllers/exportedservices_controller.go +++ b/control-plane/controller/exportedservices_controller.go @@ -1,7 +1,4 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers +package controller import ( "context" diff --git a/control-plane/controllers/exportedservices_controller_ent_test.go b/control-plane/controller/exportedservices_controller_ent_test.go similarity index 93% rename from control-plane/controllers/exportedservices_controller_ent_test.go rename to control-plane/controller/exportedservices_controller_ent_test.go index 94a605eab4..29b4e006f1 100644 --- a/control-plane/controllers/exportedservices_controller_ent_test.go +++ b/control-plane/controller/exportedservices_controller_ent_test.go @@ -1,9 +1,6 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - //go:build enterprise -package controllers_test +package controller_test import ( "context" @@ -11,10 +8,10 @@ import ( "testing" "time" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/hashicorp/consul-k8s/control-plane/api/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul-k8s/control-plane/controllers" + "github.com/hashicorp/consul-k8s/control-plane/controller" "github.com/hashicorp/consul-k8s/control-plane/helper/test" capi "github.com/hashicorp/consul/api" "github.com/stretchr/testify/require" @@ -103,11 +100,11 @@ func TestExportedServicesController_createsExportedServices(tt *testing.T) { fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(exportedServices).Build() - controller := &controllers.ExportedServicesController{ + controller := &controller.ExportedServicesController{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), Scheme: s, - ConfigEntryController: &controllers.ConfigEntryController{ + ConfigEntryController: &controller.ConfigEntryController{ ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, EnableConsulNamespaces: true, @@ -195,7 +192,7 @@ func TestExportedServicesController_updatesExportedServices(tt *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: c.SourceKubeNS, - Finalizers: []string{controllers.FinalizerName}, + Finalizers: []string{controller.FinalizerName}, }, Spec: v1alpha1.ExportedServicesSpec{ Services: []v1alpha1.ExportedService{ @@ -218,11 +215,11 @@ func TestExportedServicesController_updatesExportedServices(tt *testing.T) { consulClient := testClient.APIClient fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(exportedServices).Build() - controller := &controllers.ExportedServicesController{ + controller := &controller.ExportedServicesController{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), Scheme: s, - ConfigEntryController: &controllers.ConfigEntryController{ + ConfigEntryController: &controller.ConfigEntryController{ ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, EnableConsulNamespaces: true, @@ -332,7 +329,7 @@ func TestExportedServicesController_deletesExportedServices(tt *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: c.SourceKubeNS, - Finalizers: []string{controllers.FinalizerName}, + Finalizers: []string{controller.FinalizerName}, DeletionTimestamp: &metav1.Time{Time: time.Now()}, }, Spec: v1alpha1.ExportedServicesSpec{ @@ -356,11 +353,11 @@ func TestExportedServicesController_deletesExportedServices(tt *testing.T) { fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(exportedServices).Build() - controller := &controllers.ExportedServicesController{ + controller := &controller.ExportedServicesController{ Client: fakeClient, - Log: logrtest.NewTestLogger(t), + Log: logrtest.New(t), Scheme: s, - ConfigEntryController: &controllers.ConfigEntryController{ + ConfigEntryController: &controller.ConfigEntryController{ ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, EnableConsulNamespaces: true, diff --git a/control-plane/controllers/ingressgateway_controller.go b/control-plane/controller/ingressgateway_controller.go similarity index 94% rename from control-plane/controllers/ingressgateway_controller.go rename to control-plane/controller/ingressgateway_controller.go index 5bcb39bc2a..5a6a07776b 100644 --- a/control-plane/controllers/ingressgateway_controller.go +++ b/control-plane/controller/ingressgateway_controller.go @@ -1,7 +1,4 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers +package controller import ( "context" diff --git a/control-plane/controllers/mesh_controller.go b/control-plane/controller/mesh_controller.go similarity index 93% rename from control-plane/controllers/mesh_controller.go rename to control-plane/controller/mesh_controller.go index ba78f0c144..94b0df6a5e 100644 --- a/control-plane/controllers/mesh_controller.go +++ b/control-plane/controller/mesh_controller.go @@ -1,7 +1,4 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers +package controller import ( "context" diff --git a/control-plane/controllers/proxydefaults_controller.go b/control-plane/controller/proxydefaults_controller.go similarity index 93% rename from control-plane/controllers/proxydefaults_controller.go rename to control-plane/controller/proxydefaults_controller.go index 882843bf9e..fe929c1bf1 100644 --- a/control-plane/controllers/proxydefaults_controller.go +++ b/control-plane/controller/proxydefaults_controller.go @@ -1,7 +1,4 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers +package controller import ( "context" diff --git a/control-plane/controllers/servicedefaults_controller.go b/control-plane/controller/servicedefaults_controller.go similarity index 94% rename from control-plane/controllers/servicedefaults_controller.go rename to control-plane/controller/servicedefaults_controller.go index 0496788bb3..e58e186234 100644 --- a/control-plane/controllers/servicedefaults_controller.go +++ b/control-plane/controller/servicedefaults_controller.go @@ -1,7 +1,4 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers +package controller import ( "context" diff --git a/control-plane/controllers/serviceintentions_controller.go b/control-plane/controller/serviceintentions_controller.go similarity index 94% rename from control-plane/controllers/serviceintentions_controller.go rename to control-plane/controller/serviceintentions_controller.go index 4461a82dd6..e5bfab959e 100644 --- a/control-plane/controllers/serviceintentions_controller.go +++ b/control-plane/controller/serviceintentions_controller.go @@ -1,7 +1,4 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers +package controller import ( "context" diff --git a/control-plane/controllers/serviceresolver_controller.go b/control-plane/controller/serviceresolver_controller.go similarity index 94% rename from control-plane/controllers/serviceresolver_controller.go rename to control-plane/controller/serviceresolver_controller.go index cfc5c31ca3..3019369dc6 100644 --- a/control-plane/controllers/serviceresolver_controller.go +++ b/control-plane/controller/serviceresolver_controller.go @@ -1,7 +1,4 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers +package controller import ( "context" diff --git a/control-plane/controllers/servicerouter_controller.go b/control-plane/controller/servicerouter_controller.go similarity index 94% rename from control-plane/controllers/servicerouter_controller.go rename to control-plane/controller/servicerouter_controller.go index a0b3bc0581..9c435169f4 100644 --- a/control-plane/controllers/servicerouter_controller.go +++ b/control-plane/controller/servicerouter_controller.go @@ -1,7 +1,4 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers +package controller import ( "context" diff --git a/control-plane/controllers/servicesplitter_controller.go b/control-plane/controller/servicesplitter_controller.go similarity index 94% rename from control-plane/controllers/servicesplitter_controller.go rename to control-plane/controller/servicesplitter_controller.go index b733fb9cb5..f977301afe 100644 --- a/control-plane/controllers/servicesplitter_controller.go +++ b/control-plane/controller/servicesplitter_controller.go @@ -1,7 +1,4 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers +package controller import ( "context" diff --git a/control-plane/controllers/terminatinggateway_controller.go b/control-plane/controller/terminatinggateway_controller.go similarity index 94% rename from control-plane/controllers/terminatinggateway_controller.go rename to control-plane/controller/terminatinggateway_controller.go index d73d4e043c..159f8ff8c1 100644 --- a/control-plane/controllers/terminatinggateway_controller.go +++ b/control-plane/controller/terminatinggateway_controller.go @@ -1,7 +1,4 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers +package controller import ( "context" diff --git a/control-plane/controllers/configentry_controller_ent_test.go b/control-plane/controllers/configentry_controller_ent_test.go deleted file mode 100644 index 14ae477a56..0000000000 --- a/control-plane/controllers/configentry_controller_ent_test.go +++ /dev/null @@ -1,1387 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -//go:build enterprise - -package controllers - -import ( - "context" - "fmt" - "testing" - "time" - - "github.com/go-logr/logr" - logrtest "github.com/go-logr/logr/testing" - "github.com/hashicorp/consul-k8s/control-plane/api/common" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul-k8s/control-plane/consul" - "github.com/hashicorp/consul-k8s/control-plane/helper/test" - capi "github.com/hashicorp/consul/api" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - "sigs.k8s.io/controller-runtime/pkg/reconcile" -) - -// NOTE: We're not testing each controller type here because that's mostly done in -// the OSS tests and it would result in too many permutations. Instead -// we're only testing with the ServiceDefaults and ProxyDefaults controllers which -// will exercise all the namespaces code for config entries that are namespaced and those that -// exist in the global namespace. -// We also test Enterprise only features like SamenessGroups. - -func TestConfigEntryController_createsEntConfigEntry(t *testing.T) { - t.Parallel() - kubeNS := "default" - - cases := []struct { - kubeKind string - consulKind string - consulPrereqs []capi.ConfigEntry - configEntryResource common.ConfigEntryResource - reconciler func(client.Client, *consul.Config, consul.ServerConnectionManager, logr.Logger) testReconciler - compare func(t *testing.T, consul capi.ConfigEntry) - }{ - { - kubeKind: "SamenessGroup", - consulKind: capi.SamenessGroup, - configEntryResource: &v1alpha1.SamenessGroup{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: kubeNS, - }, - Spec: v1alpha1.SamenessGroupSpec{ - DefaultForFailover: true, - IncludeLocal: true, - Members: []v1alpha1.SamenessGroupMember{ - { - Peer: "dc1", - Partition: "", - }, - }, - }, - }, - reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) testReconciler { - return &SamenessGroupController{ - Client: client, - Log: logger, - ConfigEntryController: &ConfigEntryController{ - ConsulClientConfig: cfg, - ConsulServerConnMgr: watcher, - DatacenterName: datacenterName, - }, - } - }, - compare: func(t *testing.T, consulEntry capi.ConfigEntry) { - resource, ok := consulEntry.(*capi.SamenessGroupConfigEntry) - require.True(t, ok, "cast error") - require.Equal(t, true, resource.DefaultForFailover) - require.Equal(t, true, resource.IncludeLocal) - require.Equal(t, "dc1", resource.Members[0].Peer) - require.Equal(t, "", resource.Members[0].Partition) - }, - }, - { - kubeKind: "ControlPlaneRequestLimit", - consulKind: capi.RateLimitIPConfig, - configEntryResource: &v1alpha1.ControlPlaneRequestLimit{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: kubeNS, - }, - Spec: v1alpha1.ControlPlaneRequestLimitSpec{ - Mode: "permissive", - ReadWriteRatesConfig: v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ACL: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Catalog: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ConfigEntry: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ConnectCA: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Coordinate: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - DiscoveryChain: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Health: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Intention: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - KV: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Tenancy: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - PreparedQuery: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Session: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Txn: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - }, - }, - reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) testReconciler { - return &ControlPlaneRequestLimitController{ - Client: client, - Log: logger, - ConfigEntryController: &ConfigEntryController{ - ConsulClientConfig: cfg, - ConsulServerConnMgr: watcher, - DatacenterName: datacenterName, - }, - } - }, - compare: func(t *testing.T, consulEntry capi.ConfigEntry) { - resource, ok := consulEntry.(*capi.RateLimitIPConfigEntry) - require.True(t, ok, "cast error") - require.Equal(t, "permissive", resource.Mode) - require.Equal(t, 100.0, resource.ReadRate) - require.Equal(t, 100.0, resource.WriteRate) - require.Equal(t, 100.0, resource.ACL.ReadRate) - require.Equal(t, 100.0, resource.ACL.WriteRate) - require.Equal(t, 100.0, resource.Catalog.ReadRate) - require.Equal(t, 100.0, resource.Catalog.WriteRate) - require.Equal(t, 100.0, resource.ConfigEntry.ReadRate) - require.Equal(t, 100.0, resource.ConfigEntry.WriteRate) - require.Equal(t, 100.0, resource.ConnectCA.ReadRate) - require.Equal(t, 100.0, resource.ConnectCA.WriteRate) - require.Equal(t, 100.0, resource.Coordinate.ReadRate) - require.Equal(t, 100.0, resource.Coordinate.WriteRate) - require.Equal(t, 100.0, resource.DiscoveryChain.ReadRate) - require.Equal(t, 100.0, resource.DiscoveryChain.WriteRate) - require.Equal(t, 100.0, resource.Health.ReadRate) - require.Equal(t, 100.0, resource.Health.WriteRate) - require.Equal(t, 100.0, resource.Intention.ReadRate) - require.Equal(t, 100.0, resource.Intention.WriteRate) - require.Equal(t, 100.0, resource.KV.ReadRate) - require.Equal(t, 100.0, resource.KV.WriteRate) - require.Equal(t, 100.0, resource.Tenancy.ReadRate) - require.Equal(t, 100.0, resource.Tenancy.WriteRate) - require.Equal(t, 100.0, resource.PreparedQuery.ReadRate) - require.Equal(t, 100.0, resource.PreparedQuery.WriteRate) - require.Equal(t, 100.0, resource.Session.ReadRate) - require.Equal(t, 100.0, resource.Session.WriteRate) - require.Equal(t, 100.0, resource.Txn.ReadRate) - require.Equal(t, 100.0, resource.Txn.WriteRate, 100.0) - }, - }, - } - - for _, c := range cases { - t.Run(c.kubeKind, func(t *testing.T) { - req := require.New(t) - ctx := context.Background() - - s := runtime.NewScheme() - s.AddKnownTypes(v1alpha1.GroupVersion, c.configEntryResource) - fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(c.configEntryResource).Build() - - testClient := test.TestServerWithMockConnMgrWatcher(t, nil) - testClient.TestServer.WaitForServiceIntentions(t) - consulClient := testClient.APIClient - - for _, configEntry := range c.consulPrereqs { - written, _, err := consulClient.ConfigEntries().Set(configEntry, nil) - req.NoError(err) - req.True(written) - } - - r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.NewTestLogger(t)) - namespacedName := types.NamespacedName{ - Namespace: kubeNS, - Name: c.configEntryResource.KubernetesName(), - } - resp, err := r.Reconcile(ctx, ctrl.Request{ - NamespacedName: namespacedName, - }) - req.NoError(err) - req.False(resp.Requeue) - - cfg, _, err := consulClient.ConfigEntries().Get(c.consulKind, c.configEntryResource.ConsulName(), nil) - req.NoError(err) - req.Equal(c.configEntryResource.ConsulName(), cfg.GetName()) - c.compare(t, cfg) - - // Check that the status is "synced". - err = fakeClient.Get(ctx, namespacedName, c.configEntryResource) - req.NoError(err) - req.Equal(corev1.ConditionTrue, c.configEntryResource.SyncedConditionStatus()) - - // Check that the finalizer is added. - req.Contains(c.configEntryResource.Finalizers(), FinalizerName) - }) - } -} - -func TestConfigEntryController_updatesEntConfigEntry(t *testing.T) { - t.Parallel() - kubeNS := "default" - - cases := []struct { - kubeKind string - consulKind string - consulPrereqs []capi.ConfigEntry - configEntryResource common.ConfigEntryResource - reconciler func(client.Client, *consul.Config, consul.ServerConnectionManager, logr.Logger) testReconciler - updateF func(common.ConfigEntryResource) - compare func(t *testing.T, consul capi.ConfigEntry) - }{ - { - kubeKind: "SamenessGroup", - consulKind: capi.SamenessGroup, - configEntryResource: &v1alpha1.SamenessGroup{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: kubeNS, - }, - Spec: v1alpha1.SamenessGroupSpec{ - DefaultForFailover: true, - IncludeLocal: true, - Members: []v1alpha1.SamenessGroupMember{ - { - Peer: "dc1", - Partition: "", - }, - }, - }, - }, - reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) testReconciler { - return &SamenessGroupController{ - Client: client, - Log: logger, - ConfigEntryController: &ConfigEntryController{ - ConsulClientConfig: cfg, - ConsulServerConnMgr: watcher, - DatacenterName: datacenterName, - }, - } - }, - updateF: func(resource common.ConfigEntryResource) { - sg := resource.(*v1alpha1.SamenessGroup) - sg.Spec.IncludeLocal = false - }, - compare: func(t *testing.T, consulEntry capi.ConfigEntry) { - resource, ok := consulEntry.(*capi.SamenessGroupConfigEntry) - require.True(t, ok, "cast error") - require.Equal(t, true, resource.DefaultForFailover) - require.Equal(t, false, resource.IncludeLocal) - require.Equal(t, "dc1", resource.Members[0].Peer) - require.Equal(t, "", resource.Members[0].Partition) - }, - }, - { - kubeKind: "ControlPlaneRequestLimit", - consulKind: capi.RateLimitIPConfig, - configEntryResource: &v1alpha1.ControlPlaneRequestLimit{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: kubeNS, - }, - Spec: v1alpha1.ControlPlaneRequestLimitSpec{ - Mode: "permissive", - ReadWriteRatesConfig: v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ACL: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Catalog: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ConfigEntry: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ConnectCA: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Coordinate: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - DiscoveryChain: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Health: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Intention: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - KV: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Tenancy: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - PreparedQuery: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Session: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Txn: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - }, - }, - reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) testReconciler { - return &ControlPlaneRequestLimitController{ - Client: client, - Log: logger, - ConfigEntryController: &ConfigEntryController{ - ConsulClientConfig: cfg, - ConsulServerConnMgr: watcher, - DatacenterName: datacenterName, - }, - } - }, - updateF: func(resource common.ConfigEntryResource) { - ipRateLimit := resource.(*v1alpha1.ControlPlaneRequestLimit) - ipRateLimit.Spec.Mode = "enforcing" - }, - compare: func(t *testing.T, consulEntry capi.ConfigEntry) { - resource, ok := consulEntry.(*capi.RateLimitIPConfigEntry) - require.True(t, ok, "cast error") - require.Equal(t, "enforcing", resource.Mode) - require.Equal(t, 100.0, resource.ReadRate) - require.Equal(t, 100.0, resource.WriteRate) - require.Equal(t, 100.0, resource.ACL.ReadRate) - require.Equal(t, 100.0, resource.ACL.WriteRate) - require.Equal(t, 100.0, resource.Catalog.ReadRate) - require.Equal(t, 100.0, resource.Catalog.WriteRate) - require.Equal(t, 100.0, resource.ConfigEntry.ReadRate) - require.Equal(t, 100.0, resource.ConfigEntry.WriteRate) - require.Equal(t, 100.0, resource.ConnectCA.ReadRate) - require.Equal(t, 100.0, resource.ConnectCA.WriteRate) - require.Equal(t, 100.0, resource.Coordinate.ReadRate) - require.Equal(t, 100.0, resource.Coordinate.WriteRate) - require.Equal(t, 100.0, resource.DiscoveryChain.ReadRate) - require.Equal(t, 100.0, resource.DiscoveryChain.WriteRate) - require.Equal(t, 100.0, resource.Health.ReadRate) - require.Equal(t, 100.0, resource.Health.WriteRate) - require.Equal(t, 100.0, resource.Intention.ReadRate) - require.Equal(t, 100.0, resource.Intention.WriteRate) - require.Equal(t, 100.0, resource.KV.ReadRate) - require.Equal(t, 100.0, resource.KV.WriteRate) - require.Equal(t, 100.0, resource.Tenancy.ReadRate) - require.Equal(t, 100.0, resource.Tenancy.WriteRate) - require.Equal(t, 100.0, resource.PreparedQuery.ReadRate) - require.Equal(t, 100.0, resource.PreparedQuery.WriteRate) - require.Equal(t, 100.0, resource.Session.ReadRate) - require.Equal(t, 100.0, resource.Session.WriteRate) - require.Equal(t, 100.0, resource.Txn.ReadRate) - require.Equal(t, 100.0, resource.Txn.WriteRate) - }, - }, - } - - for _, c := range cases { - t.Run(c.kubeKind, func(t *testing.T) { - req := require.New(t) - ctx := context.Background() - - s := runtime.NewScheme() - s.AddKnownTypes(v1alpha1.GroupVersion, c.configEntryResource) - fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(c.configEntryResource).Build() - - testClient := test.TestServerWithMockConnMgrWatcher(t, nil) - testClient.TestServer.WaitForServiceIntentions(t) - consulClient := testClient.APIClient - - // Create any prereqs. - for _, configEntry := range c.consulPrereqs { - written, _, err := consulClient.ConfigEntries().Set(configEntry, nil) - req.NoError(err) - req.True(written) - } - - // We haven't run reconcile yet so we must create the config entry - // in Consul ourselves. - { - written, _, err := consulClient.ConfigEntries().Set(c.configEntryResource.ToConsul(datacenterName), nil) - req.NoError(err) - req.True(written) - } - - // Now run reconcile which should update the entry in Consul. - { - namespacedName := types.NamespacedName{ - Namespace: kubeNS, - Name: c.configEntryResource.KubernetesName(), - } - // First get it so we have the latest revision number. - err := fakeClient.Get(ctx, namespacedName, c.configEntryResource) - req.NoError(err) - - // Update the entry in Kube and run reconcile. - c.updateF(c.configEntryResource) - err = fakeClient.Update(ctx, c.configEntryResource) - req.NoError(err) - r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.NewTestLogger(t)) - resp, err := r.Reconcile(ctx, ctrl.Request{ - NamespacedName: namespacedName, - }) - req.NoError(err) - req.False(resp.Requeue) - - // Now check that the object in Consul is as expected. - cfg, _, err := consulClient.ConfigEntries().Get(c.consulKind, c.configEntryResource.ConsulName(), nil) - req.NoError(err) - req.Equal(c.configEntryResource.ConsulName(), cfg.GetName()) - c.compare(t, cfg) - } - }) - } -} - -func TestConfigEntryController_deletesEntConfigEntry(t *testing.T) { - t.Parallel() - kubeNS := "default" - - cases := []struct { - kubeKind string - consulKind string - consulPrereq []capi.ConfigEntry - configEntryResourceWithDeletion common.ConfigEntryResource - reconciler func(client.Client, *consul.Config, consul.ServerConnectionManager, logr.Logger) testReconciler - }{ - { - kubeKind: "SamenessGroup", - consulKind: capi.SamenessGroup, - configEntryResourceWithDeletion: &v1alpha1.SamenessGroup{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: kubeNS, - DeletionTimestamp: &metav1.Time{Time: time.Now()}, - Finalizers: []string{FinalizerName}, - }, - Spec: v1alpha1.SamenessGroupSpec{ - DefaultForFailover: true, - IncludeLocal: true, - Members: []v1alpha1.SamenessGroupMember{ - { - Peer: "dc1", - Partition: "", - }, - }, - }, - }, - reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) testReconciler { - return &SamenessGroupController{ - Client: client, - Log: logger, - ConfigEntryController: &ConfigEntryController{ - ConsulClientConfig: cfg, - ConsulServerConnMgr: watcher, - DatacenterName: datacenterName, - }, - } - }, - }, - { - - kubeKind: "ControlPlaneRequestLimit", - consulKind: capi.RateLimitIPConfig, - configEntryResourceWithDeletion: &v1alpha1.ControlPlaneRequestLimit{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: kubeNS, - DeletionTimestamp: &metav1.Time{Time: time.Now()}, - Finalizers: []string{FinalizerName}, - }, - Spec: v1alpha1.ControlPlaneRequestLimitSpec{ - Mode: "permissive", - ReadWriteRatesConfig: v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ACL: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Catalog: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ConfigEntry: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - ConnectCA: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Coordinate: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - DiscoveryChain: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Health: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Intention: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - KV: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Tenancy: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - PreparedQuery: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Session: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - Txn: &v1alpha1.ReadWriteRatesConfig{ - ReadRate: 100.0, - WriteRate: 100.0, - }, - }, - }, - reconciler: func(client client.Client, cfg *consul.Config, watcher consul.ServerConnectionManager, logger logr.Logger) testReconciler { - return &ControlPlaneRequestLimitController{ - Client: client, - Log: logger, - ConfigEntryController: &ConfigEntryController{ - ConsulClientConfig: cfg, - ConsulServerConnMgr: watcher, - DatacenterName: datacenterName, - }, - } - }, - }, - } - - for _, c := range cases { - t.Run(c.kubeKind, func(t *testing.T) { - req := require.New(t) - - s := runtime.NewScheme() - s.AddKnownTypes(v1alpha1.GroupVersion, c.configEntryResourceWithDeletion) - fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(c.configEntryResourceWithDeletion).Build() - - testClient := test.TestServerWithMockConnMgrWatcher(t, nil) - testClient.TestServer.WaitForServiceIntentions(t) - consulClient := testClient.APIClient - - // Create any prereqs. - for _, configEntry := range c.consulPrereq { - written, _, err := consulClient.ConfigEntries().Set(configEntry, nil) - req.NoError(err) - req.True(written) - } - - // We haven't run reconcile yet so we must create the config entry - // in Consul ourselves. - { - written, _, err := consulClient.ConfigEntries().Set(c.configEntryResourceWithDeletion.ToConsul(datacenterName), nil) - req.NoError(err) - req.True(written) - } - - // Now run reconcile. It's marked for deletion so this should delete it. - { - namespacedName := types.NamespacedName{ - Namespace: kubeNS, - Name: c.configEntryResourceWithDeletion.KubernetesName(), - } - r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.NewTestLogger(t)) - resp, err := r.Reconcile(context.Background(), ctrl.Request{ - NamespacedName: namespacedName, - }) - req.NoError(err) - req.False(resp.Requeue) - - _, _, err = consulClient.ConfigEntries().Get(c.consulKind, c.configEntryResourceWithDeletion.ConsulName(), nil) - req.EqualError(err, - fmt.Sprintf("Unexpected response code: 404 (Config entry not found for %q / %q)", - c.consulKind, c.configEntryResourceWithDeletion.ConsulName())) - } - }) - } -} - -func TestConfigEntryController_createsConfigEntry_consulNamespaces(tt *testing.T) { - tt.Parallel() - - cases := map[string]struct { - Mirror bool - MirrorPrefix string - SourceKubeNS string - DestConsulNS string - ExpConsulNS string - }{ - "SourceKubeNS=default, DestConsulNS=default": { - SourceKubeNS: "default", - DestConsulNS: "default", - ExpConsulNS: "default", - }, - "SourceKubeNS=kube, DestConsulNS=default": { - SourceKubeNS: "kube", - DestConsulNS: "default", - ExpConsulNS: "default", - }, - "SourceKubeNS=default, DestConsulNS=other": { - SourceKubeNS: "default", - DestConsulNS: "other", - ExpConsulNS: "other", - }, - "SourceKubeNS=kube, DestConsulNS=other": { - SourceKubeNS: "kube", - DestConsulNS: "other", - ExpConsulNS: "other", - }, - "SourceKubeNS=default, Mirror=true": { - SourceKubeNS: "default", - Mirror: true, - ExpConsulNS: "default", - }, - "SourceKubeNS=kube, Mirror=true": { - SourceKubeNS: "kube", - Mirror: true, - ExpConsulNS: "kube", - }, - "SourceKubeNS=default, Mirror=true, Prefix=prefix": { - SourceKubeNS: "default", - Mirror: true, - MirrorPrefix: "prefix-", - ExpConsulNS: "prefix-default", - }, - } - - for name, c := range cases { - configEntryKinds := map[string]struct { - ConsulKind string - ConsulNamespace string - KubeResource common.ConfigEntryResource - GetController func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *ConfigEntryController) reconcile.Reconciler - AssertValidConfig func(entry capi.ConfigEntry) bool - }{ - "namespaced": { - ConsulKind: capi.ServiceDefaults, - KubeResource: &v1alpha1.ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: c.SourceKubeNS, - }, - Spec: v1alpha1.ServiceDefaultsSpec{ - Protocol: "http", - }, - }, - GetController: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *ConfigEntryController) reconcile.Reconciler { - return &ServiceDefaultsController{ - Client: client, - Log: logger, - Scheme: scheme, - ConfigEntryController: cont, - } - }, - AssertValidConfig: func(cfg capi.ConfigEntry) bool { - configEntry, ok := cfg.(*capi.ServiceConfigEntry) - if !ok { - return false - } - return configEntry.Protocol == "http" - }, - ConsulNamespace: c.ExpConsulNS, - }, - "global": { - ConsulKind: capi.ProxyDefaults, - KubeResource: &v1alpha1.ProxyDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.Global, - Namespace: c.SourceKubeNS, - }, - Spec: v1alpha1.ProxyDefaultsSpec{ - MeshGateway: v1alpha1.MeshGateway{ - Mode: "remote", - }, - }, - }, - GetController: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *ConfigEntryController) reconcile.Reconciler { - return &ProxyDefaultsController{ - Client: client, - Log: logger, - Scheme: scheme, - ConfigEntryController: cont, - } - }, - AssertValidConfig: func(cfg capi.ConfigEntry) bool { - configEntry, ok := cfg.(*capi.ProxyConfigEntry) - if !ok { - return false - } - return configEntry.MeshGateway.Mode == capi.MeshGatewayModeRemote - }, - ConsulNamespace: common.DefaultConsulNamespace, - }, - "intentions": { - ConsulKind: capi.ServiceIntentions, - KubeResource: &v1alpha1.ServiceIntentions{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: c.SourceKubeNS, - }, - Spec: v1alpha1.ServiceIntentionsSpec{ - Destination: v1alpha1.IntentionDestination{ - Name: "test", - Namespace: c.ExpConsulNS, - }, - Sources: v1alpha1.SourceIntentions{ - &v1alpha1.SourceIntention{ - Name: "baz", - Namespace: "bar", - Action: "allow", - }, - }, - }, - }, - GetController: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *ConfigEntryController) reconcile.Reconciler { - return &ServiceIntentionsController{ - Client: client, - Log: logger, - Scheme: scheme, - ConfigEntryController: cont, - } - }, - AssertValidConfig: func(cfg capi.ConfigEntry) bool { - configEntry, ok := cfg.(*capi.ServiceIntentionsConfigEntry) - if !ok { - return false - } - return configEntry.Sources[0].Action == capi.IntentionActionAllow - }, - ConsulNamespace: c.ExpConsulNS, - }, - } - - for kind, in := range configEntryKinds { - tt.Run(fmt.Sprintf("%s : %s", name, kind), func(t *testing.T) { - req := require.New(t) - s := runtime.NewScheme() - s.AddKnownTypes(v1alpha1.GroupVersion, in.KubeResource) - ctx := context.Background() - - testClient := test.TestServerWithMockConnMgrWatcher(t, nil) - testClient.TestServer.WaitForServiceIntentions(t) - consulClient := testClient.APIClient - - fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(in.KubeResource).Build() - - r := in.GetController( - fakeClient, - logrtest.NewTestLogger(t), - s, - &ConfigEntryController{ - ConsulClientConfig: testClient.Cfg, - ConsulServerConnMgr: testClient.Watcher, - EnableConsulNamespaces: true, - EnableNSMirroring: c.Mirror, - NSMirroringPrefix: c.MirrorPrefix, - ConsulDestinationNamespace: c.DestConsulNS, - }, - ) - - resp, err := r.Reconcile(ctx, ctrl.Request{ - NamespacedName: types.NamespacedName{ - Namespace: c.SourceKubeNS, - Name: in.KubeResource.KubernetesName(), - }, - }) - req.NoError(err) - req.False(resp.Requeue) - - cfg, _, err := consulClient.ConfigEntries().Get(in.ConsulKind, in.KubeResource.ConsulName(), &capi.QueryOptions{ - Namespace: in.ConsulNamespace, - }) - req.NoError(err) - - result := in.AssertValidConfig(cfg) - req.True(result) - - // Check that the status is "synced". - err = fakeClient.Get(ctx, types.NamespacedName{ - Namespace: c.SourceKubeNS, - Name: in.KubeResource.KubernetesName(), - }, in.KubeResource) - req.NoError(err) - conditionSynced := in.KubeResource.SyncedConditionStatus() - req.Equal(conditionSynced, corev1.ConditionTrue) - }) - } - } -} - -func TestConfigEntryController_updatesConfigEntry_consulNamespaces(tt *testing.T) { - tt.Parallel() - - cases := map[string]struct { - Mirror bool - MirrorPrefix string - SourceKubeNS string - DestConsulNS string - ExpConsulNS string - }{ - "SourceKubeNS=default, DestConsulNS=default": { - SourceKubeNS: "default", - DestConsulNS: "default", - ExpConsulNS: "default", - }, - "SourceKubeNS=kube, DestConsulNS=default": { - SourceKubeNS: "kube", - DestConsulNS: "default", - ExpConsulNS: "default", - }, - "SourceKubeNS=default, DestConsulNS=other": { - SourceKubeNS: "default", - DestConsulNS: "other", - ExpConsulNS: "other", - }, - "SourceKubeNS=kube, DestConsulNS=other": { - SourceKubeNS: "kube", - DestConsulNS: "other", - ExpConsulNS: "other", - }, - "SourceKubeNS=default, Mirror=true": { - SourceKubeNS: "default", - Mirror: true, - ExpConsulNS: "default", - }, - "SourceKubeNS=kube, Mirror=true": { - SourceKubeNS: "kube", - Mirror: true, - ExpConsulNS: "kube", - }, - "SourceKubeNS=default, Mirror=true, Prefix=prefix": { - SourceKubeNS: "default", - Mirror: true, - MirrorPrefix: "prefix-", - ExpConsulNS: "prefix-default", - }, - } - - for name, c := range cases { - configEntryKinds := map[string]struct { - ConsulKind string - ConsulNamespace string - KubeResource common.ConfigEntryResource - GetControllerFunc func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *ConfigEntryController) reconcile.Reconciler - AssertValidConfigFunc func(entry capi.ConfigEntry) bool - WriteConfigEntryFunc func(consulClient *capi.Client, namespace string) error - UpdateResourceFunc func(client client.Client, ctx context.Context, in common.ConfigEntryResource) error - }{ - "namespaced": { - ConsulKind: capi.ServiceDefaults, - KubeResource: &v1alpha1.ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: c.SourceKubeNS, - Finalizers: []string{FinalizerName}, - }, - Spec: v1alpha1.ServiceDefaultsSpec{ - Protocol: "http", - }, - }, - ConsulNamespace: c.ExpConsulNS, - GetControllerFunc: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *ConfigEntryController) reconcile.Reconciler { - return &ServiceDefaultsController{ - Client: client, - Log: logger, - Scheme: scheme, - ConfigEntryController: cont, - } - }, - WriteConfigEntryFunc: func(consulClient *capi.Client, namespace string) error { - _, _, err := consulClient.ConfigEntries().Set(&capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "foo", - Protocol: "http", - }, &capi.WriteOptions{Namespace: namespace}) - return err - }, - UpdateResourceFunc: func(client client.Client, ctx context.Context, in common.ConfigEntryResource) error { - svcDefault := in.(*v1alpha1.ServiceDefaults) - svcDefault.Spec.Protocol = "tcp" - return client.Update(ctx, svcDefault) - }, - AssertValidConfigFunc: func(cfg capi.ConfigEntry) bool { - configEntry, ok := cfg.(*capi.ServiceConfigEntry) - if !ok { - return false - } - return configEntry.Protocol == "tcp" - }, - }, - "global": { - ConsulKind: capi.ProxyDefaults, - KubeResource: &v1alpha1.ProxyDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.Global, - Namespace: c.SourceKubeNS, - Finalizers: []string{FinalizerName}, - }, - Spec: v1alpha1.ProxyDefaultsSpec{ - MeshGateway: v1alpha1.MeshGateway{ - Mode: "remote", - }, - }, - }, - ConsulNamespace: common.DefaultConsulNamespace, - GetControllerFunc: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *ConfigEntryController) reconcile.Reconciler { - return &ProxyDefaultsController{ - Client: client, - Log: logger, - Scheme: scheme, - ConfigEntryController: cont, - } - }, - WriteConfigEntryFunc: func(consulClient *capi.Client, namespace string) error { - _, _, err := consulClient.ConfigEntries().Set(&capi.ProxyConfigEntry{ - Kind: capi.ProxyDefaults, - Name: common.Global, - MeshGateway: capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeRemote, - }, - }, &capi.WriteOptions{Namespace: namespace}) - return err - }, - UpdateResourceFunc: func(client client.Client, ctx context.Context, in common.ConfigEntryResource) error { - proxyDefaults := in.(*v1alpha1.ProxyDefaults) - proxyDefaults.Spec.MeshGateway.Mode = "local" - return client.Update(ctx, proxyDefaults) - }, - AssertValidConfigFunc: func(cfg capi.ConfigEntry) bool { - configEntry, ok := cfg.(*capi.ProxyConfigEntry) - if !ok { - return false - } - return configEntry.MeshGateway.Mode == capi.MeshGatewayModeLocal - }, - }, - "intentions": { - ConsulKind: capi.ServiceIntentions, - KubeResource: &v1alpha1.ServiceIntentions{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: c.SourceKubeNS, - Finalizers: []string{FinalizerName}, - }, - Spec: v1alpha1.ServiceIntentionsSpec{ - Destination: v1alpha1.IntentionDestination{ - Name: "foo", - Namespace: c.ExpConsulNS, - }, - Sources: v1alpha1.SourceIntentions{ - &v1alpha1.SourceIntention{ - Name: "bar", - Namespace: "baz", - Action: "deny", - }, - }, - }, - }, - ConsulNamespace: c.ExpConsulNS, - GetControllerFunc: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *ConfigEntryController) reconcile.Reconciler { - return &ServiceIntentionsController{ - Client: client, - Log: logger, - Scheme: scheme, - ConfigEntryController: cont, - } - }, - WriteConfigEntryFunc: func(consulClient *capi.Client, namespace string) error { - _, _, err := consulClient.ConfigEntries().Set(&capi.ServiceIntentionsConfigEntry{ - Kind: capi.ServiceIntentions, - Name: "foo", - Sources: []*capi.SourceIntention{ - { - Name: "bar", - Namespace: "baz", - Action: capi.IntentionActionDeny, - }, - }, - }, &capi.WriteOptions{Namespace: namespace}) - return err - }, - UpdateResourceFunc: func(client client.Client, ctx context.Context, in common.ConfigEntryResource) error { - svcIntention := in.(*v1alpha1.ServiceIntentions) - svcIntention.Spec.Sources[0].Action = "allow" - return client.Update(ctx, svcIntention) - }, - AssertValidConfigFunc: func(cfg capi.ConfigEntry) bool { - configEntry, ok := cfg.(*capi.ServiceIntentionsConfigEntry) - if !ok { - return false - } - return configEntry.Sources[0].Action == capi.IntentionActionAllow - }, - }, - } - for kind, in := range configEntryKinds { - tt.Run(fmt.Sprintf("%s : %s", name, kind), func(t *testing.T) { - req := require.New(t) - s := runtime.NewScheme() - s.AddKnownTypes(v1alpha1.GroupVersion, in.KubeResource) - ctx := context.Background() - - testClient := test.TestServerWithMockConnMgrWatcher(t, nil) - testClient.TestServer.WaitForServiceIntentions(t) - consulClient := testClient.APIClient - - fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(in.KubeResource).Build() - - r := in.GetControllerFunc( - fakeClient, - logrtest.NewTestLogger(t), - s, - &ConfigEntryController{ - ConsulClientConfig: testClient.Cfg, - ConsulServerConnMgr: testClient.Watcher, - EnableConsulNamespaces: true, - EnableNSMirroring: c.Mirror, - NSMirroringPrefix: c.MirrorPrefix, - ConsulDestinationNamespace: c.DestConsulNS, - }, - ) - - // We haven't run reconcile yet so ensure it's created in Consul. - { - if in.ConsulNamespace != "default" { - _, _, err := consulClient.Namespaces().Create(&capi.Namespace{ - Name: in.ConsulNamespace, - }, nil) - req.NoError(err) - } - - err := in.WriteConfigEntryFunc(consulClient, in.ConsulNamespace) - req.NoError(err) - } - - // Now update it. - { - // First get it so we have the latest revision number. - err := fakeClient.Get(ctx, types.NamespacedName{ - Namespace: c.SourceKubeNS, - Name: in.KubeResource.KubernetesName(), - }, in.KubeResource) - req.NoError(err) - - // Update the resource. - err = in.UpdateResourceFunc(fakeClient, ctx, in.KubeResource) - req.NoError(err) - - resp, err := r.Reconcile(ctx, ctrl.Request{ - NamespacedName: types.NamespacedName{ - Namespace: c.SourceKubeNS, - Name: in.KubeResource.KubernetesName(), - }, - }) - req.NoError(err) - req.False(resp.Requeue) - - cfg, _, err := consulClient.ConfigEntries().Get(in.ConsulKind, in.KubeResource.ConsulName(), &capi.QueryOptions{ - Namespace: in.ConsulNamespace, - }) - req.NoError(err) - req.True(in.AssertValidConfigFunc(cfg)) - } - }) - } - } -} - -func TestConfigEntryController_deletesConfigEntry_consulNamespaces(tt *testing.T) { - tt.Parallel() - - cases := map[string]struct { - Mirror bool - MirrorPrefix string - SourceKubeNS string - DestConsulNS string - ExpConsulNS string - }{ - "SourceKubeNS=default, DestConsulNS=default": { - SourceKubeNS: "default", - DestConsulNS: "default", - ExpConsulNS: "default", - }, - "SourceKubeNS=kube, DestConsulNS=default": { - SourceKubeNS: "kube", - DestConsulNS: "default", - ExpConsulNS: "default", - }, - "SourceKubeNS=default, DestConsulNS=other": { - SourceKubeNS: "default", - DestConsulNS: "other", - ExpConsulNS: "other", - }, - "SourceKubeNS=kube, DestConsulNS=other": { - SourceKubeNS: "kube", - DestConsulNS: "other", - ExpConsulNS: "other", - }, - "SourceKubeNS=default, Mirror=true": { - SourceKubeNS: "default", - Mirror: true, - ExpConsulNS: "default", - }, - "SourceKubeNS=kube, Mirror=true": { - SourceKubeNS: "kube", - Mirror: true, - ExpConsulNS: "kube", - }, - "SourceKubeNS=default, Mirror=true, Prefix=prefix": { - SourceKubeNS: "default", - Mirror: true, - MirrorPrefix: "prefix-", - ExpConsulNS: "prefix-default", - }, - } - - for name, c := range cases { - configEntryKinds := map[string]struct { - ConsulKind string - ConsulNamespace string - KubeResource common.ConfigEntryResource - GetControllerFunc func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *ConfigEntryController) reconcile.Reconciler - WriteConfigEntryFunc func(consulClient *capi.Client, namespace string) error - }{ - "namespaced": { - ConsulKind: capi.ServiceDefaults, - // Create it with the deletion timestamp set to mimic that it's already - // been marked for deletion. - KubeResource: &v1alpha1.ServiceDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: c.SourceKubeNS, - Finalizers: []string{FinalizerName}, - DeletionTimestamp: &metav1.Time{Time: time.Now()}, - }, - Spec: v1alpha1.ServiceDefaultsSpec{ - Protocol: "http", - }, - }, - ConsulNamespace: c.ExpConsulNS, - GetControllerFunc: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *ConfigEntryController) reconcile.Reconciler { - return &ServiceDefaultsController{ - Client: client, - Log: logger, - Scheme: scheme, - ConfigEntryController: cont, - } - }, - WriteConfigEntryFunc: func(consulClient *capi.Client, namespace string) error { - _, _, err := consulClient.ConfigEntries().Set(&capi.ServiceConfigEntry{ - Kind: capi.ServiceDefaults, - Name: "foo", - Protocol: "http", - }, &capi.WriteOptions{Namespace: namespace}) - return err - }, - }, - "global": { - ConsulKind: capi.ProxyDefaults, - // Create it with the deletion timestamp set to mimic that it's already - // been marked for deletion. - KubeResource: &v1alpha1.ProxyDefaults{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.Global, - Namespace: c.SourceKubeNS, - Finalizers: []string{FinalizerName}, - DeletionTimestamp: &metav1.Time{Time: time.Now()}, - }, - Spec: v1alpha1.ProxyDefaultsSpec{ - MeshGateway: v1alpha1.MeshGateway{ - Mode: "remote", - }, - }, - }, - ConsulNamespace: common.DefaultConsulNamespace, - GetControllerFunc: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *ConfigEntryController) reconcile.Reconciler { - return &ProxyDefaultsController{ - Client: client, - Log: logger, - Scheme: scheme, - ConfigEntryController: cont, - } - }, - WriteConfigEntryFunc: func(consulClient *capi.Client, namespace string) error { - _, _, err := consulClient.ConfigEntries().Set(&capi.ProxyConfigEntry{ - Kind: capi.ProxyDefaults, - Name: common.Global, - MeshGateway: capi.MeshGatewayConfig{ - Mode: capi.MeshGatewayModeRemote, - }, - }, &capi.WriteOptions{Namespace: namespace}) - return err - }, - }, - "intentions": { - ConsulKind: capi.ServiceIntentions, - // Create it with the deletion timestamp set to mimic that it's already - // been marked for deletion. - KubeResource: &v1alpha1.ServiceIntentions{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: c.SourceKubeNS, - Finalizers: []string{FinalizerName}, - DeletionTimestamp: &metav1.Time{Time: time.Now()}, - }, - Spec: v1alpha1.ServiceIntentionsSpec{ - Destination: v1alpha1.IntentionDestination{ - Name: "test", - Namespace: c.ExpConsulNS, - }, - Sources: v1alpha1.SourceIntentions{ - &v1alpha1.SourceIntention{ - Name: "bar", - Namespace: "baz", - Action: "deny", - }, - }, - }, - }, - ConsulNamespace: c.ExpConsulNS, - GetControllerFunc: func(client client.Client, logger logr.Logger, scheme *runtime.Scheme, cont *ConfigEntryController) reconcile.Reconciler { - return &ServiceIntentionsController{ - Client: client, - Log: logger, - Scheme: scheme, - ConfigEntryController: cont, - } - }, - WriteConfigEntryFunc: func(consulClient *capi.Client, namespace string) error { - _, _, err := consulClient.ConfigEntries().Set(&capi.ServiceIntentionsConfigEntry{ - Kind: capi.ServiceIntentions, - Name: "test", - Sources: []*capi.SourceIntention{ - { - Name: "bar", - Namespace: "baz", - Action: capi.IntentionActionDeny, - }, - }, - }, &capi.WriteOptions{Namespace: namespace}) - return err - }, - }, - } - for kind, in := range configEntryKinds { - tt.Run(fmt.Sprintf("%s : %s", name, kind), func(t *testing.T) { - req := require.New(t) - - s := runtime.NewScheme() - s.AddKnownTypes(v1alpha1.GroupVersion, in.KubeResource) - - testClient := test.TestServerWithMockConnMgrWatcher(t, nil) - testClient.TestServer.WaitForServiceIntentions(t) - consulClient := testClient.APIClient - - fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(in.KubeResource).Build() - - r := in.GetControllerFunc( - fakeClient, - logrtest.NewTestLogger(t), - s, - &ConfigEntryController{ - ConsulClientConfig: testClient.Cfg, - ConsulServerConnMgr: testClient.Watcher, - EnableConsulNamespaces: true, - EnableNSMirroring: c.Mirror, - NSMirroringPrefix: c.MirrorPrefix, - ConsulDestinationNamespace: c.DestConsulNS, - }, - ) - - // We haven't run reconcile yet so ensure it's created in Consul. - { - if in.ConsulNamespace != "default" { - _, _, err := consulClient.Namespaces().Create(&capi.Namespace{ - Name: in.ConsulNamespace, - }, nil) - req.NoError(err) - } - - err := in.WriteConfigEntryFunc(consulClient, in.ConsulNamespace) - req.NoError(err) - } - - // Now run reconcile. It's marked for deletion so this should delete it. - { - resp, err := r.Reconcile(context.Background(), ctrl.Request{ - NamespacedName: types.NamespacedName{ - Namespace: c.SourceKubeNS, - Name: in.KubeResource.KubernetesName(), - }, - }) - req.NoError(err) - req.False(resp.Requeue) - - _, _, err = consulClient.ConfigEntries().Get(in.ConsulKind, in.KubeResource.ConsulName(), &capi.QueryOptions{ - Namespace: in.ConsulNamespace, - }) - req.EqualError(err, fmt.Sprintf(`Unexpected response code: 404 (Config entry not found for "%s" / "%s")`, in.ConsulKind, in.KubeResource.ConsulName())) - } - }) - } - } -} diff --git a/control-plane/controllers/controlplanerequestlimit_controller.go b/control-plane/controllers/controlplanerequestlimit_controller.go deleted file mode 100644 index f5afbdcc48..0000000000 --- a/control-plane/controllers/controlplanerequestlimit_controller.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers - -import ( - "context" - - "github.com/go-logr/logr" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - - consulv1alpha1 "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" -) - -// ControlPlaneRequestLimitController reconciles a ControlPlaneRequestLimit object. -type ControlPlaneRequestLimitController struct { - client.Client - Log logr.Logger - Scheme *runtime.Scheme - ConfigEntryController *ConfigEntryController -} - -//+kubebuilder:rbac:groups=consul.hashicorp.com,resources=controlplanerequestlimits,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=consul.hashicorp.com,resources=controlplanerequestlimits/status,verbs=get;update;patch - -func (r *ControlPlaneRequestLimitController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - return r.ConfigEntryController.ReconcileEntry(ctx, r, req, &consulv1alpha1.ControlPlaneRequestLimit{}) -} - -func (r *ControlPlaneRequestLimitController) Logger(name types.NamespacedName) logr.Logger { - return r.Log.WithValues("request", name) -} - -func (r *ControlPlaneRequestLimitController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { - return r.Status().Update(ctx, obj, opts...) -} - -func (r *ControlPlaneRequestLimitController) SetupWithManager(mgr ctrl.Manager) error { - return setupWithManager(mgr, &consulv1alpha1.ControlPlaneRequestLimit{}, r) -} diff --git a/control-plane/controllers/jwtprovider_controller.go b/control-plane/controllers/jwtprovider_controller.go deleted file mode 100644 index 157f4bc855..0000000000 --- a/control-plane/controllers/jwtprovider_controller.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers - -import ( - "context" - - "github.com/go-logr/logr" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - - consulv1alpha1 "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" -) - -// JWTProviderController reconciles a JWTProvider object. -type JWTProviderController struct { - client.Client - Log logr.Logger - Scheme *runtime.Scheme - ConfigEntryController *ConfigEntryController -} - -//+kubebuilder:rbac:groups=consul.hashicorp.com,resources=jwtproviders,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=consul.hashicorp.com,resources=jwtproviders/status,verbs=get;update;patch - -func (r *JWTProviderController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - return r.ConfigEntryController.ReconcileEntry(ctx, r, req, &consulv1alpha1.JWTProvider{}) -} - -func (r *JWTProviderController) Logger(name types.NamespacedName) logr.Logger { - return r.Log.WithValues("request", name) -} - -func (r *JWTProviderController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { - return r.Status().Update(ctx, obj, opts...) -} - -func (r *JWTProviderController) SetupWithManager(mgr ctrl.Manager) error { - return setupWithManager(mgr, &consulv1alpha1.JWTProvider{}, r) -} diff --git a/control-plane/controllers/samenessgroups_controller.go b/control-plane/controllers/samenessgroups_controller.go deleted file mode 100644 index 815b7e8ab8..0000000000 --- a/control-plane/controllers/samenessgroups_controller.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package controllers - -import ( - "context" - - "k8s.io/apimachinery/pkg/types" - - "github.com/go-logr/logr" - "k8s.io/apimachinery/pkg/runtime" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - - consulv1alpha1 "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" -) - -// SamenessGroupController reconciles a SamenessGroups object. -type SamenessGroupController struct { - client.Client - Log logr.Logger - Scheme *runtime.Scheme - ConfigEntryController *ConfigEntryController -} - -//+kubebuilder:rbac:groups=consul.hashicorp.com,resources=samenessgroups,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=consul.hashicorp.com,resources=samenessgroups/status,verbs=get;update;patch - -func (r *SamenessGroupController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - return r.ConfigEntryController.ReconcileEntry(ctx, r, req, &consulv1alpha1.SamenessGroup{}) -} - -func (r *SamenessGroupController) Logger(name types.NamespacedName) logr.Logger { - return r.Log.WithValues("request", name) -} - -func (r *SamenessGroupController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { - return r.Status().Update(ctx, obj, opts...) -} - -// SetupWithManager sets up the controller with the Manager. -func (r *SamenessGroupController) SetupWithManager(mgr ctrl.Manager) error { - return setupWithManager(mgr, &consulv1alpha1.SamenessGroup{}, r) -} diff --git a/control-plane/go.mod b/control-plane/go.mod index c199f954d6..d70b04542d 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -10,40 +10,35 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83383d github.com/hashicorp/consul-server-connection-manager v0.1.3 - github.com/hashicorp/consul/api v1.10.1-0.20230821180813-217d305b38d5 - github.com/hashicorp/consul/sdk v0.14.1 - github.com/hashicorp/go-bexpr v0.1.11 + github.com/hashicorp/consul/api v1.10.1-0.20230512003852-bd0eb07ed3ca + github.com/hashicorp/consul/sdk v0.13.1 github.com/hashicorp/go-discover v0.0.0-20230519164032-214571b6a530 - github.com/hashicorp/go-hclog v1.5.0 + github.com/hashicorp/go-hclog v1.2.2 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-netaddrs v0.1.0 github.com/hashicorp/go-rootcerts v1.0.2 github.com/hashicorp/go-version v1.6.0 github.com/hashicorp/serf v0.10.1 - github.com/hashicorp/vault/api v1.8.3 github.com/kr/text v0.2.0 github.com/miekg/dns v1.1.50 github.com/mitchellh/cli v1.1.0 github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/mapstructure v1.5.0 - github.com/stretchr/testify v1.8.3 + github.com/stretchr/testify v1.8.1 go.uber.org/zap v1.24.0 - golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 golang.org/x/text v0.11.0 golang.org/x/time v0.3.0 gomodules.xyz/jsonpatch/v2 v2.3.0 - gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.26.3 - k8s.io/apimachinery v0.26.3 - k8s.io/client-go v0.26.3 - k8s.io/klog/v2 v2.100.1 + k8s.io/api v0.26.1 + k8s.io/apimachinery v0.26.1 + k8s.io/client-go v0.26.1 + k8s.io/klog/v2 v2.90.1 k8s.io/utils v0.0.0-20230209194617-a36077c30491 sigs.k8s.io/controller-runtime v0.14.6 - sigs.k8s.io/gateway-api v0.7.1 ) require ( - cloud.google.com/go v0.65.0 // indirect + cloud.google.com/go v0.81.0 // indirect github.com/Azure/azure-sdk-for-go v44.0.0+incompatible // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.18 // indirect @@ -60,112 +55,97 @@ require ( github.com/aws/aws-sdk-go v1.44.262 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect - github.com/cenkalti/backoff/v3 v3.0.0 // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661 // indirect - github.com/digitalocean/godo v1.7.5 // indirect + github.com/digitalocean/godo v1.10.0 // indirect github.com/dimchansky/utfbom v1.1.0 // indirect github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect - github.com/fatih/color v1.14.1 // indirect + github.com/fatih/color v1.13.0 // indirect github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect github.com/go-logr/zapr v1.2.3 // indirect - github.com/go-openapi/jsonpointer v0.19.6 // indirect - github.com/go-openapi/jsonreference v0.20.1 // indirect - github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.20.0 // indirect + github.com/go-openapi/swag v0.19.14 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/golang/snappy v0.0.4 // indirect + github.com/golang/protobuf v1.5.2 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 // indirect + github.com/google/go-querystring v1.0.0 // indirect github.com/google/gofuzz v1.1.0 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.1.2 // indirect github.com/googleapis/gax-go/v2 v2.0.5 // indirect github.com/gophercloud/gophercloud v0.1.0 // indirect github.com/hashicorp/consul/proto-public v0.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect - github.com/hashicorp/go-plugin v1.4.5 // indirect - github.com/hashicorp/go-retryablehttp v0.6.6 // indirect - github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 // indirect - github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect - github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect - github.com/hashicorp/go-sockaddr v1.0.2 // indirect - github.com/hashicorp/go-uuid v1.0.3 // indirect + github.com/hashicorp/go-uuid v1.0.2 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/mdns v1.0.4 // indirect - github.com/hashicorp/vault/sdk v0.7.0 // indirect github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443 // indirect - github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62 // indirect + github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/linode/linodego v0.7.1 // indirect - github.com/mailru/easyjson v0.7.7 // indirect + github.com/mailru/easyjson v0.7.6 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect - github.com/mitchellh/copystructure v1.0.0 // indirect - github.com/mitchellh/go-testing-interface v1.0.0 // indirect - github.com/mitchellh/pointerstructure v1.2.1 // indirect - github.com/mitchellh/reflectwalk v1.0.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 // indirect - github.com/oklog/run v1.0.0 // indirect github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c // indirect - github.com/pierrec/lz4 v2.5.2+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/posener/complete v1.2.3 // indirect github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 // indirect - github.com/ryanuber/go-glob v1.0.0 // indirect github.com/sirupsen/logrus v1.8.1 // indirect github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.0 // indirect - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.480 // indirect - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.480 // indirect + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.658 // indirect + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.658 // indirect github.com/vmware/govmomi v0.18.0 // indirect - go.opencensus.io v0.22.4 // indirect + go.opencensus.io v0.23.0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.6.0 // indirect golang.org/x/crypto v0.11.0 // indirect - golang.org/x/mod v0.9.0 // indirect + golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect + golang.org/x/mod v0.8.0 // indirect golang.org/x/net v0.13.0 // indirect golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect - golang.org/x/sync v0.2.0 // indirect + golang.org/x/sync v0.1.0 // indirect golang.org/x/sys v0.10.0 // indirect golang.org/x/term v0.10.0 // indirect - golang.org/x/tools v0.7.0 // indirect - google.golang.org/api v0.30.0 // indirect + golang.org/x/tools v0.6.0 // indirect + google.golang.org/api v0.43.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect google.golang.org/grpc v1.49.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/resty.v1 v1.12.0 // indirect - gopkg.in/square/go-jose.v2 v2.5.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.26.3 // indirect - k8s.io/component-base v0.26.3 // indirect - k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + k8s.io/apiextensions-apiserver v0.26.1 // indirect + k8s.io/component-base v0.26.1 // indirect + k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect + sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) -go 1.20 +replace github.com/hashicorp/consul/sdk => github.com/hashicorp/consul/sdk v0.4.1-0.20221021205723-cc843c4be892 + +go 1.19 diff --git a/control-plane/go.sum b/control-plane/go.sum index 36797f0e3d..cd604e02a3 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -12,8 +12,13 @@ cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bP cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -63,7 +68,9 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14= +github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -71,6 +78,7 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= @@ -89,11 +97,10 @@ github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c= -github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -104,34 +111,45 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/containernetworking/cni v1.1.1 h1:ky20T7c0MvKvbMOwS/FrlbNwjEoqJEUUYfsL4b0mc4k= github.com/containernetworking/cni v1.1.1/go.mod h1:sDpYKmGVENF3s6uvMvGgldDWeG8dMxakj/u+i9ht9vw= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +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/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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 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/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661 h1:lrWnAyy/F72MbxIxFUzKmcMCdt9Oi8RzpAxzTNQHD7o= github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/digitalocean/godo v1.7.5 h1:JOQbAO6QT1GGjor0doT0mXefX2FgUDPOpYh2RaXA+ko= -github.com/digitalocean/godo v1.7.5/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/digitalocean/godo v1.10.0 h1:uW1/FcvZE/hoixnJcnlmIUvTVNdZCLjRLzmDtRi1xXY= +github.com/digitalocean/godo v1.10.0/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= 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.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= @@ -141,14 +159,11 @@ github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJ github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= -github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= -github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/frankban/quicktest v1.13.0 h1:yNZif1OkDfNoDfb9zZa9aXIpejNR4F23Wely0c+Qdqk= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= @@ -171,19 +186,22 @@ github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= -github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= -github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= +github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -196,6 +214,7 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -211,11 +230,9 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= @@ -228,18 +245,21 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/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.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/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.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/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 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0= -github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -247,91 +267,75 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83383d h1:RJ1MZ8JKnfgKQ1kR3IBQAMpOpzXrdseZAYN/QR//MFM= github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83383d/go.mod h1:IHIHMzkoMwlv6rLsgwcoFBVYupR7/1pKEOHBMjD4L0k= github.com/hashicorp/consul-server-connection-manager v0.1.3 h1:fxsZ15XBNNWhV26yBVdCcnxHwSRgf9wqHGS2ZVCQIhc= github.com/hashicorp/consul-server-connection-manager v0.1.3/go.mod h1:Md2IGKaFJ4ek9GUA0pW1S2R60wpquMOUs27GiD9kZd0= -github.com/hashicorp/consul/api v1.10.1-0.20230802160219-cf2bf7fdecdf h1:XCfEJhwSx188jLPjctsGoOfG7OueuXaqceR0HwHAH1s= -github.com/hashicorp/consul/api v1.10.1-0.20230802160219-cf2bf7fdecdf/go.mod h1:TqtEfVYyzax2gaBwU1vsCGFcysmK9g9UANUWCd8qMBw= -github.com/hashicorp/consul/api v1.10.1-0.20230821140749-587663dbcbd1 h1:HulZABqJoIf1NLkWkXRqH1Vl/OLKiJQBEuuFk/XezuI= -github.com/hashicorp/consul/api v1.10.1-0.20230821140749-587663dbcbd1/go.mod h1:NZJGRFYruc/80wYowkPFCp1LbGmJC9L8izrwfyVx/Wg= -github.com/hashicorp/consul/api v1.10.1-0.20230821180813-217d305b38d5 h1:TTTgXv9YeaRnODyFP1k2b2Nq5RIGrUUgI5SkDhuSNwM= -github.com/hashicorp/consul/api v1.10.1-0.20230821180813-217d305b38d5/go.mod h1:NZJGRFYruc/80wYowkPFCp1LbGmJC9L8izrwfyVx/Wg= -github.com/hashicorp/consul/api v1.24.0 h1:u2XyStA2j0jnCiVUU7Qyrt8idjRn4ORhK6DlvZ3bWhA= -github.com/hashicorp/consul/api v1.24.0/go.mod h1:NZJGRFYruc/80wYowkPFCp1LbGmJC9L8izrwfyVx/Wg= +github.com/hashicorp/consul/api v1.10.1-0.20230512003852-bd0eb07ed3ca h1:5UPVYOlJg/HBEJ2q82rkkQ3ZLzeMnF5MOpGcw2kh+XU= +github.com/hashicorp/consul/api v1.10.1-0.20230512003852-bd0eb07ed3ca/go.mod h1:tXfrC6o0yFTgAW46xd5Ic8STHc9oIBcRVBcwhX5KNCQ= github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3usQMXZF6ziK3SzXU= github.com/hashicorp/consul/proto-public v0.1.0/go.mod h1:vs2KkuWwtjkIgA5ezp4YKPzQp4GitV+q/+PvksrA92k= -github.com/hashicorp/consul/sdk v0.14.0 h1:Hly+BMNMssVzoWddbBnBFi3W+Fzytvm0haSkihhj3GU= -github.com/hashicorp/consul/sdk v0.14.0/go.mod h1:gHYeuDa0+0qRAD6Wwr6yznMBvBwHKoxSBoW5l73+saE= -github.com/hashicorp/consul/sdk v0.14.1 h1:ZiwE2bKb+zro68sWzZ1SgHF3kRMBZ94TwOCFRF4ylPs= -github.com/hashicorp/consul/sdk v0.14.1/go.mod h1:vFt03juSzocLRFo59NkeQHHmQa6+g7oU0pfzdI1mUhg= +github.com/hashicorp/consul/sdk v0.4.1-0.20221021205723-cc843c4be892 h1:jw0NwPmNPr5CxAU04hACdj61JSaJBKZ0FdBo+kwfNp4= +github.com/hashicorp/consul/sdk v0.4.1-0.20221021205723-cc843c4be892/go.mod h1:yPkX5Q6CsxTFMjQQDJwzeNmUUF5NUGGbrDsv9wTb8cw= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-bexpr v0.1.11 h1:6DqdA/KBjurGby9yTY0bmkathya0lfwF2SeuubCI7dY= -github.com/hashicorp/go-bexpr v0.1.11/go.mod h1:f03lAo0duBlDIUMGCuad8oLcgejw4m7U+N8T+6Kz1AE= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-discover v0.0.0-20230519164032-214571b6a530 h1:WUwSDou+memX/pb6xnjA0PfAqEEJtdWSrK00kl8ySK8= github.com/hashicorp/go-discover v0.0.0-20230519164032-214571b6a530/go.mod h1:RH2Jr1/cCsZ1nRLmAOC65hp/gRehf55SsUIYV2+NAxI= -github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= -github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.2.2 h1:ihRI7YFwcZdiSD7SIenIhHfQH3OuDvWerAUBZbeQS3M= +github.com/hashicorp/go-hclog v1.2.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-netaddrs v0.1.0 h1:TnlYvODD4C/wO+j7cX1z69kV5gOzI87u3OcUinANaW8= github.com/hashicorp/go-netaddrs v0.1.0/go.mod h1:33+a/emi5R5dqRspOuZKO0E+Tuz5WV1F84eRWALkedA= -github.com/hashicorp/go-plugin v1.4.5 h1:oTE/oQR4eghggRg8VY7PAz3dr++VwDNBGCcOfIvHpBo= -github.com/hashicorp/go-plugin v1.4.5/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= -github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 h1:cCRo8gK7oq6A2L6LICkUZ+/a5rLiRXFMf1Qd4xSwxTc= -github.com/hashicorp/go-secure-stdlib/mlock v0.1.1/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= -github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= -github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.4 h1:sY0CMhFmjIPDMlTB+HfymFHCaYLhgifZ0QhjaYKD/UQ= @@ -340,30 +344,28 @@ github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= -github.com/hashicorp/vault/api v1.8.3 h1:cHQOLcMhBR+aVI0HzhPxO62w2+gJhIrKguQNONPzu6o= -github.com/hashicorp/vault/api v1.8.3/go.mod h1:4g/9lj9lmuJQMtT6CmVMHC5FW1yENaVv+Nv4ZfG8fAg= -github.com/hashicorp/vault/sdk v0.7.0 h1:2pQRO40R1etpKkia5fb4kjrdYMx3BHklPxl1pxpxDHg= -github.com/hashicorp/vault/sdk v0.7.0/go.mod h1:KyfArJkhooyba7gYCKSq8v66QdqJmnbAxtV/OX1+JTs= github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443 h1:O/pT5C1Q3mVXMyuqg7yuAWUg/jMZR1/0QTzTRdNR6Uw= github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443/go.mod h1:bEpDU35nTu0ey1EXjwNwPjI9xErAsoOCmcMb9GKvyxo= -github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= -github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= +github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da h1:FjHUJJ7oBW4G/9j1KzlHaXL09LyMVM9rupS39lncbXk= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62 h1:JHCT6xuyPUrbbgAPE/3dqlvUKzRHMNuTBKKUb6OeR/k= -github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA= +github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f h1:ENpDacvnr8faw5ugQmEF1QYk+f/Y9lXFvuYmRxykago= +github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f/go.mod h1:KDSfL7qe5ZfQqvlDMkVjCztbmcpp/c8M77vhQP8ZPvk= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -375,6 +377,7 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -382,16 +385,18 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/linode/linodego v0.7.1 h1:4WZmMpSA2NRwlPZcc0+4Gyn7rr99Evk9bnr0B3gXRKE= github.com/linode/linodego v0.7.1/go.mod h1:ga11n3ivecUrPCHN0rANxKmfWBJVkOXfLMZinAbj2sY= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -401,12 +406,13 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM= github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= @@ -414,24 +420,14 @@ github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKju github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0 h1:tEElEatulEHDeedTxwckzyYMA5c86fbmNIUL1hBIiTg= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= -github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/pointerstructure v1.2.1 h1:ZhBBeX8tSlRpu/FFhXH4RC4OJzFlqsQhoHZAz4x7TIw= -github.com/mitchellh/pointerstructure v1.2.1/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= -github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -445,10 +441,12 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 h1:BQ1HW7hr4IVovMwWg0E0PYcyW8CzqDcVmaew9cujU4s= github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.0-20180130162743-b8a9be070da4/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= @@ -464,19 +462,18 @@ github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c/go.mod h1:otz github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= -github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +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.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= @@ -490,6 +487,8 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +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.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= @@ -498,6 +497,7 @@ github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+ github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= 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= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= @@ -505,17 +505,21 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 h1:Wdi9nwnhFNAlseAOekn6B5G/+GMtks9UKbvRU/CMM/o= github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03/go.mod h1:gRAiPF5C5Nd0eyyRdqIu9qTiFSoZzpTq727b5B8fkkU= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rs/zerolog v1.4.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= -github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sean-/conswriter v0.0.0-20180208195008-f5ae3917a627/go.mod h1:7zjs06qF79/FKAJpBvFx3P8Ww4UTIMAe+lpNXDHziac= +github.com/sean-/pager v0.0.0-20180208200047-666be9bf53b5/go.mod h1:BeybITEsBEg6qbIiqJ6/Bqeq25bCLbL7YFmpaFfJDuM= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= @@ -523,8 +527,18 @@ github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d h1:bVQRCxQvfjNUeRqaY/uT0tFuvuFY0ulgnczuR684Xic= github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d/go.mod h1:Cw4GTlQccdRGSEf6KiMju767x0NEHE0YIVPJSaXjlsw= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -535,48 +549,61 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.480 h1:Dwnfdrk3KXpYRH9Kwrk9sHpZSOmrE7P9LBoNsYUJKR4= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.480/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.480 h1:YEDZmv2ABU8QvwXEVTOQgVEQzDOByhz73vdjL6sERkE= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.480/go.mod h1:zaBIuDDs+rC74X8Aog+LSu91GFtHYRYDC196RGTm2jk= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.658 h1:q208plt7F8Pj3b1w8D3XDb/vTgHsn/JlEwDCSe+lvyo= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.658/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.658 h1:wF/3PTojsx9/J8CaeiTy0zXxvwrcuu282R4g7fDlgCQ= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.658/go.mod h1:LLex9maWMIQzOFF/mYz5rEsTUwKKcmAbGRehSNRWqgQ= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/vmware/govmomi v0.18.0 h1:f7QxSmP7meCtoAmiKZogvVbLInT+CZx6Px6K5rYsJZo= github.com/vmware/govmomi v0.18.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= 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= @@ -608,6 +635,7 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -616,10 +644,12 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -631,6 +661,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -653,7 +684,12 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= @@ -670,6 +706,12 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= @@ -685,13 +727,15 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -700,6 +744,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -708,6 +753,7 @@ golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -729,12 +775,19 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -744,6 +797,7 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -763,6 +817,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -774,6 +829,7 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -817,12 +873,18 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 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= @@ -844,8 +906,13 @@ google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0 h1:yfrXXP61wVuLb0vBcG6qaOoIoqYEzOQS8jum51jkv2w= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0 h1:4sAyIHT6ZohtAQDoxws+ez7bROYmUlOVvsUscYCDTqA= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -884,11 +951,22 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 h1:hrbNEivu7Zn1pxvHk6MBrq9iE22woVILTHqexqBxe6I= google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= @@ -899,8 +977,13 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw= google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= @@ -924,17 +1007,16 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/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= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= -gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -956,20 +1038,20 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.26.3 h1:emf74GIQMTik01Aum9dPP0gAypL8JTLl/lHa4V9RFSU= -k8s.io/api v0.26.3/go.mod h1:PXsqwPMXBSBcL1lJ9CYDKy7kIReUydukS5JiRlxC3qE= -k8s.io/apiextensions-apiserver v0.26.3 h1:5PGMm3oEzdB1W/FTMgGIDmm100vn7IaUP5er36dB+YE= -k8s.io/apiextensions-apiserver v0.26.3/go.mod h1:jdA5MdjNWGP+njw1EKMZc64xAT5fIhN6VJrElV3sfpQ= -k8s.io/apimachinery v0.26.3 h1:dQx6PNETJ7nODU3XPtrwkfuubs6w7sX0M8n61zHIV/k= -k8s.io/apimachinery v0.26.3/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I= -k8s.io/client-go v0.26.3 h1:k1UY+KXfkxV2ScEL3gilKcF7761xkYsSD6BC9szIu8s= -k8s.io/client-go v0.26.3/go.mod h1:ZPNu9lm8/dbRIPAgteN30RSXea6vrCpFvq+MateTUuQ= -k8s.io/component-base v0.26.3 h1:oC0WMK/ggcbGDTkdcqefI4wIZRYdK3JySx9/HADpV0g= -k8s.io/component-base v0.26.3/go.mod h1:5kj1kZYwSC6ZstHJN7oHBqcJC6yyn41eR+Sqa/mQc8E= -k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= -k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= -k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= +k8s.io/api v0.26.1 h1:f+SWYiPd/GsiWwVRz+NbFyCgvv75Pk9NK6dlkZgpCRQ= +k8s.io/api v0.26.1/go.mod h1:xd/GBNgR0f707+ATNyPmQ1oyKSgndzXij81FzWGsejg= +k8s.io/apiextensions-apiserver v0.26.1 h1:cB8h1SRk6e/+i3NOrQgSFij1B2S0Y0wDoNl66bn8RMI= +k8s.io/apiextensions-apiserver v0.26.1/go.mod h1:AptjOSXDGuE0JICx/Em15PaoO7buLwTs0dGleIHixSM= +k8s.io/apimachinery v0.26.1 h1:8EZ/eGJL+hY/MYCNwhmDzVqq2lPl3N3Bo8rvweJwXUQ= +k8s.io/apimachinery v0.26.1/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= +k8s.io/client-go v0.26.1 h1:87CXzYJnAMGaa/IDDfRdhTzxk/wzGZ+/HUQpqgVSZXU= +k8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE= +k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4= +k8s.io/component-base v0.26.1/go.mod h1:VHrLR0b58oC035w6YQiBSbtsf0ThuSwXP+p5dD/kAWU= +k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= +k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= @@ -977,10 +1059,8 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/controller-runtime v0.14.6 h1:oxstGVvXGNnMvY7TAESYk+lzr6S3V5VFxQ6d92KcwQA= sigs.k8s.io/controller-runtime v0.14.6/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= -sigs.k8s.io/gateway-api v0.7.1 h1:Tts2jeepVkPA5rVG/iO+S43s9n7Vp7jCDhZDQYtPigQ= -sigs.k8s.io/gateway-api v0.7.1/go.mod h1:Xv0+ZMxX0lu1nSSDIIPEfbVztgNZ+3cfiYrJsa2Ooso= -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/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= diff --git a/control-plane/hack/lint-api-new-client/main.go b/control-plane/hack/lint-api-new-client/main.go index 6297f0abb1..c4c6d896b1 100644 --- a/control-plane/hack/lint-api-new-client/main.go +++ b/control-plane/hack/lint-api-new-client/main.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - // Parses golang code looking for github.com/hashicorp/consul/api.NewClient() // being used in non-test code. If it finds this, it will error. // The purpose of this lint is that we actually want to use our internal diff --git a/control-plane/helper/cert/notify.go b/control-plane/helper/cert/notify.go index 076c923119..201ca34dd5 100644 --- a/control-plane/helper/cert/notify.go +++ b/control-plane/helper/cert/notify.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package cert import ( diff --git a/control-plane/helper/cert/notify_test.go b/control-plane/helper/cert/notify_test.go index 7e7cbf6d68..8813816f86 100644 --- a/control-plane/helper/cert/notify_test.go +++ b/control-plane/helper/cert/notify_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package cert import ( diff --git a/control-plane/helper/cert/source.go b/control-plane/helper/cert/source.go index 5a85cb271d..dcc4e3640c 100644 --- a/control-plane/helper/cert/source.go +++ b/control-plane/helper/cert/source.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package cert import ( diff --git a/control-plane/helper/cert/source_gen.go b/control-plane/helper/cert/source_gen.go index 9d54f2836c..e9c79ed390 100644 --- a/control-plane/helper/cert/source_gen.go +++ b/control-plane/helper/cert/source_gen.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package cert import ( diff --git a/control-plane/helper/cert/source_gen_test.go b/control-plane/helper/cert/source_gen_test.go index fd31de654e..b68ffc7e13 100644 --- a/control-plane/helper/cert/source_gen_test.go +++ b/control-plane/helper/cert/source_gen_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package cert import ( diff --git a/control-plane/helper/cert/tls_util.go b/control-plane/helper/cert/tls_util.go index d5552d60f3..37e2f4ea97 100644 --- a/control-plane/helper/cert/tls_util.go +++ b/control-plane/helper/cert/tls_util.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package cert import ( diff --git a/control-plane/helper/coalesce/coalesce.go b/control-plane/helper/coalesce/coalesce.go index bfe341d1d6..d3e96e6be8 100644 --- a/control-plane/helper/coalesce/coalesce.go +++ b/control-plane/helper/coalesce/coalesce.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package coalesce import ( diff --git a/control-plane/helper/coalesce/coalesce_test.go b/control-plane/helper/coalesce/coalesce_test.go index d2c37135c7..8489fed8b4 100644 --- a/control-plane/helper/coalesce/coalesce_test.go +++ b/control-plane/helper/coalesce/coalesce_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package coalesce import ( diff --git a/control-plane/helper/controller/controller.go b/control-plane/helper/controller/controller.go index db6de05b07..12b84b567d 100644 --- a/control-plane/helper/controller/controller.go +++ b/control-plane/helper/controller/controller.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - // Package controller contains a reusable abstraction for efficiently // watching for changes in resources in a Kubernetes cluster. package controller diff --git a/control-plane/helper/controller/controller_test.go b/control-plane/helper/controller/controller_test.go index a7e960a6aa..43fe677280 100644 --- a/control-plane/helper/controller/controller_test.go +++ b/control-plane/helper/controller/controller_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package controller import ( diff --git a/control-plane/helper/controller/resource.go b/control-plane/helper/controller/resource.go index 9634db51ae..959d101488 100644 --- a/control-plane/helper/controller/resource.go +++ b/control-plane/helper/controller/resource.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package controller import ( diff --git a/control-plane/helper/controller/testing.go b/control-plane/helper/controller/testing.go index e4809c70bd..7575276bf5 100644 --- a/control-plane/helper/controller/testing.go +++ b/control-plane/helper/controller/testing.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package controller import ( diff --git a/control-plane/helper/go-discover/discover.go b/control-plane/helper/go-discover/discover.go index 140586634a..845a7b144b 100644 --- a/control-plane/helper/go-discover/discover.go +++ b/control-plane/helper/go-discover/discover.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package godiscover import ( diff --git a/control-plane/helper/go-discover/discover_test.go b/control-plane/helper/go-discover/discover_test.go index 5c25261516..b655dc388e 100644 --- a/control-plane/helper/go-discover/discover_test.go +++ b/control-plane/helper/go-discover/discover_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package godiscover import ( diff --git a/control-plane/helper/go-discover/mocks/mock_provider.go b/control-plane/helper/go-discover/mocks/mock_provider.go index dfdab445da..51afb86a84 100644 --- a/control-plane/helper/go-discover/mocks/mock_provider.go +++ b/control-plane/helper/go-discover/mocks/mock_provider.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package mocks import ( diff --git a/control-plane/helper/mutating-webhook-configuration/mutating_webhook_configuration.go b/control-plane/helper/mutating-webhook-configuration/mutating_webhook_configuration.go index 093b1ef908..c3c93b5204 100644 --- a/control-plane/helper/mutating-webhook-configuration/mutating_webhook_configuration.go +++ b/control-plane/helper/mutating-webhook-configuration/mutating_webhook_configuration.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package mutatingwebhookconfiguration import ( diff --git a/control-plane/helper/mutating-webhook-configuration/mutating_webhook_configuration_test.go b/control-plane/helper/mutating-webhook-configuration/mutating_webhook_configuration_test.go index be1a3b5c64..e247c71d14 100644 --- a/control-plane/helper/mutating-webhook-configuration/mutating_webhook_configuration_test.go +++ b/control-plane/helper/mutating-webhook-configuration/mutating_webhook_configuration_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package mutatingwebhookconfiguration import ( diff --git a/control-plane/helper/parsetags/parsetags.go b/control-plane/helper/parsetags/parsetags.go index caec75bb1d..e9d9625338 100644 --- a/control-plane/helper/parsetags/parsetags.go +++ b/control-plane/helper/parsetags/parsetags.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package parsetags import ( diff --git a/control-plane/helper/parsetags/parsetags_test.go b/control-plane/helper/parsetags/parsetags_test.go index 2a6b9ad47f..f403a2f711 100644 --- a/control-plane/helper/parsetags/parsetags_test.go +++ b/control-plane/helper/parsetags/parsetags_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package parsetags import ( diff --git a/control-plane/helper/test/test_util.go b/control-plane/helper/test/test_util.go index e29e44de59..0ad4601fde 100644 --- a/control-plane/helper/test/test_util.go +++ b/control-plane/helper/test/test_util.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package test import ( diff --git a/control-plane/main.go b/control-plane/main.go index 64ccd5d43a..7ec1340290 100644 --- a/control-plane/main.go +++ b/control-plane/main.go @@ -1,15 +1,11 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package main import ( "log" "os" - "github.com/mitchellh/cli" - "github.com/hashicorp/consul-k8s/control-plane/version" + "github.com/mitchellh/cli" ) func main() { diff --git a/control-plane/namespaces/namespaces.go b/control-plane/namespaces/namespaces.go index 84b8c15ee4..c17aa1f788 100644 --- a/control-plane/namespaces/namespaces.go +++ b/control-plane/namespaces/namespaces.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - // Package namespaces handles interaction with Consul namespaces needed across // commands. package namespaces diff --git a/control-plane/namespaces/namespaces_test.go b/control-plane/namespaces/namespaces_test.go index a2c9989ae8..7b6a061a65 100644 --- a/control-plane/namespaces/namespaces_test.go +++ b/control-plane/namespaces/namespaces_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - //go:build enterprise package namespaces diff --git a/control-plane/subcommand/acl-init/command.go b/control-plane/subcommand/acl-init/command.go index 77e8f87d87..af85128ea8 100644 --- a/control-plane/subcommand/acl-init/command.go +++ b/control-plane/subcommand/acl-init/command.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package aclinit import ( diff --git a/control-plane/subcommand/acl-init/command_test.go b/control-plane/subcommand/acl-init/command_test.go index acdafc939f..c9f5703459 100644 --- a/control-plane/subcommand/acl-init/command_test.go +++ b/control-plane/subcommand/acl-init/command_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package aclinit import ( diff --git a/control-plane/subcommand/auth.go b/control-plane/subcommand/auth.go index 6389e7d6f7..58a9b801e8 100644 --- a/control-plane/subcommand/auth.go +++ b/control-plane/subcommand/auth.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package subcommand import ( diff --git a/control-plane/subcommand/common/common.go b/control-plane/subcommand/common/common.go index 1636c0b10e..00535cb6fe 100644 --- a/control-plane/subcommand/common/common.go +++ b/control-plane/subcommand/common/common.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - // Package common holds code needed by multiple commands. package common diff --git a/control-plane/subcommand/common/common_test.go b/control-plane/subcommand/common/common_test.go index 9e50302b17..bce90d6b76 100644 --- a/control-plane/subcommand/common/common_test.go +++ b/control-plane/subcommand/common/common_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package common import ( @@ -11,6 +8,7 @@ import ( "net/url" "os" "testing" + "time" "github.com/hashicorp/consul-k8s/control-plane/helper/go-discover/mocks" "github.com/hashicorp/consul/api" @@ -165,33 +163,36 @@ func TestConsulLogin_TokenNotReplicated(t *testing.T) { func TestConsulLogin_EmptyBearerTokenFile(t *testing.T) { t.Parallel() + require := require.New(t) bearerTokenFile := WriteTempFile(t, "") params := LoginParams{ BearerTokenFile: bearerTokenFile, } _, err := ConsulLogin(nil, params, hclog.NewNullLogger()) - require.EqualError(t, err, fmt.Sprintf("no bearer token found in %q", bearerTokenFile)) + require.EqualError(err, fmt.Sprintf("no bearer token found in %q", bearerTokenFile)) } func TestConsulLogin_BearerTokenFileDoesNotExist(t *testing.T) { t.Parallel() + require := require.New(t) randFileName := fmt.Sprintf("/foo/%d/%d", rand.Int(), rand.Int()) params := LoginParams{ BearerTokenFile: randFileName, } _, err := ConsulLogin(nil, params, hclog.NewNullLogger()) - require.Error(t, err) - require.Contains(t, err.Error(), "unable to read bearer token file") + require.Error(err) + require.Contains(err.Error(), "unable to read bearer token file") } func TestConsulLogin_TokenFileUnwritable(t *testing.T) { t.Parallel() + require := require.New(t) bearerTokenFile := WriteTempFile(t, "foo") client := startMockServer(t) // This is a common.Logger. log, err := Logger("INFO", false) - require.NoError(t, err) + require.NoError(err) randFileName := fmt.Sprintf("/foo/%d/%d", rand.Int(), rand.Int()) params := LoginParams{ AuthMethod: testAuthMethod, @@ -200,12 +201,13 @@ func TestConsulLogin_TokenFileUnwritable(t *testing.T) { NumRetries: 2, } _, err = ConsulLogin(client, params, log) - require.Error(t, err) - require.Contains(t, err.Error(), "error writing token to file sink") + require.Error(err) + require.Contains(err.Error(), "error writing token to file sink") } func TestWriteFileWithPerms_InvalidOutputFile(t *testing.T) { t.Parallel() + rand.New(rand.NewSource(time.Now().UnixNano())) randFileName := fmt.Sprintf("/tmp/tmp/tmp/%d", rand.Int()) t.Cleanup(func() { os.RemoveAll(randFileName) @@ -216,6 +218,7 @@ func TestWriteFileWithPerms_InvalidOutputFile(t *testing.T) { func TestWriteFileWithPerms_OutputFileExists(t *testing.T) { t.Parallel() + rand.New(rand.NewSource(time.Now().UnixNano())) randFileName := fmt.Sprintf("/tmp/%d", rand.Int()) err := os.WriteFile(randFileName, []byte("foo"), os.FileMode(0444)) require.NoError(t, err) @@ -233,6 +236,7 @@ func TestWriteFileWithPerms_OutputFileExists(t *testing.T) { func TestWriteFileWithPerms(t *testing.T) { t.Parallel() payload := "foo-foo-foo-foo" + rand.New(rand.NewSource(time.Now().UnixNano())) randFileName := fmt.Sprintf("/tmp/%d", rand.Int()) t.Cleanup(func() { os.RemoveAll(randFileName) diff --git a/control-plane/subcommand/common/test_util.go b/control-plane/subcommand/common/test_util.go index 3399b40e2b..13d9017fe4 100644 --- a/control-plane/subcommand/common/test_util.go +++ b/control-plane/subcommand/common/test_util.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package common import ( diff --git a/control-plane/subcommand/connect-init/command.go b/control-plane/subcommand/connect-init/command.go index 4f83ea98f1..4750e9455c 100644 --- a/control-plane/subcommand/connect-init/command.go +++ b/control-plane/subcommand/connect-init/command.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package connectinit import ( @@ -17,19 +14,17 @@ import ( "time" "github.com/cenkalti/backoff" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" + "github.com/hashicorp/consul-k8s/control-plane/consul" + "github.com/hashicorp/consul-k8s/control-plane/namespaces" + "github.com/hashicorp/consul-k8s/control-plane/subcommand/common" + "github.com/hashicorp/consul-k8s/control-plane/subcommand/flags" "github.com/hashicorp/consul-server-connection-manager/discovery" "github.com/hashicorp/consul/api" "github.com/hashicorp/consul/sdk/iptables" "github.com/hashicorp/go-hclog" "github.com/mitchellh/cli" "github.com/mitchellh/mapstructure" - - "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" - "github.com/hashicorp/consul-k8s/control-plane/consul" - "github.com/hashicorp/consul-k8s/control-plane/namespaces" - "github.com/hashicorp/consul-k8s/control-plane/subcommand/common" - "github.com/hashicorp/consul-k8s/control-plane/subcommand/flags" - "github.com/hashicorp/consul-k8s/control-plane/version" ) const ( @@ -163,17 +158,6 @@ func (c *Command) Run(args []string) int { c.logger.Error("Unable to get client connection", "error", err) return 1 } - if version.IsFIPS() { - // make sure we are also using FIPS Consul - var versionInfo map[string]interface{} - _, err := consulClient.Raw().Query("/v1/agent/version", versionInfo, nil) - if err != nil { - c.logger.Warn("This is a FIPS build of consul-k8s, which should be used with FIPS Consul. Unable to verify FIPS Consul while setting up Consul API client.") - } - if val, ok := versionInfo["FIPS"]; !ok || val == "" { - c.logger.Warn("This is a FIPS build of consul-k8s, which should be used with FIPS Consul. A non-FIPS version of Consul was detected.") - } - } proxyService := &api.AgentService{} if c.flagGatewayKind != "" { err = backoff.Retry(c.getGatewayRegistration(consulClient), backoff.WithMaxRetries(backoff.NewConstantBackOff(1*time.Second), c.serviceRegistrationPollingAttempts)) @@ -329,7 +313,7 @@ func (c *Command) getGatewayRegistration(client *api.Client) backoff.Operation { } for _, gateway := range gatewayList.Services { switch gateway.Kind { - case api.ServiceKindAPIGateway, api.ServiceKindMeshGateway, api.ServiceKindIngressGateway, api.ServiceKindTerminatingGateway: + case api.ServiceKindMeshGateway, api.ServiceKindIngressGateway, api.ServiceKindTerminatingGateway: proxyID = gateway.ID } } diff --git a/control-plane/subcommand/connect-init/command_ent_test.go b/control-plane/subcommand/connect-init/command_ent_test.go index b3ef7109e0..ecdc34122e 100644 --- a/control-plane/subcommand/connect-init/command_ent_test.go +++ b/control-plane/subcommand/connect-init/command_ent_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - //go:build enterprise package connectinit diff --git a/control-plane/subcommand/connect-init/command_test.go b/control-plane/subcommand/connect-init/command_test.go index cb1d32d03b..14bdc5280c 100644 --- a/control-plane/subcommand/connect-init/command_test.go +++ b/control-plane/subcommand/connect-init/command_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package connectinit import ( @@ -615,7 +612,7 @@ func TestRun_Gateways_Errors(t *testing.T) { "-pod-name", testPodName, "-pod-namespace", testPodNamespace, "-proxy-id-file", proxyFile, - "-consul-api-timeout", "10s", + "-consul-api-timeout", "5s", "-consul-node-name", nodeName, } @@ -729,7 +726,7 @@ func TestRun_InvalidProxyFile(t *testing.T) { "-http-port", strconv.Itoa(serverCfg.Ports.HTTP), "-grpc-port", strconv.Itoa(serverCfg.Ports.GRPC), "-proxy-id-file", randFileName, - "-consul-api-timeout", "10s", + "-consul-api-timeout", "5s", } code := cmd.Run(flags) require.Equal(t, 1, code) diff --git a/control-plane/subcommand/consul-logout/command.go b/control-plane/subcommand/consul-logout/command.go index a6b599dccd..b0836b71f0 100644 --- a/control-plane/subcommand/consul-logout/command.go +++ b/control-plane/subcommand/consul-logout/command.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package consullogout import ( diff --git a/control-plane/subcommand/consul-logout/command_test.go b/control-plane/subcommand/consul-logout/command_test.go index 1a0744996a..22412ea752 100644 --- a/control-plane/subcommand/consul-logout/command_test.go +++ b/control-plane/subcommand/consul-logout/command_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package consullogout import ( @@ -54,7 +51,7 @@ func TestRun_InvalidSinkFile(t *testing.T) { } code := cmd.Run([]string{ "-token-file", randFileName, - "-consul-api-timeout", "10s", + "-consul-api-timeout", "5s", }) require.Equal(t, 1, code) } @@ -107,7 +104,7 @@ func Test_UnableToLogoutDueToInvalidToken(t *testing.T) { code := cmd.Run([]string{ "-http-addr", fmt.Sprintf("%s://%s", cfg.Scheme, cfg.Address), "-token-file", tokenFile, - "-consul-api-timeout", "10s", + "-consul-api-timeout", "5s", }) require.Equal(t, 1, code, ui.ErrorWriter.String()) require.Contains(t, "Unexpected response code: 403 (ACL not found)", ui.ErrorWriter.String()) @@ -172,7 +169,7 @@ func Test_RunUsingLogin(t *testing.T) { code := cmd.Run([]string{ "-http-addr", fmt.Sprintf("%s://%s", cfg.Scheme, cfg.Address), "-token-file", tokenFile, - "-consul-api-timeout", "10s", + "-consul-api-timeout", "5s", }) require.Equal(t, 0, code, ui.ErrorWriter.String()) diff --git a/control-plane/subcommand/create-federation-secret/command.go b/control-plane/subcommand/create-federation-secret/command.go index 52600aedda..3a96e18134 100644 --- a/control-plane/subcommand/create-federation-secret/command.go +++ b/control-plane/subcommand/create-federation-secret/command.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package createfederationsecret import ( diff --git a/control-plane/subcommand/create-federation-secret/command_test.go b/control-plane/subcommand/create-federation-secret/command_test.go index 15f12b132c..86ff8aec18 100644 --- a/control-plane/subcommand/create-federation-secret/command_test.go +++ b/control-plane/subcommand/create-federation-secret/command_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package createfederationsecret import ( @@ -80,7 +77,7 @@ func TestRun_FlagValidation(t *testing.T) { "-server-ca-key-file=file", "-ca-file", f.Name(), "-mesh-gateway-service-name=name", - "-consul-api-timeout=10s", + "-consul-api-timeout=5s", "-log-level=invalid", }, expErr: "unknown log level: invalid", @@ -117,7 +114,7 @@ func TestRun_CAFileMissing(t *testing.T) { "-server-ca-cert-file", f.Name(), "-server-ca-key-file", f.Name(), "-ca-file=/this/does/not/exist", - "-consul-api-timeout", "10s", + "-consul-api-timeout", "5s", }) require.Equal(t, 1, exitCode, ui.ErrorWriter.String()) require.Contains(t, ui.ErrorWriter.String(), "error reading CA file") @@ -140,7 +137,7 @@ func TestRun_ServerCACertFileMissing(t *testing.T) { "-ca-file", f.Name(), "-server-ca-cert-file=/this/does/not/exist", "-server-ca-key-file", f.Name(), - "-consul-api-timeout", "10s", + "-consul-api-timeout", "5s", }) require.Equal(t, 1, exitCode, ui.ErrorWriter.String()) require.Contains(t, ui.ErrorWriter.String(), "Error reading server CA cert file") @@ -163,7 +160,7 @@ func TestRun_ServerCAKeyFileMissing(t *testing.T) { "-ca-file", f.Name(), "-server-ca-cert-file", f.Name(), "-server-ca-key-file=/this/does/not/exist", - "-consul-api-timeout", "10s", + "-consul-api-timeout", "5s", }) require.Equal(t, 1, exitCode, ui.ErrorWriter.String()) require.Contains(t, ui.ErrorWriter.String(), "Error reading server CA key file") @@ -187,7 +184,7 @@ func TestRun_GossipEncryptionKeyFileMissing(t *testing.T) { "-server-ca-cert-file", f.Name(), "-server-ca-key-file", f.Name(), "-gossip-key-file=/this/does/not/exist", - "-consul-api-timeout", "10s", + "-consul-api-timeout", "5s", }) require.Equal(t, 1, exitCode, ui.ErrorWriter.String()) require.Contains(t, ui.ErrorWriter.String(), "Error reading gossip encryption key file") @@ -211,7 +208,7 @@ func TestRun_GossipEncryptionKeyFileEmpty(t *testing.T) { "-server-ca-cert-file", f.Name(), "-server-ca-key-file", f.Name(), "-gossip-key-file", f.Name(), - "-consul-api-timeout", "10s", + "-consul-api-timeout", "5s", }) require.Equal(t, 1, exitCode, ui.ErrorWriter.String()) require.Contains(t, ui.ErrorWriter.String(), fmt.Sprintf("gossip key file %q was empty", f.Name())) @@ -249,7 +246,7 @@ func TestRun_ReplicationTokenMissingExpectedKey(t *testing.T) { "-server-ca-cert-file", f.Name(), "-server-ca-key-file", f.Name(), "-export-replication-token", - "-consul-api-timeout", "10s", + "-consul-api-timeout", "5s", }) require.Equal(t, 1, exitCode, ui.ErrorWriter.String()) } @@ -844,7 +841,7 @@ func TestRun_ReplicationSecretDelay(t *testing.T) { "-server-ca-key-file", keyFile, "-http-addr", fmt.Sprintf("https://%s", testserver.HTTPSAddr), "-export-replication-token", - "-consul-api-timeout", "10s", + "-consul-api-timeout", "5s", } exitCode := cmd.Run(flags) require.Equal(t, 0, exitCode, ui.ErrorWriter.String()) @@ -1052,7 +1049,7 @@ func TestRun_ConsulClientDelay(t *testing.T) { "-server-ca-cert-file", caFile, "-server-ca-key-file", keyFile, "-http-addr", fmt.Sprintf("https://127.0.0.1:%d", randomPorts[2]), - "-consul-api-timeout", "10s", + "-consul-api-timeout", "5s", } exitCode := cmd.Run(flags) require.Equal(t, 0, exitCode, ui.ErrorWriter.String()) diff --git a/control-plane/subcommand/delete-completed-job/command.go b/control-plane/subcommand/delete-completed-job/command.go index f6f594393d..273e621a2a 100644 --- a/control-plane/subcommand/delete-completed-job/command.go +++ b/control-plane/subcommand/delete-completed-job/command.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package deletecompletedjob import ( diff --git a/control-plane/subcommand/delete-completed-job/command_test.go b/control-plane/subcommand/delete-completed-job/command_test.go index abfbb0e788..0da056fc88 100644 --- a/control-plane/subcommand/delete-completed-job/command_test.go +++ b/control-plane/subcommand/delete-completed-job/command_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package deletecompletedjob import ( diff --git a/control-plane/subcommand/fetch-server-region/command.go b/control-plane/subcommand/fetch-server-region/command.go deleted file mode 100644 index 248ce971e7..0000000000 --- a/control-plane/subcommand/fetch-server-region/command.go +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package fetchserverregion - -import ( - "context" - "encoding/json" - "flag" - "fmt" - "os" - "sync" - - "github.com/hashicorp/consul-k8s/control-plane/subcommand/common" - "github.com/hashicorp/consul-k8s/control-plane/subcommand/flags" - "github.com/hashicorp/go-hclog" - "github.com/mitchellh/cli" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" -) - -// The consul-logout command issues a Consul logout API request to delete an ACL token. -type Command struct { - UI cli.Ui - - flagLogLevel string - flagLogJSON bool - flagNodeName string - flagOutputFile string - - flagSet *flag.FlagSet - k8s *flags.K8SFlags - - once sync.Once - help string - logger hclog.Logger - - // for testing - clientset kubernetes.Interface -} - -type Locality struct { - Region string `json:"region"` -} - -type Config struct { - Locality Locality `json:"locality"` -} - -func (c *Command) init() { - c.flagSet = flag.NewFlagSet("", flag.ContinueOnError) - c.flagSet.StringVar(&c.flagLogLevel, "log-level", "info", - "Log verbosity level. Supported values (in order of detail) are \"trace\", "+ - "\"debug\", \"info\", \"warn\", and \"error\".") - c.flagSet.BoolVar(&c.flagLogJSON, "log-json", false, - "Enable or disable JSON output format for logging.") - c.flagSet.StringVar(&c.flagNodeName, "node-name", "", - "Specifies the node name that will be used.") - c.flagSet.StringVar(&c.flagOutputFile, "output-file", "", - "The file path for writing the locality portion of a Consul agent configuration to.") - - c.k8s = &flags.K8SFlags{} - flags.Merge(c.flagSet, c.k8s.Flags()) - - c.help = flags.Usage(help, c.flagSet) - -} - -func (c *Command) Run(args []string) int { - var err error - c.once.Do(c.init) - - if err := c.flagSet.Parse(args); err != nil { - return 1 - } - - if c.logger == nil { - c.logger, err = common.Logger(c.flagLogLevel, c.flagLogJSON) - if err != nil { - c.UI.Error(err.Error()) - return 1 - } - } - - if c.flagNodeName == "" { - c.UI.Error("-node-name is required") - return 1 - } - - if c.flagOutputFile == "" { - c.UI.Error("-output-file is required") - return 1 - } - - if c.clientset == nil { - config, err := rest.InClusterConfig() - if err != nil { - // This just allows us to test it locally. - kubeconfig := clientcmd.RecommendedHomeFile - config, err = clientcmd.BuildConfigFromFlags("", kubeconfig) - if err != nil { - c.UI.Error(err.Error()) - return 1 - } - } - - c.clientset, err = kubernetes.NewForConfig(config) - if err != nil { - c.UI.Error(err.Error()) - return 1 - } - } - - config := c.fetchLocalityConfig() - - jsonData, err := json.Marshal(config) - if err != nil { - c.UI.Error(err.Error()) - return 1 - } - - err = os.WriteFile(c.flagOutputFile, jsonData, 0644) - if err != nil { - c.UI.Error(fmt.Sprintf("Error writing locality file: %s", err)) - return 1 - } - - return 0 -} - -func (c *Command) fetchLocalityConfig() Config { - var cfg Config - node, err := c.clientset.CoreV1().Nodes().Get(context.Background(), c.flagNodeName, metav1.GetOptions{}) - if err != nil { - return cfg - } - - cfg.Locality.Region = node.Labels[corev1.LabelTopologyRegion] - - return cfg -} - -func (c *Command) Synopsis() string { return synopsis } -func (c *Command) Help() string { - c.once.Do(c.init) - return c.help -} - -const synopsis = "Fetch the cloud region for a Consul server from the Kubernetes node's region label." -const help = ` -Usage: consul-k8s-control-plane fetch-server-region [options] - - Fetch the region for a Consul server. - Not intended for stand-alone use. -` diff --git a/control-plane/subcommand/fetch-server-region/command_test.go b/control-plane/subcommand/fetch-server-region/command_test.go deleted file mode 100644 index a64dc9be95..0000000000 --- a/control-plane/subcommand/fetch-server-region/command_test.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package fetchserverregion - -import ( - "os" - "testing" - - "github.com/mitchellh/cli" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/kubernetes/fake" -) - -func TestRun_FlagValidation(t *testing.T) { - t.Parallel() - - ui := cli.NewMockUi() - cmd := Command{ - UI: ui, - } - - cases := map[string]struct { - args []string - err string - }{ - "missing node name": { - args: []string{}, - err: "-node-name is required", - }, - "missing output-file": { - args: []string{"-node-name", "n1"}, - err: "-output-file is required", - }, - } - - for n, c := range cases { - c := c - t.Run(n, func(t *testing.T) { - responseCode := cmd.Run(c.args) - require.Equal(t, 1, responseCode, ui.ErrorWriter.String()) - require.Contains(t, ui.ErrorWriter.String(), c.err) - }) - } -} - -func TestRun(t *testing.T) { - t.Parallel() - - cases := map[string]struct { - region string - expected string - missingNode bool - }{ - "no region": { - expected: `{"locality":{"region":""}}`, - }, - "region": { - region: "us-east-1", - expected: `{"locality":{"region":"us-east-1"}}`, - }, - "missing node": { - region: "us-east-1", - missingNode: true, - expected: `{"locality":{"region":""}}`, - }, - } - - for n, c := range cases { - c := c - t.Run(n, func(t *testing.T) { - outputFile, err := os.CreateTemp("", "ca") - require.NoError(t, err) - t.Cleanup(func() { - os.RemoveAll(outputFile.Name()) - }) - - var objs []runtime.Object - if !c.missingNode { - objs = append(objs, &v1.Node{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-node", - Labels: map[string]string{ - corev1.LabelTopologyRegion: c.region, - }, - }, - }) - } - - k8s := fake.NewSimpleClientset(objs...) - ui := cli.NewMockUi() - cmd := Command{ - UI: ui, - clientset: k8s, - } - - responseCode := cmd.Run([]string{ - "-node-name", - "my-node", - "-output-file", - outputFile.Name(), - }) - require.Equal(t, 0, responseCode, ui.ErrorWriter.String()) - require.NoError(t, err) - cfg, err := os.ReadFile(outputFile.Name()) - require.NoError(t, err) - require.Equal(t, c.expected, string(cfg)) - }) - } -} diff --git a/control-plane/subcommand/flags/consul.go b/control-plane/subcommand/flags/consul.go index 9368b95b3d..1294729a6b 100644 --- a/control-plane/subcommand/flags/consul.go +++ b/control-plane/subcommand/flags/consul.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flags import ( diff --git a/control-plane/subcommand/flags/consul_test.go b/control-plane/subcommand/flags/consul_test.go index 7f35dc8575..b53025daa6 100644 --- a/control-plane/subcommand/flags/consul_test.go +++ b/control-plane/subcommand/flags/consul_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flags import ( diff --git a/control-plane/subcommand/flags/flag_map_value.go b/control-plane/subcommand/flags/flag_map_value.go index c647f57bf5..9ddb4dad08 100644 --- a/control-plane/subcommand/flags/flag_map_value.go +++ b/control-plane/subcommand/flags/flag_map_value.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flags import ( diff --git a/control-plane/subcommand/flags/flag_map_value_test.go b/control-plane/subcommand/flags/flag_map_value_test.go index ea68b6b312..a3b7659cc4 100644 --- a/control-plane/subcommand/flags/flag_map_value_test.go +++ b/control-plane/subcommand/flags/flag_map_value_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flags import ( diff --git a/control-plane/subcommand/flags/flag_slice_value.go b/control-plane/subcommand/flags/flag_slice_value.go index 42c45562b2..11e838e683 100644 --- a/control-plane/subcommand/flags/flag_slice_value.go +++ b/control-plane/subcommand/flags/flag_slice_value.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flags import "strings" diff --git a/control-plane/subcommand/flags/flag_slice_value_test.go b/control-plane/subcommand/flags/flag_slice_value_test.go index 24cf4df695..e361c12b10 100644 --- a/control-plane/subcommand/flags/flag_slice_value_test.go +++ b/control-plane/subcommand/flags/flag_slice_value_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flags import ( diff --git a/control-plane/subcommand/flags/flags.go b/control-plane/subcommand/flags/flags.go index 6739684bf0..e290e876d7 100644 --- a/control-plane/subcommand/flags/flags.go +++ b/control-plane/subcommand/flags/flags.go @@ -1,5 +1,2 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - // Package flags holds common flags that are shared between our commands. package flags diff --git a/control-plane/subcommand/flags/http.go b/control-plane/subcommand/flags/http.go index 21ccb45df1..74db3c26dc 100644 --- a/control-plane/subcommand/flags/http.go +++ b/control-plane/subcommand/flags/http.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flags import ( diff --git a/control-plane/subcommand/flags/http_test.go b/control-plane/subcommand/flags/http_test.go index 3292661933..a44f79931c 100644 --- a/control-plane/subcommand/flags/http_test.go +++ b/control-plane/subcommand/flags/http_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flags import ( diff --git a/control-plane/subcommand/flags/k8s.go b/control-plane/subcommand/flags/k8s.go index 15f1a996b9..31a2284f65 100644 --- a/control-plane/subcommand/flags/k8s.go +++ b/control-plane/subcommand/flags/k8s.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flags import ( diff --git a/control-plane/subcommand/flags/mapset.go b/control-plane/subcommand/flags/mapset.go index d97419a827..c58cc9a3a2 100644 --- a/control-plane/subcommand/flags/mapset.go +++ b/control-plane/subcommand/flags/mapset.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flags import "github.com/deckarep/golang-set" diff --git a/control-plane/subcommand/flags/usage.go b/control-plane/subcommand/flags/usage.go index df63d9c8a6..960ce85926 100644 --- a/control-plane/subcommand/flags/usage.go +++ b/control-plane/subcommand/flags/usage.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flags import ( diff --git a/control-plane/subcommand/flags/usage_test.go b/control-plane/subcommand/flags/usage_test.go index 7e27ad77fb..801888e549 100644 --- a/control-plane/subcommand/flags/usage_test.go +++ b/control-plane/subcommand/flags/usage_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package flags import ( diff --git a/control-plane/subcommand/gateway-cleanup/command.go b/control-plane/subcommand/gateway-cleanup/command.go deleted file mode 100644 index 04aa8f60a9..0000000000 --- a/control-plane/subcommand/gateway-cleanup/command.go +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package gatewaycleanup - -import ( - "context" - "errors" - "flag" - "fmt" - "sync" - "time" - - "github.com/cenkalti/backoff" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul-k8s/control-plane/subcommand" - "github.com/hashicorp/consul-k8s/control-plane/subcommand/flags" - "github.com/mitchellh/cli" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/controller-runtime/pkg/client" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -type Command struct { - UI cli.Ui - - flags *flag.FlagSet - k8s *flags.K8SFlags - - flagGatewayClassName string - flagGatewayClassConfigName string - - k8sClient client.Client - - once sync.Once - help string - - ctx context.Context -} - -func (c *Command) init() { - c.flags = flag.NewFlagSet("", flag.ContinueOnError) - - c.flags.StringVar(&c.flagGatewayClassName, "gateway-class-name", "", - "Name of Kubernetes GatewayClass to delete.") - c.flags.StringVar(&c.flagGatewayClassConfigName, "gateway-class-config-name", "", - "Name of Kubernetes GatewayClassConfig to delete.") - - c.k8s = &flags.K8SFlags{} - flags.Merge(c.flags, c.k8s.Flags()) - c.help = flags.Usage(help, c.flags) -} - -func (c *Command) Run(args []string) int { - var err error - c.once.Do(c.init) - if err = c.flags.Parse(args); err != nil { - return 1 - } - // Validate flags - if err := c.validateFlags(); err != nil { - c.UI.Error(err.Error()) - return 1 - } - - if c.ctx == nil { - c.ctx = context.Background() - } - - // Create the Kubernetes clientset - if c.k8sClient == nil { - config, err := subcommand.K8SConfig(c.k8s.KubeConfig()) - if err != nil { - c.UI.Error(fmt.Sprintf("Error retrieving Kubernetes auth: %s", err)) - return 1 - } - - s := runtime.NewScheme() - if err := clientgoscheme.AddToScheme(s); err != nil { - c.UI.Error(fmt.Sprintf("Could not add client-go schema: %s", err)) - return 1 - } - if err := gwv1beta1.Install(s); err != nil { - c.UI.Error(fmt.Sprintf("Could not add api-gateway schema: %s", err)) - return 1 - } - if err := v1alpha1.AddToScheme(s); err != nil { - c.UI.Error(fmt.Sprintf("Could not add consul-k8s schema: %s", err)) - return 1 - } - - c.k8sClient, err = client.New(config, client.Options{Scheme: s}) - if err != nil { - c.UI.Error(fmt.Sprintf("Error initializing Kubernetes client: %s", err)) - return 1 - } - } - - // do the cleanup - - // find the class config and mark it for deletion first so that we - // can do an early return if the gateway class isn't found - config := &v1alpha1.GatewayClassConfig{} - err = c.k8sClient.Get(context.Background(), types.NamespacedName{Name: c.flagGatewayClassConfigName}, config) - if err != nil { - if k8serrors.IsNotFound(err) { - // no gateway class config, just ignore and return - return 0 - } - c.UI.Error(err.Error()) - return 1 - } - - // ignore any returned errors - _ = c.k8sClient.Delete(context.Background(), config) - - // find the gateway class - gatewayClass := &gwv1beta1.GatewayClass{} - err = c.k8sClient.Get(context.Background(), types.NamespacedName{Name: c.flagGatewayClassName}, gatewayClass) - if err != nil { - if k8serrors.IsNotFound(err) { - // no gateway class, just ignore and return - return 0 - } - c.UI.Error(err.Error()) - return 1 - } - - // ignore any returned errors - _ = c.k8sClient.Delete(context.Background(), gatewayClass) - - // make sure they're gone - if err := backoff.Retry(func() error { - err = c.k8sClient.Get(context.Background(), types.NamespacedName{Name: c.flagGatewayClassConfigName}, config) - if err == nil || !k8serrors.IsNotFound(err) { - return errors.New("gateway class config still exists") - } - - err = c.k8sClient.Get(context.Background(), types.NamespacedName{Name: c.flagGatewayClassName}, gatewayClass) - if err == nil || !k8serrors.IsNotFound(err) { - return errors.New("gateway class still exists") - } - - return nil - }, exponentialBackoffWithMaxIntervalAndTime()); err != nil { - c.UI.Error(err.Error()) - // if we failed, return 0 anyway after logging the error - // since we don't want to block someone from uninstallation - } - - return 0 -} - -func (c *Command) validateFlags() error { - if c.flagGatewayClassConfigName == "" { - return errors.New("-gateway-class-config-name must be set") - } - if c.flagGatewayClassName == "" { - return errors.New("-gateway-class-name must be set") - } - - return nil -} - -func (c *Command) Synopsis() string { return synopsis } -func (c *Command) Help() string { - c.once.Do(c.init) - return c.help -} - -const synopsis = "Clean up global gateway resources prior to uninstall." -const help = ` -Usage: consul-k8s-control-plane gateway-cleanup [options] - - Deletes installed gateway class and gateway class config objects - prior to helm uninstallation. This is required due to finalizers - existing on the GatewayClassConfig that will leave around a dangling - object without deleting these prior to their controllers being deleted. - The job is best effort, so if it fails to successfully delete the - objects, it will allow the uninstallation to continue. - -` - -func exponentialBackoffWithMaxIntervalAndTime() *backoff.ExponentialBackOff { - backoff := backoff.NewExponentialBackOff() - backoff.MaxElapsedTime = 10 * time.Second - backoff.MaxInterval = 1 * time.Second - backoff.Reset() - return backoff -} diff --git a/control-plane/subcommand/gateway-cleanup/command_test.go b/control-plane/subcommand/gateway-cleanup/command_test.go deleted file mode 100644 index 56b7651270..0000000000 --- a/control-plane/subcommand/gateway-cleanup/command_test.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package gatewaycleanup - -import ( - "testing" - - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/mitchellh/cli" - "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -func TestRun(t *testing.T) { - t.Parallel() - - for name, tt := range map[string]struct { - gatewayClassConfig *v1alpha1.GatewayClassConfig - gatewayClass *gwv1beta1.GatewayClass - }{ - "both exist": { - gatewayClassConfig: &v1alpha1.GatewayClassConfig{}, - gatewayClass: &gwv1beta1.GatewayClass{}, - }, - "gateway class config doesn't exist": { - gatewayClass: &gwv1beta1.GatewayClass{}, - }, - "gateway class doesn't exist": { - gatewayClassConfig: &v1alpha1.GatewayClassConfig{}, - }, - "neither exist": {}, - "finalizers on gatewayclass blocking deletion": { - gatewayClassConfig: &v1alpha1.GatewayClassConfig{}, - gatewayClass: &gwv1beta1.GatewayClass{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"finalizer"}}}, - }, - "finalizers on gatewayclassconfig blocking deletion": { - gatewayClassConfig: &v1alpha1.GatewayClassConfig{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"finalizer"}}}, - gatewayClass: &gwv1beta1.GatewayClass{}, - }, - } { - t.Run(name, func(t *testing.T) { - tt := tt - - t.Parallel() - - s := runtime.NewScheme() - require.NoError(t, gwv1beta1.Install(s)) - require.NoError(t, v1alpha1.AddToScheme(s)) - - objs := []client.Object{} - if tt.gatewayClass != nil { - tt.gatewayClass.Name = "gateway-class" - objs = append(objs, tt.gatewayClass) - } - if tt.gatewayClassConfig != nil { - tt.gatewayClassConfig.Name = "gateway-class-config" - objs = append(objs, tt.gatewayClassConfig) - } - - client := fake.NewClientBuilder().WithScheme(s).WithObjects(objs...).Build() - - ui := cli.NewMockUi() - cmd := Command{ - UI: ui, - k8sClient: client, - flagGatewayClassName: "gateway-class", - flagGatewayClassConfigName: "gateway-class-config", - } - - code := cmd.Run([]string{ - "-gateway-class-config-name", "gateway-class-config", - "-gateway-class-name", "gateway-class", - }) - - require.Equal(t, 0, code) - }) - } -} diff --git a/control-plane/subcommand/gateway-resources/command.go b/control-plane/subcommand/gateway-resources/command.go deleted file mode 100644 index 6deea27a26..0000000000 --- a/control-plane/subcommand/gateway-resources/command.go +++ /dev/null @@ -1,365 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package gatewayresources - -import ( - "context" - "errors" - "flag" - "fmt" - "sync" - "time" - - "github.com/cenkalti/backoff" - "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul-k8s/control-plane/subcommand" - "github.com/hashicorp/consul-k8s/control-plane/subcommand/flags" - "github.com/mitchellh/cli" - yaml "gopkg.in/yaml.v2" - corev1 "k8s.io/api/core/v1" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/controller-runtime/pkg/client" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -// this dupes the Kubernetes tolerations -// struct with yaml tags for validation. -type toleration struct { - Key string `yaml:"key"` - Operator string `yaml:"operator"` - Value string `yaml:"value"` - Effect string `yaml:"effect"` - TolerationSeconds *int64 `yaml:"tolerationSeconds"` -} - -func tolerationToKubernetes(t toleration) corev1.Toleration { - return corev1.Toleration{ - Key: t.Key, - Operator: corev1.TolerationOperator(t.Operator), - Value: t.Value, - Effect: corev1.TaintEffect(t.Effect), - TolerationSeconds: t.TolerationSeconds, - } -} - -type Command struct { - UI cli.Ui - - flags *flag.FlagSet - k8s *flags.K8SFlags - - flagHeritage string - flagChart string - flagApp string - flagRelease string - flagComponent string - flagControllerName string - flagGatewayClassName string - flagGatewayClassConfigName string - - flagServiceType string - flagDeploymentDefaultInstances int - flagDeploymentMaxInstances int - flagDeploymentMinInstances int - - flagNodeSelector string // this is a yaml multiline string map - flagTolerations string // this is a multiline yaml string matching the tolerations array - flagServiceAnnotations string // this is a multiline yaml string array of annotations to allow - - flagOpenshiftSCCName string - - flagMapPrivilegedContainerPorts int - - k8sClient client.Client - - once sync.Once - help string - - nodeSelector map[string]string - tolerations []corev1.Toleration - serviceAnnotations []string - - ctx context.Context -} - -func (c *Command) init() { - c.flags = flag.NewFlagSet("", flag.ContinueOnError) - - c.flags.StringVar(&c.flagGatewayClassName, "gateway-class-name", "", - "Name of Kubernetes GatewayClass to ensure is created.") - c.flags.StringVar(&c.flagGatewayClassConfigName, "gateway-class-config-name", "", - "Name of Kubernetes GatewayClassConfig to ensure is created.") - c.flags.StringVar(&c.flagHeritage, "heritage", "", - "Helm chart heritage for created objects.") - c.flags.StringVar(&c.flagChart, "chart", "", - "Helm chart name for created objects.") - c.flags.StringVar(&c.flagApp, "app", "", - "Helm chart app for created objects.") - c.flags.StringVar(&c.flagRelease, "release-name", "", - "Helm chart release for created objects.") - c.flags.StringVar(&c.flagComponent, "component", "", - "Helm chart component for created objects.") - c.flags.StringVar(&c.flagControllerName, "controller-name", "", - "The controller name value to use in the GatewayClass.") - c.flags.StringVar(&c.flagServiceType, "service-type", "", - "The service type to use for a gateway deployment.", - ) - c.flags.IntVar(&c.flagDeploymentDefaultInstances, "deployment-default-instances", 0, - "The number of instances to deploy for each gateway by default.", - ) - c.flags.IntVar(&c.flagDeploymentMaxInstances, "deployment-max-instances", 0, - "The maximum number of instances to deploy for each gateway.", - ) - c.flags.IntVar(&c.flagDeploymentMinInstances, "deployment-min-instances", 0, - "The minimum number of instances to deploy for each gateway.", - ) - c.flags.StringVar(&c.flagNodeSelector, "node-selector", "", - "The node selector to use in scheduling a gateway.", - ) - c.flags.StringVar(&c.flagTolerations, "tolerations", "", - "The tolerations to use in a deployed gateway.", - ) - c.flags.StringVar(&c.flagServiceAnnotations, "service-annotations", "", - "The annotations to copy over from a gateway to its service.", - ) - c.flags.StringVar(&c.flagOpenshiftSCCName, "openshift-scc-name", "", - "Name of security context constraint to use for gateways on Openshift.", - ) - c.flags.IntVar(&c.flagMapPrivilegedContainerPorts, "map-privileged-container-ports", 0, - "The value to add to privileged container ports (< 1024) to avoid requiring addition privileges for the "+ - "gateway container.", - ) - - c.k8s = &flags.K8SFlags{} - flags.Merge(c.flags, c.k8s.Flags()) - c.help = flags.Usage(help, c.flags) -} - -func (c *Command) Run(args []string) int { - var err error - c.once.Do(c.init) - if err = c.flags.Parse(args); err != nil { - return 1 - } - // Validate flags - if err := c.validateFlags(); err != nil { - c.UI.Error(err.Error()) - return 1 - } - - if c.ctx == nil { - c.ctx = context.Background() - } - - // Create the Kubernetes client - if c.k8sClient == nil { - config, err := subcommand.K8SConfig(c.k8s.KubeConfig()) - if err != nil { - c.UI.Error(fmt.Sprintf("Error retrieving Kubernetes auth: %s", err)) - return 1 - } - - s := runtime.NewScheme() - if err := clientgoscheme.AddToScheme(s); err != nil { - c.UI.Error(fmt.Sprintf("Could not add client-go schema: %s", err)) - return 1 - } - if err := gwv1beta1.Install(s); err != nil { - c.UI.Error(fmt.Sprintf("Could not add api-gateway schema: %s", err)) - return 1 - } - if err := v1alpha1.AddToScheme(s); err != nil { - c.UI.Error(fmt.Sprintf("Could not add consul-k8s schema: %s", err)) - return 1 - } - - c.k8sClient, err = client.New(config, client.Options{Scheme: s}) - if err != nil { - c.UI.Error(fmt.Sprintf("Error initializing Kubernetes client: %s", err)) - return 1 - } - } - - // do the creation - - labels := map[string]string{ - "app": c.flagApp, - "chart": c.flagChart, - "heritage": c.flagHeritage, - "release": c.flagRelease, - "component": c.flagComponent, - } - classConfig := &v1alpha1.GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{Name: c.flagGatewayClassConfigName, Labels: labels}, - Spec: v1alpha1.GatewayClassConfigSpec{ - ServiceType: serviceTypeIfSet(c.flagServiceType), - NodeSelector: c.nodeSelector, - CopyAnnotations: v1alpha1.CopyAnnotationsSpec{ - Service: c.serviceAnnotations, - }, - Tolerations: c.tolerations, - DeploymentSpec: v1alpha1.DeploymentSpec{ - DefaultInstances: nonZeroOrNil(c.flagDeploymentDefaultInstances), - MaxInstances: nonZeroOrNil(c.flagDeploymentMaxInstances), - MinInstances: nonZeroOrNil(c.flagDeploymentMinInstances), - }, - OpenshiftSCCName: c.flagOpenshiftSCCName, - MapPrivilegedContainerPorts: int32(c.flagMapPrivilegedContainerPorts), - }, - } - - class := &gwv1beta1.GatewayClass{ - ObjectMeta: metav1.ObjectMeta{Name: c.flagGatewayClassName, Labels: labels}, - Spec: gwv1beta1.GatewayClassSpec{ - ControllerName: gwv1beta1.GatewayController(c.flagControllerName), - ParametersRef: &gwv1beta1.ParametersReference{ - Group: gwv1beta1.Group(v1alpha1.ConsulHashicorpGroup), - Kind: gwv1beta1.Kind(v1alpha1.GatewayClassConfigKind), - Name: c.flagGatewayClassConfigName, - }, - }, - } - - if err := forceClassConfig(context.Background(), c.k8sClient, classConfig); err != nil { - c.UI.Error(err.Error()) - return 1 - } - if err := forceClass(context.Background(), c.k8sClient, class); err != nil { - c.UI.Error(err.Error()) - return 1 - } - - return 0 -} - -func (c *Command) validateFlags() error { - if c.flagGatewayClassConfigName == "" { - return errors.New("-gateway-class-config-name must be set") - } - if c.flagGatewayClassName == "" { - return errors.New("-gateway-class-name must be set") - } - if c.flagHeritage == "" { - return errors.New("-heritage must be set") - } - if c.flagChart == "" { - return errors.New("-chart must be set") - } - if c.flagApp == "" { - return errors.New("-app must be set") - } - if c.flagRelease == "" { - return errors.New("-release-name must be set") - } - if c.flagComponent == "" { - return errors.New("-component must be set") - } - if c.flagControllerName == "" { - return errors.New("-controller-name must be set") - } - if c.flagTolerations != "" { - var tolerations []toleration - if err := yaml.Unmarshal([]byte(c.flagTolerations), &tolerations); err != nil { - return fmt.Errorf("error decoding tolerations: %w", err) - } - c.tolerations = common.ConvertSliceFunc(tolerations, tolerationToKubernetes) - } - if c.flagNodeSelector != "" { - if err := yaml.Unmarshal([]byte(c.flagNodeSelector), &c.nodeSelector); err != nil { - return fmt.Errorf("error decoding node selector: %w", err) - } - } - if c.flagNodeSelector != "" { - if err := yaml.Unmarshal([]byte(c.flagNodeSelector), &c.nodeSelector); err != nil { - return fmt.Errorf("error decoding node selector: %w", err) - } - } - if c.flagServiceAnnotations != "" { - if err := yaml.Unmarshal([]byte(c.flagServiceAnnotations), &c.serviceAnnotations); err != nil { - return fmt.Errorf("error decoding service annotations: %w", err) - } - } - - return nil -} - -func (c *Command) Synopsis() string { return synopsis } -func (c *Command) Help() string { - c.once.Do(c.init) - return c.help -} - -const synopsis = "Create managed gateway resources after installation/upgrade." -const help = ` -Usage: consul-k8s-control-plane gateway-resources [options] - - Installs managed gateway class and configuration resources - after a helm installation or upgrade in order to avoid the - dependencies of CRDs being in-place prior to resource creation. - -` - -func forceClassConfig(ctx context.Context, k8sClient client.Client, o *v1alpha1.GatewayClassConfig) error { - return backoff.Retry(func() error { - var existing v1alpha1.GatewayClassConfig - err := k8sClient.Get(ctx, client.ObjectKeyFromObject(o), &existing) - if err != nil && !k8serrors.IsNotFound(err) { - return err - } - - if k8serrors.IsNotFound(err) { - return k8sClient.Create(ctx, o) - } - - existing.Spec = o.Spec - existing.Labels = o.Labels - - return k8sClient.Update(ctx, &existing) - }, exponentialBackoffWithMaxIntervalAndTime()) -} - -func forceClass(ctx context.Context, k8sClient client.Client, o *gwv1beta1.GatewayClass) error { - return backoff.Retry(func() error { - var existing gwv1beta1.GatewayClass - err := k8sClient.Get(ctx, client.ObjectKeyFromObject(o), &existing) - if err != nil && !k8serrors.IsNotFound(err) { - return err - } - - if k8serrors.IsNotFound(err) { - return k8sClient.Create(ctx, o) - } - - existing.Spec = o.Spec - existing.Labels = o.Labels - - return k8sClient.Update(ctx, &existing) - }, exponentialBackoffWithMaxIntervalAndTime()) -} - -func exponentialBackoffWithMaxIntervalAndTime() *backoff.ExponentialBackOff { - backoff := backoff.NewExponentialBackOff() - backoff.MaxElapsedTime = 10 * time.Second - backoff.MaxInterval = 1 * time.Second - backoff.Reset() - return backoff -} - -func nonZeroOrNil(v int) *int32 { - if v == 0 { - return nil - } - return common.PointerTo(int32(v)) -} - -func serviceTypeIfSet(v string) *corev1.ServiceType { - if v == "" { - return nil - } - return common.PointerTo(corev1.ServiceType(v)) -} diff --git a/control-plane/subcommand/gateway-resources/command_test.go b/control-plane/subcommand/gateway-resources/command_test.go deleted file mode 100644 index f60e376042..0000000000 --- a/control-plane/subcommand/gateway-resources/command_test.go +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package gatewayresources - -import ( - "testing" - - "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/mitchellh/cli" - "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" -) - -func TestRun_flagValidation(t *testing.T) { - t.Parallel() - - for name, tt := range map[string]struct { - cmd *Command - expectedErr string - }{ - "required gateway class config name": { - cmd: &Command{}, - expectedErr: "-gateway-class-config-name must be set", - }, - "required gateway class name": { - cmd: &Command{ - flagGatewayClassConfigName: "test", - }, - expectedErr: "-gateway-class-name must be set", - }, - "required heritage": { - cmd: &Command{ - flagGatewayClassConfigName: "test", - flagGatewayClassName: "test", - }, - expectedErr: "-heritage must be set", - }, - "required chart": { - cmd: &Command{ - flagGatewayClassConfigName: "test", - flagGatewayClassName: "test", - flagHeritage: "test", - }, - expectedErr: "-chart must be set", - }, - "required app": { - cmd: &Command{ - flagGatewayClassConfigName: "test", - flagGatewayClassName: "test", - flagHeritage: "test", - flagChart: "test", - }, - expectedErr: "-app must be set", - }, - "required release": { - cmd: &Command{ - flagGatewayClassConfigName: "test", - flagGatewayClassName: "test", - flagHeritage: "test", - flagChart: "test", - flagApp: "test", - }, - expectedErr: "-release-name must be set", - }, - "required component": { - cmd: &Command{ - flagGatewayClassConfigName: "test", - flagGatewayClassName: "test", - flagHeritage: "test", - flagChart: "test", - flagApp: "test", - flagRelease: "test", - }, - expectedErr: "-component must be set", - }, - "required controller name": { - cmd: &Command{ - flagGatewayClassConfigName: "test", - flagGatewayClassName: "test", - flagHeritage: "test", - flagChart: "test", - flagApp: "test", - flagRelease: "test", - flagComponent: "test", - }, - expectedErr: "-controller-name must be set", - }, - "required valid tolerations": { - cmd: &Command{ - flagGatewayClassConfigName: "test", - flagGatewayClassName: "test", - flagHeritage: "test", - flagChart: "test", - flagApp: "test", - flagRelease: "test", - flagComponent: "test", - flagControllerName: "test", - flagTolerations: "foo", - }, - expectedErr: "error decoding tolerations: yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `foo` into []gatewayresources.toleration", - }, - "required valid nodeSelector": { - cmd: &Command{ - flagGatewayClassConfigName: "test", - flagGatewayClassName: "test", - flagHeritage: "test", - flagChart: "test", - flagApp: "test", - flagRelease: "test", - flagComponent: "test", - flagControllerName: "test", - flagNodeSelector: "foo", - }, - expectedErr: "error decoding node selector: yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `foo` into map[string]string", - }, - "required valid service annotations": { - cmd: &Command{ - flagGatewayClassConfigName: "test", - flagGatewayClassName: "test", - flagHeritage: "test", - flagChart: "test", - flagApp: "test", - flagRelease: "test", - flagComponent: "test", - flagControllerName: "test", - flagServiceAnnotations: "foo", - }, - expectedErr: "error decoding service annotations: yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `foo` into []string", - }, - "valid without optional flags": { - cmd: &Command{ - flagGatewayClassConfigName: "test", - flagGatewayClassName: "test", - flagHeritage: "test", - flagChart: "test", - flagApp: "test", - flagRelease: "test", - flagComponent: "test", - flagControllerName: "test", - }, - }, - "valid with optional flags": { - cmd: &Command{ - flagGatewayClassConfigName: "test", - flagGatewayClassName: "test", - flagHeritage: "test", - flagChart: "test", - flagApp: "test", - flagRelease: "test", - flagComponent: "test", - flagControllerName: "test", - flagNodeSelector: ` -foo: 1 -bar: 2`, - flagTolerations: ` -- value: foo -- value: bar`, - flagServiceAnnotations: ` -- foo -- bar`, - flagOpenshiftSCCName: "restricted-v2", - }, - }, - } { - t.Run(name, func(t *testing.T) { - tt := tt - - t.Parallel() - - err := tt.cmd.validateFlags() - if tt.expectedErr == "" && err != nil { - t.Errorf("unexpected error occured: %v", err) - } - if tt.expectedErr != "" && err == nil { - t.Error("expected error but got none") - } - if tt.expectedErr != "" { - require.EqualError(t, err, tt.expectedErr) - } - }) - } -} - -func TestRun(t *testing.T) { - t.Parallel() - - for name, tt := range map[string]struct { - existingGatewayClass bool - existingGatewayClassConfig bool - }{ - "both exist": { - existingGatewayClass: true, - existingGatewayClassConfig: true, - }, - "gateway class config doesn't exist": { - existingGatewayClass: true, - }, - "gateway class doesn't exist": { - existingGatewayClassConfig: true, - }, - "neither exist": {}, - } { - t.Run(name, func(t *testing.T) { - tt := tt - - t.Parallel() - - existingGatewayClassConfig := &v1alpha1.GatewayClassConfig{ - ObjectMeta: metav1.ObjectMeta{Name: "test"}, - } - existingGatewayClass := &gwv1beta1.GatewayClass{ - ObjectMeta: metav1.ObjectMeta{Name: "test"}, - } - - s := runtime.NewScheme() - require.NoError(t, gwv1beta1.Install(s)) - require.NoError(t, v1alpha1.AddToScheme(s)) - - objs := []client.Object{} - if tt.existingGatewayClass { - objs = append(objs, existingGatewayClass) - } - if tt.existingGatewayClassConfig { - objs = append(objs, existingGatewayClassConfig) - } - - client := fake.NewClientBuilder().WithScheme(s).WithObjects(objs...).Build() - - ui := cli.NewMockUi() - cmd := Command{ - UI: ui, - k8sClient: client, - } - - code := cmd.Run([]string{ - "-gateway-class-config-name", "test", - "-gateway-class-name", "test", - "-heritage", "test", - "-chart", "test", - "-app", "test", - "-release-name", "test", - "-component", "test", - "-controller-name", "test", - "-openshift-scc-name", "restricted-v2", - }) - - require.Equal(t, 0, code) - }) - } -} diff --git a/control-plane/subcommand/get-consul-client-ca/command.go b/control-plane/subcommand/get-consul-client-ca/command.go index 619f08625d..a154d778e9 100644 --- a/control-plane/subcommand/get-consul-client-ca/command.go +++ b/control-plane/subcommand/get-consul-client-ca/command.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package getconsulclientca import ( diff --git a/control-plane/subcommand/get-consul-client-ca/command_test.go b/control-plane/subcommand/get-consul-client-ca/command_test.go index 68bdd918b1..1446018922 100644 --- a/control-plane/subcommand/get-consul-client-ca/command_test.go +++ b/control-plane/subcommand/get-consul-client-ca/command_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package getconsulclientca import ( @@ -51,7 +48,7 @@ func TestRun_FlagsValidation(t *testing.T) { flags: []string{ "-output-file=output.pem", "-server-addr=foo.com", - "-consul-api-timeout=10s", + "-consul-api-timeout=5s", "-log-level=invalid-log-level", }, expErr: "unknown log level: invalid-log-level", @@ -106,7 +103,7 @@ func TestRun(t *testing.T) { "-server-port", strings.Split(a.HTTPSAddr, ":")[1], "-ca-file", caFile, "-output-file", outputFile.Name(), - "-consul-api-timeout", "10s", + "-consul-api-timeout", "5s", }) require.Equal(t, 0, exitCode, ui.ErrorWriter.String()) @@ -138,7 +135,8 @@ func TestRun(t *testing.T) { // Test that if the Consul server is not available at first, // we continue to poll it until it comes up. func TestRun_ConsulServerAvailableLater(t *testing.T) { - t.Parallel() + // Skipping this test because it is flaky on release/1.0.x. It is much better in newer versions of Consul. + t.Skip() outputFile, err := os.CreateTemp("", "ca") require.NoError(t, err) defer os.RemoveAll(outputFile.Name()) @@ -281,7 +279,7 @@ func TestRun_GetsOnlyActiveRoot(t *testing.T) { "-server-port", strings.Split(a.HTTPSAddr, ":")[1], "-ca-file", caFile, "-output-file", outputFile.Name(), - "-consul-api-timeout", "10s", + "-consul-api-timeout", "5s", }) require.Equal(t, 0, exitCode) @@ -349,7 +347,7 @@ func TestRun_WithProvider(t *testing.T) { "-server-port", strings.Split(a.HTTPSAddr, ":")[1], "-output-file", outputFile.Name(), "-ca-file", caFile, - "-consul-api-timeout", "10s", + "-consul-api-timeout", "5s", }) require.Equal(t, 0, exitCode, ui.ErrorWriter.String()) diff --git a/control-plane/subcommand/gossip-encryption-autogenerate/command.go b/control-plane/subcommand/gossip-encryption-autogenerate/command.go index cf871eca69..3668a20c35 100644 --- a/control-plane/subcommand/gossip-encryption-autogenerate/command.go +++ b/control-plane/subcommand/gossip-encryption-autogenerate/command.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package gossipencryptionautogenerate import ( diff --git a/control-plane/subcommand/gossip-encryption-autogenerate/command_test.go b/control-plane/subcommand/gossip-encryption-autogenerate/command_test.go index 55b32bdb06..91d7101232 100644 --- a/control-plane/subcommand/gossip-encryption-autogenerate/command_test.go +++ b/control-plane/subcommand/gossip-encryption-autogenerate/command_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package gossipencryptionautogenerate import ( diff --git a/control-plane/subcommand/inject-connect/command.go b/control-plane/subcommand/inject-connect/command.go index 6767e60130..f3559e2679 100644 --- a/control-plane/subcommand/inject-connect/command.go +++ b/control-plane/subcommand/inject-connect/command.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package connectinject import ( @@ -15,8 +12,6 @@ import ( "sync" "syscall" - gatewaycommon "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" - gatewaycontrollers "github.com/hashicorp/consul-k8s/control-plane/api-gateway/controllers" apicommon "github.com/hashicorp/consul-k8s/control-plane/api/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" @@ -25,7 +20,7 @@ import ( "github.com/hashicorp/consul-k8s/control-plane/connect-inject/lifecycle" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/metrics" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/webhook" - "github.com/hashicorp/consul-k8s/control-plane/controllers" + "github.com/hashicorp/consul-k8s/control-plane/controller" mutatingwebhookconfiguration "github.com/hashicorp/consul-k8s/control-plane/helper/mutating-webhook-configuration" "github.com/hashicorp/consul-k8s/control-plane/subcommand/common" "github.com/hashicorp/consul-k8s/control-plane/subcommand/flags" @@ -42,13 +37,10 @@ import ( "k8s.io/klog/v2" ctrl "sigs.k8s.io/controller-runtime" ctrlRuntimeWebhook "sigs.k8s.io/controller-runtime/pkg/webhook" - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + ctrlwebhook "sigs.k8s.io/controller-runtime/pkg/webhook" ) -const ( - WebhookCAFilename = "ca.crt" -) +const WebhookCAFilename = "ca.crt" type Command struct { UI cli.Ui @@ -127,9 +119,6 @@ type Command struct { flagEnableAutoEncrypt bool - // Consul telemetry collector - flagEnableTelemetryCollector bool - // Consul DNS flags. flagEnableConsulDNS bool flagResourcePrefix string @@ -154,8 +143,6 @@ func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) // We need v1alpha1 here to add the peering api to the scheme utilruntime.Must(v1alpha1.AddToScheme(scheme)) - utilruntime.Must(gwv1beta1.AddToScheme(scheme)) - utilruntime.Must(gwv1alpha2.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme } @@ -215,8 +202,6 @@ func (c *Command) init() { "Enables updating the CABundle on the webhook within this controller rather than using the web cert manager.") c.flagSet.BoolVar(&c.flagEnableAutoEncrypt, "enable-auto-encrypt", false, "Indicates whether TLS with auto-encrypt should be used when talking to Consul clients.") - c.flagSet.BoolVar(&c.flagEnableTelemetryCollector, "enable-telemetry-collector", false, - "Indicates whether proxies should be registered with configuration to enable forwarding metrics to consul-telemetry-collector") c.flagSet.StringVar(&c.flagLogLevel, "log-level", zapcore.InfoLevel.String(), fmt.Sprintf("Log verbosity level. Supported values (in order of detail) are "+ "%q, %q, %q, and %q.", zapcore.DebugLevel.String(), zapcore.InfoLevel.String(), zapcore.WarnLevel.String(), zapcore.ErrorLevel.String())) @@ -478,83 +463,22 @@ func (c *Command) Run(args []string) int { ReleaseName: c.flagReleaseName, ReleaseNamespace: c.flagReleaseNamespace, EnableAutoEncrypt: c.flagEnableAutoEncrypt, - EnableTelemetryCollector: c.flagEnableTelemetryCollector, Context: ctx, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", endpoints.Controller{}) return 1 } - // API Gateway Controllers - if err := gatewaycontrollers.RegisterFieldIndexes(ctx, mgr); err != nil { - setupLog.Error(err, "unable to register field indexes") - return 1 - } - - if err = (&gatewaycontrollers.GatewayClassConfigController{ - Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controller").WithName("gateways"), - }).SetupWithManager(ctx, mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", gatewaycontrollers.GatewayClassConfigController{}) - return 1 - } - - if err := (&gatewaycontrollers.GatewayClassController{ - ControllerName: gatewaycommon.GatewayClassControllerName, - Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("GatewayClass"), - }).SetupWithManager(ctx, mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "GatewayClass") - return 1 - } - - cache, err := gatewaycontrollers.SetupGatewayControllerWithManager(ctx, mgr, gatewaycontrollers.GatewayControllerConfig{ - HelmConfig: gatewaycommon.HelmConfig{ - ConsulConfig: gatewaycommon.ConsulConfig{ - Address: c.consul.Addresses, - GRPCPort: consulConfig.GRPCPort, - HTTPPort: consulConfig.HTTPPort, - APITimeout: consulConfig.APITimeout, - }, - ImageDataplane: c.flagConsulDataplaneImage, - ImageConsulK8S: c.flagConsulK8sImage, - ConsulDestinationNamespace: c.flagConsulDestinationNamespace, - NamespaceMirroringPrefix: c.flagK8SNSMirroringPrefix, - EnableNamespaces: c.flagEnableNamespaces, - PeeringEnabled: c.flagEnablePeering, - EnableOpenShift: c.flagEnableOpenShift, - EnableNamespaceMirroring: c.flagEnableK8SNSMirroring, - AuthMethod: c.consul.ConsulLogin.AuthMethod, - LogLevel: c.flagLogLevel, - LogJSON: c.flagLogJSON, - TLSEnabled: c.consul.UseTLS, - ConsulTLSServerName: c.consul.TLSServerName, - ConsulPartition: c.consul.Partition, - ConsulCACert: string(caCertPem), - }, - AllowK8sNamespacesSet: allowK8sNamespaces, - DenyK8sNamespacesSet: denyK8sNamespaces, - ConsulClientConfig: consulConfig, - ConsulServerConnMgr: watcher, - NamespacesEnabled: c.flagEnableNamespaces, - CrossNamespaceACLPolicy: c.flagCrossNamespaceACLPolicy, - Partition: c.consul.Partition, - Datacenter: c.consul.Datacenter, - }) - - if err != nil { - setupLog.Error(err, "unable to create controller", "controller", "Gateway") - return 1 + consulMeta := apicommon.ConsulMeta{ + PartitionsEnabled: c.flagEnablePartitions, + Partition: c.consul.Partition, + NamespacesEnabled: c.flagEnableNamespaces, + DestinationNamespace: c.flagConsulDestinationNamespace, + Mirroring: c.flagEnableK8SNSMirroring, + Prefix: c.flagK8SNSMirroringPrefix, } - go cache.Run(ctx) - - // wait for the cache to fill - setupLog.Info("waiting for Consul cache sync") - cache.WaitSynced(ctx) - setupLog.Info("Consul cache synced") - - configEntryReconciler := &controllers.ConfigEntryController{ + configEntryReconciler := &controller.ConfigEntryController{ ConsulClientConfig: c.consul.ConsulClientConfig(), ConsulServerConnMgr: watcher, DatacenterName: c.consul.Datacenter, @@ -564,7 +488,7 @@ func (c *Command) Run(args []string) int { NSMirroringPrefix: c.flagK8SNSMirroringPrefix, CrossNSACLPolicy: c.flagCrossNamespaceACLPolicy, } - if err = (&controllers.ServiceDefaultsController{ + if err = (&controller.ServiceDefaultsController{ ConfigEntryController: configEntryReconciler, Client: mgr.GetClient(), Log: ctrl.Log.WithName("controller").WithName(apicommon.ServiceDefaults), @@ -573,7 +497,7 @@ func (c *Command) Run(args []string) int { setupLog.Error(err, "unable to create controller", "controller", apicommon.ServiceDefaults) return 1 } - if err = (&controllers.ServiceResolverController{ + if err = (&controller.ServiceResolverController{ ConfigEntryController: configEntryReconciler, Client: mgr.GetClient(), Log: ctrl.Log.WithName("controller").WithName(apicommon.ServiceResolver), @@ -582,7 +506,7 @@ func (c *Command) Run(args []string) int { setupLog.Error(err, "unable to create controller", "controller", apicommon.ServiceResolver) return 1 } - if err = (&controllers.ProxyDefaultsController{ + if err = (&controller.ProxyDefaultsController{ ConfigEntryController: configEntryReconciler, Client: mgr.GetClient(), Log: ctrl.Log.WithName("controller").WithName(apicommon.ProxyDefaults), @@ -591,7 +515,7 @@ func (c *Command) Run(args []string) int { setupLog.Error(err, "unable to create controller", "controller", apicommon.ProxyDefaults) return 1 } - if err = (&controllers.MeshController{ + if err = (&controller.MeshController{ ConfigEntryController: configEntryReconciler, Client: mgr.GetClient(), Log: ctrl.Log.WithName("controller").WithName(apicommon.Mesh), @@ -600,7 +524,7 @@ func (c *Command) Run(args []string) int { setupLog.Error(err, "unable to create controller", "controller", apicommon.Mesh) return 1 } - if err = (&controllers.ExportedServicesController{ + if err = (&controller.ExportedServicesController{ ConfigEntryController: configEntryReconciler, Client: mgr.GetClient(), Log: ctrl.Log.WithName("controller").WithName(apicommon.ExportedServices), @@ -609,7 +533,7 @@ func (c *Command) Run(args []string) int { setupLog.Error(err, "unable to create controller", "controller", apicommon.ExportedServices) return 1 } - if err = (&controllers.ServiceRouterController{ + if err = (&controller.ServiceRouterController{ ConfigEntryController: configEntryReconciler, Client: mgr.GetClient(), Log: ctrl.Log.WithName("controller").WithName(apicommon.ServiceRouter), @@ -618,7 +542,7 @@ func (c *Command) Run(args []string) int { setupLog.Error(err, "unable to create controller", "controller", apicommon.ServiceRouter) return 1 } - if err = (&controllers.ServiceSplitterController{ + if err = (&controller.ServiceSplitterController{ ConfigEntryController: configEntryReconciler, Client: mgr.GetClient(), Log: ctrl.Log.WithName("controller").WithName(apicommon.ServiceSplitter), @@ -627,7 +551,7 @@ func (c *Command) Run(args []string) int { setupLog.Error(err, "unable to create controller", "controller", apicommon.ServiceSplitter) return 1 } - if err = (&controllers.ServiceIntentionsController{ + if err = (&controller.ServiceIntentionsController{ ConfigEntryController: configEntryReconciler, Client: mgr.GetClient(), Log: ctrl.Log.WithName("controller").WithName(apicommon.ServiceIntentions), @@ -636,7 +560,7 @@ func (c *Command) Run(args []string) int { setupLog.Error(err, "unable to create controller", "controller", apicommon.ServiceIntentions) return 1 } - if err = (&controllers.IngressGatewayController{ + if err = (&controller.IngressGatewayController{ ConfigEntryController: configEntryReconciler, Client: mgr.GetClient(), Log: ctrl.Log.WithName("controller").WithName(apicommon.IngressGateway), @@ -645,7 +569,7 @@ func (c *Command) Run(args []string) int { setupLog.Error(err, "unable to create controller", "controller", apicommon.IngressGateway) return 1 } - if err = (&controllers.TerminatingGatewayController{ + if err = (&controller.TerminatingGatewayController{ ConfigEntryController: configEntryReconciler, Client: mgr.GetClient(), Log: ctrl.Log.WithName("controller").WithName(apicommon.TerminatingGateway), @@ -654,33 +578,6 @@ func (c *Command) Run(args []string) int { setupLog.Error(err, "unable to create controller", "controller", apicommon.TerminatingGateway) return 1 } - if err = (&controllers.SamenessGroupController{ - ConfigEntryController: configEntryReconciler, - Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controller").WithName(apicommon.SamenessGroup), - Scheme: mgr.GetScheme(), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", apicommon.SamenessGroup) - return 1 - } - if err = (&controllers.JWTProviderController{ - ConfigEntryController: configEntryReconciler, - Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controller").WithName(apicommon.JWTProvider), - Scheme: mgr.GetScheme(), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", apicommon.JWTProvider) - return 1 - } - if err = (&controllers.ControlPlaneRequestLimitController{ - ConfigEntryController: configEntryReconciler, - Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controller").WithName(apicommon.ControlPlaneRequestLimit), - Scheme: mgr.GetScheme(), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", apicommon.ControlPlaneRequestLimit) - return 1 - } if err = mgr.AddReadyzCheck("ready", webhook.ReadinessCheck{CertDir: c.flagCertDir}.Ready); err != nil { setupLog.Error(err, "unable to create readiness check", "controller", endpoints.Controller{}) @@ -770,95 +667,68 @@ func (c *Command) Run(args []string) int { LogJSON: c.flagLogJSON, }}) - consulMeta := apicommon.ConsulMeta{ - PartitionsEnabled: c.flagEnablePartitions, - Partition: c.consul.Partition, - NamespacesEnabled: c.flagEnableNamespaces, - DestinationNamespace: c.flagConsulDestinationNamespace, - Mirroring: c.flagEnableK8SNSMirroring, - Prefix: c.flagK8SNSMirroringPrefix, - } - // Note: The path here should be identical to the one on the kubebuilder // annotation in each webhook file. mgr.GetWebhookServer().Register("/mutate-v1alpha1-servicedefaults", - &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.ServiceDefaultsWebhook{ + &ctrlwebhook.Admission{Handler: &v1alpha1.ServiceDefaultsWebhook{ Client: mgr.GetClient(), Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.ServiceDefaults), ConsulMeta: consulMeta, }}) mgr.GetWebhookServer().Register("/mutate-v1alpha1-serviceresolver", - &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.ServiceResolverWebhook{ + &ctrlwebhook.Admission{Handler: &v1alpha1.ServiceResolverWebhook{ Client: mgr.GetClient(), Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.ServiceResolver), ConsulMeta: consulMeta, }}) mgr.GetWebhookServer().Register("/mutate-v1alpha1-proxydefaults", - &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.ProxyDefaultsWebhook{ + &ctrlwebhook.Admission{Handler: &v1alpha1.ProxyDefaultsWebhook{ Client: mgr.GetClient(), Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.ProxyDefaults), ConsulMeta: consulMeta, }}) mgr.GetWebhookServer().Register("/mutate-v1alpha1-mesh", - &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.MeshWebhook{ + &ctrlwebhook.Admission{Handler: &v1alpha1.MeshWebhook{ Client: mgr.GetClient(), Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.Mesh), ConsulMeta: consulMeta, }}) mgr.GetWebhookServer().Register("/mutate-v1alpha1-exportedservices", - &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.ExportedServicesWebhook{ + &ctrlwebhook.Admission{Handler: &v1alpha1.ExportedServicesWebhook{ Client: mgr.GetClient(), Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.ExportedServices), ConsulMeta: consulMeta, }}) mgr.GetWebhookServer().Register("/mutate-v1alpha1-servicerouter", - &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.ServiceRouterWebhook{ + &ctrlwebhook.Admission{Handler: &v1alpha1.ServiceRouterWebhook{ Client: mgr.GetClient(), Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.ServiceRouter), ConsulMeta: consulMeta, }}) mgr.GetWebhookServer().Register("/mutate-v1alpha1-servicesplitter", - &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.ServiceSplitterWebhook{ + &ctrlwebhook.Admission{Handler: &v1alpha1.ServiceSplitterWebhook{ Client: mgr.GetClient(), Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.ServiceSplitter), ConsulMeta: consulMeta, }}) mgr.GetWebhookServer().Register("/mutate-v1alpha1-serviceintentions", - &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.ServiceIntentionsWebhook{ + &ctrlwebhook.Admission{Handler: &v1alpha1.ServiceIntentionsWebhook{ Client: mgr.GetClient(), Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.ServiceIntentions), ConsulMeta: consulMeta, }}) mgr.GetWebhookServer().Register("/mutate-v1alpha1-ingressgateway", - &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.IngressGatewayWebhook{ + &ctrlwebhook.Admission{Handler: &v1alpha1.IngressGatewayWebhook{ Client: mgr.GetClient(), Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.IngressGateway), ConsulMeta: consulMeta, }}) mgr.GetWebhookServer().Register("/mutate-v1alpha1-terminatinggateway", - &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.TerminatingGatewayWebhook{ + &ctrlwebhook.Admission{Handler: &v1alpha1.TerminatingGatewayWebhook{ Client: mgr.GetClient(), Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.TerminatingGateway), ConsulMeta: consulMeta, }}) - mgr.GetWebhookServer().Register("/mutate-v1alpha1-samenessgroup", - &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.SamenessGroupWebhook{ - Client: mgr.GetClient(), - Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.SamenessGroup), - ConsulMeta: consulMeta, - }}) - mgr.GetWebhookServer().Register("/mutate-v1alpha1-jwtprovider", - &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.JWTProviderWebhook{ - Client: mgr.GetClient(), - Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.JWTProvider), - ConsulMeta: consulMeta, - }}) - mgr.GetWebhookServer().Register("/mutate-v1alpha1-controlplanerequestlimits", - &ctrlRuntimeWebhook.Admission{Handler: &v1alpha1.ControlPlaneRequestLimitWebhook{ - Client: mgr.GetClient(), - Logger: ctrl.Log.WithName("webhooks").WithName(apicommon.ControlPlaneRequestLimit), - ConsulMeta: consulMeta, - }}) if c.flagEnableWebhookCAUpdate { err = c.updateWebhookCABundle(ctx) diff --git a/control-plane/subcommand/inject-connect/command_test.go b/control-plane/subcommand/inject-connect/command_test.go index 9c64020376..5f067cf7c2 100644 --- a/control-plane/subcommand/inject-connect/command_test.go +++ b/control-plane/subcommand/inject-connect/command_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package connectinject import ( diff --git a/control-plane/subcommand/install-cni/binary.go b/control-plane/subcommand/install-cni/binary.go index 5bf25ab607..2429770109 100644 --- a/control-plane/subcommand/install-cni/binary.go +++ b/control-plane/subcommand/install-cni/binary.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package installcni import ( diff --git a/control-plane/subcommand/install-cni/binary_test.go b/control-plane/subcommand/install-cni/binary_test.go index 397751d42c..e65f61c63b 100644 --- a/control-plane/subcommand/install-cni/binary_test.go +++ b/control-plane/subcommand/install-cni/binary_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package installcni import ( diff --git a/control-plane/subcommand/install-cni/cniconfig.go b/control-plane/subcommand/install-cni/cniconfig.go index 448c9efa92..922d7283dd 100644 --- a/control-plane/subcommand/install-cni/cniconfig.go +++ b/control-plane/subcommand/install-cni/cniconfig.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package installcni import ( diff --git a/control-plane/subcommand/install-cni/cniconfig_test.go b/control-plane/subcommand/install-cni/cniconfig_test.go index 06fa848074..b6e2154adb 100644 --- a/control-plane/subcommand/install-cni/cniconfig_test.go +++ b/control-plane/subcommand/install-cni/cniconfig_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package installcni import ( diff --git a/control-plane/subcommand/install-cni/command.go b/control-plane/subcommand/install-cni/command.go index 53abe7cda1..7c481a0800 100644 --- a/control-plane/subcommand/install-cni/command.go +++ b/control-plane/subcommand/install-cni/command.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package installcni import ( diff --git a/control-plane/subcommand/install-cni/command_test.go b/control-plane/subcommand/install-cni/command_test.go index d5ee65f928..5cb9bea91e 100644 --- a/control-plane/subcommand/install-cni/command_test.go +++ b/control-plane/subcommand/install-cni/command_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package installcni import ( diff --git a/control-plane/subcommand/install-cni/kubeconfig.go b/control-plane/subcommand/install-cni/kubeconfig.go index 467130dea9..ca93759578 100644 --- a/control-plane/subcommand/install-cni/kubeconfig.go +++ b/control-plane/subcommand/install-cni/kubeconfig.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package installcni import ( diff --git a/control-plane/subcommand/install-cni/kubeconfig_test.go b/control-plane/subcommand/install-cni/kubeconfig_test.go index 8197115db3..899ad3f600 100644 --- a/control-plane/subcommand/install-cni/kubeconfig_test.go +++ b/control-plane/subcommand/install-cni/kubeconfig_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package installcni import ( diff --git a/control-plane/subcommand/partition-init/command.go b/control-plane/subcommand/partition-init/command.go index 72c4ceeff0..7ca70b50a7 100644 --- a/control-plane/subcommand/partition-init/command.go +++ b/control-plane/subcommand/partition-init/command.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package partition_init import ( diff --git a/control-plane/subcommand/partition-init/command_ent_test.go b/control-plane/subcommand/partition-init/command_ent_test.go index 182412c8aa..5bb1868b39 100644 --- a/control-plane/subcommand/partition-init/command_ent_test.go +++ b/control-plane/subcommand/partition-init/command_ent_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - //go:build enterprise package partition_init diff --git a/control-plane/subcommand/server-acl-init/anonymous_token.go b/control-plane/subcommand/server-acl-init/anonymous_token.go index 32c19ec208..3423ee78da 100644 --- a/control-plane/subcommand/server-acl-init/anonymous_token.go +++ b/control-plane/subcommand/server-acl-init/anonymous_token.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package serveraclinit import ( diff --git a/control-plane/subcommand/server-acl-init/command.go b/control-plane/subcommand/server-acl-init/command.go index 7ff0ae2268..698da2a25c 100644 --- a/control-plane/subcommand/server-acl-init/command.go +++ b/control-plane/subcommand/server-acl-init/command.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package serveraclinit import ( @@ -25,11 +22,12 @@ import ( "github.com/hashicorp/consul/api" "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-netaddrs" - vaultApi "github.com/hashicorp/vault/api" "github.com/mitchellh/cli" "github.com/mitchellh/mapstructure" "golang.org/x/text/cases" "golang.org/x/text/language" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" ) @@ -88,10 +86,8 @@ type Command struct { flagEnableInjectK8SNSMirroring bool // Enables mirroring of k8s namespaces into Consul for Connect inject flagInjectK8SNSMirroringPrefix string // Prefix added to Consul namespaces created when mirroring injected services - // Flags for the secrets backend. - flagSecretsBackend SecretsBackendType - flagBootstrapTokenSecretName string - flagBootstrapTokenSecretKey string + // Flag to support a custom bootstrap token. + flagBootstrapTokenFile string flagLogLevel string flagLogJSON bool @@ -102,9 +98,7 @@ type Command struct { // flagFederation indicates if federation has been enabled in the cluster. flagFederation bool - backend SecretsBackend // for unit testing. - clientset kubernetes.Interface - vaultClient *vaultApi.Client + clientset kubernetes.Interface watcher consul.ServerConnectionManager @@ -200,14 +194,9 @@ func (c *Command) init() { c.flags.BoolVar(&c.flagFederation, "federation", false, "Toggle for when federation has been enabled.") - c.flags.StringVar((*string)(&c.flagSecretsBackend), "secrets-backend", "kubernetes", - `The secrets backend to use. Either "vault" or "kubernetes". Defaults to "kubernetes"`) - c.flags.StringVar(&c.flagBootstrapTokenSecretName, "bootstrap-token-secret-name", "", - "The name of the Vault or Kuberenetes secret for the bootstrap token. This token must have `ac::write` permission "+ - "in order to create policies and tokens. If not provided or if the secret is empty, then this command will "+ - "bootstrap ACLs and write the bootstrap token to this secret.") - c.flags.StringVar(&c.flagBootstrapTokenSecretKey, "bootstrap-token-secret-key", "", - "The key within the Vault or Kuberenetes secret containing the bootstrap token.") + c.flags.StringVar(&c.flagBootstrapTokenFile, "bootstrap-token-file", "", + "Path to file containing ACL token for creating policies and tokens. This token must have 'acl:write' permissions."+ + "When provided, servers will not be bootstrapped and their policies and tokens will not be updated.") c.flags.DurationVar(&c.flagTimeout, "timeout", 10*time.Minute, "How long we'll try to bootstrap ACLs for before timing out, e.g. 1ms, 2s, 3m") @@ -242,7 +231,6 @@ func (c *Command) Help() string { // The function will retry its tasks indefinitely until they are complete. func (c *Command) Run(args []string) int { c.once.Do(c.init) - defer c.quitVaultAgent() if err := c.flags.Parse(args); err != nil { return 1 } @@ -276,6 +264,16 @@ func (c *Command) Run(args []string) int { } } + var providedBootstrapToken string + if c.flagBootstrapTokenFile != "" { + var err error + providedBootstrapToken, err = loadTokenFromFile(c.flagBootstrapTokenFile) + if err != nil { + c.UI.Error(err.Error()) + return 1 + } + } + var cancel context.CancelFunc c.ctx, cancel = context.WithTimeout(context.Background(), c.flagTimeout) // The context will only ever be intentionally ended by the timeout. @@ -308,12 +306,8 @@ func (c *Command) Run(args []string) int { c.UI.Error(err.Error()) } - if err := c.configureSecretsBackend(); err != nil { - c.log.Error(err.Error()) - return 1 - } - var bootstrapToken string + if c.flagACLReplicationTokenFile != "" && !c.flagCreateACLReplicationToken { // If ACL replication is enabled, we don't need to ACL bootstrap the servers // since they will be performing replication. @@ -322,7 +316,21 @@ func (c *Command) Run(args []string) int { c.log.Info("ACL replication is enabled so skipping Consul server ACL bootstrapping") bootstrapToken = aclReplicationToken } else { - bootstrapToken, err = c.bootstrapServers(ipAddrs, c.backend) + // Check if we've already been bootstrapped. + var bootTokenSecretName string + if providedBootstrapToken != "" { + c.log.Info("Using provided bootstrap token") + bootstrapToken = providedBootstrapToken + } else { + bootTokenSecretName = c.withPrefix("bootstrap-acl-token") + bootstrapToken, err = c.getBootstrapToken(bootTokenSecretName) + if err != nil { + c.log.Error(fmt.Sprintf("Unexpected error looking for preexisting bootstrap Secret: %s", err)) + return 1 + } + } + + bootstrapToken, err = c.bootstrapServers(ipAddrs, bootstrapToken, bootTokenSecretName) if err != nil { c.log.Error(err.Error()) return 1 @@ -798,6 +806,24 @@ func (c *Command) configureGateway(gatewayParams ConfigureGatewayParams, consulC return nil } +// getBootstrapToken returns the existing bootstrap token if there is one by +// reading the Kubernetes Secret with name secretName. +// If there is no bootstrap token yet, then it returns an empty string (not an error). +func (c *Command) getBootstrapToken(secretName string) (string, error) { + secret, err := c.clientset.CoreV1().Secrets(c.flagK8sNamespace).Get(c.ctx, secretName, metav1.GetOptions{}) + if err != nil { + if k8serrors.IsNotFound(err) { + return "", nil + } + return "", err + } + token, ok := secret.Data[common.ACLTokenSecretKey] + if !ok { + return "", fmt.Errorf("secret %q does not have data key 'token'", secretName) + } + return string(token), nil +} + func (c *Command) configureKubeClient() error { config, err := subcommand.K8SConfig(c.k8s.KubeConfig()) if err != nil { @@ -810,55 +836,6 @@ func (c *Command) configureKubeClient() error { return nil } -// configureSecretsBackend configures either the Kubernetes or Vault -// secrets backend based on flags. -func (c *Command) configureSecretsBackend() error { - if c.backend != nil { - // support a fake backend in unit tests - return nil - } - secretName := c.flagBootstrapTokenSecretName - if secretName == "" { - secretName = c.withPrefix("bootstrap-acl-token") - } - - secretKey := c.flagBootstrapTokenSecretKey - if secretKey == "" { - secretKey = common.ACLTokenSecretKey - } - - switch c.flagSecretsBackend { - case SecretsBackendTypeKubernetes: - c.backend = &KubernetesSecretsBackend{ - ctx: c.ctx, - clientset: c.clientset, - k8sNamespace: c.flagK8sNamespace, - secretName: secretName, - secretKey: secretKey, - } - return nil - case SecretsBackendTypeVault: - cfg := vaultApi.DefaultConfig() - cfg.Address = "" - cfg.AgentAddress = "http://127.0.0.1:8200" - vaultClient, err := vaultApi.NewClient(cfg) - if err != nil { - return fmt.Errorf("Error initializing Vault client: %w", err) - } - - c.vaultClient = vaultClient // must set this for c.quitVaultAgent. - c.backend = &VaultSecretsBackend{ - vaultClient: c.vaultClient, - secretName: secretName, - secretKey: secretKey, - } - return nil - default: - validValues := []SecretsBackendType{SecretsBackendTypeKubernetes, SecretsBackendTypeVault} - return fmt.Errorf("Invalid value for -secrets-backend: %q. Valid values are %v.", c.flagSecretsBackend, validValues) - } -} - // untilSucceeds runs op until it returns a nil error. // If c.cmdTimeout is cancelled it will exit. func (c *Command) untilSucceeds(opName string, op func() error) error { @@ -985,10 +962,6 @@ func (c *Command) validateFlags() error { return errors.New("-consul-api-timeout must be set to a value greater than 0") } - //if c.flagVaultNamespace != "" && c.flagSecretsBackend != SecretsBackendTypeVault { - // return fmt.Errorf("-vault-namespace not supported for -secrets-backend=%q", c.flagSecretsBackend) - //} - return nil } @@ -1004,28 +977,6 @@ func loadTokenFromFile(tokenFile string) (string, error) { return strings.TrimSpace(string(tokenBytes)), nil } -func (c *Command) quitVaultAgent() { - if c.vaultClient == nil { - return - } - - // Tell the Vault agent sidecar to quit. Without this, the Job does not - // complete because the Vault agent does not stop. This retries because it - // does not know exactly when the Vault agent sidecar will start. - err := c.untilSucceeds("tell Vault agent to quit", func() error { - // TODO: RawRequest is deprecated, but there is also not a high level - // method for this in the Vault client. - // nolint:staticcheck // SA1004 ignore - _, err := c.vaultClient.RawRequest( - c.vaultClient.NewRequest("POST", "/agent/v1/quit"), - ) - return err - }) - if err != nil { - c.log.Error("Error telling Vault agent to quit", "error", err) - } -} - const ( consulDefaultNamespace = "default" consulDefaultPartition = "default" diff --git a/control-plane/subcommand/server-acl-init/command_ent_test.go b/control-plane/subcommand/server-acl-init/command_ent_test.go index a67a2d0e41..e31b787e4b 100644 --- a/control-plane/subcommand/server-acl-init/command_ent_test.go +++ b/control-plane/subcommand/server-acl-init/command_ent_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - //go:build enterprise package serveraclinit @@ -225,6 +222,7 @@ func TestRun_ConnectInject_NamespaceMirroring(t *testing.T) { // a non-default partition. func TestRun_AnonymousToken_CreatedFromNonDefaultPartition(t *testing.T) { bootToken := "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" + tokenFile := common.WriteTempFile(t, bootToken) server := partitionedSetup(t, bootToken, "test") k8s := fake.NewSimpleClientset() setUpK8sServiceAccount(t, k8s, ns) @@ -233,7 +231,6 @@ func TestRun_AnonymousToken_CreatedFromNonDefaultPartition(t *testing.T) { cmd := Command{ UI: ui, clientset: k8s, - backend: &FakeSecretsBackend{bootstrapToken: bootToken}, } cmd.init() args := []string{ @@ -242,6 +239,7 @@ func TestRun_AnonymousToken_CreatedFromNonDefaultPartition(t *testing.T) { "-grpc-port=" + strings.Split(server.GRPCAddr, ":")[1], "-resource-prefix=" + resourcePrefix, "-k8s-namespace=" + ns, + "-bootstrap-token-file", tokenFile, "-allow-dns", "-partition=test", "-enable-namespaces", diff --git a/control-plane/subcommand/server-acl-init/command_test.go b/control-plane/subcommand/server-acl-init/command_test.go index 68ce4c1e02..3111f58820 100644 --- a/control-plane/subcommand/server-acl-init/command_test.go +++ b/control-plane/subcommand/server-acl-init/command_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package serveraclinit import ( @@ -63,6 +60,13 @@ func TestRun_FlagValidation(t *testing.T) { "-resource-prefix=prefix"}, ExpErr: "unable to read token from file \"/notexist\": open /notexist: no such file or directory", }, + { + Flags: []string{ + "-bootstrap-token-file=/notexist", + "-addresses=localhost", + "-resource-prefix=prefix"}, + ExpErr: "unable to read token from file \"/notexist\": open /notexist: no such file or directory", + }, { Flags: []string{ "-addresses=localhost", @@ -403,6 +407,7 @@ func TestRun_TokensWithProvidedBootstrapToken(t *testing.T) { for _, c := range cases { t.Run(c.TestName, func(t *testing.T) { bootToken := "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" + tokenFile := common.WriteTempFile(t, bootToken) k8s, testAgent := completeBootstrappedSetup(t, bootToken) setUpK8sServiceAccount(t, k8s, ns) @@ -412,11 +417,11 @@ func TestRun_TokensWithProvidedBootstrapToken(t *testing.T) { cmd := Command{ UI: ui, clientset: k8s, - backend: &FakeSecretsBackend{bootstrapToken: bootToken}, } cmdArgs := append([]string{ "-timeout=1m", "-k8s-namespace", ns, + "-bootstrap-token-file", tokenFile, "-addresses", strings.Split(testAgent.TestServer.HTTPAddr, ":")[0], "-http-port", strings.Split(testAgent.TestServer.HTTPAddr, ":")[1], "-grpc-port", strings.Split(testAgent.TestServer.GRPCAddr, ":")[1], @@ -910,6 +915,7 @@ func TestRun_ErrorsOnDuplicateACLPolicy(t *testing.T) { // Create Consul with ACLs already bootstrapped so that we can // then seed it with our manually created policy. bootToken := "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" + tokenFile := common.WriteTempFile(t, bootToken) k8s, testAgent := completeBootstrappedSetup(t, bootToken) setUpK8sServiceAccount(t, k8s, ns) @@ -932,11 +938,11 @@ func TestRun_ErrorsOnDuplicateACLPolicy(t *testing.T) { cmd := Command{ UI: ui, clientset: k8s, - backend: &FakeSecretsBackend{bootstrapToken: bootToken}, } cmdArgs := []string{ "-timeout=1s", "-k8s-namespace", ns, + "-bootstrap-token-file", tokenFile, "-resource-prefix=" + resourcePrefix, "-k8s-namespace=" + ns, "-addresses", strings.Split(testAgent.TestServer.HTTPAddr, ":")[0], @@ -1447,223 +1453,272 @@ func TestRun_ClientPolicyAndBindingRuleRetry(t *testing.T) { // Test if there is an old bootstrap Secret we still try to create and set // server tokens. func TestRun_AlreadyBootstrapped(t *testing.T) { - k8s := fake.NewSimpleClientset() - - type APICall struct { - Method string - Path string + t.Parallel() + cases := map[string]bool{ + "token saved in k8s secret": true, + "token provided via file": false, } - var consulAPICalls []APICall - // Start the Consul server. - consulServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Record all the API calls made. - consulAPICalls = append(consulAPICalls, APICall{ - Method: r.Method, - Path: r.URL.Path, - }) - switch r.URL.Path { - case "/v1/agent/self": - fmt.Fprintln(w, `{"Config": {"Datacenter": "dc1", "PrimaryDatacenter": "dc1"}}`) - case "/v1/acl/tokens": - fmt.Fprintln(w, `[]`) - case "/v1/acl/token": - fmt.Fprintln(w, `{}`) - case "/v1/acl/policy": - fmt.Fprintln(w, `{}`) - case "/v1/agent/token/acl_agent_token": - fmt.Fprintln(w, `{}`) - case "/v1/acl/auth-method": - fmt.Fprintln(w, `{}`) - case "/v1/acl/role/name/release-name-consul-client-acl-role": - w.WriteHeader(404) - case "/v1/acl/role": - fmt.Fprintln(w, `{}`) - case "/v1/acl/binding-rules": - fmt.Fprintln(w, `[]`) - case "/v1/acl/binding-rule": - fmt.Fprintln(w, `{}`) - default: - w.WriteHeader(500) - fmt.Fprintln(w, "Mock Server not configured for this route: "+r.URL.Path) - } - })) - defer consulServer.Close() + for name, tokenFromK8sSecret := range cases { + t.Run(name, func(t *testing.T) { + k8s := fake.NewSimpleClientset() - serverURL, err := url.Parse(consulServer.URL) - require.NoError(t, err) - port, err := strconv.Atoi(serverURL.Port()) - require.NoError(t, err) - setUpK8sServiceAccount(t, k8s, ns) + type APICall struct { + Method string + Path string + } + var consulAPICalls []APICall - cmdArgs := []string{ - "-timeout=500ms", - "-resource-prefix=" + resourcePrefix, - "-k8s-namespace=" + ns, - "-addresses=" + serverURL.Hostname(), - "-http-port=" + serverURL.Port(), - } + // Start the Consul server. + consulServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Record all the API calls made. + consulAPICalls = append(consulAPICalls, APICall{ + Method: r.Method, + Path: r.URL.Path, + }) + switch r.URL.Path { + case "/v1/agent/self": + fmt.Fprintln(w, `{"Config": {"Datacenter": "dc1", "PrimaryDatacenter": "dc1"}}`) + case "/v1/acl/tokens": + fmt.Fprintln(w, `[]`) + case "/v1/acl/token": + fmt.Fprintln(w, `{}`) + case "/v1/acl/policy": + fmt.Fprintln(w, `{}`) + case "/v1/agent/token/acl_agent_token": + fmt.Fprintln(w, `{}`) + case "/v1/acl/auth-method": + fmt.Fprintln(w, `{}`) + case "/v1/acl/role/name/release-name-consul-client-acl-role": + w.WriteHeader(404) + case "/v1/acl/role": + fmt.Fprintln(w, `{}`) + case "/v1/acl/binding-rules": + fmt.Fprintln(w, `[]`) + case "/v1/acl/binding-rule": + fmt.Fprintln(w, `{}`) + default: + w.WriteHeader(500) + fmt.Fprintln(w, "Mock Server not configured for this route: "+r.URL.Path) + } + })) + defer consulServer.Close() - // Create the bootstrap secret. - _, err = k8s.CoreV1().Secrets(ns).Create( - context.Background(), - &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: resourcePrefix + "-bootstrap-acl-token", - Labels: map[string]string{common.CLILabelKey: common.CLILabelValue}, - }, - Data: map[string][]byte{ - "token": []byte("old-token"), - }, - }, - metav1.CreateOptions{}) - require.NoError(t, err) + serverURL, err := url.Parse(consulServer.URL) + require.NoError(t, err) + port, err := strconv.Atoi(serverURL.Port()) + require.NoError(t, err) + setUpK8sServiceAccount(t, k8s, ns) - // Run the command. - ui := cli.NewMockUi() - cmd := Command{ - UI: ui, - clientset: k8s, - watcher: test.MockConnMgrForIPAndPort(serverURL.Hostname(), port), - } + cmdArgs := []string{ + "-timeout=500ms", + "-resource-prefix=" + resourcePrefix, + "-k8s-namespace=" + ns, + "-addresses=" + serverURL.Hostname(), + "-http-port=" + serverURL.Port(), + } - responseCode := cmd.Run(cmdArgs) - require.Equal(t, 0, responseCode, ui.ErrorWriter.String()) + // Create the bootstrap secret. + if tokenFromK8sSecret { + _, err = k8s.CoreV1().Secrets(ns).Create( + context.Background(), + &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourcePrefix + "-bootstrap-acl-token", + Labels: map[string]string{common.CLILabelKey: common.CLILabelValue}, + }, + Data: map[string][]byte{ + "token": []byte("old-token"), + }, + }, + metav1.CreateOptions{}) + require.NoError(t, err) + } else { + // Write token to a file. + bootTokenFile, err := os.CreateTemp("", "") + require.NoError(t, err) + defer os.RemoveAll(bootTokenFile.Name()) - // Test that the Secret is the same. - secret, err := k8s.CoreV1().Secrets(ns).Get(context.Background(), resourcePrefix+"-bootstrap-acl-token", metav1.GetOptions{}) - require.NoError(t, err) - require.Contains(t, secret.Data, "token") - require.Equal(t, "old-token", string(secret.Data["token"])) + _, err = bootTokenFile.WriteString("old-token") + require.NoError(t, err) - // Test that the expected API calls were made. - require.Equal(t, []APICall{ - // We expect calls for updating the server policy, setting server tokens, - // and updating client policy. - { - "PUT", - "/v1/acl/policy", - }, - { - "GET", - "/v1/acl/tokens", - }, - { - "PUT", - "/v1/acl/token", - }, - { - "PUT", - "/v1/agent/token/agent", - }, - { - "PUT", - "/v1/agent/token/acl_agent_token", - }, - { - "GET", - "/v1/agent/self", - }, - { - "PUT", - "/v1/acl/auth-method", - }, - { - "PUT", - "/v1/acl/policy", - }, - { - "GET", - "/v1/acl/role/name/release-name-consul-client-acl-role", - }, - { - "PUT", - "/v1/acl/role", - }, - { - "GET", - "/v1/acl/binding-rules", - }, - { - "PUT", - "/v1/acl/binding-rule", - }, - }, consulAPICalls) + require.NoError(t, err) + cmdArgs = append(cmdArgs, "-bootstrap-token-file", bootTokenFile.Name()) + } + + // Run the command. + ui := cli.NewMockUi() + cmd := Command{ + UI: ui, + clientset: k8s, + watcher: test.MockConnMgrForIPAndPort(serverURL.Hostname(), port), + } + + responseCode := cmd.Run(cmdArgs) + require.Equal(t, 0, responseCode, ui.ErrorWriter.String()) + + // Test that the Secret is the same. + if tokenFromK8sSecret { + secret, err := k8s.CoreV1().Secrets(ns).Get(context.Background(), resourcePrefix+"-bootstrap-acl-token", metav1.GetOptions{}) + require.NoError(t, err) + require.Contains(t, secret.Data, "token") + require.Equal(t, "old-token", string(secret.Data["token"])) + } + + // Test that the expected API calls were made. + require.Equal(t, []APICall{ + // We expect calls for updating the server policy, setting server tokens, + // and updating client policy. + { + "PUT", + "/v1/acl/policy", + }, + { + "GET", + "/v1/acl/tokens", + }, + { + "PUT", + "/v1/acl/token", + }, + { + "PUT", + "/v1/agent/token/agent", + }, + { + "PUT", + "/v1/agent/token/acl_agent_token", + }, + { + "GET", + "/v1/agent/self", + }, + { + "PUT", + "/v1/acl/auth-method", + }, + { + "PUT", + "/v1/acl/policy", + }, + { + "GET", + "/v1/acl/role/name/release-name-consul-client-acl-role", + }, + { + "PUT", + "/v1/acl/role", + }, + { + "GET", + "/v1/acl/binding-rules", + }, + { + "PUT", + "/v1/acl/binding-rule", + }, + }, consulAPICalls) + }) + } } // Test if there is an old bootstrap Secret and the server token exists // that we don't try and recreate the token. func TestRun_AlreadyBootstrapped_ServerTokenExists(t *testing.T) { - // First set everything up with ACLs bootstrapped. - bootToken := "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" - k8s, testAgent := completeBootstrappedSetup(t, bootToken) - setUpK8sServiceAccount(t, k8s, ns) - - cmdArgs := []string{ - "-timeout=1m", - "-k8s-namespace", ns, - "-addresses", strings.Split(testAgent.TestServer.HTTPAddr, ":")[0], - "-http-port", strings.Split(testAgent.TestServer.HTTPAddr, ":")[1], - "-grpc-port", strings.Split(testAgent.TestServer.GRPCAddr, ":")[1], - "-resource-prefix", resourcePrefix, + t.Parallel() + cases := map[string]bool{ + "token saved in k8s secret": true, + "token provided via file": false, } - _, err := k8s.CoreV1().Secrets(ns).Create(context.Background(), &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: resourcePrefix + "-bootstrap-acl-token", - }, - Data: map[string][]byte{ - "token": []byte(bootToken), - }, - }, metav1.CreateOptions{}) - require.NoError(t, err) + for name, tokenInK8sSecret := range cases { + t.Run(name, func(t *testing.T) { - consulClient, err := api.NewClient(&api.Config{ - Address: testAgent.TestServer.HTTPAddr, - Token: bootToken, - }) - require.NoError(t, err) - ui := cli.NewMockUi() - cmd := Command{ - UI: ui, - clientset: k8s, - } + // First set everything up with ACLs bootstrapped. + bootToken := "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" + k8s, testAgent := completeBootstrappedSetup(t, bootToken) + setUpK8sServiceAccount(t, k8s, ns) - cmd.init() - // Create the server policy and token _before_ we run the command. - agentPolicyRules, err := cmd.agentRules() - require.NoError(t, err) - policy, _, err := consulClient.ACL().PolicyCreate(&api.ACLPolicy{ - Name: "agent-token", - Description: "Agent Token Policy", - Rules: agentPolicyRules, - }, nil) - require.NoError(t, err) - _, _, err = consulClient.ACL().TokenCreate(&api.ACLToken{ - Description: fmt.Sprintf("Server Token for %s", strings.Split(testAgent.TestServer.HTTPAddr, ":")[0]), - Policies: []*api.ACLTokenPolicyLink{ - { - Name: policy.Name, - }, - }, - }, nil) - require.NoError(t, err) + cmdArgs := []string{ + "-timeout=1m", + "-k8s-namespace", ns, + "-addresses", strings.Split(testAgent.TestServer.HTTPAddr, ":")[0], + "-http-port", strings.Split(testAgent.TestServer.HTTPAddr, ":")[1], + "-grpc-port", strings.Split(testAgent.TestServer.GRPCAddr, ":")[1], + "-resource-prefix", resourcePrefix, + } - // Run the command. - responseCode := cmd.Run(cmdArgs) - require.Equal(t, 0, responseCode, ui.ErrorWriter.String()) + if tokenInK8sSecret { + _, err := k8s.CoreV1().Secrets(ns).Create(context.Background(), &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourcePrefix + "-bootstrap-acl-token", + }, + Data: map[string][]byte{ + "token": []byte(bootToken), + }, + }, metav1.CreateOptions{}) + require.NoError(t, err) + } else { + // Write token to a file. + bootTokenFile, err := os.CreateTemp("", "") + require.NoError(t, err) + defer os.RemoveAll(bootTokenFile.Name()) - // Check that only one server token exists, i.e. it didn't create an - // extra token. - tokens, _, err := consulClient.ACL().TokenList(nil) - require.NoError(t, err) - count := 0 - for _, token := range tokens { - if len(token.Policies) == 1 && token.Policies[0].Name == policy.Name { - count++ - } + _, err = bootTokenFile.WriteString(bootToken) + require.NoError(t, err) + + require.NoError(t, err) + cmdArgs = append(cmdArgs, "-bootstrap-token-file", bootTokenFile.Name()) + } + + consulClient, err := api.NewClient(&api.Config{ + Address: testAgent.TestServer.HTTPAddr, + Token: bootToken, + }) + require.NoError(t, err) + ui := cli.NewMockUi() + cmd := Command{ + UI: ui, + clientset: k8s, + } + + cmd.init() + // Create the server policy and token _before_ we run the command. + agentPolicyRules, err := cmd.agentRules() + require.NoError(t, err) + policy, _, err := consulClient.ACL().PolicyCreate(&api.ACLPolicy{ + Name: "agent-token", + Description: "Agent Token Policy", + Rules: agentPolicyRules, + }, nil) + require.NoError(t, err) + _, _, err = consulClient.ACL().TokenCreate(&api.ACLToken{ + Description: fmt.Sprintf("Server Token for %s", strings.Split(testAgent.TestServer.HTTPAddr, ":")[0]), + Policies: []*api.ACLTokenPolicyLink{ + { + Name: policy.Name, + }, + }, + }, nil) + require.NoError(t, err) + + // Run the command. + responseCode := cmd.Run(cmdArgs) + require.Equal(t, 0, responseCode, ui.ErrorWriter.String()) + + // Check that only one server token exists, i.e. it didn't create an + // extra token. + tokens, _, err := consulClient.ACL().TokenList(nil) + require.NoError(t, err) + count := 0 + for _, token := range tokens { + if len(token.Policies) == 1 && token.Policies[0].Name == policy.Name { + count++ + } + } + require.Equal(t, 1, count) + }) } - require.Equal(t, 1, count) } // Test if -set-server-tokens is false (i.e. servers are disabled), we skip bootstrapping of the servers @@ -1673,6 +1728,7 @@ func TestRun_SkipBootstrapping_WhenServersAreDisabled(t *testing.T) { k8s := fake.NewSimpleClientset() bootToken := "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" + tokenFile := common.WriteTempFile(t, bootToken) type APICall struct { Method string @@ -1710,7 +1766,6 @@ func TestRun_SkipBootstrapping_WhenServersAreDisabled(t *testing.T) { UI: ui, clientset: k8s, watcher: test.MockConnMgrForIPAndPort(serverURL.Hostname(), port), - backend: &FakeSecretsBackend{bootstrapToken: bootToken}, } responseCode := cmd.Run([]string{ "-timeout=500ms", @@ -1718,6 +1773,7 @@ func TestRun_SkipBootstrapping_WhenServersAreDisabled(t *testing.T) { "-k8s-namespace=" + ns, "-addresses=" + serverURL.Hostname(), "-http-port=" + serverURL.Port(), + "-bootstrap-token-file=" + tokenFile, "-set-server-tokens=false", "-client=false", // disable client token, so there are fewer calls }) diff --git a/control-plane/subcommand/server-acl-init/connect_inject.go b/control-plane/subcommand/server-acl-init/connect_inject.go index 58a36b988f..e732dae452 100644 --- a/control-plane/subcommand/server-acl-init/connect_inject.go +++ b/control-plane/subcommand/server-acl-init/connect_inject.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package serveraclinit import ( diff --git a/control-plane/subcommand/server-acl-init/connect_inject_test.go b/control-plane/subcommand/server-acl-init/connect_inject_test.go index 03e47c8ba6..e7144146b7 100644 --- a/control-plane/subcommand/server-acl-init/connect_inject_test.go +++ b/control-plane/subcommand/server-acl-init/connect_inject_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package serveraclinit import ( diff --git a/control-plane/subcommand/server-acl-init/create_or_update.go b/control-plane/subcommand/server-acl-init/create_or_update.go index 50f215eacb..af67842445 100644 --- a/control-plane/subcommand/server-acl-init/create_or_update.go +++ b/control-plane/subcommand/server-acl-init/create_or_update.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package serveraclinit import ( diff --git a/control-plane/subcommand/server-acl-init/create_or_update_test.go b/control-plane/subcommand/server-acl-init/create_or_update_test.go index 84ccdc1635..23ab46347f 100644 --- a/control-plane/subcommand/server-acl-init/create_or_update_test.go +++ b/control-plane/subcommand/server-acl-init/create_or_update_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package serveraclinit import ( diff --git a/control-plane/subcommand/server-acl-init/k8s_secrets_backend.go b/control-plane/subcommand/server-acl-init/k8s_secrets_backend.go deleted file mode 100644 index 93d9d0d2d8..0000000000 --- a/control-plane/subcommand/server-acl-init/k8s_secrets_backend.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package serveraclinit - -import ( - "context" - "fmt" - - "github.com/hashicorp/consul-k8s/control-plane/subcommand/common" - apiv1 "k8s.io/api/core/v1" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" -) - -const SecretsBackendTypeKubernetes SecretsBackendType = "kubernetes" - -type KubernetesSecretsBackend struct { - ctx context.Context - clientset kubernetes.Interface - k8sNamespace string - secretName string - secretKey string -} - -var _ SecretsBackend = (*KubernetesSecretsBackend)(nil) - -// BootstrapToken returns the existing bootstrap token if there is one by -// reading the Kubernetes Secret. If there is no bootstrap token yet, then -// it returns an empty string (not an error). -func (b *KubernetesSecretsBackend) BootstrapToken() (string, error) { - secret, err := b.clientset.CoreV1().Secrets(b.k8sNamespace).Get(b.ctx, b.secretName, metav1.GetOptions{}) - if err != nil { - if k8serrors.IsNotFound(err) { - return "", nil - } - return "", err - } - token, ok := secret.Data[b.secretKey] - if !ok { - return "", fmt.Errorf("secret %q does not have data key %q", b.secretName, b.secretKey) - } - return string(token), nil - -} - -// WriteBootstrapToken writes the given bootstrap token to the Kubernetes Secret. -func (b *KubernetesSecretsBackend) WriteBootstrapToken(bootstrapToken string) error { - secret := &apiv1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: b.secretName, - Labels: map[string]string{common.CLILabelKey: common.CLILabelValue}, - }, - Data: map[string][]byte{ - b.secretKey: []byte(bootstrapToken), - }, - } - _, err := b.clientset.CoreV1().Secrets(b.k8sNamespace).Create(b.ctx, secret, metav1.CreateOptions{}) - return err -} - -func (b *KubernetesSecretsBackend) BootstrapTokenSecretName() string { - return b.secretName -} diff --git a/control-plane/subcommand/server-acl-init/rules.go b/control-plane/subcommand/server-acl-init/rules.go index 5f65b6c75c..ee6ae41e40 100644 --- a/control-plane/subcommand/server-acl-init/rules.go +++ b/control-plane/subcommand/server-acl-init/rules.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package serveraclinit import ( @@ -154,10 +151,8 @@ partition "{{ .PartitionName }}" { operator = "write" acl = "write" {{- end }} - {{- if .EnableNamespaces }} namespace_prefix "" { - policy = "write" {{- end }} service_prefix "" { policy = "write" @@ -172,7 +167,7 @@ namespace_prefix "" { {{- if .EnablePartitions }} } {{- end }} -` + ` return c.renderRules(apiGatewayRulesTpl) } @@ -330,7 +325,6 @@ partition "{{ .PartitionName }}" { mesh = "write" acl = "write" {{- else }} - mesh = "write" operator = "write" acl = "write" {{- end }} diff --git a/control-plane/subcommand/server-acl-init/rules_test.go b/control-plane/subcommand/server-acl-init/rules_test.go index a45af33c11..22e63ed0ce 100644 --- a/control-plane/subcommand/server-acl-init/rules_test.go +++ b/control-plane/subcommand/server-acl-init/rules_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package serveraclinit import ( @@ -146,7 +143,6 @@ func TestAPIGatewayControllerRules(t *testing.T) { cases := []struct { Name string EnableNamespaces bool - Partition string Expected string }{ { @@ -169,26 +165,6 @@ acl = "write" operator = "write" acl = "write" namespace_prefix "" { - policy = "write" - service_prefix "" { - policy = "write" - intentions = "write" - } - node_prefix "" { - policy = "read" - } -}`, - }, - { - Name: "Namespaces are enabled, partitions enabled", - EnableNamespaces: true, - Partition: "Default", - Expected: ` -partition "Default" { - mesh = "write" - acl = "write" -namespace_prefix "" { - policy = "write" service_prefix "" { policy = "write" intentions = "write" @@ -196,7 +172,6 @@ namespace_prefix "" { node_prefix "" { policy = "read" } -} }`, }, } @@ -205,9 +180,7 @@ namespace_prefix "" { t.Run(tt.Name, func(t *testing.T) { cmd := Command{ flagEnableNamespaces: tt.EnableNamespaces, - consulFlags: &flags.ConsulFlags{ - Partition: tt.Partition, - }, + consulFlags: &flags.ConsulFlags{}, } meshGatewayRules, err := cmd.apiGatewayControllerRules() @@ -953,7 +926,6 @@ func TestInjectRules(t *testing.T) { EnablePartitions: false, EnablePeering: false, Expected: ` - mesh = "write" operator = "write" acl = "write" node_prefix "" { @@ -970,7 +942,6 @@ func TestInjectRules(t *testing.T) { EnablePartitions: false, EnablePeering: false, Expected: ` - mesh = "write" operator = "write" acl = "write" node_prefix "" { @@ -989,7 +960,6 @@ func TestInjectRules(t *testing.T) { EnablePartitions: false, EnablePeering: true, Expected: ` - mesh = "write" operator = "write" acl = "write" peering = "write" diff --git a/control-plane/subcommand/server-acl-init/secrets_backend.go b/control-plane/subcommand/server-acl-init/secrets_backend.go deleted file mode 100644 index e0d74462cf..0000000000 --- a/control-plane/subcommand/server-acl-init/secrets_backend.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package serveraclinit - -type SecretsBackendType string - -type SecretsBackend interface { - // BootstrapToken fetches the bootstrap token from the backend. If the - // token is not found or empty, implementations should return an empty - // string (not an error). - BootstrapToken() (string, error) - - // WriteBootstrapToken writes the given bootstrap token to the backend. - // Implementations of this method do not need to retry the write until - // successful. - WriteBootstrapToken(string) error - - // BootstrapTokenSecretName returns the name of the bootstrap token secret. - BootstrapTokenSecretName() string -} diff --git a/control-plane/subcommand/server-acl-init/servers.go b/control-plane/subcommand/server-acl-init/servers.go index c530f648e5..2dc8f8ab67 100644 --- a/control-plane/subcommand/server-acl-init/servers.go +++ b/control-plane/subcommand/server-acl-init/servers.go @@ -1,9 +1,7 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package serveraclinit import ( + "errors" "fmt" "net" "net/http" @@ -11,30 +9,29 @@ import ( "time" "github.com/hashicorp/consul/api" + apiv1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/hashicorp/consul-k8s/control-plane/consul" + "github.com/hashicorp/consul-k8s/control-plane/subcommand/common" ) // bootstrapServers bootstraps ACLs and ensures each server has an ACL token. -// If a bootstrap is found in the secrets backend, then skip ACL bootstrapping. -// Otherwise, bootstrap ACLs and write the bootstrap token to the secrets backend. -func (c *Command) bootstrapServers(serverAddresses []net.IPAddr, backend SecretsBackend) (string, error) { +// If bootstrapToken is not empty then ACLs are already bootstrapped. +func (c *Command) bootstrapServers(serverAddresses []net.IPAddr, bootstrapToken, bootTokenSecretName string) (string, error) { // Pick the first server address to connect to for bootstrapping and set up connection. firstServerAddr := fmt.Sprintf("%s:%d", serverAddresses[0].IP.String(), c.consulFlags.HTTPPort) - bootstrapToken, err := backend.BootstrapToken() - if err != nil { - return "", fmt.Errorf("Unexpected error fetching bootstrap token secret: %w", err) - } + if bootstrapToken == "" { + c.log.Info("No bootstrap token from previous installation found, continuing on to bootstrapping") - if bootstrapToken != "" { - c.log.Info("Found bootstrap token in secrets backend", "secret", backend.BootstrapTokenSecretName()) - } else { - c.log.Info("No bootstrap token found in secrets backend, continuing to ACL bootstrapping", "secret", backend.BootstrapTokenSecretName()) - bootstrapToken, err = c.bootstrapACLs(firstServerAddr, backend) + var err error + bootstrapToken, err = c.bootstrapACLs(firstServerAddr, bootTokenSecretName) if err != nil { return "", err } + } else { + c.log.Info(fmt.Sprintf("ACLs already bootstrapped - retrieved bootstrap token from Secret %q", bootTokenSecretName)) } // We should only create and set server tokens when servers are running within this cluster. @@ -50,7 +47,7 @@ func (c *Command) bootstrapServers(serverAddresses []net.IPAddr, backend Secrets // bootstrapACLs makes the ACL bootstrap API call and writes the bootstrap token // to a kube secret. -func (c *Command) bootstrapACLs(firstServerAddr string, backend SecretsBackend) (string, error) { +func (c *Command) bootstrapACLs(firstServerAddr, bootTokenSecretName string) (string, error) { config := c.consulFlags.ConsulClientConfig().APIClientConfig config.Address = firstServerAddr // Exempting this particular use of the http client from using global.consulAPITimeout @@ -81,12 +78,9 @@ func (c *Command) bootstrapACLs(firstServerAddr string, backend SecretsBackend) // Check if already bootstrapped. if strings.Contains(err.Error(), "Unexpected response code: 403") { - unrecoverableErr = fmt.Errorf( - "ACLs already bootstrapped but unable to find the bootstrap token in the secrets backend."+ - " We can't proceed without a bootstrap token."+ - " Store a token with `acl:write` permission in the secret %q.", - backend.BootstrapTokenSecretName(), - ) + unrecoverableErr = errors.New("ACLs already bootstrapped but the ACL token was not written to a Kubernetes secret." + + " We can't proceed because the bootstrap token is lost." + + " You must reset ACLs.") return nil } @@ -104,12 +98,21 @@ func (c *Command) bootstrapACLs(firstServerAddr string, backend SecretsBackend) return "", err } - // Write bootstrap token to the secrets backend. - err = c.untilSucceeds(fmt.Sprintf("writing bootstrap Secret %q", backend.BootstrapTokenSecretName()), + // Write bootstrap token to a Kubernetes secret. + err = c.untilSucceeds(fmt.Sprintf("writing bootstrap Secret %q", bootTokenSecretName), func() error { - return backend.WriteBootstrapToken(bootstrapToken) - }, - ) + secret := &apiv1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: bootTokenSecretName, + Labels: map[string]string{common.CLILabelKey: common.CLILabelValue}, + }, + Data: map[string][]byte{ + common.ACLTokenSecretKey: []byte(bootstrapToken), + }, + } + _, err := c.clientset.CoreV1().Secrets(c.flagK8sNamespace).Create(c.ctx, secret, metav1.CreateOptions{}) + return err + }) return bootstrapToken, err } diff --git a/control-plane/subcommand/server-acl-init/test_fake_secrets_backend.go b/control-plane/subcommand/server-acl-init/test_fake_secrets_backend.go deleted file mode 100644 index 87826f6c60..0000000000 --- a/control-plane/subcommand/server-acl-init/test_fake_secrets_backend.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package serveraclinit - -type FakeSecretsBackend struct { - bootstrapToken string -} - -func (b *FakeSecretsBackend) BootstrapToken() (string, error) { - return b.bootstrapToken, nil -} - -func (*FakeSecretsBackend) BootstrapTokenSecretName() string { - return "fake-bootstrap-token" -} - -func (b *FakeSecretsBackend) WriteBootstrapToken(token string) error { - b.bootstrapToken = token - return nil -} - -var _ SecretsBackend = (*FakeSecretsBackend)(nil) diff --git a/control-plane/subcommand/server-acl-init/vault_secrets_backend.go b/control-plane/subcommand/server-acl-init/vault_secrets_backend.go deleted file mode 100644 index 5a9097cca3..0000000000 --- a/control-plane/subcommand/server-acl-init/vault_secrets_backend.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package serveraclinit - -import ( - "fmt" - - "github.com/hashicorp/vault/api" -) - -const SecretsBackendTypeVault SecretsBackendType = "vault" - -type VaultSecretsBackend struct { - vaultClient *api.Client - secretName string - secretKey string -} - -var _ SecretsBackend = (*VaultSecretsBackend)(nil) - -// BootstrapToken returns the bootstrap token stored in Vault. -// If not found this returns an empty string (not an error). -func (b *VaultSecretsBackend) BootstrapToken() (string, error) { - secret, err := b.vaultClient.Logical().Read(b.secretName) - if err != nil { - return "", err - } - if secret == nil || secret.Data == nil { - // secret not found or empty. - return "", nil - } - // Grab secret.Data["data"][secretKey]. - dataRaw, found := secret.Data["data"] - if !found { - return "", nil - } - data, ok := dataRaw.(map[string]interface{}) - if !ok { - return "", nil - } - tokRaw, found := data[b.secretKey] - if !found { - return "", nil - } - if tok, ok := tokRaw.(string); ok { - return tok, nil - } - return "", fmt.Errorf("Unexpected data. To resolve this, "+ - "`vault kv put %[1]s=` if Consul is already ACL bootstrapped. "+ - "If not ACL bootstrapped, `vault kv put %[1]s=\"\"`", b.secretKey, b.secretKey) -} - -// BootstrapTokenSecretName returns the name of the bootstrap token secret. -func (b *VaultSecretsBackend) BootstrapTokenSecretName() string { - return b.secretName -} - -// WriteBootstrapToken writes the bootstrap token to Vault. -func (b *VaultSecretsBackend) WriteBootstrapToken(bootstrapToken string) error { - _, err := b.vaultClient.Logical().Write(b.secretName, - map[string]interface{}{ - "data": map[string]interface{}{ - b.secretKey: bootstrapToken, - }, - }, - ) - return err -} diff --git a/control-plane/subcommand/sync-catalog/command.go b/control-plane/subcommand/sync-catalog/command.go index 2dadf6e039..e146a76667 100644 --- a/control-plane/subcommand/sync-catalog/command.go +++ b/control-plane/subcommand/sync-catalog/command.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package synccatalog import ( diff --git a/control-plane/subcommand/sync-catalog/command_ent_test.go b/control-plane/subcommand/sync-catalog/command_ent_test.go index fb6c6c4347..fac330c557 100644 --- a/control-plane/subcommand/sync-catalog/command_ent_test.go +++ b/control-plane/subcommand/sync-catalog/command_ent_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - //go:build enterprise package synccatalog diff --git a/control-plane/subcommand/sync-catalog/command_test.go b/control-plane/subcommand/sync-catalog/command_test.go index 0223931cc1..8228986d00 100644 --- a/control-plane/subcommand/sync-catalog/command_test.go +++ b/control-plane/subcommand/sync-catalog/command_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package synccatalog import ( diff --git a/control-plane/subcommand/tls-init/command.go b/control-plane/subcommand/tls-init/command.go index c2498a3125..354a26fdc5 100644 --- a/control-plane/subcommand/tls-init/command.go +++ b/control-plane/subcommand/tls-init/command.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package tls_init import ( diff --git a/control-plane/subcommand/tls-init/command_test.go b/control-plane/subcommand/tls-init/command_test.go index 81f5893543..ae3cbd8982 100644 --- a/control-plane/subcommand/tls-init/command_test.go +++ b/control-plane/subcommand/tls-init/command_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package tls_init import ( diff --git a/control-plane/subcommand/version/command.go b/control-plane/subcommand/version/command.go index 1f6b2aed01..58768a1f92 100644 --- a/control-plane/subcommand/version/command.go +++ b/control-plane/subcommand/version/command.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package version import ( diff --git a/control-plane/subcommand/webhook-cert-manager/command.go b/control-plane/subcommand/webhook-cert-manager/command.go index 4d85565b62..214a1d41e2 100644 --- a/control-plane/subcommand/webhook-cert-manager/command.go +++ b/control-plane/subcommand/webhook-cert-manager/command.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package webhookcertmanager import ( diff --git a/control-plane/subcommand/webhook-cert-manager/command_test.go b/control-plane/subcommand/webhook-cert-manager/command_test.go index 31c98b0ebe..7e302d5261 100644 --- a/control-plane/subcommand/webhook-cert-manager/command_test.go +++ b/control-plane/subcommand/webhook-cert-manager/command_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package webhookcertmanager import ( diff --git a/control-plane/subcommand/webhook-cert-manager/mocks/mocks.go b/control-plane/subcommand/webhook-cert-manager/mocks/mocks.go index 5efa09eb29..d700b192ac 100644 --- a/control-plane/subcommand/webhook-cert-manager/mocks/mocks.go +++ b/control-plane/subcommand/webhook-cert-manager/mocks/mocks.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package mocks import ( diff --git a/control-plane/version/fips_build.go b/control-plane/version/fips_build.go deleted file mode 100644 index 63e0e68883..0000000000 --- a/control-plane/version/fips_build.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -//go:build fips - -package version - -// This validates during compilation that we are being built with a FIPS enabled go toolchain -import ( - _ "crypto/tls/fipsonly" - "runtime" - "strings" -) - -// IsFIPS returns true if consul-k8s is operating in FIPS-140-2 mode. -func IsFIPS() bool { - return true -} - -func GetFIPSInfo() string { - str := "Enabled" - // Try to get the crypto module name - gover := strings.Split(runtime.Version(), "X:") - if len(gover) >= 2 { - gover_last := gover[len(gover)-1] - // Able to find crypto module name; add that to status string. - str = "FIPS 140-2 Enabled, crypto module " + gover_last - } - return str -} diff --git a/control-plane/version/non_fips_build.go b/control-plane/version/non_fips_build.go deleted file mode 100644 index ce99575d2c..0000000000 --- a/control-plane/version/non_fips_build.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -//go:build !fips - -package version - -// IsFIPS returns true if consul-k8s is operating in FIPS-140-2 mode. -func IsFIPS() bool { - return false -} - -func GetFIPSInfo() string { - return "" -} diff --git a/control-plane/version/version.go b/control-plane/version/version.go index 952cd9ca81..c17eb5d89e 100644 --- a/control-plane/version/version.go +++ b/control-plane/version/version.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package version import ( @@ -17,7 +14,7 @@ var ( // // Version must conform to the format expected by // github.com/hashicorp/go-version for tests to work. - Version = "1.3.0" + Version = "1.0.10" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release @@ -39,12 +36,8 @@ func GetHumanVersion() string { release = "dev" } - if IsFIPS() { - version += "+fips1402" - } - if release != "" { - if !strings.Contains(version, "-"+release) { + if !strings.HasSuffix(version, "-"+release) { // if we tagged a prerelease version then the release is in the version already version += fmt.Sprintf("-%s", release) } diff --git a/docs/admin-partitions-with-acls.md b/docs/admin-partitions-with-acls.md new file mode 100644 index 0000000000..fb282fa38d --- /dev/null +++ b/docs/admin-partitions-with-acls.md @@ -0,0 +1,98 @@ +## Installing Admin Partitions with ACLs enabled + +To enable ACLs on the server cluster use the following config: +```yaml +global: + enableConsulNamespaces: true + tls: + enabled: true + image: hashicorp/consul-enterprise:1.11.1 + adminPartitions: + enabled: true + acls: + manageSystemACLs: true +server: + exposeGossipAndRPCPorts: true + enterpriseLicense: + secretName: license + secretKey: key + replicas: 1 +connectInject: + enabled: true + transparentProxy: + defaultEnabled: false + consulNamespaces: + mirroringK8S: true +controller: + enabled: true +meshGateway: + enabled: true +``` + +Identify the LoadBalancer External IP of the `partition-service` +```bash +kubectl get svc consul-consul-partition-service -o json | jq -r '.status.loadBalancer.ingress[0].ip' +``` + +Migrate the TLS CA credentials from the server cluster to the workload clusters +```bash +kubectl get secret consul-consul-ca-key --context "server-context" -o json | kubectl apply --context "workload-context" -f - +kubectl get secret consul-consul-ca-cert --context "server-context" -o json | kubectl apply --context "workload-context" -f - +``` + +Migrate the Partition token from the server cluster to the workload clusters +```bash +kubectl get secret consul-consul-partitions-acl-token --context "server-context" -o json | kubectl apply --context "workload-context" -f - +``` + +Identify the Kubernetes AuthMethod URL of the workload cluster to use as the `k8sAuthMethodHost`: +```bash +kubectl config view -o "jsonpath={.clusters[?(@.name=='workload-cluster-name')].cluster.server}" +``` + +Configure the workload cluster using the following: + +```yaml +global: + enabled: false + enableConsulNamespaces: true + image: hashicorp/consul-enterprise:1.11.1 + adminPartitions: + enabled: true + name: "partition-name" + tls: + enabled: true + caCert: + secretName: consul-consul-ca-cert + secretKey: tls.crt + caKey: + secretName: consul-consul-ca-key + secretKey: tls.key + acls: + manageSystemACLs: true + bootstrapToken: + secretName: consul-consul-partitions-acl-token + secretKey: token +server: + enterpriseLicense: + secretName: license + secretKey: key +externalServers: + enabled: true + hosts: [ "loadbalancer IP" ] + tlsServerName: server.dc1.consul + k8sAuthMethodHost: "authmethod-host IP" +client: + enabled: true + exposeGossipPorts: true + join: [ "loadbalancer IP" ] +connectInject: + enabled: true + consulNamespaces: + mirroringK8S: true +controller: + enabled: true +meshGateway: + enabled: true +``` +This should create clusters that have Admin Partitions deployed on them with ACLs enabled. diff --git a/hack/aws-acceptance-test-cleanup/go.mod b/hack/aws-acceptance-test-cleanup/go.mod index ac4f7b0d1a..13e8f48909 100644 --- a/hack/aws-acceptance-test-cleanup/go.mod +++ b/hack/aws-acceptance-test-cleanup/go.mod @@ -1,6 +1,6 @@ module github.com/hashicorp/consul-helm/hack/aws-acceptance-test-cleanup -go 1.20 +go 1.19 require ( github.com/aws/aws-sdk-go v1.38.63 diff --git a/hack/aws-acceptance-test-cleanup/main.go b/hack/aws-acceptance-test-cleanup/main.go index e4094ec47e..b72b88c12f 100644 --- a/hack/aws-acceptance-test-cleanup/main.go +++ b/hack/aws-acceptance-test-cleanup/main.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package main // This script deletes AWS resources created for acceptance tests that have @@ -25,7 +22,6 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/eks" "github.com/aws/aws-sdk-go/service/elb" - "github.com/aws/aws-sdk-go/service/iam" "github.com/cenkalti/backoff/v4" ) @@ -39,11 +35,6 @@ var ( errNotDestroyed = errors.New("not yet destroyed") ) -type oidc struct { - arn string - buildUrl string -} - func main() { flag.BoolVar(&flagAutoApprove, "auto-approve", false, "Skip interactive approval before destroying.") flag.Parse() @@ -74,106 +65,6 @@ func realMain(ctx context.Context) error { eksClient := eks.New(clientSession, awsCfg) ec2Client := ec2.New(clientSession, awsCfg) elbClient := elb.New(clientSession, awsCfg) - iamClient := iam.New(clientSession, awsCfg) - - // Find OIDC providers to delete. - oidcProvidersOutput, err := iamClient.ListOpenIDConnectProvidersWithContext(ctx, &iam.ListOpenIDConnectProvidersInput{}) - if err != nil { - return err - } else if oidcProvidersOutput == nil { - return fmt.Errorf("nil output for OIDC Providers") - } - - toDeleteOidcArns := []*oidc{} - for _, providerEntry := range oidcProvidersOutput.OpenIDConnectProviderList { - arnString := "" - if providerEntry.Arn != nil { - arnString = *providerEntry.Arn - } - // Check if it's older than 8 hours. - older, err := oidcOlderThanEightHours(ctx, iamClient, providerEntry.Arn) - if err != nil { - return err - } - // Only add to delete list if it's older than 8 hours and has a buildURL tag. - if older { - output, err := iamClient.ListOpenIDConnectProviderTags(&iam.ListOpenIDConnectProviderTagsInput{OpenIDConnectProviderArn: providerEntry.Arn}) - if err != nil { - return err - } - for _, tag := range output.Tags { - if tag.Key != nil && *tag.Key == buildURLTag { - var buildUrl string - if tag.Value != nil { - buildUrl = *tag.Value - } - toDeleteOidcArns = append(toDeleteOidcArns, &oidc{arn: arnString, buildUrl: buildUrl}) - } - } - } else { - fmt.Printf("Skipping OIDC provider: %s because it's not over 8 hours old\n", arnString) - } - } - - oidcProvidersExist := true - if len(toDeleteOidcArns) == 0 { - fmt.Println("Found no OIDC Providers to clean up") - oidcProvidersExist = false - } else { - // Print out the OIDC Provider arns and the build tags. - var oidcPrint string - for _, oidcProvider := range toDeleteOidcArns { - oidcPrint += fmt.Sprintf("- %s (%s)\n", oidcProvider.arn, oidcProvider.buildUrl) - } - - fmt.Printf("Found OIDC Providers:\n%s", oidcPrint) - } - - // Check for approval. - if !flagAutoApprove && oidcProvidersExist { - type input struct { - text string - err error - } - inputCh := make(chan input) - - // Read input in a goroutine so we can also exit if we get a Ctrl-C - // (see select{} below). - go func() { - reader := bufio.NewReader(os.Stdin) - fmt.Println("\nDo you want to delete these OIDC Providers (y/n)?") - inputStr, err := reader.ReadString('\n') - if err != nil { - inputCh <- input{err: err} - return - } - inputCh <- input{text: inputStr} - }() - - select { - case in := <-inputCh: - if in.err != nil { - return in.err - } - inputTrimmed := strings.TrimSpace(in.text) - if inputTrimmed != "y" && inputTrimmed != "yes" { - return errors.New("exiting after negative") - } - case <-ctx.Done(): - return errors.New("context cancelled") - } - } - - // Actually delete the OIDC providers. - for _, oidcArn := range toDeleteOidcArns { - fmt.Printf("Deleting OIDC provider: %s\n", oidcArn.arn) - _, err := iamClient.DeleteOpenIDConnectProviderWithContext(ctx, &iam.DeleteOpenIDConnectProviderInput{ - OpenIDConnectProviderArn: &oidcArn.arn, - }) - if err != nil { - return err - } - } // Find VPCs to delete. Most resources we create belong to a VPC, except // for IAM resources, and so if there are no VPCs, that means all leftover resources have been deleted. @@ -643,25 +534,6 @@ func realMain(ctx context.Context) error { return nil } -// oidcOlderThanEightHours checks if the oidc provider is older than 8 hours. -func oidcOlderThanEightHours(ctx context.Context, iamClient *iam.IAM, oidcArn *string) (bool, error) { - fullOidc, err := iamClient.GetOpenIDConnectProviderWithContext(ctx, &iam.GetOpenIDConnectProviderInput{ - OpenIDConnectProviderArn: oidcArn, - }) - if err != nil { - return false, err - } - if fullOidc != nil { - if fullOidc.CreateDate != nil { - d := time.Since(*fullOidc.CreateDate) - if d.Hours() > 8 { - return true, nil - } - } - } - return false, nil -} - func vpcNameAndBuildURL(vpc *ec2.Vpc) (string, string) { var vpcName string var buildURL string diff --git a/hack/copy-crds-to-chart/go.mod b/hack/copy-crds-to-chart/go.mod index c224f8f244..73b1f10306 100644 --- a/hack/copy-crds-to-chart/go.mod +++ b/hack/copy-crds-to-chart/go.mod @@ -1,3 +1,3 @@ module github.com/hashicorp/consul-k8s/hack/copy-crds-to-chart -go 1.20 +go 1.19 diff --git a/hack/copy-crds-to-chart/main.go b/hack/copy-crds-to-chart/main.go index 149126b392..7276c468f8 100644 --- a/hack/copy-crds-to-chart/main.go +++ b/hack/copy-crds-to-chart/main.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - // Script to copy generated CRD yaml into chart directory and modify it to match // the expected chart format (e.g. formatted YAML). package main @@ -12,14 +9,6 @@ import ( "strings" ) -var ( - // HACK IT! - requiresPeering = map[string]struct{}{ - "consul.hashicorp.com_peeringacceptors.yaml": {}, - "consul.hashicorp.com_peeringdialers.yaml": {}, - } -) - func main() { if len(os.Args) != 1 { fmt.Println("Usage: go run ./...") @@ -34,82 +23,54 @@ func main() { } func realMain(helmPath string) error { - root := "../../control-plane/config/crd/" - dirs := []string{"bases", "external"} - - for _, dir := range dirs { - err := filepath.Walk(root+dir, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - - if info.IsDir() || filepath.Ext(path) != ".yaml" || filepath.Base(path) == "kustomization.yaml" { - return nil - } - - printf("processing %s", filepath.Base(path)) - - contentBytes, err := os.ReadFile(path) - if err != nil { - return err - } - contents := string(contentBytes) - - // Strip leading newline. - contents = strings.TrimPrefix(contents, "\n") - - if _, ok := requiresPeering[info.Name()]; ok { - // Add {{- if and .Values.connectInject.enabled .Values.global.peering.enabled }} {{- end }} wrapper. - contents = fmt.Sprintf("{{- if and .Values.connectInject.enabled .Values.global.peering.enabled }}\n%s{{- end }}\n", contents) - } else if dir == "external" { - contents = fmt.Sprintf("{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }}\n%s{{- end }}\n", contents) - } else { - // Add {{- if .Values.connectInject.enabled }} {{- end }} wrapper. - contents = fmt.Sprintf("{{- if .Values.connectInject.enabled }}\n%s{{- end }}\n", contents) - } + return filepath.Walk("../../control-plane/config/crd/bases", func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } - // Add labels, this is hacky because we're relying on the line number - // but it means we don't need to regex or yaml parse. - splitOnNewlines := strings.Split(contents, "\n") - labelLines := []string{ - ` labels:`, - ` app: {{ template "consul.name" . }}`, - ` chart: {{ template "consul.chart" . }}`, - ` heritage: {{ .Release.Service }}`, - ` release: {{ .Release.Name }}`, - ` component: crd`, - } - withLabels := append(splitOnNewlines[0:9], append(labelLines, splitOnNewlines[9:]...)...) - contents = strings.Join(withLabels, "\n") + if info.IsDir() || filepath.Ext(path) != ".yaml" { + return nil + } - var crdName string - if dir == "bases" { - // Construct the destination filename. - filenameSplit := strings.Split(info.Name(), "_") - crdName = filenameSplit[1] - } else if dir == "external" { - filenameSplit := strings.Split(info.Name(), ".") - crdName = filenameSplit[0] + ".yaml" - } + printf("processing %s", filepath.Base(path)) - destinationPath := filepath.Join(helmPath, "templates", fmt.Sprintf("crd-%s", crdName)) - // Write it. - printf("writing to %s", destinationPath) - return os.WriteFile(destinationPath, []byte(contents), 0644) - }) + contentBytes, err := os.ReadFile(path) if err != nil { return err } - } - return nil + contents := string(contentBytes) + + // Strip leading newline. + contents = strings.TrimPrefix(contents, "\n") + + // Add {{- if .Values.connectInject.enabled }} {{- end }} wrapper. + contents = fmt.Sprintf("{{- if .Values.connectInject.enabled }}\n%s{{- end }}\n", contents) + + // Add labels, this is hacky because we're relying on the line number + // but it means we don't need to regex or yaml parse. + splitOnNewlines := strings.Split(contents, "\n") + labelLines := []string{ + ` labels:`, + ` app: {{ template "consul.name" . }}`, + ` chart: {{ template "consul.chart" . }}`, + ` heritage: {{ .Release.Service }}`, + ` release: {{ .Release.Name }}`, + ` component: crd`, + } + withLabels := append(splitOnNewlines[0:9], append(labelLines, splitOnNewlines[9:]...)...) + contents = strings.Join(withLabels, "\n") + + // Construct the destination filename. + filenameSplit := strings.Split(info.Name(), "_") + crdName := filenameSplit[1] + destinationPath := filepath.Join(helmPath, "templates", fmt.Sprintf("crd-%s", crdName)) + + // Write it. + printf("writing to %s", destinationPath) + return os.WriteFile(destinationPath, []byte(contents), 0644) + }) } func printf(format string, args ...interface{}) { fmt.Println(fmt.Sprintf(format, args...)) } - -func formatCRDName(name string) string { - name = strings.TrimSuffix(name, ".yaml") - segments := strings.Split(name, "_") - return fmt.Sprintf("%s.%s.yaml", segments[1], segments[0]) -} diff --git a/hack/helm-reference-gen/doc_node.go b/hack/helm-reference-gen/doc_node.go index cf28cf9374..a183ead969 100644 --- a/hack/helm-reference-gen/doc_node.go +++ b/hack/helm-reference-gen/doc_node.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package main import ( diff --git a/hack/helm-reference-gen/fixtures/full-values.yaml b/hack/helm-reference-gen/fixtures/full-values.yaml index a79fbc6944..b0eec07666 100644 --- a/hack/helm-reference-gen/fixtures/full-values.yaml +++ b/hack/helm-reference-gen/fixtures/full-values.yaml @@ -1,6 +1,3 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - # Available parameters and their default values for the Consul chart. # Holds values that affect multiple components of the chart. diff --git a/hack/helm-reference-gen/go.mod b/hack/helm-reference-gen/go.mod index 36e1ff3a8d..7e41675f18 100644 --- a/hack/helm-reference-gen/go.mod +++ b/hack/helm-reference-gen/go.mod @@ -1,6 +1,6 @@ module github.com/hashicorp/consul-k8s/hack/helm-reference-gen -go 1.20 +go 1.19 require ( github.com/stretchr/testify v1.6.1 diff --git a/hack/helm-reference-gen/main.go b/hack/helm-reference-gen/main.go index 0bc623cff0..5f23143caa 100644 --- a/hack/helm-reference-gen/main.go +++ b/hack/helm-reference-gen/main.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package main // This script generates markdown documentation out of the values.yaml file @@ -162,14 +159,11 @@ func GenerateDocs(yamlStr string) (string, error) { return "", err } - docsStr := strings.Join(children, "\n\n") - docsStr = strings.ReplaceAll(docsStr, "[Enterprise Only]", "") - // Remove https://developer.hashicorp.com prefix from links because docs linting requires it. - docsStr = strings.ReplaceAll(docsStr, "https://developer.hashicorp.com/", "/") + enterpriseSubst := strings.ReplaceAll(strings.Join(children, "\n\n"), "[Enterprise Only]", "") // Add table of contents. toc := generateTOC(node) - return toc + "\n\n" + docsStr + "\n", nil + return toc + "\n\n" + enterpriseSubst + "\n", nil } // Parse parses yamlStr into a tree of DocNode's. diff --git a/hack/helm-reference-gen/main_test.go b/hack/helm-reference-gen/main_test.go index fc91032203..03e8648218 100644 --- a/hack/helm-reference-gen/main_test.go +++ b/hack/helm-reference-gen/main_test.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package main import ( diff --git a/hack/helm-reference-gen/parse_error.go b/hack/helm-reference-gen/parse_error.go index 914737db5f..f474664f1f 100644 --- a/hack/helm-reference-gen/parse_error.go +++ b/hack/helm-reference-gen/parse_error.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package main import "fmt" From 4713bbf59b87db6d5da3771e3c9ee0e0823b5702 Mon Sep 17 00:00:00 2001 From: Derek Menteer Date: Tue, 22 Aug 2023 13:58:07 +0000 Subject: [PATCH 340/340] backport of commit 58b159ab53f14131d3b70b75760130b8920ecc77 --- .changelog/2808.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/2808.txt diff --git a/.changelog/2808.txt b/.changelog/2808.txt new file mode 100644 index 0000000000..d6d0270e44 --- /dev/null +++ b/.changelog/2808.txt @@ -0,0 +1,3 @@ +```release-note:bug +control-plane: Fix issue where ACL tokens would have an empty pod name that prevented proper token cleanup. +```